Commit Graph
15 Commits
Author SHA1 Message Date
alehelandClaude Opus 5.5 2929ba854e fix(ui): a click on a link or a form button counts only released over it
A click acted on what its press was on wherever the button came up: a press on a link, a push
button, a signature field or a list box that slipped off it before the release (still a click to
egui, within 6 px) followed the link, ran the button's action, opened signing or picked a list
box's option. On main a click went to what lay under its release. Now it needs both: the
link's or the fields' hit test at the release point must give what the press was on, as a button
pressed and released off itself does nothing. Check boxes and radio buttons already did so, by
their overlay's click-outside test.

New tests a_press_on_a_link_released_off_it_follows_nothing and
a_press_on_a_button_released_off_it_runs_nothing, seen failing with this commit's source
reverted; the check box test shares their slipping_click helper. From CodeRabbit's second review
of #5300.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 15:14:31 +01:00
alehelandClaude Opus 5.5 4b3033c869 fix(ui): review fixes for the page router: Escape on text drags, the open frame's sides, a stale owner
From a review of this branch:
- Escape mid-drag with a text markup tool (Highlight, Underline, Strikeout, Squiggly, Replace
  Text) or Redact over text left the half-made selection on the page: the cancel took the Escape
  that would have cleared it. It is dropped now. The marks made from a selection at its end
  (after the page tools, which the release blocking did not reach) also wait for a cancelled
  press's button; egui ends its own drag on Escape without a drag stop, so they were not made
  in practice, but nothing but egui stopped them.
- The open paragraph's frame took the press above and below the text only: its sides still gave
  edge grips (a closed paragraph's rewrap handles) on the Editor layer, over the frame. Edges now
  belong to closed paragraphs only.
- A press in the frame right after a release kept the last press's owner until a page claimed
  it, so a drag from the margin beside a page could move the comment just clicked. A new press
  now drops the last owner before the pages give it theirs.
- A release and a new press in one frame no longer leave the new press cancelled.
- Added content is picked up with the tools that act on it (Select, Add text, Edit text), not with
  every tool while Edit a PDF is open.
- The layer docs in page_cursor.rs say what lies on each layer now.

New tests an_open_paragraphs_frame_takes_the_box_beside_its_text and
a_drag_from_beside_the_page_right_after_a_click_leaves_the_comment_alone (the release and the
next press in one frame) fail on the previous commit. escape_cancels_a_highlight_being_dragged_over_text
(with a control: the drag highlights without Escape) passes there too, for the reason above, and
stays as a guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 14:01:17 +01:00
alehelandClaude Opus 5.5 a4c5fbed64 fix(ui): Edit text's paragraphs, pictures, objects and added content decide a press by one hit test
Edit text's paragraph edges were tested twice: by edit_text_ui for the cursor, on the page's
paragraphs, and by object_ui for the press, on its own object inventory (grabs_edge). Where the
two inventories disagree, the resize arrow shows and a press starts an invisible area selection
instead (#2276, the review's step-3 probe). The open paragraph's frame showed the Move cursor
4 px out and took the press 6 px out.

Each of these parts now has one hit test, in the page router's table, and acts only on the
press it was captured on:
- edit_text_ui::hit: a paragraph's edges (Handle), its body (Text; Editor for the open one).
- edit_text_ui::picture_hit and picture_handle_hit: a picture's body on the Text layer, asked
  before the paragraphs, so text on top of artwork takes the press; the selected picture's
  corners and rotate knob on Handle, asked after them, so they win over text.
- object_ui::hit: the objects selected together (Placed) and the open paragraph's frame
  (Handle), with one reach (FRAME_REACH, 6 px) for the cursor and the press. An area selection
  starts only where nobody's hit claims the press.
- content_ui::hit: added text and pictures (Placed; the selected one's corners on Handle), over
  form fields; Add text starts a new box only where nobody's hit claims the press.
image_owns_pointer and content_ui::select_grabs go, with the edge test object_ui kept a copy of
(grabs_edge). The page's hits move into canvas::PageHits.

Un-ignores a_paragraph_edge_resizes_wherever_it_shows_the_resize_cursor: it passes on main
already (the inventories agree for its paragraph since the probe was written), so it stays as a
guard rather than as proof. New test an_open_paragraphs_frame_shows_move_wherever_a_press_takes_the_box
fails on the previous commit (the cursor is Default there).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 12:53:23 +01:00
alehelandClaude Opus 5.5 63fa5010d8 fix(ui): form fields and Prepare decide a press by one hit test
Forms: forms_ui::hit is the fields' only hit test (cursor and press). The press opens a text
field (#2213) and its click acts on the field the press was on, from the page router, instead
of the field under the release point: a press on one check box released over the one beside it
ticked the second. Now it ticks neither, as a button pressed and released off itself does
nothing. Forms no longer wait on comments::select_grabs: a comment that moves (a note, a shape,
a signature, a Fill & Sign mark) hits on the Placed layer, over form fields, so it still takes a
press over a field (a signature can be dragged off the field it covers), and text markup stays
on the Comment layer, under them, so a click on a box under a highlight ticks the box.
select_grabs goes.

Prepare Form: prepare::hit (the selected field's corner handles, then a field, on Placed so a
field being moved stays over the comments as before) gives the cursor, and the drag, click and
double click act on what the press was on; a press held by something else is not Prepare's.

New tests: a_press_on_one_check_box_released_over_another_ticks_neither (fails without this
commit: the second box is ticked) and a_check_box_under_a_highlight_takes_the_click (fails with
markup on the Placed layer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 12:29:57 +01:00
alehelandClaude Opus 5.5 7d72f21da3 fix(ui): the area and placement tools act only on their own presses, of the primary button
The Sign area, marquee zoom and snapshot, crop and redaction boxes, and the stamps, act where
nothing drawn over the page holds the press (the page router has no owner for it), rather than
wherever the press lands on the page. Fill & Sign already asks the router (fill_grabs).

Their drags start on the primary button and end with its release, however egui reported it:
its drag stop, or the button found up. They started on any button, so a right-drag drew a
signature rectangle and opened the signing dialog (and drew a crop or a marquee), and the end
waited for a pointer position, so a drag whose release came without one never ended.

New test a_right_drag_draws_no_signature_rectangle, seen failing with the drag started on any
button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 12:21:35 +01:00
alehelandClaude Opus 5.5 35d28a60cc fix(ui): comments, links and text selection decide a press by one hit test
The page router (canvas/page_router.rs): a page module answers "what of mine is under this
point?" in one hit test, which gives a Hit (an owner, a layer, a cursor, a tip). On the frame of
a press, the hits at the press point decide who it belongs to, by PR 2's table (the highest
layer the tool reaches), and the router holds that capture until the button is up and its
release has been seen; automation's cancel and Escape drop it. The pointer's hits claim the
cursor and the tip, after the tools that are not converted yet, as before.

Comments: comments::hit is the module's only hit test, with each tool's filter (Select: point
handles, resize handles, then the comment; Fill & Sign: the same over its marks only; shape
tools: the selected comment's handles only). The Select tool's drag, click and double click act
on the comment and part the press was captured on, instead of testing the press origin and the
release point again; fill_grabs and select_grabs ask the router while the button is down. The
topmost comment is the one drawn last (/Annots order), not the last in inspection's top-edge
order: over stacked comments the tip named the top one while a click selected the one
underneath. Text markup declines the drag, so a click on a highlight selects it and a drag
across it still selects the text.

Links: the link hover is a hit (hand and target), and a click follows the link its press was
on. Text selection starts unless the press is held by something that keeps its drag (a link, a
comment that moves); it tested for a link under the pointer at the moment egui called the press
a drag, so a drag from text onto a link selected nothing.

Un-ignores a_click_on_stacked_comments_selects_the_one_drawn_on_top; new test
a_drag_from_text_onto_a_link_selects_the_text. Both fail with their fix reverted (inspection
order for the topmost comment; the link test at the pointer).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 12:15:07 +01:00
alehelandClaude Opus 5.5 9f483490c7 fix(ui): Escape cancels any drag the pages hold, and its release does nothing
Escape cancelled a comment gesture, Prepare's marquee, measuring and the camera tool, each in its
own module; with the Sign area, crop, marquee zoom, a redaction box, the Link tool's rectangle,
a paragraph or picture drag, added content, an object move or a text selection it did nothing,
and the release then drew, placed or opened what the drag had begun (the step-4 probe: Escape
mid-rectangle still opened the signing dialog).

document_area now handles Escape once, before the pages: while the pages hold a press, it drops
every pointer gesture (DocView::cancel_pointer_gestures, as automation's cancel does) and marks
the press cancelled. Until the button is up the page tools take no input from it, so its
release neither clicks (a stamp, a Fill & Sign mark) nor ends a drag. Escape without a press
keeps its meanings (close the editor, clear the selection, back to the Select tool).

Un-ignores escape_cancels_a_signature_rectangle. Two new tests: Escape mid-press keeps the
release from placing a stamp, and Escape mid-drag abandons a crop rectangle. All three fail
with the cancel taken out; the stamp test also fails with the cancel kept and the release let
through.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 12:01:18 +01:00
alehelandClaude Opus 5.5 24eb327a80 fix(ui): the page sets its cursor once, from the top claim the tool reaches
Every page module set egui's cursor itself and the last write of a frame won, so the
order of document_area's per-page chain decided the cursor, not what a press there does.
The link hover loop ran last and showed the hand over any link for every tool but Fill &
Sign, though only the Select tool follows links: a click with the stamp tool placed a
stamp, a drag with the rectangle tool drew, and in Edit text a drag from linked words
moved the paragraph, all under the hand. The text layer ran after the comments and showed
the I-beam over a movable comment lying on text, which a drag there moves.

Page modules now claim the cursor instead (src/page_cursor.rs): a layer, an icon and
optionally a tip. The layers are the review's priority table, Editor > Handle > Placed >
Field > Comment > Link > Text > tool area, with Drag on top for a drag in progress, whose
owner names the cursor until the release. document_area resolves the claims once per
frame, after the pages, and calls set_cursor_icon once: the claim on the highest layer
the active tool reaches wins. Links count only for the Select tool (follow) and the Link
tool (edit), comments only for the Select tool; the other layers are claimed only by the
modules of the tools that act on them. The claims go into a per-frame collector on
DocView, which every page module already takes, so the calls in the dispatch block keep
their arguments (camera, content and redact lose a `ui` they took only for the cursor).

A link's tip goes with its claim, so it shows only where the link wins: no longer with a
stamp, drawing or other tool that does not follow links. A comment's tip is a description
instead (comments::describe), shown with any tool beside that tool's cursor unless the
winning claim has a tip of its own: Acrobat shows a comment's tooltip with every tool
(observed with Select, Hand, Add comment, Highlight, Rectangle and Stamp). It keeps the old
exceptions (Fill & Sign, freehand drawing for #429, a comment gesture in progress) and
comments. A signed field's validation badge (#1895) says its verdict the same way, with any
tool, ranked below a comment's description as it was below the comment's tip; the topmost
of overlapping badges still speaks.

FieldPointer::Over existed only to keep the text layer from overwriting a field's cursor
(#2099); the Field layer above Text does that now, so forms_ui::page_input returns whether
a field owns the press. Who acts on a press is unchanged (step 3 of the router).

The picture's rotate knob claims too: Grab as a handle, Grabbing as a drag while it turns,
which dragging_the_rotate_knob_turns_a_page_image now checks.
The contains_pointer checks #2755 put before the cursor in content_ui, edit_text_ui,
forms_ui and the text layer go: the pages' pointer has no position where the mode bar or a
side panel covers a page, so nothing is claimed there, and #2755's tests pass unchanged.
During a press the pages hold, those four now keep their cursor over the chrome too, as
the other page modules and a text selection being dragged already did.

Un-ignores the four step-2 probes in tests/it/pointer.rs, each seen failing on the
previous commit at its cursor assertion, after its press assertions passed. A new test,
also seen failing there: a link's tip shows only with its hand. Another pins a comment's
tip with every tool beside its cursor, and its exceptions; it was seen failing with comment
tips tied to the cursor's winner instead, which shows them with the Select tool only. In
tests/it/signing.rs, a signed field's badge says its verdict with the Select tool and with a
stamp; it fails when the badge is left out of the resolver, as resolving after the page
loop would otherwise do. A new ignored probe pins a step-3 bug: over stacked comments the
tip names the one drawn on top, but a click selects the one underneath, because
PageCx::hit takes the last in inspection's top-edge order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 21:54:55 +01:00
skylar taylor-barrick e90f7882f1 Migrate PdfCraft to egui 0.37 (#4591)
* Record successful PdfCraft web application check

* E21: bound startup requests and validate font and license inputs

* E21: separate pending control completion and gesture regression phases

* M-egui037: enforce control admission and per-family font notices

* M-egui037: qualify capture fixes and document migration behavior

* egui-037: identify current and historical qualification results
2026-10-25 14:30:31 -04:00
aiongg d764a8ebca refactor(ui): side panels on craft-ui task panes and the shared rail (#4606)
* refactor(ui): side panels on craft-ui task panes and the shared rail

The Tools pane and the open side panel are docked craft-ui task panes
(craft_ui::panes::PaneHost, Columns policy, no title bars, not movable),
and the rail is craft_ui::panes::Rail with the page navigation cluster
as its foot. On the right, outermost first: the rail, Tools when
Preferences > Tools panel side is Right, the side panel, the page.
Read mode uses the same panes without Tools; All tools full view still
covers them. Floating, tab groups and drag-in are gone with docking.rs.

- Widths are resizable within the old limits and saved under a new
  side_panels preference (version 1) with the Tools side and open state.
  The old docking value is read once as plain JSON and dropped on save.
- ui.dock becomes ui.panel {panel: tools|side, open, width};
  ui.dock.reset resets the side panels ("Reset side panels").
- F4 shows or hides the side panel (reopening the last one) and
  Shift+F4 the Tools panel, as View menu commands.
- craft-ui pinned to e9630ae7 (craft-libs#309).

* fix(ui): side panels clippy lint and the dragged-width test's grab offset

* chore(deps): pin craft-ui at 7c42807, whose pane resize edges work over the page

* chore(deps): pin craft-ui at craft-libs main ea4c823, with panes::Rail
2026-10-25 12:06:35 -04:00
aiongg 418ac6a2af fix(ui): the page ignores the pointer while a panel is being dragged (#4581) 2026-10-24 22:12:46 -04:00
aiongg 80d8c60ac2 Integration: no link or comment tip beside the open palette (#2685) 2026-10-24 23:23:47 +00:00
alehelandClaude Opus 5.5 866de03fd8 fix(ui): a dialog covers the pages from the frame it opens
The page pointer has the pointer only where egui has the pages on top, and egui knows what
is drawn over them from the layers it met in the frame before. Dialogs are drawn after the
pages, so on the frame one opens egui has not met its layer yet. For that frame the pages
kept the pointer, and a press that landed then was theirs: a link's tip showed over the
dialog, and a click placed a stamp or followed a link under it. main's page_covered had
kept the link's tip and click off the page while a dialog was open, from a hand-kept list.

PagePointer::read now also takes whether a dialog is open: PdfCraftApp::modal_open, the
list the page press and the edge scroll already check, the Unlock prompt included. With one
open the pages see no pointer and no press, except a drag they already have, which goes on
to its release. The palette is not a dialog and the page beside it keeps its tools, so it
is left to egui's layers, from the frame after it opens.

Tests: tests/it/pointer.rs hovers a link and opens the Unlock prompt (no tip in that
frame), and clicks with the Stamp tool in the frame the prompt opens (no stamp). A
page_pointer unit test hides the pointer and a click under a dialog, and keeps a drag the
pages have until its release. With the fix reverted, all three fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 16:54:28 +02:00
alehelandClaude Opus 5.5 80e290acd1 fix(ui): a tap on a focused check box or radio button keeps its focus
A click on a check box or radio button focuses it, so Tab, Space and the arrows reach it
(#2223). The page hands the click to the field, and the field overlay toggles the box in
the same frame unless the click landed elsewhere. That check read hover_pos, which is None
on the frame egui reports a tap: egui-winit sends PointerGone with every touch release,
and winit reports Windows pen input as touch. So a finger or pen tap counted as a click
away. The focus closed and the toggle was dropped: a tap left a check box unticked and
unfocused, and a tap on a radio button selected nothing. The check now uses the overlay's
clicked_outside, which reads interact_pos, as the other field editors do.

Tests: touch_tap_ticks_a_check_box runs again. tests/it/editor_taps.rs taps a focused
check box and the other button of a focused radio group, each with a mouse click as the
control. Each checks that the focus stays and the tap toggles, and for the radio group that
the arrows still move within it. With the fix reverted, the three tap tests fail and the
controls pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 16:54:28 +02:00
alehelandClaude Opus 5.5 cc4726f6e6 fix(ui): one page pointer per frame, so taps work and nothing over a page lets it through
The page view had no single reading of the pointer. About twenty page modules each
read egui's raw state (hover_pos, latest_pos, interact_pos, press_origin) and tested it
against an unclipped page rectangle. That state knows nothing of what is drawn over the
pages, and hover_pos is None on the frame a finger or a Windows pen lifts: egui-winit
sends PointerGone with every touch release, and winit reports Windows pen input as
touch. So a tap placed no stamp, followed no link and ticked no check box. A touch drag
of a comment never committed, and the next drag anywhere on the page moved the comment.
The Stamp crosshair showed on the command palette, on the quick bar and on the chrome
over a page scrolled under it, a link's tip floated over the Unlock prompt, and a
double-click on a dialog's backdrop selected the word under it.

canvas::document_area now reads one PagePointer per frame from the pages' content
widget (step 1 of the page pointer router in the cursor-handling review):
- pos: interact_pos, which keeps the position through a touch release, only while egui
  has the pages under the pointer with nothing over them (contains_pointer, and the top
  layer there is the pages', as the closed #2626 did) or while the pages hold a press.
- origin: where the held press began, also on the frame that brings both the press and
  its release. This replaces the text layer's raw-event fallback (#2100).
- edges: the frame's primary presses and releases on the pages, in order.

It reaches the page modules through comments::PageCx and as an argument: the stamp
blocks, text layer, page press and link and comment hover in canvas.rs; comments and the
signature drag preview; forms and Prepare a form; Fill & Sign and the Sign area; crop,
marquee zoom, snapshot, camera and measure; redact and the Link tool; added content,
Edit text, images and objects. page_covered, a hand-kept list of what covers the pages
that missed the Unlock prompt, is gone. The pages' hover tip is click-through, as a
tooltip is, so a quick move onto it does not take the pointer off the link under it.

Tests: tests/it/support/input.rs sends pointer input in the frames the platform delivers
it in (a touch tap and drag, a click in one frame, a double-click). kittest otherwise
runs one frame per event, so the suite never saw a touch release. tests/it/pointer.rs
holds the review's probes and #2626's layer tests. With the fix reverted, the
tap (stamp, link, check box), touch drag, palette, quick bar, chrome, Unlock tip and
dialog double-click tests fail, and the mouse-click and one-frame-click controls pass.
The tip test fails with the tip interactable, and each page_pointer unit test fails
under a mutation of the part it pins. The probes for later steps stay ignored: the
link hand and the I-beam over other tools' targets (step 2), paragraph edges (step 3)
and Escape mid-drag (step 4). touch_tap_ticks_a_check_box is ignored until the
check box focus fix that follows: since #2223 a tap on a check box counts as a click away
from its focus.

tests/it/comments.rs: the multiline markup hover test (#3541) hovered a point below the
window on a page rotated 270 degrees, and its tip showed only because the hover was
tested against the page's whole rectangle. Its harness now zooms to 100 %, so every
rotation shows the whole page, and the hover helper asserts the point is on screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 16:54:28 +02:00