diff --git a/common/src/app/common/logic/swap.cljc b/common/src/app/common/logic/swap.cljc index c20e45986a..7b70c9aac7 100644 --- a/common/src/app/common/logic/swap.cljc +++ b/common/src/app/common/logic/swap.cljc @@ -343,6 +343,26 @@ (ctf/take-position file* libraries objects new-shape (:shape-ref shape) position) {:ignore-touched true}))) +(defn generate-sync-swap-constraints + "Give a nested swap the constraints of its position, after a switch has + carried any compatible overrides. Instantiation supplies content-main + constraints, which do not own the swapped head's placement." + [changes page libraries shape-id] + (let [objects (pcb/get-objects changes) + shape (get objects shape-id) + parent (get objects (:parent-id shape)) + position (when (and (ctk/in-component-copy? parent) + (not (ctk/touched-group? shape :constraints-group))) + (ctf/find-ref-shape nil (assoc page :objects objects) libraries shape + :include-deleted? true))] + (cond-> changes + position + (pcb/update-shapes [shape-id] + #(d/patch-object % {:constraints-h (:constraints-h position) + :constraints-v (:constraints-v position) + :fixed-scroll (:fixed-scroll position)}) + {:ignore-touched true})))) + (defn generate-new-shape-for-swap [changes shape file page libraries id-new-component index target-cell keep-props-values] (let [objects (:objects page) @@ -408,5 +428,9 @@ {:allow-altering-copies true :ignore-children-fn ignore-swapped-fn :ignore-mask true :ignore-flows-for #{(:id shape)}})) [new-shape changes] (-> changes - (generate-new-shape-for-swap shape file page libraries id-new-component index target-cell keep-props-values))] + (generate-new-shape-for-swap shape file page libraries id-new-component index target-cell keep-props-values)) + ;; A switch carries overrides before taking untouched constraints. + changes (cond-> changes + (not ignore-swapped?) + (generate-sync-swap-constraints page libraries (:id new-shape)))] [new-shape all-parents changes])) diff --git a/common/src/app/common/logic/variants.cljc b/common/src/app/common/logic/variants.cljc index ed055571a2..4aedb15c13 100644 --- a/common/src/app/common/logic/variants.cljc +++ b/common/src/app/common/logic/variants.cljc @@ -444,7 +444,8 @@ (some #(ctk/touched-group? % :geometry-group) orig-touched) (cll/generate-sync-shape-direct nil libraries (assoc container :objects (pcb/get-objects changes)) - (:id new-shape) false))] + (:id new-shape) false)) + changes (clsw/generate-sync-swap-constraints changes page libraries (:id new-shape))] [changes (mapv (comp :parent-id :new) pairs)])) (defn- shape-index diff --git a/common/test/common_tests/logic/composable_gen_test.cljc b/common/test/common_tests/logic/composable_gen_test.cljc index 8ad1b6867b..e2571701e1 100644 --- a/common/test/common_tests/logic/composable_gen_test.cljc +++ b/common/test/common_tests/logic/composable_gen_test.cljc @@ -29,6 +29,12 @@ (t/use-fixtures :each thi/test-fixture) +(t/deftest swapping-a-copy-with-authored-position-constraints-stays-settled + (t/is (map? (cg/run {:world [2 1 {:library true :variants true :rich-variants true + :text true :text-styles true :layout :flex :layout-copies true}] + :ops [[:style 12 9 11 6] [:delete 10 14 10 12] [:swap 17 2 14 5] + [:undo] [:redo]]})))) + (t/deftest nesting-a-swapped-copy-in-a-blocked-main-authors-its-lock (t/is (map? (cg/run {:world [2 0 {:library true :variants true :rich-variants true :text true :text-styles true :layout nil :layout-copies true}] diff --git a/common/test/common_tests/logic/swap_group_test.cljc b/common/test/common_tests/logic/swap_group_test.cljc index b6709425f9..9820cd04a0 100644 --- a/common/test/common_tests/logic/swap_group_test.cljc +++ b/common/test/common_tests/logic/swap_group_test.cljc @@ -652,6 +652,56 @@ (thc/make-component :card :card-main) (thc/instantiate-component :card :copy :children-labels [:copy-star]))) +(t/deftest swapping-a-nested-copy-takes-constraints-from-its-position + (let [initial (-> (tm/make-situation (setup-sized)) + (r/step (n/change-property :card-star :constraints-h :right)) + (r/step (n/change-property :card-star :constraints-v :bottom)) + (r/step (n/change-property :card-star :fixed-scroll true)) + r/settle + (r/step (n/change-property :copy-star :constraints-h :scale)) + (r/step (n/change-property :copy-star :constraints-v :scale)) + (r/step (n/change-property :copy-star :fixed-scroll false)) + r/settle) + swapped (-> initial (r/step (n/swap-shape :copy-star :heart)) r/settle) + head (tm/resolve-shape swapped :copy-star) + placement #(select-keys % [:constraints-h :constraints-v :fixed-scroll]) + synced (tm/with-file swapped (propagate (tm/file swapped) :card)) + undone (r/step swapped (n/undo)) + redone (r/step undone (n/redo))] + (t/is (= {:constraints-h :right :constraints-v :bottom :fixed-scroll true} + (placement head))) + (t/is (= #{:swap-group} (:touched head))) + (t/is (= (placement head) (placement (tm/resolve-shape synced :copy-star)))) + (t/is (= (thn/normalize-file (tm/file initial)) + (thn/normalize-file (tm/file undone)))) + (t/is (= (thn/normalize-file (tm/file swapped)) + (thn/normalize-file (tm/file redone)))))) + +(t/deftest swapping-a-nested-copy-clears-content-main-constraints-at-a-default-position + (let [initial (-> (tm/make-situation (setup-sized)) + (r/step (n/change-property :heart-main :constraints-h :right)) + (r/step (n/change-property :heart-main :constraints-v :bottom)) + (r/step (n/change-property :heart-main :fixed-scroll true)) + r/settle) + swapped (-> initial (r/step (n/swap-shape :copy-star :heart)) r/settle) + placement #(select-keys % [:constraints-h :constraints-v :fixed-scroll])] + (t/is (= (placement (tm/resolve-shape initial :card-star)) + (placement (tm/resolve-shape swapped :copy-star)))) + (t/is (= #{:swap-group} (:touched (tm/resolve-shape swapped :copy-star)))))) + +(t/deftest switching-a-nested-copy-retains-compatible-authored-constraints + (let [initial (-> (tm/make-situation (setup-sized)) + (r/step (n/change-property :card-star :constraints-v :bottom)) + r/settle + (r/step (n/change-property :copy-star :constraints-v :scale)) + r/settle) + switched (-> initial (r/step (n/swap-shape :copy-star :heart :keep-touched? true)) r/settle) + head (tm/resolve-shape switched :copy-star)] + (t/is (= :scale (:constraints-v head))) + (t/is (= #{:swap-group :constraints-group} (:touched head))) + (t/is (= (thn/normalize-file (tm/file initial)) + (thn/normalize-file (tm/file (r/step switched (n/undo)))))))) + (defn- orientation [file label] (let [shape (ths/get-shape file label)] diff --git a/frontend/test/frontend_tests/composable_tests/generated_test.cljs b/frontend/test/frontend_tests/composable_tests/generated_test.cljs index 6bf3356baa..66c8550fc0 100644 --- a/frontend/test/frontend_tests/composable_tests/generated_test.cljs +++ b/frontend/test/frontend_tests/composable_tests/generated_test.cljs @@ -88,6 +88,12 @@ rx/timer mock/timer-mock} (await (replay-program program))))) +(t/deftest ^:async swapping-a-copy-with-authored-position-constraints-stays-settled + (await (replay {:world [2 1 {:library true :variants true :rich-variants true + :text true :text-styles true :layout :flex :layout-copies true}] + :ops [[:style 12 9 11 6] [:delete 10 14 10 12] [:swap 17 2 14 5] + [:undo] [:redo]]}))) + (t/deftest ^:async accepting-a-library-does-not-record-equal-numeric-geometry (await (replay {:world [2 1 {:library true :variants true}] :ops [[:resize 10 2 2 10] [:library-sync 3 1 2 8]