mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-24 10:09:01 -06:00
LR-VIEW-ZOOM: a middle-button drag pans the photo whatever tool is active
In Detail, dragging with the middle button (the mouse wheel pressed) pans the zoomed photo with a hand cursor, in every tool. Remove / Heal / Clone, the mask brushes and Crop no longer have to be left to look around a zoomed photo, and they don't paint or move during that drag: it is handled with the pinch and scroll gestures, before the tool overlays. Right-click keeps the photo's context menu and Space keeps the zoom toggle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
98702b5c44
commit
6c17d29aed
@@ -65,6 +65,8 @@ mod tests_button;
|
||||
#[cfg(test)]
|
||||
mod tests_canvas_keys;
|
||||
#[cfg(test)]
|
||||
mod tests_canvas_pan;
|
||||
#[cfg(test)]
|
||||
mod tests_compare;
|
||||
#[cfg(test)]
|
||||
mod tests_copy_paste_dialogs;
|
||||
|
||||
@@ -187,6 +187,20 @@ pub(crate) fn pan_image(app: &mut LightcraftApp, area: Rect, img: Rect, delta: e
|
||||
let _ = app.run("view.navigate", json!({"pan": pan}));
|
||||
}
|
||||
|
||||
/// A drag with the middle button (the mouse wheel pressed) pans the zoomed photo whatever tool is
|
||||
/// active, so the healing brush, mask brushes and crop never paint or move while you look around;
|
||||
/// the hand cursor shows while it does.
|
||||
fn middle_pan(app: &mut LightcraftApp, ui: &egui::Ui, resp: &egui::Response, area: Rect, img: Rect) -> bool {
|
||||
if !resp.dragged_by(egui::PointerButton::Middle) {
|
||||
return false;
|
||||
}
|
||||
ui.ctx().set_cursor_icon(egui::CursorIcon::Grabbing);
|
||||
if img.width() > area.width() + 1.0 || img.height() > area.height() + 1.0 {
|
||||
pan_image(app, area, img, resp.drag_delta());
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Net vertical notches (up is positive) of unmodified line/page-unit wheel events: a real mouse wheel, not a trackpad.
|
||||
fn wheel_notches(events: &[egui::Event]) -> f32 {
|
||||
events
|
||||
@@ -532,7 +546,7 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) {
|
||||
let mut navigating = false;
|
||||
for &view_area in &areas {
|
||||
let view_img = in_pane(view_area, img_rect);
|
||||
if navigate_gesture(app, ui, &resp, view_area, view_img, native) {
|
||||
if navigate_gesture(app, ui, &resp, view_area, view_img, native) || middle_pan(app, ui, &resp, view_area, view_img) {
|
||||
navigating = true;
|
||||
target_rect = fit_rect(main_area, display_aspect, app.ui.zoom, native, ppp, app.ui.pan);
|
||||
img_rect = target_rect;
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
//! A middle-button drag (the mouse wheel pressed) pans the zoomed photo in Detail whatever tool is
|
||||
//! active: the healing brush doesn't paint meanwhile, and the hand cursor shows.
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use egui::{Event, PointerButton, Pos2, vec2};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::headless::Headless;
|
||||
use crate::{LightcraftApp, Services};
|
||||
|
||||
const T: Duration = Duration::from_secs(20);
|
||||
|
||||
fn spots(h: &Headless) -> usize {
|
||||
let id = h.app.session.active().expect("active photo");
|
||||
h.app.session.develop_of(id).unwrap_or_default().spots.len()
|
||||
}
|
||||
|
||||
/// Press `button` at `from`, move to `to` over a few frames and release; the cursor seen mid-drag.
|
||||
fn drag(h: &mut Headless, button: PointerButton, from: Pos2, to: Pos2) -> egui::CursorIcon {
|
||||
let modifiers = egui::Modifiers::NONE;
|
||||
h.app.synthetic.push(Event::PointerMoved(from));
|
||||
h.app.synthetic.push(Event::PointerButton { pos: from, button, pressed: true, modifiers });
|
||||
h.step();
|
||||
for i in 1..=6 {
|
||||
h.app.synthetic.push(Event::PointerMoved(from + (to - from) * (i as f32 / 6.0)));
|
||||
h.step();
|
||||
}
|
||||
let cursor = h.last_cursor;
|
||||
h.app.synthetic.push(Event::PointerButton { pos: to, button, pressed: false, modifiers });
|
||||
h.step();
|
||||
h.step();
|
||||
cursor
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_middle_drag_pans_with_the_healing_brush_and_adds_no_spot() {
|
||||
let app = LightcraftApp::new(lightcraft_engine::Session::with_demo(), Services { png: None, ..Default::default() });
|
||||
let mut h = Headless::new(app, [1200.0, 800.0], 1.0);
|
||||
for (method, params) in [
|
||||
("ui.set", json!({"view": "detail"})),
|
||||
("engine.execute", json!({"command": "panel.remove"})),
|
||||
("engine.execute", json!({"command": "view.zoom100"})),
|
||||
] {
|
||||
let r = h.request(method, params, T);
|
||||
assert_eq!(r["ok"], true, "{r}");
|
||||
h.step();
|
||||
}
|
||||
h.step();
|
||||
let at = h.app.image_rect.expect("the photo is shown").center();
|
||||
let pan = h.app.ui.pan;
|
||||
let cursor = drag(&mut h, PointerButton::Middle, at, at + vec2(-150.0, -100.0));
|
||||
assert_eq!(cursor, egui::CursorIcon::Grabbing, "the hand shows while panning");
|
||||
assert_ne!(h.app.ui.pan, pan, "the middle drag pans");
|
||||
assert_eq!(spots(&h), 0, "the healing brush doesn't paint during a middle drag");
|
||||
// the left button still paints a spot, and doesn't pan
|
||||
let pan = h.app.ui.pan;
|
||||
let at = h.app.image_rect.expect("the photo is shown").center();
|
||||
drag(&mut h, PointerButton::Primary, at, at + vec2(40.0, 0.0));
|
||||
assert_eq!(spots(&h), 1, "a left drag paints a spot");
|
||||
assert_eq!(h.app.ui.pan, pan);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user