0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-03 04:49:03 -05:00

Improved performance for hover shapes

This commit is contained in:
alonso.torres 2024-01-23 15:39:37 +01:00 committed by Andrey Antukh
parent 4f09688af7
commit 9e24ba7b39
2 changed files with 105 additions and 81 deletions

View file

@ -176,11 +176,11 @@
(->> (get-root-shapes objects) (->> (get-root-shapes objects)
(mapv :id))) (mapv :id)))
(defn get-base (defn- get-base
[objects id-a id-b] [id-a id-b id-parents]
(let [[parents-a parents-a-index] (cfh/get-parent-ids-with-index objects id-a) (let [[parents-a parents-a-index] (get id-parents id-a)
[parents-b parents-b-index] (cfh/get-parent-ids-with-index objects id-b) [parents-b parents-b-index] (get id-parents id-b)
parents-a (cons id-a parents-a) parents-a (cons id-a parents-a)
parents-b (into #{id-b} parents-b) parents-b (into #{id-b} parents-b)
@ -194,9 +194,9 @@
[base-id idx-a idx-b])) [base-id idx-a idx-b]))
(defn- is-shape-over-shape? (defn- is-shape-over-shape?
[objects base-shape-id over-shape-id bottom-frames?] [objects base-shape-id over-shape-id bottom-frames? id-parents]
(let [[base-id index-a index-b] (get-base objects base-shape-id over-shape-id)] (let [[base-id index-a index-b] (get-base base-shape-id over-shape-id id-parents)]
(cond (cond
;; The base the base shape, so the other item is below (if not bottom-frames) ;; The base the base shape, so the other item is below (if not bottom-frames)
(= base-id base-shape-id) (= base-id base-shape-id)
@ -234,33 +234,37 @@
([objects ids {:keys [bottom-frames?] :as options ([objects ids {:keys [bottom-frames?] :as options
:or {bottom-frames? false}}] :or {bottom-frames? false}}]
(letfn [(comp [id-a id-b] ;; Create an index of the parents of the shapes. This will speed the sorting because we use
(cond ;; this information down the line.
(= id-a id-b) (let [id-parents (into {} (map #(vector % (cfh/get-parent-ids-with-index objects %))) ids)]
0 (letfn [(comp [id-a id-b]
(cond
(= id-a id-b)
0
(is-shape-over-shape? objects id-a id-b bottom-frames?) (is-shape-over-shape? objects id-a id-b bottom-frames? id-parents)
1 1
:else :else
-1))] -1))]
(sort comp ids)))) (sort comp ids)))))
(defn sort-z-index-objects (defn sort-z-index-objects
([objects items] ([objects items]
(sort-z-index-objects objects items nil)) (sort-z-index-objects objects items nil))
([objects items {:keys [bottom-frames?] ([objects items {:keys [bottom-frames?]
:or {bottom-frames? false}}] :or {bottom-frames? false}}]
(d/unstable-sort (let [id-parents (into {} (map #(vector (dm/get-prop % :id) (cfh/get-parent-ids-with-index objects (dm/get-prop % :id)))) items)]
(fn [obj-a obj-b] (d/unstable-sort
(let [id-a (dm/get-prop obj-a :id) (fn [obj-a obj-b]
id-b (dm/get-prop obj-b :id)] (let [id-a (dm/get-prop obj-a :id)
(if (= id-a id-b) id-b (dm/get-prop obj-b :id)]
0 (if (= id-a id-b)
(if ^boolean (is-shape-over-shape? objects id-a id-b bottom-frames?) 0
1 (if ^boolean (is-shape-over-shape? objects id-a id-b bottom-frames? id-parents)
-1)))) 1
items))) -1))))
items))))
(defn get-frame-by-position (defn get-frame-by-position
([objects position] ([objects position]

View file

@ -159,7 +159,6 @@
[group-id objects hover-ids] [group-id objects hover-ids]
(and (contains? #{:group :bool} (get-in objects [group-id :type])) (and (contains? #{:group :bool} (get-in objects [group-id :type]))
;; If there are no children in the hover-ids we're in the empty side ;; If there are no children in the hover-ids we're in the empty side
(->> hover-ids (->> hover-ids
(remove #(contains? #{:group :bool} (get-in objects [% :type]))) (remove #(contains? #{:group :bool} (get-in objects [% :type])))
@ -253,77 +252,98 @@
(fn [_] (fn [_]
(reset! hover-top-frame-id (ctt/top-nested-frame objects (deref last-point-ref))))) (reset! hover-top-frame-id (ctt/top-nested-frame objects (deref last-point-ref)))))
(hooks/use-stream ;; This ref is a cache of sorted ids. Sorting is expensive so we save the list
over-shapes-stream (let [sorted-ids-cache (mf/use-ref {})]
(mf/deps page-id objects show-measures?) (hooks/use-stream
(fn [ids] over-shapes-stream
(let [selected (mf/ref-val selected-ref) (mf/deps page-id objects show-measures?)
focus (mf/ref-val focus-ref) (fn [ids]
mod? (mf/ref-val mod-ref) (let [selected (mf/ref-val selected-ref)
focus (mf/ref-val focus-ref)
mod? (mf/ref-val mod-ref)
cached-ids (mf/ref-val sorted-ids-cache)
ids (into (d/ordered-set) make-sorted-ids
(remove #(dm/get-in objects [% :blocked])) (fn [mod? ids]
(ctt/sort-z-index objects ids {:bottom-frames? mod?})) (let [sorted-ids
(into (d/ordered-set)
(comp (remove #(dm/get-in objects [% :blocked]))
(remove (partial cfh/svg-raw-shape? objects)))
(ctt/sort-z-index objects ids {:bottom-frames? mod?}))]
(mf/set-ref-val! sorted-ids-cache (assoc cached-ids [mod? ids] sorted-ids))
sorted-ids))
grouped? (fn [id] ids (or (get cached-ids [mod? ids]) (make-sorted-ids mod? ids))
(and (cfh/group-shape? objects id)
(not (cfh/mask-shape? objects id))))
selected-with-parents grouped?
(into #{} (mapcat #(cfh/get-parent-ids objects %)) selected) (fn [id]
(and (cfh/group-shape? objects id)
(not (cfh/mask-shape? objects id))))
root-frame-with-data? selected-with-parents
#(as-> (get objects %) obj (into #{} (mapcat #(cfh/get-parent-ids objects %)) selected)
(and (cfh/root-frame? obj)
(d/not-empty? (:shapes obj))
(not (ctk/instance-head? obj))
(not (ctk/main-instance? obj))))
;; Set with the elements to remove from the hover list root-frame-with-data?
remove-id-xf #(as-> (get objects %) obj
(cond (and (cfh/root-frame? obj)
mod? (d/not-empty? (:shapes obj))
(filter grouped?) (not (ctk/instance-head? obj))
(not (ctk/main-instance? obj))))
(not mod?) ;; Set with the elements to remove from the hover list
(filter #(or (root-frame-with-data? %) remove-id-xf
(group-empty-space? % objects ids)))) (cond
mod?
(filter grouped?)
remove-id? (not mod?)
(into selected-with-parents remove-id-xf ids) (let [child-parent?
(into #{}
(comp (remove #(cfh/group-like-shape? objects %))
(mapcat #(cfh/get-parent-ids objects %)))
ids)]
(filter #(or (root-frame-with-data? %)
(and (contains? #{:group :bool} (dm/get-in objects [% :type]))
(not (contains? child-parent? %)))))))
no-fill-nested-frames? remove-id?
(fn [id] (into selected-with-parents remove-id-xf ids)
(let [shape (get objects id)]
(and (cfh/frame-shape? shape)
(not (cfh/is-direct-child-of-root? shape))
(empty? (get shape :fills)))))
hover-shape no-fill-nested-frames?
(->> ids (fn [id]
(remove remove-id?) (let [shape (get objects id)]
(remove (partial cfh/hidden-parent? objects)) (and (cfh/frame-shape? shape)
(remove (partial cfh/svg-raw-shape? objects)) (not (cfh/is-direct-child-of-root? shape))
(remove #(and mod? (no-fill-nested-frames? %))) (empty? (get shape :fills)))))
(filter #(or (empty? focus) (cpf/is-in-focus? objects focus %)))
(first)
(get objects))
;; We keep track of a diferent shape for measures hover-shape
measure-hover-shape
(when show-measures?
(->> ids (->> ids
(remove #(group-empty-space? % objects ids)) (remove remove-id?)
(remove (partial cfh/hidden-parent? objects)) (remove (partial cfh/hidden-parent? objects))
(remove (partial cfh/svg-raw-shape? objects))
(remove #(and mod? (no-fill-nested-frames? %))) (remove #(and mod? (no-fill-nested-frames? %)))
(filter #(or (empty? focus) (cpf/is-in-focus? objects focus %))) (filter #(or (empty? focus) (cpf/is-in-focus? objects focus %)))
(first) (first)
(get objects)))] (get objects))
(reset! hover hover-shape) ;; We keep track of a diferent shape for measures
(reset! measure-hover measure-hover-shape) measure-hover-shape
(reset! hover-ids ids)))))) (when show-measures?
(->> ids
(remove #(group-empty-space? % objects ids))
(remove (partial cfh/hidden-parent? objects))
(remove #(and mod? (no-fill-nested-frames? %)))
(filter #(or (empty? focus) (cpf/is-in-focus? objects focus %)))
(first)
(get objects)))]
(reset! hover hover-shape)
(reset! measure-hover measure-hover-shape)
(reset! hover-ids ids)))
(fn []
;; Clean the cache
(mf/set-ref-val! sorted-ids-cache {}))))))
(defn setup-viewport-modifiers (defn setup-viewport-modifiers
[modifiers objects] [modifiers objects]