The field applied every keystroke: an undo step and a render per key,
and half-typed angles (-3° on the way to -3.25°). It now applies on
Return or when focus leaves, as one undo step, and ignores changes that
leave the angle as it is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The readout was bounded by the photo's rect, so rotating in the margin
beside a narrow photo pinned it to the photo's edge, far from the
pointer (the earlier commit said "kept on the canvas" but used the
photo). It is now bounded by the canvas. Placing it also no longer
panics on NaN or empty bounds (f32::clamp panics on NaN).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The issue asks for a numeric field for an exact, repeatable rotation.
Typing into the Straighten value worked, but it was hidden behind a
hover hint. The Crop panel now has an Angle field under Straighten (a
drag value in degrees, −45° to 45°, two decimals): click and type, or
drag it. A drag is one undo step, like the slider's
(begin / endInteraction). "Angle" is translated in all seven locales.
Headless tests: typing -3.25 into the field sets exactly -3.25°; a
six-step drag on it adds one undo step (it added five before the
interaction was wrapped).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From review:
- With ⌘ held a drag draws a level line, but outside the crop box the
pointer showed the rotation arrow (the crosshair was set, then
overwritten in the same frame). The crop tool now leaves the
straighten crosshair alone while ⌘ is held and nothing is rotating.
- The angle readout ran past the canvas (and the window) near its
right or bottom edge. It now flips to the pointer's other side and is
kept inside the canvas (readout_rect).
- The readout showed the angle from before this frame's rotation. It is
now drawn after the drag is applied, with the angle just set.
Headless tests: ⌘ held outside the box gives the crosshair and no
glyph; rotating at the canvas's corner keeps the readout on the canvas.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every slider's value already takes a typed number when clicked, which
is how to enter an exact crop angle (#534), but nothing said so.
Hovering a slider value now shows "Click to type a value", translated
in all seven locales. A headless test hovers the Straighten value and
finds the hint painted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dragging outside the crop box rotates the photo, but the pointer there
was egui's generic Alias arrow, so nothing showed that a drag would
rotate (issue #534).
- Outside the box, and throughout a rotation drag, the system cursor is
hidden and a curved double arrow (new code-drawn Icon::RotateDrag) is
drawn at the pointer on a dark disc, on the tooltip layer
(`cropRotateCursor`). No system cursor has a rotate shape.
- While rotating, the angle is shown next to the pointer
(`cropAngleReadout`), formatted like the Straighten value
(crop_angle_label, reusing widgets::shown_value), every zero as
"0.00°".
- Headless tests: the cursor outside vs inside the box, the readout
during a drag and gone after it, the label format, and typing an
exact angle into the Straighten value (which already worked; now
guarded).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For the crop tool's rotation pointer tests (#534): Headless::last_cursor
is the pointer cursor the last frame asked for (what a real host would
show), and painted_text lists every text the last frame painted,
tooltips included. An import test already did the latter inline. Both
let UI tests check what a person sees.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
std::process::exit skipped eframe's on_exit (ui.json, close_library) and the
may_close check, so changes still in memory could be lost on Cmd+Q.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>