mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 05:50:35 +00:00
🐛 Take nested swap constraints from their position source
Settle a nested swap's untouched constraint group from the replaced position, including fixed-scroll. Carry compatible switch overrides first so an inherited position cannot discard an authored constraint override. Cover F224's reduced history in common and the async frontend, both constraint axes, absent position fields, compatible carry and exact undo. The complete original history and browser ShapeProxy replay also pass. Validation: focused swap/variant/golden tests (123 tests, 1,764 assertions), full common JVM/JS, frontend, backend and exporter suites; lint and format. Golden data is unchanged. Benchmark ratios against the unchanged baseline, with no regression flags: mains 10/100/1,000: 0.53/0.58/0.63; three levels: 0.56; chain: 0.55; library: 0.56; many shapes: 0.48; variant switch: 0.65. The baseline uses another machine/JVM, so ratios are rough comparisons. AI-assisted-by: GPT-6
This commit is contained in:
parent
11d7a630dc
commit
ca97bae421
@ -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]))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}]
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user