Skip to content

Canvas: right click selects the shape under the pointer - #244

Merged
h-a-n-a-s-h-i merged 2 commits into
storytold:mainfrom
pierrevbc:right-click-select
Oct 14, 2026
Merged

h-a-n-a-s-h-i merged 2 commits into
storytold:mainfrom
pierrevbc:right-click-select

Conversation

@pierrevbc

@pierrevbc pierrevbc commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A right click on an unselected shape opens the slide's context menu (Layout, Reset Slide, Format
Background...), not the shape's: the shape has to be left-clicked first. In PowerPoint, the
right-button press selects what's under the pointer, then the menu matches it.

Reproduce on main (0.6.0): deckcraft --sample, right-click the title of slide 1 without
selecting it first: slide menu, title not selected.

Cause: the canvas only sends the primary button to Session::pointer
(crates/ui-egui/src/canvas.rs, pointer), and context_menu picks the shape or slide menu from
the current selection.

Expected (as in PowerPoint):

  • unselected shape: selected alone (a text box or placeholder is not put into editing, nothing is
    dragged), shape menu;
  • shape already selected, multiple selection included: selection kept;
  • empty slide: selection cleared, slide menu;
  • inside the text being edited: caret and text selection kept, and the menu acts on the text.

What changes

Two commits: the change, and the progress docs.

  • Engine. New PointerKind::ContextClick (contextclick, or rightclick, in ui.pointer
    and the MCP pointer tool). It selects as listed above and returns {hit, selected, editing}.
    It never edits text, starts a drag or adds an undo step, and does nothing while a drawing tool
    or a gesture is active. In the box being edited it uses the same zone as a left press that
    places the caret (the box's bounds inflated by the hit tolerance).
  • Canvas. The secondary-button press goes through Session::pointer before egui opens the
    context menu (on release). While text is being edited, the menu shows text items: Cut, Copy and
    Paste (which already act on the selected text in text mode), Paragraph..., Bullets, Numbering,
    Link..., Format Shape....
  • docs/control-protocol.md lists the new pointer kind; the MCP pointer tool describes it.
  • Progress docs. The context-menu row of docs/ui-parity.md (status line, history) and a
    ROADMAP.md progress log line. No checklist row changes score and no gaps.md entry covered
    this.

How it was tested

cargo xtask ci passes on this branch (fmt, clippy -D warnings, workspace tests, layers,
assets, wasm32 check).

New tests in crates/engine/src/tests.rs, on decks built in code:

  • right_click_selects_the_shape_under_the_pointer: unselected shape, text box (selected, not
    edited), multiple selection kept, empty slide cleared, no drag, no undo step.
  • right_click_in_edited_text_keeps_the_text_selection: text selection kept; Copy and Cut act on
    it; right click on another shape leaves editing and selects it.

Checked by hand in the app, on 0.5.0 with this change and the frame-drag one together
(deckcraft --sample --control, real egui pointer events, ui.screenshot): right click on an unselected subtitle selects it and shows the shape menu; right
click in edited text keeps the selected word and shows the text menu; right click on empty slide
shows the slide menu.

Known limits

  • A shape inside a group: right click selects the group, as a left click does today.
  • The text items of the menu are the common ones; PowerPoint's text menu is longer.

Related PRs

A right click on an unselected shape opened the slide's context menu
(Layout, Reset Slide...) because only the primary button reached the
pointer tool; the shape had to be left-clicked first. The right-button
press now goes through Session::pointer as PointerKind::ContextClick
(`contextclick` on the control channel and MCP) and selects like
PowerPoint before the menu opens: an unselected shape is selected alone
(a text box or placeholder is not put into text editing, nothing is
dragged), a selected shape keeps the selection, multiple selection
included, and empty slide clears it. In the box whose text is being
edited, the caret and text selection stay, and the context menu offers
text items (Paragraph, Bullets, Numbering, Link) next to Cut, Copy and
Paste, which act on the selected text.
- ui-parity.md: context menu row; status line and history.
- ROADMAP.md: progress log line. No checklist row changes score; no gaps.md entry covered this.
@h-a-n-a-s-h-i
h-a-n-a-s-h-i merged commit 44f4b03 into storytold:main Oct 14, 2026
@echelon

echelon commented Oct 14, 2026

Copy link
Copy Markdown
Member

Thank you for this contribution, @pierrevbc! It has landed on main and will ship in the next DeckCraft release.

If you haven't already, please come join us on Discord: https://discord.gg/artcraft

anishguduru pushed a commit to anishguduru/deckcraft that referenced this pull request Oct 16, 2026
* Print: Windows lists its printers and spools jobs (storytold#756)

The Print dialog's printer list comes from Win32_Printer through PowerShell's
CIM (wmic is gone from Windows 11), and jobs are spooled by the embedded
windows_print.ps1: the in-box Windows.Data.Pdf renders each sheet at the
printer's own resolution and System.Drawing.Printing carries copies,
collation, duplex and colour as the job's settings. Both run through
powershell, so the Rust code touches no Win32 API. Fixes storytold#756, with the
printer-not-showing duplicates storytold#160, storytold#244, storytold#293, storytold#465 and storytold#644; CIM approach
from storytold#452. On CI a paused Generic / Text Only test printer catches the job in
its queue; a parser test keeps the script valid PowerShell.

* Print: fix the Windows await helper and render the job on CI (dry run)

Real-machine testing found [System.WindowsRuntimeSystemExtensions]::GetMethods()
failing with 'does not contain a method named GetMethods': GetMethods is an
instance method of [Type], so it is called with '.', the form the canonical
WinRT-await snippets use, not '::'. The first CI run missed it because the
end-to-end test skipped silently when the runner refused the test printer, so
the script had never actually run.

The script now honours PDFCRAFT_PRINT_DRYRUN=1: render every sheet with
Windows.Data.Pdf to PNGs in the temporary folder and print nothing. The test
runs that half unconditionally, so CI exercises the whole WinRT path —
projection, await helpers, render — on any machine, while the paused-printer
half stays best-effort and prints why it skipped. Win32_Printer enumeration
asks only for Name and Default, so a machine with nine printers waits less.

* Tests: the find-options wait in the viewer test is a deadline too

4280f83 converted the frame-count waits in this test to deadlines after the
same 12-of-15 race under a full workspace test run, but left the first wait —
the one for the 'page' matches — on the old 60-frame loop, and it flaked the
same way on PR storytold#847's CI. Same fix: wait up to 20 s for the 15 matches, then
assert.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants