✨ Don't show duplicate cursor when selection cannot be alt-duplicated (#11195)

* 🐛 Don't show duplicate cursor when selection cannot be alt-duplicated (#11165)

When pressing Alt and dragging a shape that is inside a component copy
(but is not its root), Penpot showed a :duplicate cursor, suggesting
the operation would clone the shape.  However duplicate-shapes filters
those shapes out via ctk/allow-duplicate?, so the move proceeds but no
duplicate is ever created — the cursor lied.

Fix: compute can-alt-duplicate? in the viewport, which is truthy only
when at least one selected shape passes ctk/allow-duplicate?.  Pass it
to setup-cursor and gate the :duplicate cursor branch on it.  When
none of the selected shapes can be duplicated the cursor falls through
to :pointer-inner, honestly indicating that only a move will happen.

* 📎 Add alt-duplicate check to cursor setup

Signed-off-by: Andrey Antukh <niwi@niwi.nz>

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
0xTHAC0 2026-09-24 18:59:33 +05:30 committed by GitHub
parent e3e75725d4
commit 128c495b2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 19 additions and 5 deletions

View File

@ -129,6 +129,12 @@
(into [] (keep (d/getf objects-modified)))
(not-empty))
;; True when at least one selected shape can be alt-duplicated.
;; Inner shapes of component copies are excluded by ctk/allow-duplicate?
can-alt-duplicate? (mf/with-memo [selected base-objects]
(some #(ctk/allow-duplicate? base-objects %)
(map (d/getf base-objects) selected)))
;; STATE
alt? (mf/use-state false)
shift? (mf/use-state false)
@ -317,7 +323,7 @@
(hooks/setup-dom-events zoom disable-paste-ref in-viewport-ref read-only? drawing-tool path-drawing?)
(hooks/setup-viewport-size vport viewport-ref)
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool path-drawing? path-editing? (get path-bar-state :drag-cursor) z? read-only?)
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool path-drawing? path-editing? (get path-bar-state :drag-cursor) z? read-only? can-alt-duplicate?)
(hooks/setup-keyboard alt? mod? space? z? shift?)
(hooks/setup-hover-shapes page-id move-stream base-objects selected mod? hover measure-hover
hover-ids hover-top-frame-id @hover-disabled? focus zoom show-measures? read-only? transform)

View File

@ -92,9 +92,9 @@
(when (not= size vport)
(st/emit! (dw/initialize-viewport (dom/get-client-size prnt)))))))
(defn setup-cursor [cursor alt? mod? space? panning drawing-tool drawing-path? path-editing? path-drag-cursor z? workspace-read-only?]
(defn setup-cursor [cursor alt? mod? space? panning drawing-tool drawing-path? path-editing? path-drag-cursor z? workspace-read-only? can-alt-duplicate?]
(mf/use-effect
(mf/deps @cursor @alt? @mod? @space? panning drawing-tool drawing-path? path-editing? path-drag-cursor z? workspace-read-only?)
(mf/deps @cursor @alt? @mod? @space? panning drawing-tool drawing-path? path-editing? path-drag-cursor z? workspace-read-only? can-alt-duplicate?)
(fn []
(let [show-pen? (or (= drawing-tool :path)
(and drawing-path?
@ -123,7 +123,8 @@
path-editing? (utils/get-cursor :edit-path)
(and
@alt?
(not workspace-read-only?)) (utils/get-cursor :duplicate)
(not workspace-read-only?)
can-alt-duplicate?) (utils/get-cursor :duplicate)
:else (utils/get-cursor :pointer-inner))]
(when (not= @cursor new-cursor)

View File

@ -204,6 +204,13 @@
selected-shapes (->> selected
(into [] (keep (d/getf objects-modified)))
(not-empty))
;; True when at least one selected shape can be alt-duplicated.
;; Inner shapes of component copies are excluded by ctk/allow-duplicate?
can-alt-duplicate? (mf/with-memo [selected base-objects]
(some #(ctk/allow-duplicate? base-objects %)
(map (d/getf base-objects) selected)))
;; STATE
alt? (mf/use-state false)
shift? (mf/use-state false)
@ -630,7 +637,7 @@
(hooks/setup-dom-events zoom disable-paste-ref in-viewport-ref read-only? drawing-tool path-drawing?)
(hooks/setup-viewport-size vport viewport-ref)
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool path-drawing? path-editing? (get path-bar-state :drag-cursor) z? read-only?)
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool path-drawing? path-editing? (get path-bar-state :drag-cursor) z? read-only? can-alt-duplicate?)
(hooks/setup-keyboard alt? mod? space? z? shift?)
(hooks/setup-hover-shapes page-id move-stream base-objects selected mod? hover measure-hover
hover-ids hover-top-frame-id @hover-disabled? focus zoom show-measures? read-only? transform)