Merge PR #970: Control: Go To and Select over the control channel scroll the active cell into view

This commit is contained in:
aiongg
2026-10-22 02:35:22 +00:00
3 changed files with 52 additions and 4 deletions
+12 -2
View File
@@ -71,6 +71,16 @@ fn wrap(r: Result<Json, String>) -> Outcome {
}
}
/// Runs a command for an agent. Commands that move the selection scroll the active cell into
/// view, as the Go To dialog, the Name Box and the arrow keys do.
fn run(app: &mut SheetApp, id: &str, params: Json) -> Result<Json, String> {
let r = app.run(id, params);
if r.is_ok() && matches!(id, "edit.goTo" | "edit.goToSpecial" | "selection.set" | "selection.move") {
app.grid.ensure_visible = true;
}
r
}
pub fn inspect(app: &SheetApp, ctx: &egui::Context) -> Json {
let r = ctx.content_rect();
let v = app.view();
@@ -171,7 +181,7 @@ pub fn handle(app: &mut SheetApp, ctx: &egui::Context, method: &str, p: &Json) -
"engine.execute" | "command" | "ui.menu.invoke" => {
let Some(id) = s("command").or(s("id")) else { return err("missing `command`") };
let params = p.get("params").cloned().unwrap_or(json!({}));
wrap(app.run(id, if params.is_null() { json!({}) } else { params }))
wrap(run(app, id, if params.is_null() { json!({}) } else { params }))
}
"engine.commands" => {
let mut v: Vec<Json> = app.session.commands().into_iter().map(|c| serde_json::to_value(c).unwrap_or_default()).collect();
@@ -387,7 +397,7 @@ pub fn handle(app: &mut SheetApp, ctx: &egui::Context, method: &str, p: &Json) -
ok(Json::Null)
}
// Anything else that names a command runs it.
other if gridcraft_engine::find_command(other).is_some() => wrap(app.run(other, p.clone())),
other if gridcraft_engine::find_command(other).is_some() => wrap(run(app, other, p.clone())),
other => err(format!("unknown method `{other}`")),
};
ctx.request_repaint();
+39 -1
View File
@@ -1,10 +1,12 @@
//! Extending the selection from the keyboard scrolls its moving end into view, not the anchor.
//! Go To and Select over the control channel scroll the active cell into view.
use egui::{Event, Key, Modifiers};
use gridcraft_engine::Session;
use gridcraft_engine::core::{CellRef, RangeRef};
use gridcraft_ui_egui::control::{Outcome, handle};
use gridcraft_ui_egui::{SheetApp, grid::Geo};
use serde_json::json;
use serde_json::{Value as Json, json};
type Harness = egui_kittest::Harness<'static, SheetApp>;
@@ -104,3 +106,39 @@ fn ctrl_shift_home_and_end_scroll_the_moving_end_into_view() {
key(&mut h, Key::Home, CTRL_SHIFT);
assert_extended(&h, "CV200", "A1");
}
fn control(h: &mut Harness, method: &str, params: Json) -> Json {
let ctx = h.ctx.clone();
let out = match handle(h.state_mut(), &ctx, method, &params) {
Outcome::Done(v) => v,
Outcome::Screenshot { .. } => json!(null),
};
h.run_steps(2);
out
}
fn active(h: &Harness) -> CellRef {
h.state().session.active().unwrap().selection.active
}
#[test]
fn go_to_over_the_control_channel_scrolls_the_target_into_view() {
let mut h = harness();
assert!(!visible(&h, "Z300"));
let r = control(&mut h, "engine.execute", json!({"command": "edit.goTo", "params": {"reference": "Z300"}}));
assert_eq!(r["ok"], json!(true), "{r}");
assert_eq!(active(&h), CellRef::parse("Z300").unwrap());
assert!(visible(&h, "Z300"), "Go To scrolls Z300 into view");
assert!(!visible(&h, "A1"));
// A command named as the method runs the same way.
control(&mut h, "edit.goTo", json!({"reference": "A1"}));
assert!(visible(&h, "A1") && !visible(&h, "Z300"));
}
#[test]
fn select_over_the_control_channel_scrolls_the_active_cell_into_view() {
let mut h = harness();
control(&mut h, "engine.execute", json!({"command": "selection.set", "params": {"range": "BZ150:CA160"}}));
assert_eq!(active(&h), CellRef::parse("BZ150").unwrap());
assert!(visible(&h, "BZ150") && !visible(&h, "A1"), "selecting a far range scrolls its active cell into view");
}
+1 -1
View File
@@ -23,7 +23,7 @@ connection; nothing sent after it runs. Blank lines are skipped.
| Method | Params | What it does |
|---|---|---|
| `engine.execute` | `{command, params}` | Runs any engine command (see `engine.commands` or `gridcraft-cli commands`) |
| `engine.execute` | `{command, params}` | Runs any engine command (see `engine.commands` or `gridcraft-cli commands`). `edit.goTo`, `edit.goToSpecial`, `selection.set` and `selection.move` scroll the active cell into view, as Go To does in the window |
| `engine.commands` | `{search?}` | Every command: id, label, ribbon path, shortcut, params doc, enabled; `search` keeps those whose id, label or ribbon path contains it (case-insensitive) |
| `engine.journal` | | Commands run so far (replayable). Passwords (`password`, `…Password` params) are left out, so a replayed Protect Sheet has no password |
| `document.inspect` | | Workbook summary: sheets, used ranges, tables, charts, names, selection, undo labels |