mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-27 03:27:14 -06:00
LR-CROP-STRAIGHTEN: the Angle field applies a typed angle on Return
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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
676e8c01ce
commit
7e109e880f
@@ -196,14 +196,19 @@ fn crop(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) {
|
||||
.clamp_existing_to_range(false)
|
||||
.speed(0.05)
|
||||
.fixed_decimals(2)
|
||||
.suffix("°"),
|
||||
.suffix("°")
|
||||
// a typed angle applies on Return (or leaving the field), not keystroke by
|
||||
// keystroke: no half-typed angles, one undo step
|
||||
.update_while_editing(false),
|
||||
);
|
||||
crate::widgets::register(ui.ctx(), "cropAngleField", r.rect);
|
||||
// a drag on the field is one undo step, like the slider's
|
||||
if r.drag_started() {
|
||||
let _ = app.run("develop.beginInteraction", json!({"label": "Straighten"}));
|
||||
}
|
||||
if r.changed() && angle.is_finite() {
|
||||
// egui reports a change while text is typed even when the value isn't updated yet:
|
||||
// apply only a new angle
|
||||
if r.changed() && angle.is_finite() && angle != d.crop.geometry.angle {
|
||||
let _ = app.run("crop.straighten", json!({"angle": (angle.clamp(spec.min, spec.max) * 100.0).round() / 100.0}));
|
||||
}
|
||||
if r.drag_stopped() {
|
||||
|
||||
@@ -179,6 +179,33 @@ fn dragging_the_angle_field_is_one_undo_step() {
|
||||
assert_eq!(h.app.session.undo.len(), undo + 1, "one step for the whole drag");
|
||||
}
|
||||
|
||||
/// Typing into the Angle field one key at a time applies the angle once, on Return: no undo step
|
||||
/// or render per keystroke, no half-typed angles (-3° on the way to -3.25°).
|
||||
#[test]
|
||||
fn typing_in_the_angle_field_applies_on_return() {
|
||||
let mut h = crop_tool();
|
||||
let r = h.request("ui.clickWidget", json!({"id": "cropAngleField"}), T);
|
||||
assert_eq!(r["ok"], true, "{r}");
|
||||
h.settle(SETTLE);
|
||||
for key in ["a", "Backspace"] {
|
||||
let r = h.request("ui.key", json!({"key": key, "cmd": key == "a"}), T);
|
||||
assert_eq!(r["ok"], true, "{r}");
|
||||
}
|
||||
h.settle(SETTLE);
|
||||
let (before, undo) = (angle(&h), h.app.session.undo.len());
|
||||
for ch in ["-", "3", ".", "2", "5"] {
|
||||
let r = h.request("ui.text", json!({"text": ch}), T);
|
||||
assert_eq!(r["ok"], true, "{r}");
|
||||
h.settle(SETTLE);
|
||||
}
|
||||
assert_eq!((angle(&h), h.app.session.undo.len()), (before, undo), "nothing applied while typing");
|
||||
let r = h.request("ui.key", json!({"key": "Enter"}), T);
|
||||
assert_eq!(r["ok"], true, "{r}");
|
||||
h.settle(SETTLE);
|
||||
assert!((angle(&h) + 3.25).abs() < 1e-9, "applied on Return: {}", angle(&h));
|
||||
assert_eq!(h.app.session.undo.len(), undo + 1, "as one undo step");
|
||||
}
|
||||
|
||||
/// Rotating in the margin beside a narrow photo keeps the readout next to the pointer: it is kept
|
||||
/// on the canvas, not pinned to the photo's edge.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user