Repository navigation
Canvas: drag the frame of a text box being edited to move it - #243
Merged
Merged
Conversation
While a text box or placeholder is being edited, every press inside its box placed the caret, so the box could not be moved without leaving editing first. Like PowerPoint, a band on either side of the box edge (the hit tolerance wide, in the shape's rotated frame) is now its frame: hit_test reports Hit::Frame there, the canvas shows the move cursor, and a press leaves text editing, selects the box and drags it as one undo step. A click without a drag just selects the whole box. Inside the box the caret and text selection work as before; resize and adjust handles still win. The text-click zone now follows the rotated box instead of its axis-aligned bounds.
- ui-parity.md: Direct manipulation row added; status line and history. - ROADMAP.md: progress log line. The checklist rows involved (Text boxes, In-place text editing) are already D; no gaps.md entry covered this.
pierrevbc
force-pushed
the
text-border-move
branch
from
October 13, 2026 10:13
0e36587 to
8c5f101
Compare
Member
|
Thank you for this contribution, @pierrevbc! It has landed on 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
…, and refuses them without one (storytold#134) (storytold#243) The schema documented printing/changes/copy/accessibility and their defaults unconditionally, but the engine (like Acrobat) only enforces /P behind an owner password: open_password alone encrypts the document and leaves everything allowed, even when a caller passed copy: false or changes: "none" explicitly, and the call reported success. State the condition in the tool and property descriptions, and turn an explicit restriction without permissions_password into InvalidArgs that names the argument and what is needed, instead of writing an unrestricted file. open_password alone is unchanged. Co-authored-by: wstock <1394687+wstock@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Once a text box or placeholder is in text editing, it can't be dragged: every press inside its
box, border included, places the caret or starts a text selection. A click on an unselected text
box or placeholder enters editing straight away (like PowerPoint), so moving a text box takes an
extra step: click outside, then drag it before the click turns into editing. In PowerPoint,
grabbing the border of a box being edited moves it, with the move cursor on hover.
Reproduce on
main(0.6.0):deckcraft --sample, click into the title of slide 1: the caret appears.doesn't move. The cursor over the border is the text cursor.
Cause:
Session::down(crates/engine/src/tools.rs) sends any press inside the edited box'saxis-aligned bounds, inflated by the hit tolerance, to the caret. Only the resize, rotate and
adjust handles are checked first. The canvas shows the text cursor over the whole box while
editing (
crates/ui-egui/src/canvas.rs).What changes
Two commits: the change, and the progress docs.
Hit::Frame { id }:hit_testreturns it, after the handles of the selection,when the point lies in a band the hit tolerance wide on either side of the edge of the box being
edited. The test runs in the shape's own frame (
Xfrm::affine().inverse()), so it followsrotation and flips; in very small boxes the inner half of the band shrinks to a quarter of the
box so text stays reachable. Lines (connectors with text) and locked shapes have no frame.
Session::down): leaves text editing, selects the box and starts theusual
Drag::Move(smart guides, snapping, Shift to constrain, Alt to duplicate), committed asone undo step ("Move"). A click without a drag only selects the box, with no undo step, as in
PowerPoint.
box instead of its axis-aligned bounds, so a press in the corner area of a rotated box's bounds,
off the box, no longer moves the caret.
Hit::Frameshows the move cursor, read fromtool.hoverlike the other cursors;no geometry in the UI.
{"hit": {"kind": "frame", "id": …}}from adownevent (ui.pointer, MCPpointer), since the gesture goes throughSession::pointer.docs/ui-parity.mdgets a Direct manipulation row (status line, history);ROADMAP.mda progress log line. The checklist rows involved (Text boxes, In-place text editing)are already D, and no
gaps.mdentry covered this.How it was tested
cargo xtask cipasses 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:dragging_the_frame_of_edited_text_moves_the_box: hover on either side of the edge reportsHit::Frame, inside reports the text; press just outside the top edge and drag: the box movesby the drag, editing ends, one undo step that restores it; a click on the inner side of the band
selects the box with no undo step.
dragging_inside_edited_text_selects_text: the box doesn't move, the text is selected, no undostep.
frame_of_rotated_edited_text_follows_the_rotation: 30° box, frame hit on the rotated edge,move keeps the rotation; inside still places the caret; a point in the axis-aligned bounds but
off the box doesn't.
resize_handles_beat_the_frame_of_edited_text: the SE handle, inside the band, still resizes.Checked by hand in the app, on 0.5.0 (
deckcraft --sample --control, real egui pointer events throughui.click/ui.drag,ui.screenshot): the title dragged by its border while editing, move cursorover the border.
Known limits
selected).
Related PRs
independent. Whichever lands second needs one adjustment:
context_clickshould useedited_box/edit_zonefor the edited-text test and listHit::Frameamong the hits thatcarry a shape id (the compiler flags the
match). I will rebase the second one once the firstlands. The adjustment is already written and tested in a local integration of both: a right
click anywhere in the edit zone, frame and rotated boxes included, keeps the caret and the text
menu.
Session::down: it adds an equationcheck inside the caret branch this PR rewrites. Small textual conflict; the resolution is to keep
the equation check at the top of the new branch.
crates/engine/src/tests.rs: append-only conflict.crates/engine/src/tools.rs, without textual conflict.docs/ui-parity.mdandROADMAP.md(status line, progress log) conflict with other PRs thatupdate them (Add Ukrainian interface translation and language selection #34, i18n: add Russian, Spanish, French, Portuguese and German interfaces #110, feat(theme): add System appearance across DeckCraft controls #134, Text: typing goes on after Tab in a text box (#78) #139, Export: animated GIF of the deck (Export… dialog, CLI, MCP) (#171) #177, Charts: GridCraft spreadsheet data editor and live editing for the first 5 chart types #195, Animation effects: follow the system's reduced-motion setting #196, i18n: add Polish (pl) interface #199); the resolution is to keep both lines.