0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-13 18:48:37 -05:00

Detect movements inside a component and not override them

This commit is contained in:
Andrés Moya 2021-06-10 11:04:11 +02:00 committed by Alonso Torres
parent ad4115acc8
commit 71759386c5
4 changed files with 107 additions and 48 deletions

View file

@ -457,13 +457,18 @@
kw (if (keyword? kw) (name kw) kw)]
(keyword (str prefix kw))))
(defn tap
"Simpilar to the tap in rxjs but for plain collections"
[f coll]
(f coll)
coll)
(defn tap-r
"Same but with args reversed, for -> threads"
[coll f]
(f coll)
coll)
(defn map-diff
"Given two maps returns the diff of its attributes in a map where
the keys will be the attributes that change and the values the previous

View file

@ -408,20 +408,22 @@
(let [attr (:attr op)
val (:val op)
ignore (:ignore-touched op)
ignore-geometry (:ignore-geometry op)
shape-ref (:shape-ref shape)
group (get component-sync-attrs attr)
root-name? (and (= group :name-group)
(:component-root? shape))]
(cond-> shape
;; Depending on the origin of the attribute change, we need or not to
;; set the "touched" flag for the group the attribute belongs to.
;; In some cases we need to ignore touched only if the attribute is
;; geometric (position, width or transformation).
(and shape-ref group (not ignore) (not= val (get shape attr))
(not root-name?)
;; FIXME: it's difficult to tell if the geometry changes affect
;; an individual shape inside the component, or are for
;; the whole component (in which case we shouldn't set
;; touched). For the moment we disable geometry touched
;; except width and height that seems to work well.
(or (not= group :geometry-group) (#{:width :height} attr)))
(not (and ignore-geometry
(and (= group :geometry-group)
(not (#{:width :height} attr))))))
(->
(update :touched cph/set-touched-group group)
(dissoc :remote-synced?))

View file

@ -34,26 +34,26 @@
(defn- generate-operation
"Given an object old and new versions and an attribute will append into changes
the set and undo operations"
[changes attr old new]
[changes attr old new ignore-geometry?]
(let [old-val (get old attr)
new-val (get new attr)]
(if (= old-val new-val)
changes
(-> changes
(update :rops conj {:type :set :attr attr :val new-val})
(update :rops conj {:type :set :attr attr :val new-val :ignore-geometry ignore-geometry?})
(update :uops conj {:type :set :attr attr :val old-val :ignore-touched true})))))
(defn- update-shape-changes
"Calculate the changes and undos to be done when a function is applied to a
single object"
[changes page-id objects update-fn attrs id]
[changes page-id objects update-fn attrs id ignore-geometry?]
(let [old-obj (get objects id)
new-obj (update-fn old-obj)
attrs (or attrs (d/concat #{} (keys old-obj) (keys new-obj)))
{rops :rops uops :uops}
(reduce #(generate-operation %1 %2 old-obj new-obj)
(reduce #(generate-operation %1 %2 old-obj new-obj ignore-geometry?)
{:rops [] :uops []}
attrs)
@ -72,8 +72,8 @@
(defn update-shapes
([ids f] (update-shapes ids f nil))
([ids f {:keys [reg-objects? save-undo? keys]
:or {reg-objects? false save-undo? true}}]
([ids f {:keys [reg-objects? save-undo? keys ignore-tree]
:or {reg-objects? false save-undo? true attrs nil}}]
(us/assert ::coll-of-uuid ids)
(us/assert fn? f)
@ -90,7 +90,9 @@
ids (into [] (filter some?) ids)
changes (reduce #(update-shape-changes %1 page-id objects f keys %2) changes ids)]
changes (reduce
#(update-shape-changes %1 page-id objects f keys %2 (get ignore-tree %2))
changes ids)]
(when-not (empty? (:redo-changes changes))
(let [reg-objs {:type :reg-objects

View file

@ -421,27 +421,72 @@
;; -- Apply modifiers
(defn- check-delta
"If the shape is a component instance, check its relative position respect the
root of the component, and see if it changes after applying a transformation."
[shape root transformed-shape transformed-root objects]
(let [root (cond
(:component-root? shape)
shape
(nil? root)
(cp/get-root-shape shape objects)
:else root)
transformed-root (cond
(:component-root? transformed-shape)
transformed-shape
(nil? transformed-root)
(cp/get-root-shape transformed-shape objects)
:else transformed-root)
shape-delta (when root
(gpt/point (- (:x shape) (:x root))
(- (:y shape) (:y root))))
transformed-shape-delta (when transformed-root
(gpt/point (- (:x transformed-shape) (:x transformed-root))
(- (:y transformed-shape) (:y transformed-root))))
ignore-geometry? (= shape-delta transformed-shape-delta)]
[root transformed-root ignore-geometry?]))
(defn- set-modifiers-recursive
[modif-tree objects shape modifiers]
"Apply the modifiers to one shape, and the corresponding ones to all children,
depending on the child constraints. The modifiers are not directly applied to
the objects tree, but to a separated structure (modif-tree), that may be
merged later with the real objects."
[modif-tree objects shape modifiers root transformed-root]
(let [children (->> (get shape :shapes [])
(map #(get objects %)))
transformed-shape (when (seq children) ; <- don't calculate it if not needed
(gsh/transform-shape
(assoc shape :modifiers (select-keys modifiers
[:resize-origin
:resize-vector]))))
transformed-shape (gsh/transform-shape (assoc shape :modifiers modifiers))
[root transformed-root ignore-geometry?]
(check-delta shape root transformed-shape transformed-root objects)
modifiers (assoc modifiers :ignore-geometry? ignore-geometry?)
resized-shape (when (seq children) ; <- don't calculate it if not needed
(gsh/transform-shape
(assoc shape :modifiers (select-keys modifiers
[:resize-origin
:resize-vector]))))
set-child (fn [modif-tree child]
(let [child-modifiers (gsh/calc-child-modifiers shape
transformed-shape
resized-shape
child
modifiers)]
(set-modifiers-recursive modif-tree
objects
child
child-modifiers)))]
child-modifiers
root
transformed-root)))]
(reduce set-child
(update-in modif-tree [(:id shape) :modifiers] #(merge % modifiers))
children)))
@ -460,11 +505,13 @@
ids (->> ids (into #{} (remove #(get-in objects [% :blocked] false))))]
(reduce (fn [state id]
(update state :workspace-modifiers
#(set-modifiers-recursive %
objects
(get objects id)
modifiers)))
(update state :workspace-modifiers
#(set-modifiers-recursive %
objects
(get objects id)
modifiers
nil
nil)))
state
ids))))))
@ -526,28 +573,31 @@
state (if set-modifiers?
(ptk/update (set-modifiers ids) state)
state)
object-modifiers (get state :workspace-modifiers)]
object-modifiers (get state :workspace-modifiers)
ignore-tree (d/mapm #(get-in %2 [:modifiers :ignore-geometry?]) object-modifiers)]
(rx/of (dwu/start-undo-transaction)
(dch/update-shapes
ids-with-children
(fn [shape]
(-> shape
(merge (get object-modifiers (:id shape)))
(gsh/transform-shape)))
{:reg-objects? true
;; Attributes that can change in the transform. This way we don't have to check
;; all the attributes
:attrs [:selrect :points
:x :y
:width :height
:content
:transform
:transform-inverse
:rotation
:flip-x
:flip-y]
})
ids-with-children
(fn [shape]
(-> shape
(merge (get object-modifiers (:id shape)))
(gsh/transform-shape)))
{:reg-objects? true
:ignore-tree ignore-tree
;; Attributes that can change in the transform. This way we don't have to check
;; all the attributes
:attrs [:selrect :points
:x :y
:width :height
:content
:transform
:transform-inverse
:rotation
:flip-x
:flip-y]
})
(clear-local-transform)
(dwu/commit-undo-transaction)))))))
@ -577,7 +627,7 @@
(fn [objects shape-id]
(let [shape (get objects shape-id)
modifier (gsh/resize-modifiers shape attr value)]
(set-modifiers-recursive objects objects shape modifier)))]
(set-modifiers-recursive objects objects shape modifier nil nil)))]
(d/update-in-when
state