From dfb780a544ea1131b63f3cbac49dd6eaa0f9e8f2 Mon Sep 17 00:00:00 2001 From: Marco Arnulfo Date: Mon, 12 Oct 2026 15:56:22 +0200 Subject: [PATCH] UI: clicking a column or row header in point mode inserts N:N / n:n (#691) --- crates/ui-egui/src/grid.rs | 16 ++++ crates/ui-egui/tests/formula_point_headers.rs | 75 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 crates/ui-egui/tests/formula_point_headers.rs diff --git a/crates/ui-egui/src/grid.rs b/crates/ui-egui/src/grid.rs index d0eef85..5f4583c 100644 --- a/crates/ui-egui/src/grid.rs +++ b/crates/ui-egui/src/grid.rs @@ -1770,6 +1770,9 @@ fn interact(app: &mut SheetApp, ui: &mut egui::Ui, resp: &egui::Response, geo: & return; } let c = geo.col_at(sh, p.x); + if point_header(app, wb, sh, RangeRef::cols(c, c)) { + return; + } if resp.drag_started() && !mods.any() && sel.ranges.len() == 1 @@ -1790,6 +1793,9 @@ fn interact(app: &mut SheetApp, ui: &mut egui::Ui, resp: &egui::Response, geo: & return; } let r = geo.row_at(sh, p.y); + if point_header(app, wb, sh, RangeRef::rows(r, r)) { + return; + } let range = if mods.shift { RangeRef::rows(sel.anchor.row.min(r), sel.anchor.row.max(r)) } else { RangeRef::rows(r, r) }; select_ranges(app, range, mods.command, CellRef::new(r, geo.col_at(sh, geo.cells.left() + 1.0))); app.grid.drag = Drag::Rows(if mods.shift { sel.anchor.row } else { r }); @@ -2097,6 +2103,16 @@ fn interact(app: &mut SheetApp, ui: &mut egui::Ui, resp: &egui::Response, geo: & keyboard(app, &ctx, resp, geo, sh); } +/// Point mode on a header: inserts the whole-column or whole-row reference instead of selecting. +fn point_header(app: &mut SheetApp, wb: &Workbook, sh: &Sheet, range: RangeRef) -> bool { + let Some(ed) = app.editor.as_mut().filter(|ed| ed.can_point()) else { return false }; + let r = range.a1(); + let r = if ed.sheet != wb.active_sheet { format!("{}!{r}", gridcraft_engine::formula::quote_sheet(&sh.name)) } else { r }; + ed.insert_ref(&r); + ed.request_focus = true; + true +} + fn select_to_pointer(app: &mut SheetApp, geo: &Geo, sh: &Sheet, wb: &Workbook, p: Pos2) { let cell = CellRef::new( geo.row_at(sh, p.y.clamp(geo.cells.top() + 1.0, geo.cells.bottom() - 1.0)), diff --git a/crates/ui-egui/tests/formula_point_headers.rs b/crates/ui-egui/tests/formula_point_headers.rs new file mode 100644 index 0000000..4313fb9 --- /dev/null +++ b/crates/ui-egui/tests/formula_point_headers.rs @@ -0,0 +1,75 @@ +//! Point mode on the headers: while a formula waits for a reference, a header click inserts a +//! whole-column or whole-row reference, driven through real egui pointer events. + +use egui::{Event, Modifiers, PointerButton, Pos2, pos2, vec2}; +use gridcraft_engine::Session; +use gridcraft_ui_egui::{SheetApp, grid::Geo}; +use serde_json::json; + +fn harness() -> egui_kittest::Harness<'static, SheetApp> { + let mut session = Session::new(); + session.new_workbook(); + session.execute("selection.set", json!({"range": "F8"})).unwrap(); + let mut h = egui_kittest::Harness::builder().with_step_dt(1.0 / 60.0).with_size(vec2(1200.0, 800.0)).build_ui_state( + |ui, app: &mut SheetApp| { + app.logic(ui.ctx()); + app.ui(ui); + }, + SheetApp::new(session, Default::default()), + ); + h.run_steps(4); + h.state_mut().begin_edit(Some("=SUM(".into()), false); + h.run_steps(2); + h +} + +fn header_center(h: &egui_kittest::Harness<'_, SheetApp>, rows: bool, index: u32) -> Pos2 { + let app = h.state(); + let sheet = app.session.active().unwrap().wb.active().unwrap(); + let geo = Geo::new(sheet, app.grid.rect.unwrap(), app.view().scroll); + if rows { + pos2(geo.rect.left() + geo.header_w * 0.5, geo.y(sheet, index) + sheet.row_height(index) * geo.z * 0.5) + } else { + pos2(geo.x(sheet, index) + sheet.col_width(index) * geo.z * 0.5, geo.rect.top() + geo.header_h * 0.5) + } +} + +fn click(h: &mut egui_kittest::Harness<'_, SheetApp>, pos: Pos2) { + for pressed in [true, false] { + h.input_mut() + .events + .extend([Event::PointerMoved(pos), Event::PointerButton { pos, button: PointerButton::Primary, pressed, modifiers: Modifiers::NONE }]); + h.step(); + } + h.run_steps(2); +} + +#[test] +fn column_header_click_inserts_a_column_reference() { + let mut h = harness(); + let at = header_center(&h, false, 2); + click(&mut h, at); + let app = h.state(); + assert_eq!(app.editor.as_ref().map(|e| e.text.as_str()), Some("=SUM(C:C")); + assert_eq!(app.session.active().unwrap().selection.current().a1(), "F8"); +} + +#[test] +fn row_header_click_inserts_a_row_reference() { + let mut h = harness(); + let at = header_center(&h, true, 3); + click(&mut h, at); + let app = h.state(); + assert_eq!(app.editor.as_ref().map(|e| e.text.as_str()), Some("=SUM(4:4")); + assert_eq!(app.session.active().unwrap().selection.current().a1(), "F8"); +} + +#[test] +fn a_second_header_click_replaces_the_pointed_reference() { + let mut h = harness(); + let first = header_center(&h, false, 2); + click(&mut h, first); + let second = header_center(&h, false, 4); + click(&mut h, second); + assert_eq!(h.state().editor.as_ref().map(|e| e.text.as_str()), Some("=SUM(E:E")); +}