mirror of
https://github.com/penpot/penpot.git
synced 2026-10-30 22:40:33 +00:00
🐛 Keep nested copies in a detached parent's orientation during sync
Express the source subtree in its ordinary parent's orientation before syncing a surviving nested copy. Detaching a rotated outer instance no longer makes a later main edit turn the nested copy back. Fix F131. The focused regression failed on points and rotation before the fix. Geometry/reset suites: 19 tests, 111 assertions, all green. Benchmark ratios against Phase 1e: main 10/100/1000 0.46/0.52/0.53; three levels 0.47; chain 0.51; library 0.51; many shapes 0.49; variant switch 0.61. No regression flag. AI-assisted-by: GPT-6
This commit is contained in:
parent
1ed6476a53
commit
6831417993
@ -260,6 +260,27 @@
|
||||
:include-deleted? true))]
|
||||
(recur shape-inst' next-data component')))))))
|
||||
|
||||
(defn- main-in-parent-orientation
|
||||
"Place a main in the orientation of an ordinary instance parent. Such a
|
||||
parent can survive detach, while the nested copy stays untouched."
|
||||
[main-container shape-main container shape-inst]
|
||||
(let [parent (ctn/get-shape container (:parent-id shape-inst))
|
||||
main-parent (get-in main-container [:objects (:parent-id shape-main)])
|
||||
relative (gmt/multiply (or (:transform parent) (gmt/matrix))
|
||||
(or (:transform-inverse main-parent) (gmt/matrix)))]
|
||||
(if (or (nil? parent) (:shape-ref parent) (gmt/unit? relative))
|
||||
main-container
|
||||
(let [transform (gmt/transform-in (gsh/shape->center shape-main) relative)
|
||||
angle (- (or (:rotation parent) 0) (or (:rotation main-parent) 0))
|
||||
turn #(-> %
|
||||
(assoc-in [:modifiers :rotation] angle)
|
||||
(gsh/apply-transform transform)
|
||||
(dissoc :modifiers))]
|
||||
(update main-container :objects
|
||||
(fn [objects]
|
||||
(reduce #(update %1 %2 turn) objects
|
||||
(cfh/get-children-ids-with-self objects (:id shape-main)))))))))
|
||||
|
||||
(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."
|
||||
@ -275,11 +296,14 @@
|
||||
(let [shape-main (if reset?
|
||||
;; the reset is against the ref-shape, not against the original shape of the component
|
||||
(ctf/find-ref-shape file container libraries shape-inst :include-deleted? true)
|
||||
(ctf/get-content-ref-shape library component shape-inst))]
|
||||
(ctf/get-content-ref-shape library component shape-inst))
|
||||
main-container (find-main-container container shape-inst shape-main
|
||||
library file libraries component)
|
||||
main-container (main-in-parent-orientation main-container shape-main container shape-inst)
|
||||
shape-main (ctn/get-shape main-container (:id shape-main))]
|
||||
(sync-copy-recursive changes
|
||||
{:container container
|
||||
:main-container (find-main-container container shape-inst shape-main
|
||||
library file libraries component)
|
||||
:main-container main-container
|
||||
:component component
|
||||
:library library
|
||||
:root-inst shape-inst
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
[app.common.test-helpers.components :as thc]
|
||||
[app.common.test-helpers.composable.comp.nodes :as n]
|
||||
[app.common.test-helpers.composable.comp.runner :as r]
|
||||
[app.common.test-helpers.composable.comp.setups :as setup]
|
||||
[app.common.test-helpers.composable.core :as tm]
|
||||
[app.common.test-helpers.compositions :as tho]
|
||||
[app.common.test-helpers.files :as thf]
|
||||
@ -34,6 +35,20 @@
|
||||
|
||||
(t/use-fixtures :each thi/test-fixture)
|
||||
|
||||
(t/deftest a-nested-copy-keeps-its-detached-parents-orientation-on-sync
|
||||
(let [detached (-> (setup/nested-component-with-copy)
|
||||
(r/step (n/rotate :copy-root 90)) r/settle
|
||||
(r/step (n/detach :copy-root)) r/settle)
|
||||
head (tm/role-shape detached :copy-instance)
|
||||
id (first (:shapes head))
|
||||
before (tm/shape-by-id detached id)
|
||||
synced (-> detached (r/step (n/change-property :main1-child :fills "#ff0000")) r/settle)
|
||||
after (tm/shape-by-id synced id)]
|
||||
(t/is (= "#ff0000" (-> after :fills first :fill-color)))
|
||||
(t/is (gsh/close-attrs? :points (:points before) (:points after) 0.001))
|
||||
(t/is (= (:rotation before) (:rotation after)))
|
||||
(t/is (nil? (:touched after)))))
|
||||
|
||||
(defn- component-with-child
|
||||
"A 100x100 main board with a 20x20 rect at (70, 10) of it, constrained
|
||||
`constraints-h`, and a copy of it at (300, 0)."
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user