Commit Graph
34 Commits
Author SHA1 Message Date
alehelandClaude Opus 5.5 e69d1221ff fix(ui): a right-click between the pages deselects the comment; Paste keeps to the picture's page
Two of CodeRabbit's findings on #5803. A right-click off the pages
cleared only the menu's anchor and kept the selected comment, so the
menu that opened was that comment's, not the current page's. A
right-click on a page's empty part deselects it, so one that no page
takes (`PageFrame.right_clicked`) now does the same. A selected Prepare
field keeps its menu off the pages, as it does on a page's empty part;
the comment there says so.

Where pages touch, the first page takes a right-click on the row they
share and anchors the menu there, but a picture on the second page under
it still opens its menu. Paste from that menu centred the image on page
1's point, which on page 2 is at its foot. The image menu now uses the
anchor only on the picture's own page, and otherwise pastes as Ctrl+V
does.

Tests, each seen failing with its fix reverted:
`a_right_click_between_the_pages_opens_the_page_menu_not_the_selected_comments`,
`paste_from_a_picture_menu_where_two_pages_touch_keeps_off_the_first_pages_point`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 14:41:30 +01:00
alehelandClaude Opus 5.5 bdd2ff0ee6 fix(ui): a camera click is located where it came up
Found in review (CodeRabbit): the last fix located a camera click by the pointer, which can have
moved on by the end of the release's frame. A click pressed on page 2 and released on page 1,
with the pointer back on page 2 by then, moved the quadrilateral to page 2. `PagePointer` keeps
each release's position, so `PagePointer::released` gives where the click came up, and the click
is located there.

Test: `a_camera_click_released_off_its_page_moves_nothing`, seen failing without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 22:04:19 +01:00
alehelandClaude Opus 5.5 b77903012d fix(ui): a camera click on another page moves the quadrilateral there
Found in review (CodeRabbit): with the camera tool, a press nobody holds on a page without the
quadrilateral moves it there, but a click was located by its press's origin, which egui has
cleared by the release frame. So only a drag moved it, and a click did nothing. This was so on
`main` too. A click is now located where it came up; `PagePress::Free` already says its press
began on this page.

Test: `a_camera_click_on_another_page_moves_the_quadrilateral_there`, seen failing without the
fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 21:51:52 +01:00
alehelandClaude Opus 5.5 568ea94f48 fix(ui): a right-click between the pages anchors no page's menu; the guard finds router readers
Found in review:
- **A right-click off the pages.** Between the pages or beside one, no page took the
  right-click, but the page menu still opened over the page view, on the last anchor: Delete
  page, Paste and Copy Image acted on a page right-clicked long before. This was so on `main`
  too. The anchor is now cleared on every right-click before the pages set it, so off the pages
  the menu acts on the current page.
- **The guard** took a file as a page module only by marks a router read doesn't need. A file
  that reads `.router.` is one too, so its router reads are checked.
- **Docs.** egui ends a press's drag when any button is released, not when a second goes down.
  `link_ui::hit` takes the tip from the link it found rather than looking it up again.

Tests: a right-click between the pages clears the anchor, seen failing without the fix. A
planted router read in an unlisted file fails the guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:56 +01:00
alehelandClaude Opus 5.5 6752b36807 fix(ui): a mark being moved keeps Fill & Sign's presses; the guard sees calls by path
Found in review:
- **Fill & Sign during a mark's move.** `main` placed no Fill & Sign mark while a comment's move,
  resize or reshape was live on the page, whoever held the press. That guard was lost: with a
  move left over (its release never seen), the next click placed a new mark. A live gesture on
  the page now counts as over a mark again (`comments::moving_on`, which the comments' own
  dispatch uses too).
- **The guard** missed the router's and the page view's hits called by path
  (`PageRouter::owner(&view.router, i)`), and its doc said the `.at(` it matches was confined to
  `page_input.rs`.
- `measure_ui::Clicks` derived traits nothing uses.

Tests: a Fill & Sign click while a mark's move is live places nothing, seen failing without the
fix. A planted router read by path fails the guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:51 +01:00
alehelandClaude Opus 5.5 6756ab0ed4 fix(ui): a right-click where two pages touch is one page's; the guard reads split calls
Found in review:
- **A right-click where two pages touch.** With the page gaps hidden, a right-click on the row
  two pages share ran on both: the first page selected its comment, the second unselected it,
  and the second page's anchor for the menu stood. A right-click is now the first page's, as a
  press has been since the last fix. This was so on `main` too.
- **The guard** matched one line at a time, so a call rustfmt splits across lines
  (`view` / `.router` / `.owner(i)`) got past it. A continued line is now read with the one it
  continues. It also refuses a hit test imported under a name that isn't a hit test's
  (`use …::hit as under`), which the checks by name would miss.
- **Docs.** Links still take a right-click while the primary button is held, as before, and when
  Escape comes in a press's own frame every page is `Elsewhere`, not `Free`.

Tests: a stamp clicked where two pages touch lands on the first, a click pressed on one page and
released on the next places nothing, a right-click where two pages touch is the first page's,
Prepare keeps its selection on a click that began off the page, and the Note tool starts no note
on added text with the Edit panel open. Each was seen failing with its fix, or the rule it pins,
reverted. A planted split router read and a planted aliased hit test fail the guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:41 +01:00
alehelandClaude Opus 5.5 c1d34fc9ea fix(ui): commenting tools leave a frame another tool took; Escape keeps the menu's anchor true
Found in review:
- **A double-click another tool took finished a polygon.** Round 5 kept commenting tools off a
  press another tool had taken, but a polygon, polyline or cloud finishes on a double-click or
  Enter whoever has the press. With Prepare open, a double-click on a field opened its properties
  and committed the polygon being clicked out. Added text opened for editing did the same with
  the Edit panel open. The commenting tools now skip such a frame entirely, as `main`'s consumed
  chain did. The comments' own presses and a click's unselecting still run.
- **The menu's anchor after Escape.** While a press Escape cancelled is held, the page's tools
  are skipped, and a right-click left the page menu on the page right-clicked before. It now
  anchors where it is, as every other right-click does.
- **The guard.** It now lists who asks the page view's hits at a point (`PageHits::at` and
  `context_at`), so a new place in `page_input.rs` can't test a point again. It takes a file
  that uses the comments' page context (`PageCx`) for a page module. Its doc of the point tests
  it doesn't list is now true.
- **Docs and visibility.** True docs for `PagePress::Free`, where a press is off the pages, and
  the camera's hit. `link_ui::screen_rect` is private.

Tests: a polygon left unfinished by a double-click on a Prepare field, and a right-click after
Escape anchoring the menu. Each was seen failing without its fix. A planted ask of the page
view's hits, and a planted unlisted file using `PageCx`, each fail the guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:35 +01:00
alehelandClaude Opus 5.5 f0caaf9259 fix(ui): a press Prepare or added content took draws no comment; menus open where right-clicked
Found in review:
- **Comments acted on presses another tool had taken.** Comments run every frame now, and a
  commenting tool acted on any press nobody held, so with Prepare open a Rectangle drag drew a
  rectangle as well as Prepare's selection rectangle. Added content, with the Edit panel open,
  could likewise open a note composer under the Note tool. `main` skipped comments when another
  module took the frame. They are now told (`taken`), and their drawing and placing tools leave
  such a press alone. A click there still unselects.
- **The page menu's anchor went stale.** A right-click while the primary button was held skipped
  the call that also anchors the page menu, so Delete page, Paste and Copy Image acted on the page
  right-clicked before. Every right-click on a page now anchors the menu there, whoever takes it,
  and only the selections wait for the release. That includes the right-clicks Add text, Edit
  text, the Link tool and a field tool take, which used to keep the previous anchor.
- **Edit text's right-clicks.** Edit text took right-clicks on added content too. It now takes
  those on its paragraphs, pictures and the objects selected together, and comments see a
  right-click on added text, as before.
- **Camera corners.** A camera corner at the page's edge showed its grab cursor past the edge,
  where a press is no page's. Its hit now stops at the page, as a link's does.
- One source for a comment's layer, dead code in Add text's right-click, and true docs for
  `PagePress::Elsewhere` and the "Page presses" rule.

Tests: a shape-tool drag with Prepare open, a right-click with the button held on another page,
a right-click on added text with Edit text, the menu anchored by an Edit text right-click, and a
camera corner at the page's edge. Each was seen failing with its fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:32 +01:00
alehelandClaude Opus 5.5 aa415811d7 fix(ui): right-clicks leave a held press's selections alone; guard finds more page modules
Found in review:
- **Right-clicks during a press.** A comment being moved kept its selection only while its
  gesture state was live, so a gesture left over (a release the pages never saw) blocked every
  later right-click. Comments and Prepare now leave their selection alone while the primary
  button is held. egui ends the press's own interaction (and its drag) when another button goes
  down, so the button is what tells. A touch's long press, which egui reports as a right-click
  with the finger still down, still opens its menu.
- **A click that began beside the page** unselected the selected comment, though the Link tool and
  Prepare now leave theirs alone for such a click. Comments do the same.
- **The guard** took a file as a page module only if it named `PagePointer`; a file using
  `PageToolContext`, `PagePress` or the router's hits or owners is one too. Its message about
  tooltips and release checks now points at the page view's own hits, and its doc of the router's
  readers names link following.

Tests: a right-click with a gesture left over, a click that began beside the page, and a long
press on a comment. Each was seen failing with its fix
reverted. A planted page-router name in a module the guard didn't list fails the guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:29 +01:00
alehelandClaude Opus 5.5 47ece03a54 fix(ui): the Link tool's click told too; a moved comment keeps its selection
Found in review:
- **The Link tool's click** acted on any press: with Prepare open, a click on a field also
  unselected the selected link, and a click that began off the page did too. It now acts only on
  a press on a link, or one nobody holds.
- **A right-click while a comment is moved** selected the comment under it mid-drag, though the
  release still moved the first. Comments now keep their selection during a move, resize or
  reshape, as Prepare does.
- The hits under the pointer before the tools act are taken only on the pointer's page, and the
  Link tool's right-click reuses them.
- The router has one source for the press's page (`Capture` no longer carries its own).
- The guard lists which functions may ask which comment lies under a point (`comment_at`); the
  guard finds hit tests by name, and its docs say so.
- Docs made true: what `PagePress::Mine` promises, what `tool_input` gates, and that a dialog does
  not cancel the pages' press (only Escape and automation's cancel do).

Tests: the two fixes, and ruling 1 for added text (a click on it unselects the comment), each seen
failing with its fix reverted; the drawing-tool right-click test now checks the comment's menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:25 +01:00
alehelandClaude Opus 5.5 ae62b66304 fix(ui): right-clicks with the Link tool and the camera as before; true docs
Routing right-clicks changed a few more cases beyond the rulings, found in review:
- **The Link tool.** A right-click on a link also unselected the selected comment and moved the
  page menu's anchor; the Link tool used to take it whole. On a document it can't change, its
  right-click on a note over a link opened the plain page menu instead of the note's. Now the Link
  tool's right-click is what its press there would take, on a document it can change, and on a
  link it leaves comments alone.
- **The camera.** A corner, on the handle layer, took right-clicks from a comment under it,
  though it has no menu. Corners are no longer right-click targets.
- **The Link tool's hand** showed in the 2 px margin past the page's edge, where a press is no
  page's. `link_ui::hit` now finds nothing off the page.

Docs made true: what a comment's press is (`comments::page_input`), what reaches the free-press
gate (`page_tool_input`), and when every page is `PagePress::Free`.

Tests: those three, and two that pin right-clicks kept as before: Edit text's on a paragraph
under a comment, and a Prepare field tool's. Each seen failing with its fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:22 +01:00
alehelandClaude Opus 5.5 ce90c81ffb fix(ui): a drag seen past its page's edge still draws on that page
egui reports a drag once the pointer has moved a few points from the press. A press near a page's
bottom edge, flicked onto the next page in one frame, is seen as a drag only with the pointer on
the next page. Redact (and the area Highlight), the Link tool and Prepare returned before their
drag start unless the pointer was on their own page, and the next page is `Elsewhere`, so the
press drew nothing anywhere. (On main the next page took it, with the box's start on the page
before.)

They now start the gesture from where the press began on their own page, as zoom, snapshot, crop
and Sign area already did, so the box, link rectangle, marquee, field drawn or field moved belongs
to the page the press began on. Tests: one per tool, each seen failing with the fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:16 +01:00
alehelandClaude Opus 5.5 b12d091496 fix(ui): hover painting beside the open palette, and one hit test under the pointer
The page's hover (the top hit under the pointer) was only worked out when nothing was drawn over
the pages, so with the palette open a field beside it lost its tooltip and its hover outline,
and comments their outline. The palette keeps the page's cursor and tips away, which is what
that gate is for; hover now ignores it, as the outlines and the field tooltip did before.

The hits under the pointer were also asked up to four times a page each frame, each pass running
every module's hit test: for a click's release, for Fill & Sign's preview, for a right-click, and
for the cursor. A click's release, Fill & Sign's preview and a right-click's tool checks now share
one pass, taken before the tools act and only on frames that need it. A right-click's own pass
(comments by `context_hit`) stays, and the cursor and hover keep theirs, after the tools act.

A unit test pins that cancelling the pages' gestures lets go of a camera corner. The kittest for
Escape mid corner drag passes on `main` too, because Escape leaves the camera tool first.

Tests: `a_fields_tooltip_shows_beside_the_open_palette`,
`cancelling_pointer_gestures_lets_go_of_a_camera_corner`; each seen failing with its fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:13 +01:00
alehelandClaude Opus 5.5 b1a0657c42 fix(ui): right-clicks with Prepare, the Link tool and Add text as before
Routing right-clicks by what lies on top under the pointer changed three cases beyond the
rulings:
- **Prepare.** A right-click while a field was dragged selected the field under the pointer, and
  the release then moved that one. Prepare used to ignore right-clicks during a drag; it does
  again (`prepare::context` returns while a grab is held).
- **The Link tool.** A note over a link took the right-click, though the Link tool's press there
  is the link's. The Link tool's right-click on a link is the link's again, whatever lies over it.
- **Add text.** Comments took right-clicks away from paragraphs and added items. Add text used to
  take every right-click on a page it can change; it does again.

Tests: those three, and two that pin ruling 6's right-click half with the Select tool: no link
menu on a link with no action, and none on a form field over a link. Each was seen failing with
its fix reverted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:10 +01:00
alehelandClaude Opus 5.5 ed8a10e83e refactor(ui): tell forms and Prepare whether the press is theirs
Forms and Prepare read the router themselves, and forms tested the pointer twice more: for a
field's tooltip, and for whether a click came up over the widget it was pressed on. Following a
link tested the pointer again too.

Both are now told the page's `PagePress`. Prepare's Select tool draws a selection rectangle, or
unselects, only on a press nobody holds, and a field tool draws only on such a press. Whether a
click came up over what its press began on is answered once, from the hits under the pointer
(`released_over`), for fields and for following links. The field tooltip (`forms_ui::tooltip`)
and the field hover outline come from the page's hover, the top hit under the pointer.

`PageHits::hits` is now the only function that calls a hit test, and the press-handler check no
longer lists forms or Prepare.

Behaviour changes: a field's tooltip shows only where a press would take the field, not under
a comment drawn over it (the user's ruling, 2026-10-31); a field's hover outline follows the
same rule, so it shows only with the Select tool, which fills fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:39:04 +01:00
alehelandClaude Opus 5.5 951ef7e362 refactor(ui): one gate for the presses nobody holds
The tools that act wherever on the page each decided for themselves whether a press was
theirs: Sign area, zoom and snapshot, crop, redact and the stamps asked the router, and Measure,
Fill & Sign, the camera and the Link tool's rectangle acted on any press. Stamp placement lived
inline in the page view.

`tool_input` is now the one gate. Measure, the Stamp tool and custom stamps, Fill & Sign, Sign
area, zoom and snapshot, the camera and crop are each told whether the press is the tool's (it
began on their page and no hit won it), and act only then; their cursors and gestures run as
before. Redact's boxes and the Link tool's rectangle are told the same where they are called.
Stamp placement moves to `stamps_ui::place` and `place_custom`, beside custom stamps. Measure
is told its clicks (`measure_ui::Clicks`), Sign area takes a `PageToolContext`.

The camera's corners are hits (`camera_ui::hit`, `Owner::CameraCorner` on the handle layer), so
a corner's drag is an owned press like any other: it starts on the corner's own press, ends on
`drag_stopped() || !primary_down`, and `cancel_pointer_gestures` drops it. Until now only the
order of two key handlers kept Escape mid-drag from leaving a corner following the pointer:
Escape leaves the camera tool before the pages see it. The kittest for Escape mid-drag can't
fail with that order; a unit test of the cancel comes later in this series.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:38:56 +01:00
alehelandClaude Opus 5.5 84fe5e54b3 refactor(ui): tell comments and Fill & Sign whether the press is theirs
Comments ran last in the page view's chain of `||`, so only when no earlier module had taken the
frame, and asked the router themselves (`grabs`, `fill_grabs`, `draw_grabs`), with a hit test of
their own for hover. A link, a field or added text taking the frame first hid a comment's
gesture end, its Escape and the click that unselects it.

Comments now run on every frame and are told the page's `PagePress`:
- a press on a comment (its body, a handle or a point) goes to `select_input` with the part,
  with the Select tool, a shape tool (the selected shape's handles) or Fill & Sign (its marks);
- the drawing tools act only on a press nobody holds (`Gestures::free`);
- a click on anything but a comment leaves comments unselected.

`grabs`, `fill_grabs`, `draw_grabs` and `cancelled_drag` (with the raw button read in `keys` it
needed) are gone; `grabs_points` is `reaches_points`. Fill & Sign leaves the press to a mark
when the press is someone's, or, with no button down, when a mark is the top hit under the
pointer. The page's hover is that top hit (`PageInput.hover`), and the comments' hover outline
follows it, so it no longer shows under a field or added text that would take a press.

Behaviour change (the user's ruling, 2026-10-31): a click on a form field, or on added text,
leaves the selected comment unselected, as a click on a link or the page already did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:38:52 +01:00
alehelandClaude Opus 5.5 bab615fed1 refactor(ui): route right-clicks by what lies on top under the pointer
Each right-click handler tested the pointer itself, and the chain's order decided which ran:
comments only when no earlier module took the frame, Prepare by its own widget scan.

A right-click now goes to what lies on top under the pointer (`PageHits::context_at`), by the
layer ranking a press uses, with two differences. Comments count with every tool, as a
comment's menu opens whatever tool is active (`comments::context_hit`). A highlight over a link
is the highlight's: a left click there follows the link, a right-click opens the highlight's
menu. On every right-click the page's menu is anchored at the pointer, and the comment it is
on is selected, or none (`comments::context`); a Prepare field is selected (`prepare::context`);
links take theirs from the same answer. Edit text's own paragraphs and pictures, and a Prepare
field tool, keep their right-clicks as before.

Behaviour change (the user's ruling, 2026-10-31): a right-click on a highlight over a link opens
the highlight's menu, not the link's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:38:40 +01:00
alehelandClaude Opus 5.5 2b256ab0c7 refactor(ui): one link test for the Select tool, the Link tool and right-clicks
Three link tests disagreed. The Select tool's hand and click (`link_hit`) took the topmost link
the renderer lists, with no margin, by its place in that list. The Link tool's selection and
Select's right-click took the bottom one of every link the engine lists, 2 px around each, by
`/Annots` index. Comments had a fourth (`on_link`).

Now `link_ui::hit` is the one test, and `Owner::Link` carries the `/Annots` index:
- the Link tool finds every link the engine lists, 2 px around each, and takes the topmost; a
  drag from a link still draws a new one (the hit declines the drag);
- every other tool finds the links the renderer can follow, with no margin (so Select shows no
  hand over a link with no action, as before), and takes the topmost;
- following a link looks its target up by that index;
- `comments::hit` is told whether a link is under the point.

The Link tool is told the link its press is on, and the one a right-click is on, instead of
testing the pointer itself. A right-click goes to the top hit under the pointer, so with the
Select tool a right-click on a note over a link selects the note and opens its menu, and one
just beside a link no longer opens the link's.

Behaviour changes (the user's rulings, 2026-10-31): the Link tool takes the topmost of stacked
links; Select's right-click has no margin; a right-click on a note over a link is the note's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 20:38:37 +01:00
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