diff --git a/crates/ely-lok/src/view.rs b/crates/ely-lok/src/view.rs index e3bc9f7..6a63846 100644 --- a/crates/ely-lok/src/view.rs +++ b/crates/ely-lok/src/view.rs @@ -375,8 +375,9 @@ impl LokView { } if batch.cursor != self.cursor { self.moves += 1; - // A moved cursor stays in view. + // A moved cursor stays in view, but not mid-drag. if self.viewport.1 > 0.0 + && !self.dragging && let Some(cursor) = batch.cursor { self.show(cursor, cx); @@ -385,6 +386,7 @@ impl LokView { // A moved cell cursor stays in view too. if batch.cell.cursor != self.cell.cursor && self.viewport.1 > 0.0 + && !self.dragging && let Some(cell) = batch.cell.cursor { self.show(cell, cx); diff --git a/crates/ely-lok/src/view/scroll.rs b/crates/ely-lok/src/view/scroll.rs index ca0e9de..67af759 100644 --- a/crates/ely-lok/src/view/scroll.rs +++ b/crates/ely-lok/src/view/scroll.rs @@ -61,11 +61,11 @@ impl LokView { 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; + if x >= fx { + self.scroll.0 = least(x - fx, w, self.scroll.0, vw); } - if y >= fy && (y < fy + self.scroll.1 || y + h > fy + self.scroll.1 + vh) { - self.scroll.1 = y - fy - vh / 3; + if y >= fy { + self.scroll.1 = least(y - fy, h, self.scroll.1, vh); } self.clamp_scroll(); cx.notify(); @@ -172,3 +172,10 @@ impl LokView { }) } } + +/// The scroll nearest `scroll` that shows `start..start + len`. +fn least(start: i32, len: i32, scroll: i32, view: i32) -> i32 { + // A span wider than the view keeps covering it. + let end = start + len - view; + scroll.clamp(start.min(end), start.max(end)) +} diff --git a/crates/ely-lok/src/view/tests/large.rs b/crates/ely-lok/src/view/tests/large.rs index 5ddc0a0..ad91428 100644 --- a/crates/ely-lok/src/view/tests/large.rs +++ b/crates/ely-lok/src/view/tests/large.rs @@ -202,6 +202,16 @@ fn a_sheet_brings_far_cut_and_frozen_cells_into_view() { let cut = (width - 200, 0, 1280, 256); view.show(cut, cx); assert!(view.scroll.0 > 0 && shows(view, cut), "a cell cut at the right edge comes into view"); + let (x, _, w, _) = view.tile_rect(cut); + assert_eq!(view.scroll.0, x + w - width, "it scrolls no further than the cell's edge"); + let before = view.scroll; + view.show((0, 0, view.print_of((x + w + width, 0)).0, 256), cx); + assert_eq!(view.scroll, before, "a cell wider than the view and covering it keeps still"); + view.dragging = true; + let cell = crate::Cell { cursor: Some((1418, 90_000, 1280, 277)), ..crate::Cell::default() }; + view.apply_batch(crate::Events { cell, ..crate::Events::default() }, None, cx); + assert_eq!(view.scroll, before, "a drag does not scroll under the pointer"); + view.dragging = false; view.scroll = (0, 0); view.set_frozen((0, 600), cx); let frozen = view.place((0, 300)).y; diff --git a/tasks/BUGS.md b/tasks/BUGS.md index 8d934b8..f142100 100644 --- a/tasks/BUGS.md +++ b/tasks/BUGS.md @@ -3,7 +3,7 @@ `docs/BugList.md` 的 93 条,按清单次序逐条处理:先核对当前代码,已由先前改动修好的写明出处,其余修好、审查、提交、推送后勾上。 - [x] T-01 内核的图块与命中按像素网格,Ely 的光标、选区、表头按文档 twips,越远越错位 — R2.16 已修(图块 twips 与打印 twips 互换) -- [ ] T-02 单元格光标露出边缘即把视图滚到中央,点击与拖选时视图跳动 +- [x] T-02 单元格光标露出边缘即把视图滚到中央,点击与拖选时视图跳动 — R1 拖选中仍滚、超宽格跳回起点;R2 PASS - [ ] T-03 视口比工作表宽时网格先居中再左移一次 - [x] T-04 从不调用 `setClientZoom`,内核命中与几何按默认缩放算 — R2.16 已修(worker 每次报可见区前 setClientZoom) - [ ] T-05 文档尺寸按内核像素取整,表尾被裁 diff --git a/tasks/reviews/T-02-r1.md b/tasks/reviews/T-02-r1.md new file mode 100644 index 0000000..6f49f28 --- /dev/null +++ b/tasks/reviews/T-02-r1.md @@ -0,0 +1,5 @@ +FAIL + +crates/ely-lok/src/view/scroll.rs:64 — `show()` 在鼠标按下、`dragging=true` 时仍根据单元格光标调整滚动,拖选坐标随回调改变,违反 docs/BugList.md T-02 的拖选要求及 PRD TABLE-02、LOK-04。真实 LOK 探针使用 large.rs 的 SHORT_ROWS、100% 缩放、200×70 像素视口:在 (180,65) 按下 C4,等待光标回调,再拖到原画面 B3 所在的 (130,40) 并释放;按下后的 scroll 从 (0,0) 变成 (824,29),最终选区为 C3:C4,预期为 B3:C4,连续两次复现。large.rs:145 的现有拖选测试在同一次 update 中发送全部鼠标事件,覆盖不到这次中途滚动。修复方向:拖选期间保持视图坐标稳定,保留键盘导航的最小滚动;回归测试在按下与移动之间等待内核回调,并核对滚动与最终选区。副本中抑制拖选期间的自动滚动后,此探针通过。 + +crates/ely-lok/src/view/scroll.rs:178 — 超出视口尺寸的目标总被对齐到起点,视口已处于单元格内部时仍大幅跳动,T-02 的最小滚动要求在超宽列上仍失败。真实 LOK 探针设置 A 列宽 20 cm、视口 400×200 像素、100% 缩放,横向 scroll=4500 twips(300 像素);A2 的图块横向区间为 0..11325 twips,完整覆盖当前视口 4500..10500。在 (50,30) 点击当前可见的 A2 后,scroll 立即归零,画面左跳 300 像素,连续两次复现。修复方向:目标大于视口且覆盖当前可视区时保持当前位置,其余情况采用最小必要位移;补充超宽列及超高行的回归断言。副本中保留这种已覆盖视口的位置后,此探针通过。 diff --git a/tasks/reviews/T-02-r2.md b/tasks/reviews/T-02-r2.md new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/tasks/reviews/T-02-r2.md @@ -0,0 +1 @@ +PASS