diff --git a/common/src/app/common/data.cljc b/common/src/app/common/data.cljc index df18739ea..bdf35dc00 100644 --- a/common/src/app/common/data.cljc +++ b/common/src/app/common/data.cljc @@ -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 diff --git a/common/src/app/common/pages/changes.cljc b/common/src/app/common/pages/changes.cljc index 47eec12ea..e3a5274f2 100644 --- a/common/src/app/common/pages/changes.cljc +++ b/common/src/app/common/pages/changes.cljc @@ -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?)) diff --git a/frontend/src/app/main/data/workspace/changes.cljs b/frontend/src/app/main/data/workspace/changes.cljs index f18a87104..5b5c3fbbb 100644 --- a/frontend/src/app/main/data/workspace/changes.cljs +++ b/frontend/src/app/main/data/workspace/changes.cljs @@ -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 diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 643831771..feedb357f 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -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