mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-27 11:56:50 -06:00
#280: a drag released off the window ends where the pointer was last seen, not mid-canvas; the test follows #278
This commit is contained in:
@@ -371,16 +371,12 @@ fn input(app: &mut FigApp, ui: &mut Ui, resp: &egui::Response) -> bool {
|
||||
}
|
||||
}
|
||||
let Some(pos) = hover_pos.filter(|p| app.canvas.rect.contains(*p)) else {
|
||||
if app.canvas.dragging && ctx.input(|i| i.pointer.button_released(egui::PointerButton::Primary)) {
|
||||
// A drag released off the canvas, or off the window, ends where the pointer was last seen.
|
||||
if app.canvas.dragging && !ctx.input(|i| i.pointer.primary_down()) {
|
||||
app.canvas.dragging = false;
|
||||
let pos = hover_pos.unwrap_or(app.canvas.rect.center());
|
||||
let ev = pointer_event(app, PointerKind::Up, pos, &mods);
|
||||
app.canvas.pressure = None;
|
||||
app.canvas.last_pointer = Some(Point::new(ev.x, ev.y));
|
||||
if let Err(e) = app.session.pointer(&ev) {
|
||||
app.toast(e.to_string());
|
||||
if let Some(at) = hover_pos.or_else(|| app.canvas.last_pointer.map(|p| app.to_screen(p.x, p.y))) {
|
||||
send(app, PointerKind::Up, at, &mods);
|
||||
}
|
||||
app.handle_ui_requests();
|
||||
}
|
||||
// Leaving the canvas: nothing is hovered there any more.
|
||||
if !app.canvas.dragging && std::mem::take(&mut app.canvas.hovering) {
|
||||
@@ -493,19 +489,24 @@ fn input(app: &mut FigApp, ui: &mut Ui, resp: &egui::Response) -> bool {
|
||||
app.canvas.pressure = force;
|
||||
}
|
||||
if let Some(k) = kind {
|
||||
let ev = pointer_event(app, k, pos, &mods);
|
||||
if k == PointerKind::Up {
|
||||
app.canvas.pressure = None;
|
||||
}
|
||||
app.canvas.last_pointer = Some(Point::new(ev.x, ev.y));
|
||||
if let Err(e) = app.session.pointer(&ev) {
|
||||
app.toast(e.to_string());
|
||||
}
|
||||
app.handle_ui_requests();
|
||||
send(app, k, pos, &mods);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Pointer event `kind` at screen point `pos`, to the tools.
|
||||
fn send(app: &mut FigApp, kind: PointerKind, pos: Pos2, m: &egui::Modifiers) {
|
||||
let ev = pointer_event(app, kind, pos, m);
|
||||
if kind == PointerKind::Up {
|
||||
app.canvas.pressure = None;
|
||||
}
|
||||
app.canvas.last_pointer = Some(Point::new(ev.x, ev.y));
|
||||
if let Err(e) = app.session.pointer(&ev) {
|
||||
app.toast(e.to_string());
|
||||
}
|
||||
app.handle_ui_requests();
|
||||
}
|
||||
|
||||
/// Right-click at screen `pos`: select what's under the pointer (`deep`: the nested layer), then
|
||||
/// open the context menu there.
|
||||
pub fn open_context_menu(app: &mut FigApp, pos: Pos2, deep: bool) {
|
||||
|
||||
@@ -75,3 +75,49 @@ fn releasing_a_canvas_drag_outside_finishes_the_interaction() {
|
||||
|
||||
assert!(u.app.session.ds().unwrap().interaction.is_none(), "the document interaction is committed");
|
||||
}
|
||||
|
||||
fn key(u: &mut Ui, key: egui::Key) {
|
||||
let modifiers = egui::Modifiers::NONE;
|
||||
u.frame(vec![
|
||||
Event::Key { key, physical_key: None, pressed: true, repeat: false, modifiers },
|
||||
Event::Key { key, physical_key: None, pressed: false, repeat: false, modifiers },
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_drag_released_off_the_window_ends_where_the_pointer_was_last_seen_and_later_edits_stay() {
|
||||
// #278: a drag left open made a later text edit roll back the changes made since.
|
||||
let mut s = Session::new();
|
||||
let id = |v: serde_json::Value| figcraft_engine::params::id_of(&v["id"]).unwrap();
|
||||
let kept = id(s.execute("shape.rectangle", &json!({"x": 40, "y": 400, "width": 80, "height": 40})).unwrap());
|
||||
let gone = id(s.execute("shape.rectangle", &json!({"x": 160, "y": 400, "width": 80, "height": 40})).unwrap());
|
||||
let text = id(s.execute("text.create", &json!({"text": "A", "x": 40, "y": 500})).unwrap());
|
||||
s.execute("tool.rectangle", &json!({})).unwrap();
|
||||
let mut u = Ui::new(s);
|
||||
// A rectangle drawn from the middle of the canvas, released off the window, the pointer gone.
|
||||
let start = u.app.canvas.rect.center();
|
||||
u.button(start, true);
|
||||
for i in 1..=6 {
|
||||
u.frame(vec![Event::PointerMoved(start + vec2(10.0, 6.0) * i as f32)]);
|
||||
}
|
||||
let release =
|
||||
Event::PointerButton { pos: egui::pos2(-50.0, -50.0), button: egui::PointerButton::Primary, pressed: false, modifiers: Default::default() };
|
||||
u.frame(vec![release, Event::PointerGone]);
|
||||
let ds = u.app.session.ds().unwrap();
|
||||
assert!(ds.interaction.is_none(), "the drawing is committed");
|
||||
let (drawn, zoom) = (ds.selection[0], ds.view.zoom);
|
||||
let b = ds.doc.world_bounds(drawn).unwrap();
|
||||
// Not the 100 x 100 shape of a click, as an end in the middle of the canvas would make.
|
||||
assert!((b.width() * zoom - 60.0).abs() < 1.5 && (b.height() * zoom - 36.0).abs() < 1.5, "{b:?} at zoom {zoom}");
|
||||
// #278's steps: a move in the panel, a delete, then a text edit.
|
||||
u.app.run("node.set", json!({"ids": [kept], "props": {"x": 650}}));
|
||||
u.app.run("edit.delete", json!({"ids": [gone]}));
|
||||
u.app.run("select.set", json!({"ids": [text]}));
|
||||
key(&mut u, egui::Key::Enter);
|
||||
u.frame(vec![Event::Text("B".into())]);
|
||||
key(&mut u, egui::Key::Escape);
|
||||
let doc = u.app.session.doc().unwrap();
|
||||
assert_eq!(doc.world_bounds(kept).unwrap().x0, 650.0, "the move stays");
|
||||
assert!(!doc.contains(gone), "the delete stays");
|
||||
assert_eq!(doc.node(text).unwrap().kind.text().unwrap().characters, "B");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user