Skip to content

Free Transform: a handle drag follows the modifiers held during the drag - #269

Merged
bflatastic merged 3 commits into
storytold:mainfrom
theironchef:fix/free-transform-modifiers
Oct 7, 2026
Merged

bflatastic merged 3 commits into
storytold:mainfrom
theironchef:fix/free-transform-modifiers

Conversation

@theironchef

Copy link
Copy Markdown
Contributor

Fixes #268.

Problem: FreeTransformTool (crates/tools/src/xform/free.rs) chose scale, free distort, perspective distort or shear from the modifiers on the press only. In the app neither key order reaches those modes:

  • Cmd held before the press: the canvas turns a Cmd press into the temporary Selection tool, so the Free Transform tool never sees it.
  • Cmd pressed after the drag starts (the documented "start dragging a corner, then hold Cmd"): the operation was already chosen.

So with the mouse, the tool could only scale, rotate and move. The cmd_side_handle_shears unit test sent Cmd with the press, which the canvas never delivers.

Change

  • The handle → operation mapping moves into handle_op, which classify still uses on the press.
  • On every Drag, a handle drag now re-derives its operation from the modifiers held at that moment:
    • corners: scale ↔ free distort (Cmd) ↔ perspective (Cmd+Alt+Shift);
    • sides: scale ↔ shear (Cmd).
  • When the operation changes after the drag has begun, the tool emits Cancel and then Begin with the new name, so the single undo step is named after what was done ("Distort" rather than "Scale"). Previews already re-apply on top of the snapshot, so switching between object.transform and object.distort mid-drag is safe.
  • Move, rotate and the tool's mode option are unchanged.
  • Op gains handle() and label(), replacing the inline label match.

Tests

  • New xform::free::tests::modifiers_pressed_during_a_handle_drag_change_what_it_does:
    • a corner drag begun as "Scale" turns into Cancel, Begin("Distort"), Preview(object.distort …) when Cmd comes in;
    • with Cmd+Alt+Shift it stays in that step;
    • letting go of the keys goes back to "Scale";
    • Cmd during a side handle's drag gives "Shear".
  • New tests_xform::free_transform_cmd_held_once_a_corner_drag_started_distorts_in_one_step: through the session, the dragged corner lands on the pointer, the other three corners stay, and History gains exactly one "Distort" step.
  • Both fail without the change (the drag stays a scale).
  • cargo test -p vectorcraft-tools: 186 passed. cargo test -p vectorcraft-engine --lib: 1237 passed.
  • cargo clippy -p vectorcraft-tools -p vectorcraft-engine --all-targets -- -D warnings (Rust 1.99): clean.
  • cargo fmt --all -- --check: clean.
  • Checked by hand in a release build on Windows with the real mouse and keyboard:
    • a corner drag, then Ctrl, moves just that corner (History: "Distort");
    • Ctrl+Alt+Shift spreads the opposite corners symmetrically;
    • Ctrl on a side handle shears.

I didn't run the full cargo xtask ci (wasm step) here.

🤖 Generated with Claude Code

theironchef and others added 3 commits October 7, 2026 12:15
… works

A double-click on a tool button runs tool.options, which only knew a few
tools, so the common double-clicks did nothing: the Hand tool now fits
the artboard in the window, the Zoom tool shows 100%, and Rotate, Scale,
Reflect and Shear open their Object > Transform dialog (when something
is selected, like the menu items).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ting shows its artboard

The navigator's buttons were painted only and its number was always 1,
and Fit Artboard in Window and Actual Size always used the first
artboard. The per-document View now keeps the navigator's artboard; the
new view.goToArtboard UI command (an index, or first / previous / next /
last) sets it and fits it in the window, and the four buttons call it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… handle drag

The tool chose scale, distort, perspective or shear from the modifiers at
the press only, but a Cmd press on the canvas is the temporary Selection
tool, and the documented gesture is to start dragging and then hold Cmd.
A handle drag now follows the modifiers held during the drag; when the
operation changes, the interaction restarts so the undo step is named
after what was done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bflatastic

Copy link
Copy Markdown
Member

Thanks @theironchef, merged! I rebased it on main and updated the ROADMAP's Free Transform entry. Re-deriving the operation from the keys held on every drag, and restarting the step only when its name changes, keeps a single, correctly named undo step. Nicely tested.

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.

Bug: Free Transform's Free Distort, Perspective Distort and Shear can't be reached with the mouse

2 participants