diff --git a/common/src/app/common/files/changes.cljc b/common/src/app/common/files/changes.cljc index 2f1858505..c200749d8 100644 --- a/common/src/app/common/files/changes.cljc +++ b/common/src/app/common/files/changes.cljc @@ -507,8 +507,7 @@ (and (:shape-ref parent) (#{:group :frame} (:type parent)) (not ignore-touched)) - (-> (update :touched cfh/set-touched-group :shapes-group) - (dissoc :remote-synced))))) + (dissoc :remote-synced)))) (remove-from-old-parent [old-objects objects shape-id] (let [prev-parent-id (dm/get-in old-objects [shape-id :parent-id])] @@ -525,9 +524,7 @@ (-> objects (d/update-in-when [pid :shapes] d/without-obj sid) (d/update-in-when [pid :shapes] d/vec-without-nils) - (cond-> component? (d/update-when pid #(-> % - (update :touched cfh/set-touched-group :shapes-group) - (dissoc :remote-synced))))))))) + (cond-> component? (d/update-when pid #(dissoc % :remote-synced)))))))) (update-parent-id [objects id] (-> objects (d/update-when id assoc :parent-id parent-id))) diff --git a/common/src/app/common/files/helpers.cljc b/common/src/app/common/files/helpers.cljc index 6203d5db1..287bb7bb8 100644 --- a/common/src/app/common/files/helpers.cljc +++ b/common/src/app/common/files/helpers.cljc @@ -360,7 +360,8 @@ (defn set-touched-group [touched group] - (conj (or touched #{}) group)) + (when group + (conj (or touched #{}) group))) (defn touched-group? [shape group] diff --git a/common/src/app/common/types/component.cljc b/common/src/app/common/types/component.cljc index ec9a679a9..70b030eaf 100644 --- a/common/src/app/common/types/component.cljc +++ b/common/src/app/common/types/component.cljc @@ -4,7 +4,11 @@ ;; ;; Copyright (c) KALEIDOS INC -(ns app.common.types.component) +(ns app.common.types.component + (:require + [app.common.data :as d] + [app.common.uuid :as uuid] + [cuerdas.core :as str])) ;; Attributes that may be synced in components, and the group they belong to. ;; When one attribute is modified in a shape inside a component, the corresponding @@ -170,6 +174,29 @@ (and (= shape-id (:main-instance-id component)) (= page-id (:main-instance-page component)))) +(defn build-swap-slot-group + "Convert a swap-slot into a :touched group" + [swap-slot] + (when swap-slot + (keyword (str "swap-slot-" swap-slot)))) + +(defn get-swap-slot + "If the shape has a :touched group in the form :swap-slot-, get the id." + [shape] + (let [group (->> (:touched shape) + (map name) + (d/seek #(str/starts-with? % "swap-slot-")))] + (when group + (uuid/uuid (subs group 10))))) + +(defn match-swap-slot? + [shape-main shape-inst] + (let [slot-main (get-swap-slot shape-main) + slot-inst (get-swap-slot shape-inst)] + (when (some? slot-inst) + (or (= slot-main slot-inst) + (= (:id shape-main) slot-inst))))) + (defn get-component-root [component] (if (true? (:main-instance-id component)) diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 42a0a7789..676db6dc7 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -166,22 +166,33 @@ (ctk/get-component-root component)))) (defn get-component-shape - "Retrieve one shape in the component by id." - [file-data component shape-id] + "Retrieve one shape in the component by id. If with-context? is true, add the + file and container where the shape resides in its metadata." + [file-data component shape-id & {:keys [with-context?] :or {with-context? false}}] (let [components-v2 (dm/get-in file-data [:options :components-v2])] (if (and components-v2 (not (:deleted component))) (let [component-page (get-component-page file-data component)] (when component-page - (cfh/get-child (:objects component-page) - (:main-instance-id component) - shape-id))) - (dm/get-in component [:objects shape-id])))) + (let [child (cfh/get-child (:objects component-page) + (:main-instance-id component) + shape-id)] + (cond-> child + (and child with-context?) + (with-meta {:file {:id (:id file-data) + :data file-data} + :container (ctn/make-container component-page :page)}))))) + + (cond-> (dm/get-in component [:objects shape-id]) + with-context? + (with-meta {:file {:id (:id file-data) + :data file-data} + :container (ctn/make-container component :component)}))))) (defn get-ref-shape "Retrieve the shape in the component that is referenced by the instance shape." - [file-data component shape] + [file-data component shape & {:keys [with-context?] :or {with-context? false}}] (when (:shape-ref shape) - (get-component-shape file-data component (:shape-ref shape)))) + (get-component-shape file-data component (:shape-ref shape) :with-context? with-context?))) (defn get-shape-in-copy "Given a shape in the main component and the root of the copy component returns the equivalent @@ -193,16 +204,16 @@ (defn find-ref-shape "Locate the nearest component in the local file or libraries, and retrieve the shape referenced by the instance shape." - [file page libraries shape & {:keys [include-deleted?] :or {include-deleted? false}}] + [file container libraries shape & {:keys [include-deleted? with-context?] :or {include-deleted? false with-context? false}}] (let [find-ref-shape-in-head (fn [head-shape] - (let [head-file (find-component-file file libraries (:component-file head-shape)) - head-component (when (some? head-file) - (ctkl/get-component (:data head-file) (:component-id head-shape) include-deleted?))] - (when (some? head-component) - (get-ref-shape (:data head-file) head-component shape))))] + (let [component-file (find-component-file file libraries (:component-file head-shape)) + component (when (some? component-file) + (ctkl/get-component (:data component-file) (:component-id head-shape) include-deleted?))] + (when (some? component) + (get-ref-shape (:data component-file) component shape :with-context? with-context?))))] - (some find-ref-shape-in-head (ctn/get-parent-heads (:objects page) shape)))) + (some find-ref-shape-in-head (ctn/get-parent-heads (:objects container) shape)))) (defn find-ref-component "Locate the nearest component in the local file or libraries that is referenced by the @@ -210,12 +221,14 @@ [file page libraries shape & {:keys [include-deleted?] :or {include-deleted? false}}] (let [find-ref-component-in-head (fn [head-shape] - (let [head-file (find-component-file file libraries (:component-file head-shape)) - head-component (when (some? head-file) - (ctkl/get-component (:data head-file) (:component-id head-shape) include-deleted?))] - (when (some? head-component) - (when (get-ref-shape (:data head-file) head-component shape) - head-component))))] + (let [component-file (find-component-file file libraries (:component-file head-shape)) + component (when (some? component-file) + (ctkl/get-component (:data component-file) + (:component-id head-shape) + include-deleted?))] + (when (some? component) + (when (get-ref-shape (:data component-file) component shape) + component))))] (some find-ref-component-in-head (ctn/get-parent-copy-heads (:objects page) shape)))) @@ -251,6 +264,34 @@ (let [ref-component (find-ref-component file page libraries shape :include-deleted? true)] (true? (= (:id component) (:id ref-component))))) +(defn find-swap-slot + [shape container file libraries] + (if-let [swap-slot (ctk/get-swap-slot shape)] + swap-slot + (let [ref-shape (find-ref-shape file + container + libraries + shape + :include-deleted? true + :with-context? true) + shape-meta (meta ref-shape) + ref-file (:file shape-meta) + ref-container (:container shape-meta)] + (when ref-shape + (if-let [swap-slot (ctk/get-swap-slot ref-shape)] + swap-slot + (if (ctk/main-instance? ref-shape) + (:id shape) + (find-swap-slot ref-shape ref-container ref-file libraries))))))) + +(defn match-swap-slot? + [shape-main shape-inst container-inst container-main file libraries] + (let [slot-main (find-swap-slot shape-main container-main file libraries) + slot-inst (find-swap-slot shape-inst container-inst file libraries)] + (when (some? slot-inst) + (or (= slot-main slot-inst) + (= (:id shape-main) slot-inst))))) + (defn get-component-shapes "Retrieve all shapes of the component" [file-data component] diff --git a/common/src/app/common/types/shape_tree.cljc b/common/src/app/common/types/shape_tree.cljc index 92e8880be..646c26641 100644 --- a/common/src/app/common/types/shape_tree.cljc +++ b/common/src/app/common/types/shape_tree.cljc @@ -40,8 +40,7 @@ (update :shapes update-parent-shapes) (update :shapes d/vec-without-nils) (cond-> (and (ctk/in-component-copy? parent) (not ignore-touched)) - (-> (update :touched cfh/set-touched-group :shapes-group) - (dissoc :remote-synced))))) + (dissoc :remote-synced)))) update-objects (fn [objects parent-id] @@ -85,8 +84,7 @@ (let [parent (update parent :shapes d/without-obj shape-id)] (cond-> parent (and (:shape-ref parent) (not ignore-touched)) - (-> (update :touched cfh/set-touched-group :shapes-group) - (dissoc :remote-synced))))) + (dissoc :remote-synced)))) (delete-from-objects [objects] (if-let [target (get objects shape-id)] diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index e981a1420..41fdb90ba 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -777,7 +777,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update selected-ids) + (ptk/data-event :layout/update {:ids selected-ids}) (dwu/commit-undo-transaction undo-id)))))) ;; --- Change Shape Order (D&D Ordering) @@ -987,7 +987,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dwco/expand-collapse parent-id) - (ptk/data-event :layout/update (concat all-parents ids)) + (ptk/data-event :layout/update {:ids (concat all-parents ids)}) (dwu/commit-undo-transaction undo-id)))))) (defn relocate-selected-shapes @@ -1105,7 +1105,7 @@ (when (can-align? selected objects) (rx/of (dwu/start-undo-transaction undo-id) (dwt/position-shapes moved) - (ptk/data-event :layout/update selected) + (ptk/data-event :layout/update {:ids selected}) (dwu/commit-undo-transaction undo-id))))))) (defn can-distribute? [selected] @@ -1132,7 +1132,7 @@ (when (can-distribute? selected) (rx/of (dwu/start-undo-transaction undo-id) (dwt/position-shapes moved) - (ptk/data-event :layout/update selected) + (ptk/data-event :layout/update {:ids selected}) (dwu/commit-undo-transaction undo-id))))))) ;; --- Shape Proportions @@ -2016,7 +2016,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dws/select-shapes selected) - (ptk/data-event :layout/update [frame-id]) + (ptk/data-event :layout/update {:ids [frame-id]}) (dwu/commit-undo-transaction undo-id))))))) (defn as-content [text] diff --git a/frontend/src/app/main/data/workspace/changes.cljs b/frontend/src/app/main/data/workspace/changes.cljs index 061b3c550..b2d595086 100644 --- a/frontend/src/app/main/data/workspace/changes.cljs +++ b/frontend/src/app/main/data/workspace/changes.cljs @@ -101,7 +101,7 @@ ;; Update layouts for properties marked (if (d/not-empty? update-layout-ids) - (rx/of (ptk/data-event :layout/update update-layout-ids)) + (rx/of (ptk/data-event :layout/update {:ids update-layout-ids})) (rx/empty)))))))) (defn send-update-indices diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index a89d845cb..c3a403269 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -194,7 +194,7 @@ (prepare-create-group it objects page-id shapes "Group" false)] (rx/of (dch/commit-changes changes) (dws/select-shapes (d/ordered-set (:id group))) - (ptk/data-event :layout/update parents)))))))) + (ptk/data-event :layout/update {:ids parents})))))))) (def ungroup-selected (ptk/reify ::ungroup-selected @@ -244,7 +244,7 @@ (when-not (empty? selected) (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update parents) + (ptk/data-event :layout/update {:ids parents}) (dwu/commit-undo-transaction undo-id) (dws/select-shapes child-ids))))))) @@ -289,7 +289,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dws/select-shapes (d/ordered-set (:id group))) - (ptk/data-event :layout/update [(:id group)]) + (ptk/data-event :layout/update {:ids [(:id group)]}) (dwu/commit-undo-transaction undo-id)))))))) (def unmask-group diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index c24d1d469..c27c00b0f 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -334,7 +334,7 @@ (when-not (empty? (:redo-changes changes)) (rx/of (dch/commit-changes changes) (dws/select-shapes (d/ordered-set (:id root))) - (ptk/data-event :layout/update parents))))))))) + (ptk/data-event :layout/update {:ids parents}))))))))) (defn add-component "Add a new component to current file library, from the currently selected shapes. @@ -566,7 +566,7 @@ undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update [(:id new-shape)]) + (ptk/data-event :layout/update {:ids [(:id new-shape)]}) (dws/select-shapes (d/ordered-set (:id new-shape))) (when start-move? (dwtr/start-move initial-point #{(:id new-shape)})) @@ -699,6 +699,7 @@ (watch [it state _] (log/info :msg "RESET-COMPONENT of shape" :id (str id)) (let [file (wsh/get-local-file state) + file-full (wsh/get-local-file-full state) libraries (wsh/get-libraries state) page-id (:current-page-id state) @@ -711,7 +712,7 @@ (-> (pcb/empty-changes it) (pcb/with-container container) (pcb/with-objects (:objects container)) - (dwlh/generate-sync-shape-direct libraries container id true components-v2))] + (dwlh/generate-sync-shape-direct file-full libraries container id true components-v2))] (log/debug :msg "RESET-COMPONENT finished" :js/rchanges (log-changes (:redo-changes changes) @@ -750,6 +751,7 @@ (log/info :msg "UPDATE-COMPONENT of shape" :id (str id) :undo-group undo-group) (let [page-id (get state :current-page-id) local-file (wsh/get-local-file state) + full-file (wsh/get-local-file-full state) container (cfh/get-container local-file :page page-id) shape (ctn/get-shape container id) components-v2 (features/active-feature? state "components/v2")] @@ -761,7 +763,7 @@ (-> (pcb/empty-changes it) (pcb/set-undo-group undo-group) (pcb/with-container container) - (dwlh/generate-sync-shape-inverse libraries container id components-v2)) + (dwlh/generate-sync-shape-inverse full-file libraries container id components-v2)) file-id (:component-file shape) file (wsh/get-file state file-id) @@ -870,16 +872,12 @@ 0))))) (defn- add-component-for-swap - [shape file-id id-new-component index target-cell keep-props-values {:keys [undo-group]}] + [shape file page libraries id-new-component index target-cell keep-props-values {:keys [undo-group]}] (dm/assert! (uuid? id-new-component)) - (dm/assert! (uuid? file-id)) (ptk/reify ::add-component-for-swap ptk/WatchEvent - (watch [it state _] - (let [page (wsh/lookup-page state) - libraries (wsh/get-libraries state) - - objects (:objects page) + (watch [it _ _] + (let [objects (:objects page) position (gpt/point (:x shape) (:y shape)) changes (-> (pcb/empty-changes it (:id page)) (pcb/set-undo-group undo-group) @@ -889,7 +887,7 @@ [new-shape changes] (dwlh/generate-instantiate-component changes objects - file-id + (:id file) id-new-component position page @@ -898,6 +896,15 @@ (:parent-id shape) (:frame-id shape)) + new-shape (cond-> new-shape + (nil? (ctk/get-swap-slot new-shape)) + (update :touched cfh/set-touched-group (-> (ctf/find-swap-slot shape + page + {:id (:id file) + :data file} + libraries) + (ctk/build-swap-slot-group)))) + changes (-> changes ;; Restore the properties @@ -905,7 +912,11 @@ ;; We need to set the same index as the original shape (pcb/change-parent (:parent-id shape) [new-shape] index {:component-swap true - :ignore-touched true}))] + :ignore-touched true}) + (dwlh/change-touched new-shape + shape + (ctn/make-container page :page) + {}))] ;; First delete so we don't break the grid layout cells (rx/of (dch/commit-changes changes) @@ -921,7 +932,10 @@ (watch [_ state _] ;; First delete shapes so we have space in the layout otherwise we can have problems ;; in the grid creating new rows/columns to make space - (let [objects (wsh/lookup-page-objects state) + (let [file (wsh/get-file state file-id) + libraries (wsh/get-libraries state) + page (wsh/lookup-page state) + objects (wsh/lookup-page-objects state) parent (get objects (:parent-id shape)) ;; If the target parent is a grid layout we need to pass the target cell @@ -935,15 +949,14 @@ undo-id (js/Symbol) undo-group (uuid/next)] - (rx/of (dwu/start-undo-transaction undo-id) (dwsh/delete-shapes nil (d/ordered-set (:id shape)) {:component-swap true :undo-id undo-id :undo-group undo-group}) - (add-component-for-swap shape file-id id-new-component index target-cell keep-props-values + (add-component-for-swap shape file page libraries id-new-component index target-cell keep-props-values {:undo-group undo-group}) - (ptk/data-event :layout/update [(:parent-id shape)]) + (ptk/data-event :layout/update {:ids [(:parent-id shape)] :undo-group undo-group}) (dwu/commit-undo-transaction undo-id)))))) (defn component-multi-swap @@ -958,8 +971,12 @@ {::ev/name "component-swap"}) ptk/WatchEvent - (watch [_ _ _] + (watch [_ state _] (let [undo-id (js/Symbol)] + (log/info :msg "COMPONENT-SWAP" + :file (dwlh/pretty-file file-id state) + :id-new-component id-new-component + :undo-id undo-id) (rx/concat (rx/of (dwu/start-undo-transaction undo-id)) (rx/map #(component-swap % file-id id-new-component) (rx/from shapes)) @@ -1065,7 +1082,7 @@ :file-id file-id)))) (when-not (empty? updated-frames) (rx/merge - (rx/of (ptk/data-event :layout/update (map :id updated-frames))) + (rx/of (ptk/data-event :layout/update {:ids (map :id updated-frames) :undo-group undo-group})) (->> (rx/from updated-frames) (rx/mapcat (fn [shape] diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index 32152b2ba..d157dcd3d 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -320,16 +320,17 @@ (loop [containers (ctf/object-containers-seq file) changes (pcb/empty-changes it)] (if-let [container (first containers)] - (recur (next containers) - (pcb/concat-changes - changes - (generate-sync-container it - asset-type - asset-id - library-id - state - container - components-v2))) + (do + (recur (next containers) + (pcb/concat-changes + changes + (generate-sync-container it + asset-type + asset-id + library-id + state + container + components-v2)))) changes)))) (defn generate-sync-library @@ -424,8 +425,9 @@ (defmethod generate-sync-shape :components [_ changes _library-id state container shape components-v2] (let [shape-id (:id shape) + file (wsh/get-local-file-full state) libraries (wsh/get-libraries state)] - (generate-sync-shape-direct changes libraries container shape-id false components-v2))) + (generate-sync-shape-direct changes file libraries container shape-id false components-v2))) (defmethod generate-sync-shape :colors [_ changes library-id state _ shape _] @@ -593,8 +595,8 @@ (defn generate-sync-shape-direct "Generate changes to synchronize one shape that is the root of a component instance, and all its children, from the given component." - [changes libraries container shape-id reset? components-v2] - (log/debug :msg "Sync shape direct" :shape (str shape-id) :reset? reset?) + [changes file libraries container shape-id reset? components-v2] + (log/debug :msg "Sync shape direct" :shape-inst (str shape-id) :reset? reset?) (let [shape-inst (ctn/get-shape container shape-id) library (dm/get-in libraries [(:component-file shape-inst) :data]) component (ctkl/get-component library (:component-id shape-inst) true)] @@ -622,6 +624,8 @@ shape-inst component library + file + libraries shape-main root-inst root-main @@ -654,9 +658,9 @@ nil)))))) (defn- generate-sync-shape-direct-recursive - [changes container shape-inst component library shape-main root-inst root-main reset? initial-root? redirect-shaperef components-v2] + [changes container shape-inst component library file libraries shape-main root-inst root-main reset? initial-root? redirect-shaperef components-v2] (log/debug :msg "Sync shape direct recursive" - :shape (str (:name shape-inst)) + :shape-inst (str (:name shape-inst) " " (pretty-uuid (:id shape-inst))) :component (:name component)) (if (nil? shape-main) @@ -713,6 +717,8 @@ (map #(redirect-shaperef %) children-inst) children-inst) only-inst (fn [changes child-inst] + (log/trace :msg "Only inst" + :child-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst)))) (if-not (and omit-touched? (contains? (:touched shape-inst) :shapes-group)) @@ -723,6 +729,8 @@ changes)) only-main (fn [changes child-main] + (log/trace :msg "Only main" + :child-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) (if-not (and omit-touched? (contains? (:touched shape-inst) :shapes-group)) @@ -739,11 +747,16 @@ changes)) both (fn [changes child-inst child-main] + (log/trace :msg "Both" + :child-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) + :child-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) (generate-sync-shape-direct-recursive changes container child-inst component library + file + libraries child-main root-inst root-main @@ -752,7 +765,17 @@ redirect-shaperef components-v2)) + swapped (fn [changes child-inst child-main] + (log/trace :msg "Match slot" + :child-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) + :child-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) + ;; For now we don't make any sync here. + changes) + moved (fn [changes child-inst child-main] + (log/trace :msg "Move" + :child-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) + :child-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) (move-shape changes child-inst @@ -764,11 +787,17 @@ (compare-children changes children-inst children-main + container + component-container + file + libraries only-inst only-main both + swapped moved - false)))) + false + reset?)))) (defn- generate-rename-component @@ -792,7 +821,7 @@ (defn generate-sync-shape-inverse "Generate changes to update the component a shape is linked to, from the values in the shape and all its children." - [changes libraries container shape-id components-v2] + [changes file libraries container shape-id components-v2] (log/debug :msg "Sync shape inverse" :shape (str shape-id)) (let [redirect-shaperef (partial redirect-shaperef container libraries) shape-inst (ctn/get-shape container shape-id) @@ -823,6 +852,8 @@ shape-inst component library + file + libraries shape-main root-inst root-main @@ -832,7 +863,7 @@ changes))) (defn- generate-sync-shape-inverse-recursive - [changes container shape-inst component library shape-main root-inst root-main initial-root? redirect-shaperef components-v2] + [changes container shape-inst component library file libraries shape-main root-inst root-main initial-root? redirect-shaperef components-v2] (log/trace :msg "Sync shape inverse recursive" :shape (str (:name shape-inst)) :component (:name component)) @@ -915,6 +946,8 @@ child-inst component library + file + libraries child-main root-inst root-main @@ -922,6 +955,13 @@ redirect-shaperef components-v2)) + swapped (fn [changes child-inst child-main] + (log/trace :msg "Match slot" + :child-inst (str (:name child-inst) " " (pretty-uuid (:id child-inst))) + :child-main (str (:name child-main) " " (pretty-uuid (:id child-main)))) + ;; For now we don't make any sync here. + changes) + moved (fn [changes child-inst child-main] (move-shape changes @@ -935,10 +975,16 @@ (compare-children changes children-inst children-main + container + component-container + file + libraries only-inst only-main both + swapped moved + true true) ;; The inverse sync may be made on a component that is inside a @@ -957,12 +1003,15 @@ ;; ---- Operation generation helpers ---- (defn- compare-children - [changes children-inst children-main only-inst-cb only-main-cb both-cb moved-cb inverse?] + [changes children-inst children-main container-inst container-main file libraries only-inst-cb only-main-cb both-cb swapped-cb moved-cb inverse? reset?] + (log/trace :msg "Compare children") (loop [children-inst (seq (or children-inst [])) children-main (seq (or children-main [])) changes changes] (let [child-inst (first children-inst) child-main (first children-main)] + (log/trace :main (str (:name child-main) " " (pretty-uuid (:id child-main))) + :inst (str (:name child-inst) " " (pretty-uuid (:id child-inst)))) (cond (and (nil? child-inst) (nil? child-main)) changes @@ -974,13 +1023,20 @@ (reduce only-inst-cb changes children-inst) :else - (if (ctk/is-main-of? child-main child-inst) + (if (or (ctk/is-main-of? child-main child-inst) + (and (ctf/match-swap-slot? child-main child-inst container-inst container-main file libraries) (not reset?))) (recur (next children-inst) (next children-main) - (both-cb changes child-inst child-main)) + (if (ctk/is-main-of? child-main child-inst) + (both-cb changes child-inst child-main) + (swapped-cb changes child-inst child-main))) - (let [child-inst' (d/seek #(ctk/is-main-of? child-main %) children-inst) - child-main' (d/seek #(ctk/is-main-of? % child-inst) children-main)] + (let [child-inst' (d/seek #(or (ctk/is-main-of? child-main %) + (and (ctf/match-swap-slot? child-main % container-inst container-main file libraries) (not reset?))) + children-inst) + child-main' (d/seek #(or (ctk/is-main-of? % child-inst) + (and (ctf/match-swap-slot? % child-inst container-inst container-main file libraries) (not reset?))) + children-main)] (cond (nil? child-inst') (recur children-inst @@ -994,16 +1050,26 @@ :else (if inverse? - (recur (next children-inst) - (remove #(= (:id %) (:id child-main')) children-main) - (-> changes + (let [is-main? (ctk/is-main-of? child-inst child-main')] + (recur (next children-inst) + (remove #(= (:id %) (:id child-main')) children-main) + (cond-> changes + is-main? (both-cb child-inst child-main') - (moved-cb child-inst child-main'))) - (recur (remove #(= (:id %) (:id child-inst')) children-inst) - (next children-main) - (-> changes + (not is-main?) + (swapped-cb child-inst child-main') + :always + (moved-cb child-inst child-main')))) + (let [is-main? (ctk/is-main-of? child-inst' child-main)] + (recur (remove #(= (:id %) (:id child-inst')) children-inst) + (next children-main) + (cond-> changes + is-main? (both-cb child-inst' child-main) - (moved-cb child-inst' child-main))))))))))) + (not is-main?) + (swapped-cb child-inst' child-main) + :always + (moved-cb child-inst' child-main)))))))))))) (defn- add-shape-to-instance [changes component-shape index component-page container root-instance root-main omit-touched? set-remote-synced?] @@ -1033,7 +1099,8 @@ (assoc :remote-synced true) :always - (assoc :shape-ref (:id original-shape))))) + (-> (assoc :shape-ref (:id original-shape)) + (dissoc :touched))))) ; New shape, by definition, is synced to the main shape update-original-shape (fn [original-shape _new-shape] original-shape) @@ -1270,11 +1337,10 @@ changes changes'))) -(defn- change-touched +(defn change-touched [changes dest-shape origin-shape container {:keys [reset-touched? copy-touched?] :as options}] - (if (or (nil? (:shape-ref dest-shape)) - (not (or reset-touched? copy-touched?))) + (if (nil? (:shape-ref dest-shape)) changes (do (log/info :msg (str "CHANGE-TOUCHED " @@ -1287,12 +1353,16 @@ (let [new-touched (cond reset-touched? nil + copy-touched? (if (:remote-synced origin-shape) nil (set/union (:touched dest-shape) - (:touched origin-shape))))] + (:touched origin-shape))) + + :else + (:touched dest-shape))] (-> changes (update :redo-changes conj (make-change diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index c76c91742..b552bee67 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -458,7 +458,7 @@ ([] (apply-modifiers nil)) - ([{:keys [modifiers undo-transation? stack-undo? ignore-constraints ignore-snap-pixel] + ([{:keys [modifiers undo-transation? stack-undo? ignore-constraints ignore-snap-pixel undo-group] :or {undo-transation? true stack-undo? false ignore-constraints false ignore-snap-pixel false}}] (ptk/reify ::apply-modifiers ptk/WatchEvent @@ -508,6 +508,7 @@ {:reg-objects? true :stack-undo? stack-undo? :ignore-tree ignore-tree + :undo-group undo-group ;; Attributes that can change in the transform. This way we don't have to check ;; all the attributes :attrs [:selrect diff --git a/frontend/src/app/main/data/workspace/path/edition.cljs b/frontend/src/app/main/data/workspace/path/edition.cljs index a10b24b03..51d688707 100644 --- a/frontend/src/app/main/data/workspace/path/edition.cljs +++ b/frontend/src/app/main/data/workspace/path/edition.cljs @@ -332,7 +332,7 @@ ptk/WatchEvent (watch [_ _ _] - (rx/of (ptk/data-event :layout/update [id]))))) + (rx/of (ptk/data-event :layout/update {:ids [id]}))))) (defn split-segments [{:keys [from-p to-p t]}] diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index 89904f836..15d3d5ab4 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -751,7 +751,7 @@ (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (select-shapes new-selected) - (ptk/data-event :layout/update frames) + (ptk/data-event :layout/update {:ids frames}) (memorize-duplicated id-original id-duplicated) (dwu/commit-undo-transaction undo-id)))))))))) diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 19e17a5a2..37e40cf91 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -94,7 +94,7 @@ ;; Never call this directly but through the data-event `:layout/update` ;; Otherwise a lot of cycle dependencies could be generated (defn- update-layout-positions - [ids] + [{:keys [ids undo-group]}] (ptk/reify ::update-layout-positions ptk/WatchEvent (watch [_ state _] @@ -103,7 +103,8 @@ (if (d/not-empty? ids) (let [modif-tree (dwm/create-modif-tree ids (ctm/reflow-modifiers))] (rx/of (dwm/apply-modifiers {:modifiers modif-tree - :stack-undo? true}))) + :stack-undo? true + :undo-group undo-group}))) (rx/empty)))))) (defn initialize @@ -139,7 +140,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/update-shapes [id] layout-initializer {:with-objects? true}) (dch/update-shapes (dm/get-prop parent :shapes) #(dissoc % :constraints-h :constraints-v)) - (ptk/data-event :layout/update [id]) + (ptk/data-event :layout/update {:ids [id]}) (dwu/commit-undo-transaction undo-id)))))) (defn create-layout-from-selection @@ -180,7 +181,7 @@ (dch/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) (dch/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)) (dwsh/delete-shapes page-id selected) - (ptk/data-event :layout/update [new-shape-id]) + (ptk/data-event :layout/update {:ids [new-shape-id]}) (dwu/commit-undo-transaction undo-id))) ;; Create Layout from selection @@ -191,7 +192,7 @@ (dch/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto)) (dch/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix)))) - (rx/of (ptk/data-event :layout/update [new-shape-id]) + (rx/of (ptk/data-event :layout/update {:ids [new-shape-id]}) (dwu/commit-undo-transaction undo-id))))))) (defn remove-layout @@ -203,7 +204,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/update-shapes ids #(apply dissoc % layout-keys)) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn create-layout @@ -256,7 +257,7 @@ (let [undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) (dch/update-shapes ids (d/patch-object changes)) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn add-layout-track @@ -275,7 +276,7 @@ (case type :row (ctl/add-grid-row shape value index) :column (ctl/add-grid-column shape value index)))) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id))))))) (defn remove-layout-track @@ -309,7 +310,7 @@ :row (ctl/remove-grid-row shape index objects) :column (ctl/remove-grid-column shape index objects))) {:with-objects? true}) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id))))))) (defn duplicate-layout-track @@ -363,7 +364,7 @@ undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn reorder-layout-track @@ -381,7 +382,7 @@ (case type :row (ctl/reorder-grid-row shape from-index to-index move-content?) :column (ctl/reorder-grid-column shape from-index to-index move-content?)))) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn hover-layout-track @@ -426,7 +427,7 @@ (fn [shape] (-> shape (update-in [property index] merge props)))) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn fix-child-sizing @@ -523,7 +524,7 @@ (cond-> (ctl/grid-layout? parent) (ctl/assign-cells objects)))) {:with-objects? true}) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))) (defn update-grid-cells @@ -546,7 +547,7 @@ [:layout-grid-cells cell-id] d/patch-object props)) shape)))) - (ptk/data-event :layout/update [layout-id]) + (ptk/data-event :layout/update {:ids [layout-id]}) (dwu/commit-undo-transaction undo-id)))))) (defn change-cells-mode @@ -612,7 +613,7 @@ (d/update-in-when [:layout-grid-cells (:id target-cell)] assoc :position :area))))) {:with-objects? true}) (dwge/clean-selection layout-id) - (ptk/data-event :layout/update [layout-id]) + (ptk/data-event :layout/update {:ids [layout-id]}) (dwu/commit-undo-transaction undo-id)))))) (defn merge-cells @@ -644,7 +645,7 @@ (ctl/assign-cells objects)))) {:with-objects? true}) (dwge/clean-selection layout-id) - (ptk/data-event :layout/update [layout-id]) + (ptk/data-event :layout/update {:ids [layout-id]}) (dwu/commit-undo-transaction undo-id)))))) (defn update-grid-cell-position @@ -669,7 +670,7 @@ (:row-span new-data) (:column-span new-data)) (ctl/assign-cells objects)))) {:with-objects? true}) - (ptk/data-event :layout/update [layout-id]) + (ptk/data-event :layout/update {:ids [layout-id]}) (dwu/commit-undo-transaction undo-id)))))) @@ -724,5 +725,5 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update [layout-id]) + (ptk/data-event :layout/update {:ids [layout-id]}) (dwu/commit-undo-transaction undo-id)))))) diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index 72cbd6f7e..7febfef6f 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -57,7 +57,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (when-not no-update-layout? - (ptk/data-event :layout/update [(:parent-id shape)])) + (ptk/data-event :layout/update {:ids [(:parent-id shape)]})) (when-not no-select? (dws/select-shapes (d/ordered-set (:id shape)))) (dwu/commit-undo-transaction undo-id)) @@ -141,10 +141,11 @@ (rx/concat (rx/of (dwu/start-undo-transaction undo-id) - (update-shape-flags ids-to-hide {:hidden true})) + (update-shape-flags ids-to-hide {:hidden true :undo-group (:undo-group options)})) (real-delete-shapes file page objects ids-to-delete it {:components-v2 components-v2 :ignore-touched (:component-swap options) - :undo-group (:undo-group options)}) + :undo-group (:undo-group options) + :undo-id undo-id}) (rx/of (dwu/commit-undo-transaction undo-id)))))))) (defn- real-delete-shapes-changes @@ -291,11 +292,11 @@ (defn- real-delete-shapes [file page objects ids it options] (let [[changes all-parents] (real-delete-shapes-changes file page objects ids it options) - undo-id (js/Symbol)] + undo-id (or (:undo-id options) (js/Symbol))] (rx/of (dwu/start-undo-transaction undo-id) (dc/detach-comment-thread ids) (dch/commit-changes changes) - (ptk/data-event :layout/update all-parents) + (ptk/data-event :layout/update {:ids all-parents :undo-group (:undo-group options)}) (dwu/commit-undo-transaction undo-id)))) (defn create-and-add-shape @@ -377,7 +378,7 @@ (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dws/select-shapes (d/ordered-set (:id frame-shape))) - (ptk/data-event :layout/update [(:id frame-shape)]) + (ptk/data-event :layout/update {:ids [(:id frame-shape)]}) (dwu/commit-undo-transaction undo-id)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -385,7 +386,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn update-shape-flags - [ids {:keys [blocked hidden transforming] :as flags}] + [ids {:keys [blocked hidden transforming undo-group] :as flags}] (dm/assert! "expected valid coll of uuids" (every? uuid? ids)) @@ -409,7 +410,7 @@ ids (if (boolean? blocked) (into ids (->> ids (mapcat #(cfh/get-children-ids objects %)))) ids)] - (rx/of (dch/update-shapes ids update-fn {:attrs #{:blocked :hidden :transforming}})))))) + (rx/of (dch/update-shapes ids update-fn {:attrs #{:blocked :hidden :transforming} :undo-group undo-group})))))) (defn toggle-visibility-selected [] diff --git a/frontend/src/app/main/data/workspace/state_helpers.cljs b/frontend/src/app/main/data/workspace/state_helpers.cljs index c33f62d18..b04fa88f6 100644 --- a/frontend/src/app/main/data/workspace/state_helpers.cljs +++ b/frontend/src/app/main/data/workspace/state_helpers.cljs @@ -110,6 +110,11 @@ [state] (get state :workspace-data)) +(defn get-local-file-full + [state] + (-> (get state :workspace-file) + (assoc :data (get state :workspace-data)))) + (defn get-file "Get the data content of the given file (it may be the current file or one library)." diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index d0a262312..fa159cf04 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -102,7 +102,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) (dws/select-shapes (d/ordered-set (:id new-shape))) - (ptk/data-event :layout/update [(:id new-shape)]) + (ptk/data-event :layout/update {:ids [(:id new-shape)]}) (dwu/commit-undo-transaction undo-id))) (catch :default cause diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 492cc6a83..c42a65378 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -382,7 +382,7 @@ :stack-undo? true :ignore-remote? true :ignore-touched true}) - (ptk/data-event :layout/update ids) + (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id)))))))) (defn resize-text diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index be9635715..fc8ee350d 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -719,7 +719,7 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) - (ptk/data-event :layout/update selected) + (ptk/data-event :layout/update {:ids selected}) (dwu/commit-undo-transaction undo-id)))))) (defn nudge-selected-shapes