mirror of
https://github.com/penpot/penpot.git
synced 2025-05-19 15:46:10 +02:00
✨ Detect movements inside a component and not override them
This commit is contained in:
parent
ad4115acc8
commit
71759386c5
4 changed files with 107 additions and 48 deletions
|
@ -457,13 +457,18 @@
|
||||||
kw (if (keyword? kw) (name kw) kw)]
|
kw (if (keyword? kw) (name kw) kw)]
|
||||||
(keyword (str prefix kw))))
|
(keyword (str prefix kw))))
|
||||||
|
|
||||||
|
|
||||||
(defn tap
|
(defn tap
|
||||||
"Simpilar to the tap in rxjs but for plain collections"
|
"Simpilar to the tap in rxjs but for plain collections"
|
||||||
[f coll]
|
[f coll]
|
||||||
(f coll)
|
(f coll)
|
||||||
coll)
|
coll)
|
||||||
|
|
||||||
|
(defn tap-r
|
||||||
|
"Same but with args reversed, for -> threads"
|
||||||
|
[coll f]
|
||||||
|
(f coll)
|
||||||
|
coll)
|
||||||
|
|
||||||
(defn map-diff
|
(defn map-diff
|
||||||
"Given two maps returns the diff of its attributes in a map where
|
"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
|
the keys will be the attributes that change and the values the previous
|
||||||
|
|
|
@ -408,20 +408,22 @@
|
||||||
(let [attr (:attr op)
|
(let [attr (:attr op)
|
||||||
val (:val op)
|
val (:val op)
|
||||||
ignore (:ignore-touched op)
|
ignore (:ignore-touched op)
|
||||||
|
ignore-geometry (:ignore-geometry op)
|
||||||
shape-ref (:shape-ref shape)
|
shape-ref (:shape-ref shape)
|
||||||
group (get component-sync-attrs attr)
|
group (get component-sync-attrs attr)
|
||||||
root-name? (and (= group :name-group)
|
root-name? (and (= group :name-group)
|
||||||
(:component-root? shape))]
|
(:component-root? shape))]
|
||||||
|
|
||||||
(cond-> 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))
|
(and shape-ref group (not ignore) (not= val (get shape attr))
|
||||||
(not root-name?)
|
(not root-name?)
|
||||||
;; FIXME: it's difficult to tell if the geometry changes affect
|
(not (and ignore-geometry
|
||||||
;; an individual shape inside the component, or are for
|
(and (= group :geometry-group)
|
||||||
;; the whole component (in which case we shouldn't set
|
(not (#{:width :height} attr))))))
|
||||||
;; 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)))
|
|
||||||
(->
|
(->
|
||||||
(update :touched cph/set-touched-group group)
|
(update :touched cph/set-touched-group group)
|
||||||
(dissoc :remote-synced?))
|
(dissoc :remote-synced?))
|
||||||
|
|
|
@ -34,26 +34,26 @@
|
||||||
(defn- generate-operation
|
(defn- generate-operation
|
||||||
"Given an object old and new versions and an attribute will append into changes
|
"Given an object old and new versions and an attribute will append into changes
|
||||||
the set and undo operations"
|
the set and undo operations"
|
||||||
[changes attr old new]
|
[changes attr old new ignore-geometry?]
|
||||||
(let [old-val (get old attr)
|
(let [old-val (get old attr)
|
||||||
new-val (get new attr)]
|
new-val (get new attr)]
|
||||||
(if (= old-val new-val)
|
(if (= old-val new-val)
|
||||||
changes
|
changes
|
||||||
(-> 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})))))
|
(update :uops conj {:type :set :attr attr :val old-val :ignore-touched true})))))
|
||||||
|
|
||||||
(defn- update-shape-changes
|
(defn- update-shape-changes
|
||||||
"Calculate the changes and undos to be done when a function is applied to a
|
"Calculate the changes and undos to be done when a function is applied to a
|
||||||
single object"
|
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)
|
(let [old-obj (get objects id)
|
||||||
new-obj (update-fn old-obj)
|
new-obj (update-fn old-obj)
|
||||||
|
|
||||||
attrs (or attrs (d/concat #{} (keys old-obj) (keys new-obj)))
|
attrs (or attrs (d/concat #{} (keys old-obj) (keys new-obj)))
|
||||||
|
|
||||||
{rops :rops uops :uops}
|
{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 []}
|
{:rops [] :uops []}
|
||||||
attrs)
|
attrs)
|
||||||
|
|
||||||
|
@ -72,8 +72,8 @@
|
||||||
|
|
||||||
(defn update-shapes
|
(defn update-shapes
|
||||||
([ids f] (update-shapes ids f nil))
|
([ids f] (update-shapes ids f nil))
|
||||||
([ids f {:keys [reg-objects? save-undo? keys]
|
([ids f {:keys [reg-objects? save-undo? keys ignore-tree]
|
||||||
:or {reg-objects? false save-undo? true}}]
|
:or {reg-objects? false save-undo? true attrs nil}}]
|
||||||
|
|
||||||
(us/assert ::coll-of-uuid ids)
|
(us/assert ::coll-of-uuid ids)
|
||||||
(us/assert fn? f)
|
(us/assert fn? f)
|
||||||
|
@ -90,7 +90,9 @@
|
||||||
|
|
||||||
ids (into [] (filter some?) ids)
|
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))
|
(when-not (empty? (:redo-changes changes))
|
||||||
(let [reg-objs {:type :reg-objects
|
(let [reg-objs {:type :reg-objects
|
||||||
|
|
|
@ -421,27 +421,72 @@
|
||||||
|
|
||||||
;; -- Apply modifiers
|
;; -- 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
|
(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 [])
|
(let [children (->> (get shape :shapes [])
|
||||||
(map #(get objects %)))
|
(map #(get objects %)))
|
||||||
|
|
||||||
transformed-shape (when (seq children) ; <- don't calculate it if not needed
|
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
|
(gsh/transform-shape
|
||||||
(assoc shape :modifiers (select-keys modifiers
|
(assoc shape :modifiers (select-keys modifiers
|
||||||
[:resize-origin
|
[:resize-origin
|
||||||
:resize-vector]))))
|
:resize-vector]))))
|
||||||
|
|
||||||
set-child (fn [modif-tree child]
|
set-child (fn [modif-tree child]
|
||||||
(let [child-modifiers (gsh/calc-child-modifiers shape
|
(let [child-modifiers (gsh/calc-child-modifiers shape
|
||||||
transformed-shape
|
resized-shape
|
||||||
child
|
child
|
||||||
modifiers)]
|
modifiers)]
|
||||||
(set-modifiers-recursive modif-tree
|
(set-modifiers-recursive modif-tree
|
||||||
objects
|
objects
|
||||||
child
|
child
|
||||||
child-modifiers)))]
|
child-modifiers
|
||||||
|
root
|
||||||
|
transformed-root)))]
|
||||||
(reduce set-child
|
(reduce set-child
|
||||||
(update-in modif-tree [(:id shape) :modifiers] #(merge % modifiers))
|
(update-in modif-tree [(:id shape) :modifiers] #(merge % modifiers))
|
||||||
children)))
|
children)))
|
||||||
|
@ -464,7 +509,9 @@
|
||||||
#(set-modifiers-recursive %
|
#(set-modifiers-recursive %
|
||||||
objects
|
objects
|
||||||
(get objects id)
|
(get objects id)
|
||||||
modifiers)))
|
modifiers
|
||||||
|
nil
|
||||||
|
nil)))
|
||||||
state
|
state
|
||||||
ids))))))
|
ids))))))
|
||||||
|
|
||||||
|
@ -526,7 +573,9 @@
|
||||||
state (if set-modifiers?
|
state (if set-modifiers?
|
||||||
(ptk/update (set-modifiers ids) state)
|
(ptk/update (set-modifiers ids) state)
|
||||||
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)
|
(rx/of (dwu/start-undo-transaction)
|
||||||
(dch/update-shapes
|
(dch/update-shapes
|
||||||
|
@ -536,6 +585,7 @@
|
||||||
(merge (get object-modifiers (:id shape)))
|
(merge (get object-modifiers (:id shape)))
|
||||||
(gsh/transform-shape)))
|
(gsh/transform-shape)))
|
||||||
{:reg-objects? true
|
{:reg-objects? true
|
||||||
|
:ignore-tree ignore-tree
|
||||||
;; Attributes that can change in the transform. This way we don't have to check
|
;; Attributes that can change in the transform. This way we don't have to check
|
||||||
;; all the attributes
|
;; all the attributes
|
||||||
:attrs [:selrect :points
|
:attrs [:selrect :points
|
||||||
|
@ -577,7 +627,7 @@
|
||||||
(fn [objects shape-id]
|
(fn [objects shape-id]
|
||||||
(let [shape (get objects shape-id)
|
(let [shape (get objects shape-id)
|
||||||
modifier (gsh/resize-modifiers shape attr value)]
|
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
|
(d/update-in-when
|
||||||
state
|
state
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue