openpencil/packages/docs/user-guide/selection-and-manipulation.md
Danila Poyarkov 4faccd047d
feat(canvas): select auto layout frames by their gaps and dot their children, as in Figma (#943)
* fix: select top-level auto layout frames by their empty area

Checked against Figma desktop 126 with real pointer input. The gaps and
padding of a top-level frame with auto layout (any direction or grid,
filled or not, on the page or in a section) select, highlight, and drag
the frame, while clicks on its layers still select those layers. A plain
top-level frame's empty area stays background, as #900 recorded; its
oracle scenarios only used frames without auto layout.

Auto layout frames are hit by their bounds even without a fill, and a
press on such a frame's empty area no longer starts a marquee.

* feat(canvas): outline auto layout children with dotted lines, as in Figma

Hovering a horizontal or vertical auto layout frame outlines its visible
direct children, and a single selected layer dots the border of its auto
layout parent, in the container's outline color. Grid and plain frames,
grandchildren, and multi-selections show none. Line weight and dash were
measured from Figma 126 screenshots: 1 px with 1.75 px dashes and 1.25 px
gaps in screen space.

The editor gains a transforming flag that canvas input sets while layers
move, resize, or rotate; the outlines and the padding and gap markers of a
selected auto layout frame hide while it is set, as Figma's do.

The children outline a selected auto layout frame drew under the pointer
now uses the same stroke, and the dash effect is freed after each draw.

* fix(canvas): select unfilled frames by their empty area only on a click

Live Figma 126, with real pointer input on a top-level card holding an
unfilled auto layout row and an unfilled plain frame, with and without
auto layout on the card: a click on either frame's empty area selects
that frame, while a ⌘-click looks through it to the card.

Only top-level auto layout frames now own their empty area in hit
testing, so a deep hit no longer stops at a nested unfilled auto layout
frame. A click that ends on an open container selects the frame child
under the point by its bounds, filled or not, which plain frames did not
get before; boards that open by themselves keep their empty area as
background.

* fix(canvas): select an empty unfilled frame on the page by its bounds

Live Figma 126: a click inside an empty frame without a fill selects it,
on the page and in a section. The section case already did after the
previous commit; on the page the click found nothing, since an unfilled
frame without children is not hit. A click that hits nothing now looks
for a frame under the point among the scope's children, which skips
boards that hold layers, as their empty area stays background.

* refactor(scene-graph): read the containers the selection opens in their own helper

* fix(canvas): open a selected container, so repeated clicks reach deeper layers

In Figma, clicking again at the same point inside a selected frame,
component, or instance selects the layer under the pointer, so repeated
clicks walk down to a text in a nested component such as shadcn's
calendar. The click hit test opened only the ancestors of the selection,
so a selected nested container stopped the walk at itself and only a
double-click went deeper.

A selected container with layers now opens like its ancestors; groups
and booleans stay closed, as in Figma. Pressing inside the selection
still drags it, and its empty area keeps it selected.
2026-10-07 11:29:52 +00:00

5.6 KiB

title description
Selection & Manipulation Selecting, moving, resizing, rotating, duplicating, and organizing nodes in OpenPencil.

Selection & Manipulation

Select objects to move, resize, rotate, duplicate, and organize them on the canvas.

Selecting

  • Click a node to select it (deselects everything else). As in Figma, a click inside a top-level frame selects that frame's direct child, such as a nested frame or a group, rather than the deepest layer; click a top-level frame by its title, anywhere when it is empty, or by its gaps and padding when it has auto layout
  • Hover an auto layout frame to see its direct children outlined with dotted lines; while a single layer is selected, the border of its auto layout parent is dotted
  • Double-click to go one level deeper, or click again inside a selected frame, component, or instance; once a layer is selected, clicks reach its siblings and cousins directly
  • ⌘ + click (Ctrl + click on Windows and Linux) selects the deepest layer under the cursor
  • Shift + click to add or remove a node from the current selection
  • Marquee drag — drag on empty canvas to draw a selection rectangle; intersecting nodes are selected on release. Started inside a top-level frame without auto layout or a section, it selects that container's layers; from the page, a frame or section that holds layers is selected only when fully enclosed
  • ⌘A — select all nodes on the current page
  • Click empty canvas — deselect all

Moving

  • Drag a selected node to move it (all selected nodes move together)
  • Arrow keys — nudge selected nodes by 1 px
  • Shift + arrow keys — nudge by 10 px

Resizing

Selected nodes show 8 resize handles (4 corners + 4 edge midpoints). Drag any handle to resize.

  • Shift + drag a corner handle to constrain proportions

Moving into and out of frames

A dragged node lands in the frame under the cursor when you release it, however little of it is inside, and leaves its frame as soon as the cursor does. Groups and locked frames never take dropped nodes, and a component set takes back only its own variants; a node inside a group stays in it unless you drop it on a different frame, and the group resizes to fit what it holds. A group whose last node leaves is removed. Pressing inside a selected frame, group, or component set drags it rather than the node under the cursor.

  • Shift + drag — lock the move to one axis
  • Hold Space while dragging — keep the nodes in their current parents
  • ⌃ + drag (Ctrl + drag on Windows and Linux) — turn off snapping; dropped into an auto-layout frame, the node is positioned absolutely

Locked nodes in a selection stay where they are when the rest of it moves.

Rotating

Hover just outside a corner handle to see the rotation cursor. Drag to rotate.

  • Shift + drag snaps rotation to 15° increments

Duplicating

  • Alt + drag (⌥ + drag on Mac) — duplicate the selected node and move the copy
  • ⌘D — duplicate in place; a lone top-level frame's copy, on the page or in a section, goes into the first free space to its right

Duplicates keep their names. Duplicating a main component this way creates an instance of it.

Pasting

⌘V keeps the copied position. Pasted into a selected frame, nodes keep their offset from the frame they were copied from, and are centered along any axis where they don't fit; nodes that would land out of view are centered in it. Use Paste here in the context menu to paste at the cursor.

Deleting

Press Backspace or Delete to remove all selected nodes.

Z-Order

Change the stacking order of nodes within their parent:

  • ] — bring to front (top of sibling list)
  • [ — send to back (bottom of sibling list)

Visibility & Lock

  • ⇧⌘H — toggle visibility. Hidden nodes don't render but stay in the layers panel.
  • ⇧⌘L — toggle lock. Locked nodes can't be selected or moved on canvas.

Move to Page

Move selected nodes to a different page via the context menu. The nodes are reparented under the target page's canvas.

Sections

A section takes in the sibling nodes it fully covers when you draw it, move it, or resize it over them.

Keyboard Shortcuts

Action Mac Windows / Linux
Select all ⌘A Ctrl + A
Duplicate ⌘D Ctrl + D
Duplicate + move ⌥ + drag Alt + drag
Move along one axis ⇧ + drag Shift + drag
Keep current parent Space while dragging Space while dragging
Move without snapping ⌃ + drag Ctrl + drag
Delete ⌫ / Delete Backspace / Delete
Nudge 1 px Arrow keys Arrow keys
Nudge 10 px ⇧ + Arrow keys Shift + Arrow keys
Bring to front ] ]
Send to back [ [
Toggle visibility ⇧⌘H Shift + Ctrl + H
Toggle lock ⇧⌘L Shift + Ctrl + L

Tips

  • Use the Layers & Pages panel to see and reorder nodes when they overlap.
  • See Context Menu for additional actions like grouping and component creation.