diff --git a/apps/ely-doc/Cargo.toml b/apps/ely-doc/Cargo.toml index 58390c2..0c6ae3e 100644 --- a/apps/ely-doc/Cargo.toml +++ b/apps/ely-doc/Cargo.toml @@ -22,6 +22,7 @@ ely-ui.workspace = true [dev-dependencies] ely-ui = { workspace = true, features = ["test-support"] } +ely-gpui-component = { workspace = true, features = ["test-support"] } env_logger.workspace = true image.workspace = true tempfile.workspace = true diff --git a/apps/ely-doc/src/navigate.rs b/apps/ely-doc/src/navigate.rs index 30e5234..220cdc6 100644 --- a/apps/ely-doc/src/navigate.rs +++ b/apps/ely-doc/src/navigate.rs @@ -272,15 +272,13 @@ impl Render for Navigator { if let Some(at) = self.outline.iter().position(|heading| heading.current) { outline = outline.current(at.to_string()); } - let this = this.clone(); + // The places shown: a reread may change the list. + let places: Vec<_> = self.outline.iter().map(|heading| heading.rect).collect(); + let docs = self.docs.clone(); outline .on_select(move |key, _, cx| { let at: usize = key.parse().expect("an outline index"); - let (to, docs) = { - let navigator = this.read(cx); - (navigator.outline[at].rect, navigator.docs.clone()) - }; - Navigator::jump(&docs, to, cx); + Navigator::jump(&docs, places[at], cx); }) .into_any_element() } diff --git a/apps/ely-doc/src/navigate/tests.rs b/apps/ely-doc/src/navigate/tests.rs index 2e263c4..46bda18 100644 --- a/apps/ely-doc/src/navigate/tests.rs +++ b/apps/ely-doc/src/navigate/tests.rs @@ -252,3 +252,54 @@ fn pdfs_export_a_range_and_locked_files_ask() { "the locked text opens" ); } + +/// The navigator alone as a window's view. +struct Host(Entity); + +impl gpui::Render for Host { + fn render(&mut self, _: &mut gpui::Window, _: &mut gpui::Context) -> impl gpui::IntoElement { + gpui::div().size_full().child(self.0.clone()) + } +} + +#[test] +fn an_outline_click_goes_where_its_row_was_drawn() { + let dir = tempfile::tempdir().expect("a temp dir"); + let mut cx = TestAppContext::single(); + let Some(docs) = docs(&mut cx, dir.path()) else { + eprintln!("SKIP: no LOK build"); + return; + }; + cx.update(ely_gpui_component::init_for_tests); + crate::tests::blank(&mut cx, &docs); + let navigator = cx.update(|app| docs.read(app).navigator.clone()); + let heading = |n: i32| ely_lok::review::Heading { text: format!("Part {n}"), level: 0, rect: (0, n * 100, 10, 10), current: false }; + cx.update(|app| navigator.update(app, |navigator, cx| { + navigator.outline = vec![heading(1), heading(2), heading(3)]; + cx.notify(); + })); + let (_, vcx) = cx.add_window_view(|_, _| Host(navigator.clone())); + let target = |vcx: &mut gpui::VisualTestContext| vcx.update(|_, app| docs.read(app).going.map(|going| going.3)); + // Find where the drawn third row sits. + let third = (0..120) + .map(|step| gpui::point(gpui::px(40.), gpui::px(step as f32 * 4.))) + .find(|at| { + vcx.simulate_click(*at, gpui::Modifiers::none()); + target(vcx) == Some((0, 300, 10, 10)) + }) + .expect("the third row is drawn"); + vcx.update(|_, app| docs.update(app, |docs, _| docs.going = None)); + // The list shrinks; the click lands on the old frame. + vcx.update(|window, app| { + navigator.update(app, |navigator, cx| { + navigator.outline.truncate(1); + cx.notify(); + }); + let (position, modifiers, button) = (third, gpui::Modifiers::none(), gpui::MouseButton::Left); + let down = gpui::MouseDownEvent { position, modifiers, button, click_count: 1, first_mouse: false }; + window.dispatch_event(gpui::PlatformInput::MouseDown(down), app); + let up = gpui::MouseUpEvent { position, modifiers, button, click_count: 1 }; + window.dispatch_event(gpui::PlatformInput::MouseUp(up), app); + }); + assert_eq!(target(vcx), Some((0, 300, 10, 10)), "the click goes where its row was"); +} diff --git a/tasks/BUGS.md b/tasks/BUGS.md index e4e51b2..f722728 100644 --- a/tasks/BUGS.md +++ b/tasks/BUGS.md @@ -52,7 +52,7 @@ - [x] D-03 界面线程同步持 worker 锁读内核(样式、字体、书签、公式、分节名) — 后台读取,回来核对标签、视图与对话框请求号 - [x] D-04 一条批注的时间读不出,全部批注与修订都不显示 — 逐条解析,读不出的批注写明作者与原因(AX 可读、可滚动),修订照留 - [x] D-05 邮件合并某条记录失败时内核文档泄漏 — 失败的记录照样关闭内核文档,再传出首个错误 -- [ ] D-06 导航栏大纲点击按当前列表取下标,后台重读后可能越界 +- [x] D-06 导航栏大纲点击按当前列表取下标,后台重读后可能越界 — 点击按绘制时的列表跳转;补点击路径的回归测试 - [ ] D-07 导航栏隐藏时仍随每次光标移动重读大纲与书签 - [ ] D-08 Linux、Windows 上“打印…”可用,导出 PDF 后才报“此平台未构建” - [x] M-01 键入期间外部以相同内容重写文件,丢掉上次停顿之后键入的全部文字 — 先等页面送出未送的键;同文只接过文件与字节、标签为已保存;磁盘回到上次保存的文字时保留编辑并恢复自动保存;R1–R4 字节、自动保存、在途写入、已保存基线;R5 PASS