Create in a lane at the frame double-clicked, palette lanes included

The gesture was handed the pointer's frame and threw it away, creating at
the playhead instead; and the palette row carried :lane? only once its
track existed, so the first double-click on a palette lane -- the one that
has to make the track -- dispatched nothing at all.

Both are one rule now. ::new-symbol-at uses the frame it is given and
resolves every row through drop-destination, with the destination deciding
what is created: a clip of a palette track is a palette symbol, a clip of
any other lane is blank. A row with no path of its own resolves to the
symbol it names, which is what lets a palette track -- hanging off its
owner by :palette-track rather than placed in it -- be reached without a
special case; it also stops a palette cel's slide resolving against the
open symbol and looking like a transfer out of the track. The one thing
left that knows about palettes is materializing the lane a palette row
names before anything asks where the row leads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-10-03 03:52:39 -04:00
parent 09b74de162
commit 90b1fbe2f8
3 changed files with 89 additions and 36 deletions

View file

@ -72,11 +72,14 @@
(vals (get-in saved [:symbols :main :nodes]))))
"claiming the frame leaves no overlapping cel"))))
(deftest double-click-creation-uses-the-lane-and-playhead
(deftest double-click-creation-uses-the-lane-and-the-frame-double-clicked
(let [doc (fixture/document)
id (store/install! {:clip doc :store {}} "double-click-new-symbol")]
;; THE PLAYHEAD IS SOMEWHERE ELSE, deliberately: the gesture names a frame
;; as well as a lane, and the empty space double-clicked is where the new
;; symbol goes.
(reset! rf-db/app-db {:clip/current id :paint/revision 0
:ui {:open :main} :playback {:frame 5}})
:ui {:open :main} :playback {:frame 9}})
(rf/dispatch-sync [::ui/new-symbol-at 5 [:node :main nil []]])
(let [saved (:clip (store/entry id))
[_ sid instance-id] (get-in @rf-db/app-db [:ui :selection])
@ -88,14 +91,28 @@
(is (= (node/source instance)
(:sid (creation/target saved {} :main selection 5)))
"the new cel can immediately be selected as the creation target")
(is (= 5 (get-in @rf-db/app-db [:playback :frame]))
"the playhead chooses the new cel's time"))))
(is (= 9 (get-in @rf-db/app-db [:playback :frame]))
"creating in a lane does not move the playhead"))))
(deftest the-palette-row-is-a-lane-row-whether-or-not-its-track-exists
(let [empty-row (timeline/palette-track-row (clip/blank) :main 120 #{} nil)
filled (-> (clip/blank)
(assoc-in [:symbols :main :palette-track] :track)
(assoc-in [:symbols :track]
{:id :track :type :palette-track :display :lane
:frames 120 :nodes {}}))
filled-row (timeline/palette-track-row filled :main 120 #{} nil)]
;; The double-click that creates in a lane asks nothing else of a row.
(is (:lane? empty-row))
(is (:lane? filled-row))
(is (= [:node :track nil []] (:select filled-row))
"a track that exists is addressed as the lane it is")))
(deftest the-same-double-click-command-creates-a-palette-symbol-on-the-palette-row
(let [doc (clip/blank)
id (store/install! {:clip doc :store {}} "double-click-palette-symbol")]
(reset! rf-db/app-db {:clip/current id :paint/revision 0
:ui {:open :main} :playback {:frame 5}})
:ui {:open :main} :playback {:frame 9}})
(rf/dispatch-sync [::ui/new-symbol-at 5
[:arthur.ui.timeline/palette-track :main]])
(let [saved (:clip (store/entry id))
@ -108,7 +125,19 @@
(is (= :palette (get-in saved [:symbols source :type])))
(is (= [5 6] (node/placed-span instance)))
(is (= pal/inherit
(get-in instance [:channels [:palette] :value]))))))
(get-in instance [:channels [:palette] :value])))
;; ONCE THE TRACK EXISTS ITS ROW IS AN ORDINARY LANE ROW, which is the
;; address the pane then hands back: the second transition is created by
;; the same command resolving the same way as any other lane's.
(rf/dispatch-sync [::ui/new-symbol-at 20 [:node track-id nil []]])
(let [saved (:clip (store/entry id))
[_ sid2 id2] (get-in @rf-db/app-db [:ui :selection])
second-clip (get-in saved [:symbols sid2 :nodes id2])]
(is (= track-id sid2) "it lands in the same palette track")
(is (= :palette (get-in saved [:symbols (node/source second-clip) :type])))
(is (= [20 21] (node/placed-span second-clip)))
(is (= pal/inherit
(get-in second-clip [:channels [:palette] :value])))))))
(deftest a-new-lane-uses-the-symbol-selected-at-the-playhead
(let [doc (clip/blank)