From 018464aedfc451834cc9cecd21d3e396b42bcee9 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Mon, 27 May 2024 17:33:55 +0200 Subject: [PATCH] :recycle: Unify move shape on workspace and relocate on layers panel --- common/src/app/common/logic/shapes.cljc | 216 +++------- common/src/app/common/types/shape/layout.cljc | 12 +- .../logic/comp_remove_swap_slots_test.cljc | 398 ++++-------------- .../common_tests/logic/comp_reset_test.cljc | 28 +- .../common_tests/logic/comp_sync_test.cljc | 30 +- .../common_tests/logic/comp_touched_test.cljc | 28 +- .../common_tests/logic/move_shapes_test.cljc | 84 ++++ frontend/src/app/main/data/workspace.cljs | 15 +- .../app/main/data/workspace/transforms.cljs | 33 +- 9 files changed, 317 insertions(+), 527 deletions(-) create mode 100644 common/test/common_tests/logic/move_shapes_test.cljc diff --git a/common/src/app/common/logic/shapes.cljc b/common/src/app/common/logic/shapes.cljc index a48155f2e..f5d38f0c2 100644 --- a/common/src/app/common/logic/shapes.cljc +++ b/common/src/app/common/logic/shapes.cljc @@ -204,15 +204,35 @@ (reduce ctp/remove-flow flows))))))] [all-parents changes])) -(defn generate-relocate-shapes [changes objects parents parent-id page-id to-index ids] - (let [groups-to-delete + +(defn generate-relocate + [changes objects parent-id page-id to-index ids & {:keys [cell ignore-parents?]}] + (let [ids (cfh/order-by-indexed-shapes objects ids) + shapes (map (d/getf objects) ids) + parent (get objects parent-id) + all-parents (into #{parent-id} (map #(cfh/get-parent-id objects %)) ids) + parents (if ignore-parents? #{parent-id} all-parents) + + children-ids + (->> ids + (mapcat #(cfh/get-children-ids-with-self objects %))) + + child-heads + (->> ids + (mapcat #(ctn/get-child-heads objects %)) + (map :id)) + + component-main-parent + (ctn/find-component-main objects parent false) + + groups-to-delete (loop [current-id (first parents) to-check (rest parents) removed-id? (set ids) result #{}] (if-not current-id - ;; Base case, no next element + ;; Base case, no next element result (let [group (get objects current-id)] @@ -220,14 +240,14 @@ (not= current-id parent-id) (empty? (remove removed-id? (:shapes group)))) - ;; Adds group to the remove and check its parent + ;; Adds group to the remove and check its parent (let [to-check (concat to-check [(cfh/get-parent-id objects current-id)])] (recur (first to-check) (rest to-check) (conj removed-id? current-id) (conj result current-id))) - ;; otherwise recur + ;; otherwise recur (recur (first to-check) (rest to-check) removed-id? @@ -248,7 +268,6 @@ #{} ids) - ;; TODO: Probably implementing this using loop/recur will ;; be more efficient than using reduce and continuous data ;; desturcturing. @@ -283,17 +302,7 @@ (->> ids (mapcat #(ctn/get-child-heads objects %)) (map :id))) - - shapes-to-unconstraint ids - - ordered-indexes (cfh/order-by-indexed-shapes objects ids) - shapes (map (d/getf objects) ordered-indexes) - parent (get objects parent-id) - component-main-parent (ctn/find-component-main objects parent false) - child-heads - (->> ordered-indexes - (mapcat #(ctn/get-child-heads objects %)) - (map :id))] + cell (or cell (ctl/get-cell-by-index parent to-index))] (-> changes (pcb/with-page-id page-id) @@ -301,18 +310,23 @@ ;; Remove layout-item properties when moving a shape outside a layout (cond-> (not (ctl/any-layout? parent)) - (pcb/update-shapes ordered-indexes ctl/remove-layout-item-data)) + (pcb/update-shapes ids ctl/remove-layout-item-data)) ;; Remove the hide in viewer flag (cond-> (and (not= uuid/zero parent-id) (cfh/frame-shape? parent)) - (pcb/update-shapes ordered-indexes #(cond-> % (cfh/frame-shape? %) (assoc :hide-in-viewer true)))) + (pcb/update-shapes ids #(cond-> % (cfh/frame-shape? %) (assoc :hide-in-viewer true)))) ;; Remove the swap slots if it is moving to a different component - (pcb/update-shapes child-heads - (fn [shape] - (cond-> shape - (not= component-main-parent (ctn/find-component-main objects shape false)) - (ctk/remove-swap-slot)))) + (pcb/update-shapes + child-heads + (fn [shape] + (cond-> shape + (not= component-main-parent (ctn/find-component-main objects shape false)) + (ctk/remove-swap-slot)))) + + ;; Remove component-root property when moving a shape inside a component + (cond-> (ctn/get-instance-root objects parent) + (pcb/update-shapes children-ids #(dissoc % :component-root))) ;; Add component-root property when moving a component outside a component (cond-> (not (ctn/get-instance-root objects parent)) @@ -345,7 +359,7 @@ (assoc shape :component-root true))) ;; Reset constraints depending on the new parent - (pcb/update-shapes shapes-to-unconstraint + (pcb/update-shapes ids (fn [shape] (let [frame-id (if (= (:type parent) :frame) (:id parent) @@ -370,153 +384,29 @@ (assoc :layout-item-v-sizing :fix)) parent))) - ;; Update grid layout + ;; Change the grid cell in a grid layout (cond-> (ctl/grid-layout? objects parent-id) - (pcb/update-shapes [parent-id] #(ctl/add-children-to-index % ids objects to-index))) - - (pcb/update-shapes parents - (fn [parent objects] - (cond-> parent - (ctl/grid-layout? parent) - (ctl/assign-cells objects))) - {:with-objects? true}) - - (pcb/reorder-grid-children parents) + (-> (pcb/update-shapes + [parent-id] + (fn [frame objects] + (-> frame + ;; Assign the cell when pushing into a specific grid cell + (cond-> (some? cell) + (-> (ctl/free-cell-shapes ids) + (ctl/push-into-cell ids (:row cell) (:column cell)) + (ctl/assign-cells objects))) + (ctl/assign-cell-positions objects))) + {:with-objects? true}) + (pcb/reorder-grid-children [parent-id]))) ;; If parent locked, lock the added shapes (cond-> (:blocked parent) - (pcb/update-shapes ordered-indexes #(assoc % :blocked true))) + (pcb/update-shapes ids #(assoc % :blocked true))) ;; Resize parent containers that need to (pcb/resize-parents parents)))) -(defn generate-move-shapes-to-frame - [changes ids frame-id page-id objects drop-index [row column :as cell]] - (let [lookup (d/getf objects) - frame (get objects frame-id) - layout? (:layout frame) - - component-main-frame (ctn/find-component-main objects frame false) - - shapes (->> ids - (cfh/clean-loops objects) - (keep lookup) - ;;remove shapes inside copies, because we can't change the structure of copies - (remove #(ctk/in-component-copy? (get objects (:parent-id %))))) - - moving-shapes - (cond->> shapes - (not layout?) - (remove #(= (:frame-id %) frame-id)) - - layout? - (remove #(and (= (:frame-id %) frame-id) - (not= (:parent-id %) frame-id)))) - - ordered-indexes (cfh/order-by-indexed-shapes objects (map :id moving-shapes)) - moving-shapes (map (d/getf objects) ordered-indexes) - - all-parents - (reduce (fn [res id] - (into res (cfh/get-parent-ids objects id))) - (d/ordered-set) - ids) - - find-all-empty-parents - (fn recursive-find-empty-parents [empty-parents] - (let [all-ids (into empty-parents ids) - contains? (partial contains? all-ids) - xform (comp (map lookup) - (filter cfh/group-shape?) - (remove #(->> (:shapes %) (remove contains?) seq)) - (map :id)) - parents (into #{} xform all-parents)] - (if (= empty-parents parents) - empty-parents - (recursive-find-empty-parents parents)))) - - empty-parents - ;; Any empty parent whose children are moved to another frame should be deleted - (if (empty? moving-shapes) - #{} - (into (d/ordered-set) (find-all-empty-parents #{}))) - - ;; Not move absolute shapes that won't change parent - moving-shapes - (->> moving-shapes - (remove (fn [shape] - (and (ctl/position-absolute? shape) - (= frame-id (:parent-id shape)))))) - - frame-component - (ctn/get-component-shape objects frame) - - shape-ids-to-detach - (reduce (fn [result shape] - (if (and (some? shape) (ctk/in-component-copy-not-head? shape)) - (let [shape-component (ctn/get-component-shape objects shape)] - (if (= (:id frame-component) (:id shape-component)) - result - (into result (cfh/get-children-ids-with-self objects (:id shape))))) - result)) - #{} - moving-shapes) - - moving-shapes-ids - (map :id moving-shapes) - - moving-shapes-children-ids - (->> moving-shapes-ids - (mapcat #(cfh/get-children-ids-with-self objects %))) - - child-heads - (->> moving-shapes-ids - (mapcat #(ctn/get-child-heads objects %)) - (map :id))] - (-> changes - (pcb/with-page-id page-id) - (pcb/with-objects objects) - - ;; Remove layout-item properties when moving a shape outside a layout - (cond-> (not (ctl/any-layout? objects frame-id)) - (pcb/update-shapes moving-shapes-ids ctl/remove-layout-item-data)) - - ;; Remove the swap slots if it is moving to a different component - (pcb/update-shapes - child-heads - (fn [shape] - (cond-> shape - (not= component-main-frame (ctn/find-component-main objects shape false)) - (ctk/remove-swap-slot)))) - - ;; Remove component-root property when moving a shape inside a component - (cond-> (ctn/get-instance-root objects frame) - (pcb/update-shapes moving-shapes-children-ids #(dissoc % :component-root))) - - ;; Add component-root property when moving a component outside a component - (cond-> (not (ctn/get-instance-root objects frame)) - (pcb/update-shapes child-heads #(assoc % :component-root true))) - - (pcb/update-shapes moving-shapes-ids #(cond-> % (cfh/frame-shape? %) (assoc :hide-in-viewer true))) - (pcb/update-shapes shape-ids-to-detach ctk/detach-shape) - (pcb/change-parent frame-id moving-shapes drop-index) - - ;; Change the grid cell in a grid layout - (cond-> (ctl/grid-layout? objects frame-id) - (-> (pcb/update-shapes - [frame-id] - (fn [frame objects] - (-> frame - ;; Assign the cell when pushing into a specific grid cell - (cond-> (some? cell) - (-> (ctl/free-cell-shapes moving-shapes-ids) - (ctl/push-into-cell moving-shapes-ids row column) - (ctl/assign-cells objects))) - (ctl/assign-cell-positions objects))) - {:with-objects? true}) - (pcb/reorder-grid-children [frame-id]))) - (pcb/remove-objects empty-parents)))) (defn change-show-in-viewer [shape hide?] diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index f791eaee1..d30598974 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -1474,13 +1474,15 @@ (push-into-cell children row column)) (assign-cells objects)))) +(defn get-cell-by-index + [parent to-index] + (let [cells (get-cells parent {:sort? true :remove-empty? true}) + to-index (- (count cells) to-index)] + (nth cells to-index nil))) + (defn add-children-to-index [parent ids objects to-index] - (let [ids (into (d/ordered-set) ids) - cells (get-cells parent {:sort? true :remove-empty? true}) - to-index (- (count cells) to-index) - target-cell (nth cells to-index nil)] - + (let [target-cell (get-cell-by-index parent to-index)] (cond-> parent (some? target-cell) (add-children-to-cell ids objects [(:row target-cell) (:column target-cell)])))) diff --git a/common/test/common_tests/logic/comp_remove_swap_slots_test.cljc b/common/test/common_tests/logic/comp_remove_swap_slots_test.cljc index 3bf5d8ceb..0decac57c 100644 --- a/common/test/common_tests/logic/comp_remove_swap_slots_test.cljc +++ b/common/test/common_tests/logic/comp_remove_swap_slots_test.cljc @@ -61,41 +61,12 @@ blue1 (ths/get-shape file :blue1) ;; ==== Action - changes (cls/generate-relocate-shapes (pcb/empty-changes nil) - (:objects page) - #{(:parent-id blue1)} ;; parents - uuid/zero ;; parent-id - (:id page) ;; page-id - 0 ;; to-index - #{(:id blue1)}) ;; ids - file' (thf/apply-changes file changes) - - ;; ==== Get - blue1' (ths/get-shape file' :blue1)] - - ;; ==== Check - - ;; blue1 had swap-id before move - (t/is (some? (ctk/get-swap-slot blue1))) - - ;; blue1 has not swap-id after move - (t/is (some? blue1')) - (t/is (nil? (ctk/get-swap-slot blue1'))))) - -(t/deftest test-remove-swap-slot-move-blue1-to-root - (let [;; ==== Setup - file (setup-file) - page (thf/current-page file) - blue1 (ths/get-shape file :blue1) - - ;; ==== Action - changes (cls/generate-move-shapes-to-frame (pcb/empty-changes nil) - #{(:id blue1)} ;; ids - uuid/zero ;; frame-id - (:id page) ;; page-id - (:objects page) ;; objects - 0 ;; drop-index - nil) ;; cell + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + uuid/zero ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id blue1)}) ;; ids file' (thf/apply-changes file changes) @@ -111,7 +82,6 @@ (t/is (some? blue1')) (t/is (nil? (ctk/get-swap-slot blue1'))))) - (t/deftest test-remove-swap-slot-relocating-blue1-to-b2 (let [;; ==== Setup file (setup-file) @@ -121,43 +91,12 @@ ;; ==== Action - changes (cls/generate-relocate-shapes (pcb/empty-changes nil) - (:objects page) - #{(:parent-id blue1)} ;; parents - (:id b2) ;; parent-id - (:id page) ;; page-id - 0 ;; to-index - #{(:id blue1)}) ;; ids - file' (thf/apply-changes file changes) - - ;; ==== Get - blue1' (ths/get-shape file' :blue1)] - - ;; ==== Check - - ;; blue1 had swap-id before move - (t/is (some? (ctk/get-swap-slot blue1))) - - ;; blue1 has not swap-id after move - (t/is (some? blue1')) - (t/is (nil? (ctk/get-swap-slot blue1'))))) - -(t/deftest test-remove-swap-slot-move-blue1-to-b2 - (let [;; ==== Setup - file (setup-file) - page (thf/current-page file) - blue1 (ths/get-shape file :blue1) - b2 (ths/get-shape file :frame-b2) - - - ;; ==== Action - changes (cls/generate-move-shapes-to-frame (pcb/empty-changes nil) - #{(:id blue1)} ;; ids - (:id b2) ;; frame-id - (:id page) ;; page-id - (:objects page) ;; objects - 0 ;; drop-index - nil) ;; cell + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + (:id b2) ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id blue1)}) ;; ids file' (thf/apply-changes file changes) @@ -182,26 +121,26 @@ ;; ==== Action ;; Move blue1 into yellow - changes (cls/generate-relocate-shapes (pcb/empty-changes nil) - (:objects page) - #{(:parent-id blue1)} ;; parents - (:id yellow) ;; parent-id - (:id page) ;; page-id - 0 ;; to-index - #{(:id blue1)}) ;; ids + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + (:id yellow) ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id blue1)}) ;; ids + file' (thf/apply-changes file changes) page' (thf/current-page file') yellow' (ths/get-shape file' :frame-yellow) ;; Move yellow into root - changes' (cls/generate-relocate-shapes (pcb/empty-changes nil) - (:objects page') - #{(:parent-id yellow')} ;; parents - uuid/zero ;; parent-id - (:id page') ;; page-id - 0 ;; to-index - #{(:id yellow')}) ;; ids + changes' (cls/generate-relocate (pcb/empty-changes nil) + (:objects page') + uuid/zero ;; parent-id + (:id page') ;; page-id + 0 ;; to-index + #{(:id yellow')}) ;; ids + file'' (thf/apply-changes file' changes') ;; ==== Get @@ -216,50 +155,6 @@ (t/is (some? blue1'')) (t/is (nil? (ctk/get-swap-slot blue1''))))) -(t/deftest test-remove-swap-slot-move-yellow-to-root - (let [;; ==== Setup - file (setup-file) - page (thf/current-page file) - blue1 (ths/get-shape file :blue1) - yellow (ths/get-shape file :frame-yellow) - - ;; ==== Action - ;; Move blue1 into yellow - changes (cls/generate-move-shapes-to-frame (pcb/empty-changes nil) - #{(:id blue1)} ;; ids - (:id yellow) ;; frame-id - (:id page) ;; page-id - (:objects page) ;; objects - 0 ;; drop-index - nil) ;; cell - - file' (thf/apply-changes file changes) - page' (thf/current-page file') - yellow' (ths/get-shape file' :frame-yellow) - - ;; Move yellow into root - changes' (cls/generate-move-shapes-to-frame (pcb/empty-changes nil) - #{(:id yellow')} ;; ids - uuid/zero ;; frame-id - (:id page') ;; page-id - (:objects page') ;; objects - 0 ;; drop-index - nil) ;; cell - file'' (thf/apply-changes file' changes') - - ;; ==== Get - blue1'' (ths/get-shape file'' :blue1)] - - ;; ==== Check - - ;; blue1 had swap-id before move - (t/is (some? (ctk/get-swap-slot blue1))) - - ;; blue1 has not swap-id after move - (t/is (some? blue1'')) - (t/is (nil? (ctk/get-swap-slot blue1''))))) - - (t/deftest test-remove-swap-slot-relocating-yellow-to-b2 (let [;; ==== Setup file (setup-file) @@ -269,13 +164,13 @@ ;; ==== Action ;; Move blue1 into yellow - changes (cls/generate-relocate-shapes (pcb/empty-changes nil) - (:objects page) - #{(:parent-id blue1)} ;; parents - (:id yellow) ;; parent-id - (:id page) ;; page-id - 0 ;; to-index - #{(:id blue1)}) ;; ids + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + (:id yellow) ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id blue1)}) ;; ids + file' (thf/apply-changes file changes) page' (thf/current-page file') @@ -283,57 +178,12 @@ b2' (ths/get-shape file' :frame-b2) ;; Move yellow into b2 - changes' (cls/generate-relocate-shapes (pcb/empty-changes nil) - (:objects page') - #{(:parent-id yellow')} ;; parents - (:id b2') ;; parent-id - (:id page') ;; page-id - 0 ;; to-index - #{(:id yellow')}) ;; ids - file'' (thf/apply-changes file' changes') - - ;; ==== Get - blue1'' (ths/get-shape file'' :blue1)] - - ;; ==== Check - - ;; blue1 had swap-id before move - (t/is (some? (ctk/get-swap-slot blue1))) - - ;; blue1 has not swap-id after move - (t/is (some? blue1'')) - (t/is (nil? (ctk/get-swap-slot blue1''))))) - -(t/deftest test-remove-swap-slot-move-yellow-to-b2 - (let [;; ==== Setup - file (setup-file) - page (thf/current-page file) - blue1 (ths/get-shape file :blue1) - yellow (ths/get-shape file :frame-yellow) - - ;; ==== Action - ;; Move blue1 into yellow - changes (cls/generate-move-shapes-to-frame (pcb/empty-changes nil) - #{(:id blue1)} ;; ids - (:id yellow) ;; frame-id - (:id page) ;; page-id - (:objects page) ;; objects - 0 ;; drop-index - nil) ;; cell - - file' (thf/apply-changes file changes) - page' (thf/current-page file') - yellow' (ths/get-shape file' :frame-yellow) - b2' (ths/get-shape file' :frame-b2) - - ;; Move yellow into b2 - changes' (cls/generate-move-shapes-to-frame (pcb/empty-changes nil) - #{(:id yellow')} ;; ids - (:id b2') ;; frame-id - (:id page') ;; page-id - (:objects page') ;; objects - 0 ;; drop-index - nil) ;; cell + changes' (cls/generate-relocate (pcb/empty-changes nil) + (:objects page') + (:id b2') ;; parent-id + (:id page') ;; page-id + 0 ;; to-index + #{(:id yellow')}) ;; ids file'' (thf/apply-changes file' changes') @@ -404,13 +254,12 @@ ;; ==== Action ;; Move blue1 into yellow - changes (cls/generate-move-shapes-to-frame (pcb/empty-changes nil) - #{(:id blue1)} ;; ids - (:id yellow) ;; frame-id - (:id page) ;; page-id - (:objects page) ;; objects - 0 ;; drop-index - nil) ;; cell + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + (:id yellow) ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id blue1)}) ;; ids file' (thf/apply-changes file changes) page' (thf/current-page file') @@ -459,13 +308,13 @@ blue1 (ths/get-shape file :blue1) ;; ==== Action - changes (cls/generate-relocate-shapes (pcb/empty-changes nil) - (:objects page) - #{(:parent-id blue1)} ;; parents - (:parent-id blue1) ;; parent-id - (:id page) ;; page-id - 2 ;; to-index - #{(:id blue1)}) ;; ids + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + (:parent-id blue1) ;; parent-id + (:id page) ;; page-id + 2 ;; to-index + #{(:id blue1)}) ;; ids + file' (thf/apply-changes file changes) ;; ==== Get @@ -489,13 +338,12 @@ ;; ==== Action ;; Move blue1 into yellow - changes (cls/generate-move-shapes-to-frame (pcb/empty-changes nil) - #{(:id blue1)} ;; ids - (:id yellow) ;; frame-id - (:id page) ;; page-id - (:objects page) ;; objects - 0 ;; drop-index - nil) ;; cell + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + (:id yellow) ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id blue1)}) ;; ids file' (thf/apply-changes file changes) @@ -503,13 +351,12 @@ page' (thf/current-page file') blue1' (ths/get-shape file' :blue1) b1' (ths/get-shape file' :frame-b1) - changes' (cls/generate-move-shapes-to-frame (pcb/empty-changes nil) - #{(:id blue1')} ;; ids - (:id b1') ;; frame-id - (:id page') ;; page-id - (:objects page') ;; objects - 0 ;; drop-index - nil) ;; cell + changes' (cls/generate-relocate (pcb/empty-changes nil) + (:objects page') + (:id b1') ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id blue1')}) ;; ids file'' (thf/apply-changes file' changes') @@ -535,13 +382,13 @@ ;; ==== Action ;; Relocate blue1 into yellow - changes (cls/generate-relocate-shapes (pcb/empty-changes nil) - (:objects page) - #{(:parent-id blue1)} ;; parents - (:id yellow) ;; parent-id - (:id page) ;; page-id - 0 ;; to-index - #{(:id blue1)}) ;; ids + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + (:id yellow) ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id blue1)}) ;; ids + file' (thf/apply-changes file changes) @@ -549,13 +396,13 @@ page' (thf/current-page file') blue1' (ths/get-shape file' :blue1) b1' (ths/get-shape file' :frame-b1) - changes' (cls/generate-relocate-shapes (pcb/empty-changes nil) - (:objects page') - #{(:parent-id blue1')} ;; parents - (:id b1') ;; parent-id - (:id page') ;; page-id - 0 ;; to-index - #{(:id blue1')}) ;; ids + changes' (cls/generate-relocate (pcb/empty-changes nil) + (:objects page') + (:id b1') ;; parent-id + (:id page') ;; page-id + 0 ;; to-index + #{(:id blue1')}) ;; ids + file'' (thf/apply-changes file' changes') @@ -581,43 +428,12 @@ green-copy (ths/get-shape file :green-copy) ;; ==== Action - changes (cls/generate-relocate-shapes (pcb/empty-changes nil) - (:objects page) - #{(:parent-id green-copy)} ;; parents - uuid/zero ;; parent-id - (:id page) ;; page-id - 0 ;; to-index - #{(:id green-copy)}) ;; ids - file' (thf/apply-changes file changes) - - ;; ==== Get - blue2' (ths/get-shape file' :blue2)] - - ;; ==== Check - - ;; blue2 had swap-id before move - (t/is (some? (ctk/get-swap-slot blue2))) - - ;; blue1still has swap-id after move - (t/is (some? blue2')) - (t/is (some? (ctk/get-swap-slot blue2'))))) - -(t/deftest test-remove-swap-slot-moving-green-copy-to-root - (let [;; ==== Setup - file (setup-file) - - page (thf/current-page file) - blue2 (ths/get-shape file :blue2) - green-copy (ths/get-shape file :green-copy) - - ;; ==== Action - changes (cls/generate-move-shapes-to-frame (pcb/empty-changes nil) - #{(:id green-copy)} ;; ids - uuid/zero ;; frame-id - (:id page) ;; page-id - (:objects page) ;; objects - 0 ;; drop-index - nil) ;; cell + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + uuid/zero ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id green-copy)}) ;; ids file' (thf/apply-changes file changes) @@ -633,7 +449,6 @@ (t/is (some? blue2')) (t/is (some? (ctk/get-swap-slot blue2'))))) - (t/deftest test-remove-swap-slot-relocating-green-copy-to-b2 (let [;; ==== Setup file (setup-file) @@ -644,44 +459,12 @@ b2 (ths/get-shape file :frame-b2) ;; ==== Action - changes (cls/generate-relocate-shapes (pcb/empty-changes nil) - (:objects page) - #{(:parent-id green-copy)} ;; parents - (:id b2) ;; parent-id - (:id page) ;; page-id - 0 ;; to-index - #{(:id green-copy)}) ;; ids - file' (thf/apply-changes file changes) - - ;; ==== Get - blue2' (ths/get-shape file' :blue2)] - - ;; ==== Check - - ;; blue2 had swap-id before move - (t/is (some? (ctk/get-swap-slot blue2))) - - ;; blue1still has swap-id after move - (t/is (some? blue2')) - (t/is (some? (ctk/get-swap-slot blue2'))))) - -(t/deftest test-remove-swap-slot-moving-green-copy-to-b2 - (let [;; ==== Setup - file (setup-file) - - page (thf/current-page file) - blue2 (ths/get-shape file :blue2) - green-copy (ths/get-shape file :green-copy) - b2 (ths/get-shape file :frame-b2) - - ;; ==== Action - changes (cls/generate-move-shapes-to-frame (pcb/empty-changes nil) - #{(:id green-copy)} ;; ids - (:id b2) ;; frame-id - (:id page) ;; page-id - (:objects page) ;; objects - 0 ;; drop-index - nil) ;; cell + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + (:id b2) ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id green-copy)}) ;; ids file' (thf/apply-changes file changes) @@ -697,7 +480,6 @@ (t/is (some? blue2')) (t/is (some? (ctk/get-swap-slot blue2'))))) - (t/deftest test-remove-swap-slot-duplicating-green-copy (let [;; ==== Setup file (setup-file) diff --git a/common/test/common_tests/logic/comp_reset_test.cljc b/common/test/common_tests/logic/comp_reset_test.cljc index d7f441ed9..1c663917e 100644 --- a/common/test/common_tests/logic/comp_reset_test.cljc +++ b/common/test/common_tests/logic/comp_reset_test.cljc @@ -136,13 +136,13 @@ ;; IMPORTANT: as modifying copies structure is now forbidden, this action ;; will not have any effect, and so the parent shape won't also be touched. - changes (cls/generate-relocate-shapes (pcb/empty-changes) - (:objects page) - #{(:parent-id copy-root)} ; parents - (thi/id :copy-root) ; parent-id - (:id page) ; page-id - 0 ; to-index - #{(thi/id :free-shape)}) ; ids + changes (cls/generate-relocate (pcb/empty-changes) + (:objects page) + (thi/id :copy-root) ; parent-id + (:id page) ; page-id + 0 ; to-index + #{(thi/id :free-shape)}) ; ids + file-mdf (thf/apply-changes file changes) page-mdf (thf/current-page file-mdf) @@ -231,13 +231,13 @@ ;; IMPORTANT: as modifying copies structure is now forbidden, this action ;; will not have any effect, and so the parent shape won't also be touched. - changes (cls/generate-relocate-shapes (pcb/empty-changes) - (:objects page) - #{(:parent-id copy-child1)} ; parents - (thi/id :copy-root) ; parent-id - (:id page) ; page-id - 2 ; to-index - #{(:id copy-child1)}) ; ids + changes (cls/generate-relocate (pcb/empty-changes) + (:objects page) + (thi/id :copy-root) ; parent-id + (:id page) ; page-id + 2 ; to-index + #{(:id copy-child1)}) ; ids + file-mdf (thf/apply-changes file changes) page-mdf (thf/current-page file-mdf) diff --git a/common/test/common_tests/logic/comp_sync_test.cljc b/common/test/common_tests/logic/comp_sync_test.cljc index 75abacea3..94f093ff3 100644 --- a/common/test/common_tests/logic/comp_sync_test.cljc +++ b/common/test/common_tests/logic/comp_sync_test.cljc @@ -196,13 +196,15 @@ main-root (ths/get-shape file :main-root) ;; ==== Action - changes1 (cls/generate-relocate-shapes (pcb/empty-changes) - (:objects page) - #{(:parent-id main-root)} ; parents - (thi/id :main-root) ; parent-id - (:id page) ; page-id - 0 ; to-index - #{(thi/id :free-shape)}) ; ids + changes1 (cls/generate-relocate (pcb/empty-changes) + (:objects page) + (thi/id :main-root) ; parent-id + (:id page) ; page-id + 0 ; to-index + #{(thi/id :free-shape)}) ; ids + + + updated-file (thf/apply-changes file changes1) @@ -294,13 +296,13 @@ main-child1 (ths/get-shape file :main-child1) ;; ==== Action - changes1 (cls/generate-relocate-shapes (pcb/empty-changes) - (:objects page) - #{(:parent-id main-child1)} ; parents - (thi/id :main-root) ; parent-id - (:id page) ; page-id - 2 ; to-index - #{(:id main-child1)}) ; ids + changes1 (cls/generate-relocate (pcb/empty-changes) + (:objects page) + (thi/id :main-root) ; parent-id + (:id page) ; page-id + 2 ; to-index + #{(:id main-child1)}) ; ids + updated-file (thf/apply-changes file changes1) diff --git a/common/test/common_tests/logic/comp_touched_test.cljc b/common/test/common_tests/logic/comp_touched_test.cljc index a0907e37c..1f16a2107 100644 --- a/common/test/common_tests/logic/comp_touched_test.cljc +++ b/common/test/common_tests/logic/comp_touched_test.cljc @@ -112,13 +112,13 @@ ;; IMPORTANT: as modifying copies structure is now forbidden, this action ;; will not have any effect, and so the parent shape won't also be touched. - changes (cls/generate-relocate-shapes (pcb/empty-changes) - (:objects page) - #{(:parent-id copy-root)} ; parents - (thi/id :copy-root) ; parent-id - (:id page) ; page-id - 0 ; to-index - #{(thi/id :free-shape)}) ; ids + changes (cls/generate-relocate (pcb/empty-changes) + (:objects page) + (thi/id :copy-root) ; parent-id + (:id page) ; page-id + 0 ; to-index + #{(thi/id :free-shape)}) ; ids + file' (thf/apply-changes file changes) @@ -187,13 +187,13 @@ ;; IMPORTANT: as modifying copies structure is now forbidden, this action ;; will not have any effect, and so the parent shape won't also be touched. - changes (cls/generate-relocate-shapes (pcb/empty-changes) - (:objects page) - #{(:parent-id copy-child1)} ; parents - (thi/id :copy-root) ; parent-id - (:id page) ; page-id - 2 ; to-index - #{(:id copy-child1)}) ; ids + changes (cls/generate-relocate (pcb/empty-changes) + (:objects page) + (thi/id :copy-root) ; parent-id + (:id page) ; page-id + 2 ; to-index + #{(:id copy-child1)}) ; ids + file' (thf/apply-changes file changes) diff --git a/common/test/common_tests/logic/move_shapes_test.cljc b/common/test/common_tests/logic/move_shapes_test.cljc new file mode 100644 index 000000000..f85a72cbe --- /dev/null +++ b/common/test/common_tests/logic/move_shapes_test.cljc @@ -0,0 +1,84 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns common-tests.logic.move-shapes-test + (:require + [app.common.files.changes-builder :as pcb] + [app.common.logic.shapes :as cls] + [app.common.test-helpers.compositions :as tho] + [app.common.test-helpers.files :as thf] + [app.common.test-helpers.ids-map :as thi] + [app.common.test-helpers.shapes :as ths] + [app.common.uuid :as uuid] + [clojure.test :as t])) + +(t/use-fixtures :each thi/test-fixture) + +(t/deftest test-relocate-shape + (let [;; ==== Setup + file (-> (thf/sample-file :file1) + (tho/add-frame :frame-to-move) + (tho/add-frame :frame-parent)) + + page (thf/current-page file) + frame-to-move (ths/get-shape file :frame-to-move) + frame-parent (ths/get-shape file :frame-parent) + + ;; ==== Action + + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + (:id frame-parent) ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id frame-to-move)}) ;; ids + + file' (thf/apply-changes file changes) + + ;; ==== Get + frame-to-move' (ths/get-shape file' :frame-to-move) + frame-parent' (ths/get-shape file' :frame-parent)] + + ;; ==== Check + ;; frame-to-move has moved + (t/is (= (:parent-id frame-to-move) uuid/zero)) + (t/is (= (:parent-id frame-to-move') (:id frame-parent'))))) + + +(t/deftest test-relocate-shape-out-of-group + (let [;; ==== Setup + file (-> (thf/sample-file :file1) + (tho/add-frame :frame-1) + (tho/add-group :group-1 :parent-label :frame-1) + (ths/add-sample-shape :circle-1 :parent-label :group-1)) + + page (thf/current-page file) + circle (ths/get-shape file :circle-1) + group (ths/get-shape file :group-1) + + ;; ==== Action + + changes (cls/generate-relocate (pcb/empty-changes nil) + (:objects page) + uuid/zero ;; parent-id + (:id page) ;; page-id + 0 ;; to-index + #{(:id circle)}) ;; ids + + + file' (thf/apply-changes file changes) + + ;; ==== Get + circle' (ths/get-shape file' :circle-1) + group' (ths/get-shape file' :group-1)] + + ;; ==== Check + + ;; the circle has moved, and the group is deleted + (t/is (= (:parent-id circle) (:id group))) + (t/is (= (:parent-id circle') uuid/zero)) + (t/is group) + (t/is (nil? group')))) \ No newline at end of file diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index fd4fa64c9..3c0e9229c 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -856,15 +856,14 @@ ids (filter #(not (cfh/is-parent? objects parent-id %)) ids) all-parents (into #{parent-id} (map #(cfh/get-parent-id objects %)) ids) - parents (if ignore-parents? #{parent-id} all-parents) - changes (cls/generate-relocate-shapes (pcb/empty-changes it) - objects - parents - parent-id - page-id - to-index - ids) + changes (cls/generate-relocate (pcb/empty-changes it) + objects + parent-id + page-id + to-index + ids + :ignore-parents? ignore-parents?) undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index abb7a7c83..3107f9ace 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -832,6 +832,30 @@ :ignore-constraints false :ignore-snap-pixel true})))))) +(defn- cleanup-invalid-moving-shapes [ids objects frame-id] + (let [lookup (d/getf objects) + frame (get objects frame-id) + layout? (:layout frame) + + shapes (->> ids + set + (cfh/clean-loops objects) + (keep lookup) + ;;remove shapes inside copies, because we can't change the structure of copies + (remove #(ctk/in-component-copy? (get objects (:parent-id %)))) + ;; remove absolute shapes that won't change parent + (remove #(and (ctl/position-absolute? %) (= frame-id (:parent-id %))))) + + shapes + (cond->> shapes + (not layout?) + (remove #(= (:frame-id %) frame-id)) + + layout? + (remove #(and (= (:frame-id %) frame-id) + (not= (:parent-id %) frame-id))))] + (map :id shapes))) + (defn move-shapes-to-frame [ids frame-id drop-index cell] (ptk/reify ::move-shapes-to-frame @@ -839,7 +863,14 @@ (watch [it state _] (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) - changes (cls/generate-move-shapes-to-frame (pcb/empty-changes it) ids frame-id page-id objects drop-index cell)] + ids (cleanup-invalid-moving-shapes ids objects frame-id) + changes (cls/generate-relocate (pcb/empty-changes it) + objects + frame-id + page-id + drop-index + ids + :cell cell)] (when (and (some? frame-id) (d/not-empty? changes)) (rx/of (dch/commit-changes changes)