Files
gridcraft/crates/ui-egui/tests/it/chart_resize.rs
T
prxnvt aa86b0c327 UI: merge fallout from the UI kit and the recorder heads
Icon buttons no longer carry an accessible name: the tests that came
with main since the branch was written (menu hover, popup dismissal, the
File menu, More Colors) find Copy, Save and Cancel by label, and the
Quick Access Toolbar and formula bar icons gave those labels twice. The
macro tests need the names of large, small and toggle ribbon buttons
only. The chart menu test takes the menu's Delete, which is drawn after
the ribbon's Home > Cells > Delete. The Trust Scripts row ported into
the Macros dialog is formatted.
2026-10-25 06:48:57 -04:00

169 lines
7.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Resizing a chart by dragging its bottom-right corner or with the Format Chart Area Size fields, through real input.
use egui::{Event, Key, Modifiers, PointerButton, accesskit::Role, pos2, vec2};
use egui_kittest::kittest::Queryable as _;
use gridcraft_engine::Session;
use gridcraft_engine::core::CellRef;
use gridcraft_ui_egui::{SheetApp, grid::Geo};
use serde_json::json;
type Harness = egui_kittest::Harness<'static, SheetApp>;
fn fixture() -> (Harness, u32) {
let mut session = Session::new();
session.new_workbook();
session.execute("cell.set", json!({"cell": "A1", "input": "1"})).unwrap();
session.execute("cell.set", json!({"cell": "A2", "input": "2"})).unwrap();
session.execute("selection.set", json!({"range": "A1:A2"})).unwrap();
let chart = session.execute("insert.chart", json!({"type": "column", "at": "D3", "width": 300, "height": 200})).unwrap();
let id = chart["chart"].as_u64().unwrap() as u32;
let mut h = egui_kittest::Harness::builder().with_size(vec2(1200.0, 800.0)).build_ui_state(
|ui, app: &mut SheetApp| {
let ctx = ui.ctx().clone();
app.logic(&ctx);
app.ui(ui);
},
SheetApp::new(session, Default::default()),
);
h.run_steps(4);
(h, id)
}
fn size(h: &Harness, id: u32) -> (f32, f32) {
let a = h.state().session.doc().unwrap().wb.active().unwrap().charts.iter().find(|c| c.id == id).unwrap().anchor;
(a.width, a.height)
}
fn corner(h: &Harness, id: u32) -> egui::Pos2 {
let app = h.state();
let sh = app.session.doc().unwrap().wb.active().unwrap();
let geo = Geo::new(sh, app.grid.rect.unwrap(), app.view().scroll);
let a = sh.charts.iter().find(|c| c.id == id).unwrap().anchor;
let min = geo.cell_rect(sh, CellRef::parse("D3").unwrap()).min + vec2(a.dx, a.dy) * geo.z;
min + vec2(a.width, a.height) * geo.z
}
fn button(h: &mut Harness, pos: egui::Pos2, pressed: bool) {
h.input_mut().events.push(Event::PointerButton { pos, button: PointerButton::Primary, pressed, modifiers: Modifiers::NONE });
h.run_steps(2);
}
#[test]
fn dragging_the_corner_handle_resizes_the_chart() {
let (mut h, id) = fixture();
let (w0, h0) = size(&h, id);
let start = corner(&h, id);
// Select the chart first, as the user does before grabbing a handle.
let inside = start - vec2(40.0, 40.0);
h.input_mut().events.push(Event::PointerMoved(inside));
h.run_steps(2);
button(&mut h, inside, true);
button(&mut h, inside, false);
assert_eq!(h.state().selected_chart, Some(id));
h.input_mut().events.push(Event::PointerMoved(start));
h.run_steps(2);
assert_eq!(h.output().platform_output.cursor_icon, egui::CursorIcon::ResizeNwSe, "the corner shows a resize cursor");
button(&mut h, start, true);
// The pointer moves well past the drag threshold in one step, like a quick drag.
let end = start + vec2(60.0, 30.0);
h.input_mut().events.push(Event::PointerMoved(pos2(end.x, end.y)));
h.run_steps(3);
button(&mut h, end, false);
let (w1, h1) = size(&h, id);
assert!(w1 > w0 + 40.0 && h1 > h0 + 20.0, "chart grew from {w0}x{h0} to {w1}x{h1}");
}
/// Opens Format Chart Area for the chart and expands its Size section; returns the Width field's rect.
fn open_size_fields(h: &mut Harness, id: u32) -> egui::Rect {
h.state_mut().selected_chart = Some(id);
h.state_mut().grid.pane = Some("formatChart".into());
h.run_steps(2);
h.get_by_role_and_label(Role::Button, "Size").click();
h.run_steps(2);
h.get_all_by_role(Role::SpinButton).next().unwrap().rect()
}
#[test]
fn typing_a_width_in_the_size_field_resizes_the_chart() {
let (mut h, id) = fixture();
let (_, h0) = size(&h, id);
let field = open_size_fields(&mut h, id);
h.input_mut().events.push(Event::PointerMoved(field.center()));
button(&mut h, field.center(), true);
button(&mut h, field.center(), false);
// Clicking the field selects its text; each keystroke must survive the next frame.
for digit in ["6", "4", "0"] {
h.input_mut().events.push(Event::Text(digit.into()));
h.run_steps(2);
}
h.key_press(Key::Enter);
h.run_steps(2);
assert_eq!(size(&h, id), (640.0, h0));
}
#[test]
fn dragging_the_size_field_resizes_the_chart() {
let (mut h, id) = fixture();
let (w0, _) = size(&h, id);
let field = open_size_fields(&mut h, id);
h.input_mut().events.push(Event::PointerMoved(field.center()));
button(&mut h, field.center(), true);
for step in 1..=6 {
h.input_mut().events.push(Event::PointerMoved(field.center() + vec2(10.0 * step as f32, 0.0)));
h.run_steps(1);
}
button(&mut h, field.center() + vec2(60.0, 0.0), false);
let (w1, _) = size(&h, id);
assert!(w1 > w0 + 20.0, "width went from {w0} to {w1}");
}
fn right_click(h: &mut Harness, pos: egui::Pos2, wobble: egui::Vec2) {
h.input_mut().events.push(Event::PointerMoved(pos));
h.run_steps(2);
h.input_mut().events.push(Event::PointerButton { pos, button: PointerButton::Secondary, pressed: true, modifiers: Modifiers::NONE });
h.run_steps(2);
let end = pos + wobble;
if wobble != vec2(0.0, 0.0) {
h.input_mut().events.push(Event::PointerMoved(end));
h.run_steps(2);
}
h.input_mut().events.push(Event::PointerButton { pos: end, button: PointerButton::Secondary, pressed: false, modifiers: Modifiers::NONE });
h.run_steps(2);
}
#[test]
fn right_clicking_a_chart_opens_its_menu_not_the_cells() {
// #2067: a right-click on a chart opened the menu of the cell under it, or nothing at all.
for wobble in [vec2(0.0, 0.0), vec2(20.0, 10.0)] {
let (mut h, id) = fixture();
let (w0, h0) = size(&h, id);
let inside = corner(&h, id) - vec2(100.0, 80.0);
right_click(&mut h, inside, wobble);
assert_eq!(h.state().selected_chart, Some(id), "the right-clicked chart is selected ({wobble:?})");
assert!(matches!(h.state().grid.object_menu, Some((_, "chart", i)) if i == id), "the chart's menu opens ({wobble:?})");
assert!(h.state().grid.context_menu.is_none(), "the cell menu stays closed ({wobble:?})");
assert_eq!(size(&h, id), (w0, h0));
let a = h.state().session.doc().unwrap().wb.active().unwrap().charts.iter().find(|c| c.id == id).unwrap().anchor;
assert_eq!(a.cell, CellRef::parse("D3").unwrap(), "a right-button drag does not move the chart ({wobble:?})");
}
// Delete from the menu removes the chart.
let (mut h, id) = fixture();
let inside = corner(&h, id) - vec2(100.0, 80.0);
right_click(&mut h, inside, vec2(0.0, 0.0));
// Home › Cells › Delete has the same accessible name; the menu is drawn over the ribbon, last.
h.get_all_by_label("Delete").last().unwrap().click();
h.run_steps(3);
assert!(h.state().session.doc().unwrap().wb.active().unwrap().charts.is_empty());
assert!(h.state().grid.object_menu.is_none());
// Outside the chart a right-click still opens the cell menu.
let (mut h, _) = fixture();
let cell = {
let app = h.state();
let sh = app.session.doc().unwrap().wb.active().unwrap();
Geo::new(sh, app.grid.rect.unwrap(), app.view().scroll).cell_rect(sh, CellRef::parse("A10").unwrap()).center()
};
right_click(&mut h, cell, vec2(0.0, 0.0));
assert!(h.state().grid.context_menu.is_some() && h.state().grid.object_menu.is_none());
}