ElyTable: zoom and DPI keep the scroll in print twips; drag and zoom tests far down a sheet (R2.16)

This commit is contained in:
ZacharyZhang-NY committed 2026-10-09 02:29:46 -04:00
1 parent eb844cc72c
commit 56855acfb4
20 files changed
+155 -64

No files matched your search

-26
View File
@@ -485,29 +485,3 @@ fn enter_and_tab_move_the_cell_cursor() {
});
assert_eq!(right.cell.address, "G2", "Tab moves right");
}
#[test]
fn a_click_far_down_selects_the_cell_under_it() {
if skip() {
return;
}
let (_dir, mut worker, doc, seen) = open();
// One painted tile fixes the kernel's scale.
let (side, twips) = (512u32, 3840);
let ppt = f64::from(side) / f64::from(twips);
doc.paint_tile(&mut worker, (0, 0, twips, twips), (side, side)).expect("a tile");
let geometry = sheet::geometry(&worker.values(&doc, sheet::GEOMETRY).expect("geometry")).expect("parsed");
worker.run(&doc, "cell.goto", &["B4998"]).expect("B4998");
let at = poll_until(&mut worker, &doc, &seen, SETTLE, |seen| seen.cell.address == "B4998");
let (x, y, w, h) = at.cell.cursor.expect("the cursor");
worker.run(&doc, "cell.goto", &["A1"]).expect("A1");
let home = poll_until(&mut worker, &doc, &at, SETTLE, |seen| seen.cell.address == "A1");
// The cursor is in print twips; clicks take tile twips.
let (x, y) = (geometry.columns.tile(x + w / 2, ppt), geometry.rows.tile(y + h / 2, ppt));
for kind in [0, 1] {
let click = crate::Mouse { kind, x, y, count: 1, buttons: 1, modifiers: 0 };
worker.post_mouse(&doc, click).expect("clicks");
}
let after = poll_until(&mut worker, &doc, &home, SETTLE, |seen| seen.cell.address != "A1");
assert_eq!(after.cell.address, "B4998");
}
+2 -2
View File
@@ -40,7 +40,7 @@ impl Render for LokView {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let dpi = window.scale_factor();
if dpi != self.geom.dpi {
self.geom.dpi = dpi;
self.keep_scroll(|view| view.geom.dpi = dpi);
self.retire_tiles();
}
self.follow_theme(cx.theme().is_dark());
@@ -210,7 +210,7 @@ impl LokView {
let down = self.viewport.1 * TWIPS_PER_PIXEL / h as f32;
let zoom = (across.min(down) * MARGIN).min(4.0);
if (zoom - self.geom.zoom).abs() > 0.001 {
self.geom.zoom = zoom;
self.keep_scroll(|view| view.geom.zoom = zoom);
self.retire_tiles();
self.failed.clear();
}
+5 -7
View File
@@ -49,11 +49,9 @@ impl LokView {
self.geom.twips_of(self.viewport.0) - fx,
self.geom.twips_of(self.viewport.1) - fy,
);
// Room past the target, so the clamp lets it show.
self.reach = (
self.reach.0.max(x + w + vw + fx),
self.reach.1.max(y + h + vh + fy),
);
// Room past the target, in print twips for later zooms.
let need = self.print_of((x + w + vw + fx, y + h + vh + fy));
self.reach = (self.reach.0.max(need.0), self.reach.1.max(need.1));
if x >= fx && (x < fx + self.scroll.0 || x + w > fx + self.scroll.0 + vw) {
self.scroll.0 = x - fx - vw / 2;
}
@@ -104,10 +102,10 @@ impl LokView {
if self.fit {
return self.doc_size;
}
(
self.tile_of((
self.doc_size.0.max(self.reach.0),
self.doc_size.1.max(self.reach.1),
)
))
}
/// The print-twips document point under a view position (LOK-04).
+8
View File
@@ -48,6 +48,14 @@ impl LokView {
self.tile_of(self.frozen)
}
/// Rescales, keeping the scrolling pane's start in place.
pub(super) fn keep_scroll(&mut self, change: impl FnOnce(&mut Self)) {
let start = self.scrolled_start();
change(self);
let ((x, y), (fx, fy)) = (self.tile_of(start), self.frozen_tiles());
self.scroll = (x - fx, y - fy);
}
/// Where the scrolling pane starts, in print twips.
pub fn scrolled_start(&self) -> (i32, i32) {
let (fx, fy) = self.frozen_tiles();
+87 -28
View File
@@ -73,6 +73,44 @@ const SHORT_ROWS: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
</table:table></office:spreadsheet></office:body>
</office:document>"#;
/// A short-row sheet at 2× DPI, scrolled to `cell`'s row.
fn far_down(cx: &mut gpui::TestAppContext, install: &std::path::Path) -> (Entity<LokView>, crate::sheet::Geometry) {
let (view, worker) = cx.update(|app| live_view_of(app, install, "short.fods", SHORT_ROWS));
let json = cx.update(|app| {
let read = view.read(app);
worker.lock().expect("the worker lock").values(&read.doc, crate::sheet::GEOMETRY).expect("geometry")
});
let geometry = crate::sheet::geometry(&json).expect("parsed");
let row = cell(&geometry, 1, 4997);
cx.update(|app| {
view.update(app, |view, cx| {
view.geom.dpi = 2.0;
view.set_geometry(geometry.clone(), cx);
view.prepare(Bounds { origin: point(px(0.0), px(0.0)), size: size(px(800.0), px(600.0)) });
view.show(row, cx);
})
});
(view, geometry)
}
/// Column `x`, row `y` in print twips.
fn cell(geometry: &crate::sheet::Geometry, x: u32, y: u32) -> crate::Rect {
(geometry.columns.start(x), geometry.rows.start(y), geometry.columns.size(x), geometry.rows.size(y))
}
/// Waits until the kernel reports `want` as the selection.
fn selected(view: &Entity<LokView>, cx: &mut gpui::TestAppContext, want: &str) -> String {
let mut address = String::new();
for _ in 0..200 {
cx.executor().tick();
address = cx.update(|app| view.read(app).cell().address.clone());
if address == want {
break;
}
}
address
}
#[test]
fn a_click_far_down_a_sheet_selects_the_cell_under_it() {
let Some(install) = crate::client::tests::install() else {
@@ -80,38 +118,59 @@ fn a_click_far_down_a_sheet_selects_the_cell_under_it() {
return;
};
let mut cx = gpui::TestAppContext::single();
let (view, worker) = cx.update(|app| live_view_of(app, &install, "short.fods", SHORT_ROWS));
let json = cx.update(|app| {
let read = view.read(app);
worker.lock().expect("the worker lock").values(&read.doc, crate::sheet::GEOMETRY).expect("geometry")
});
let geometry = crate::sheet::geometry(&json).expect("parsed");
let (row, column) = (4997, 1);
let cell = (
geometry.columns.start(column),
geometry.rows.start(row),
geometry.columns.size(column),
geometry.rows.size(row),
);
let (view, geometry) = far_down(&mut cx, &install);
let (x, y, w, h) = cell(&geometry, 1, 4997);
cx.update(|app| {
view.update(app, |view, cx| {
view.geom.dpi = 2.0;
view.set_geometry(geometry.clone(), cx);
view.prepare(Bounds { origin: point(px(0.0), px(0.0)), size: size(px(800.0), px(600.0)) });
view.show(cell, cx);
let at = view.window_point((cell.0 + cell.2 / 2, cell.1 + cell.3 / 2));
view.update(app, |view, _| {
let at = view.window_point((x + w / 2, y + h / 2));
for kind in [0, 1] {
view.mouse(kind, at, 1, 1, &gpui::Modifiers::default());
}
})
});
let mut address = String::new();
for _ in 0..200 {
cx.executor().tick();
address = cx.update(|app| view.read(app).cell().address.clone());
if address == "B4998" {
break;
}
}
assert_eq!(address, "B4998");
assert_eq!(selected(&view, &mut cx, "B4998"), "B4998");
}
#[test]
fn a_drag_far_down_a_sheet_selects_the_range_under_it() {
let Some(install) = crate::client::tests::install() else {
eprintln!("SKIP: no pinned LOK build");
return;
};
let mut cx = gpui::TestAppContext::single();
let (view, geometry) = far_down(&mut cx, &install);
let middle = |(x, y, w, h): crate::Rect| (x + w / 2, y + h / 2);
let (from, to) = (middle(cell(&geometry, 1, 4997)), middle(cell(&geometry, 3, 5000)));
cx.update(|app| {
view.update(app, |view, _| {
let modifiers = gpui::Modifiers::default();
view.mouse(0, view.window_point(from), 1, 1, &modifiers);
view.mouse(2, view.window_point(((from.0 + to.0) / 2, (from.1 + to.1) / 2)), 1, 1, &modifiers);
view.mouse(2, view.window_point(to), 1, 1, &modifiers);
view.mouse(1, view.window_point(to), 1, 1, &modifiers);
})
});
assert_eq!(selected(&view, &mut cx, "B4998:D5001"), "B4998:D5001");
}
#[test]
fn zooming_far_down_a_sheet_keeps_the_rows_in_view() {
let Some(install) = crate::client::tests::install() else {
eprintln!("SKIP: no pinned LOK build");
return;
};
let mut cx = gpui::TestAppContext::single();
let (view, geometry) = far_down(&mut cx, &install);
let middle = |cx: &mut gpui::TestAppContext| {
cx.update(|app| {
let read = view.read(app);
let (w, h) = read.viewport();
let at = read.origin() + point(px(w / 2.0), px(h / 2.0));
geometry.rows.at(read.doc_point(at).1)
})
};
let before = middle(&mut cx);
cx.update(|app| view.update(app, |view, cx| view.set_zoom(0.8, cx)));
let after = middle(&mut cx);
assert!(before.abs_diff(after) <= 1, "row {before} became {after}");
}
+3 -1
View File
@@ -47,13 +47,15 @@ impl LokView {
Some(at) => (f32::from(at.x - self.origin.x), f32::from(at.y - self.origin.y)),
None => (self.viewport.0 / 2.0, self.viewport.1 / 2.0),
};
let under = (self.scroll.0 + self.geom.twips_of(ax), self.scroll.1 + self.geom.twips_of(ay));
// Anchored in print twips: tile twips change with the zoom.
let under = self.print_of((self.scroll.0 + self.geom.twips_of(ax), self.scroll.1 + self.geom.twips_of(ay)));
self.fit = false;
if (zoom - self.geom.zoom).abs() > 0.0001 {
self.geom.zoom = zoom;
self.retire_tiles();
self.failed.clear();
}
let under = self.tile_of(under);
self.scroll = (under.0 - self.geom.twips_of(ax), under.1 - self.geom.twips_of(ay));
self.clamp_scroll();
log::debug!("lok: zoom {:.0}%", zoom * 100.0);