diff --git a/crates/ui-egui/src/layers.rs b/crates/ui-egui/src/layers.rs index 212ae413..f172b309 100644 --- a/crates/ui-egui/src/layers.rs +++ b/crates/ui-egui/src/layers.rs @@ -229,8 +229,9 @@ fn pages(app: &mut FigApp, ui: &mut Ui) { let mut move_to: Option<(NodeId, String)> = None; // Pages are dragged up and down the list to reorder them. let mut reorder = crate::list_drag::ListDrag::in_order(ui, "pages"); - let scroll_to_cur = app.ui.pages_seen_page != Some(cur); - app.ui.pages_seen_page = Some(cur); + let page_key = (uid, cur); + let scroll_to_cur = app.ui.pages_seen_page != Some(page_key); + let mut current_row_rendered = false; // A long list scrolls in its own area, so the layers keep their room below it. let max_h = pages_list_height(app.ui.pages_height, ui.available_height()); let mut moved = egui::ScrollArea::vertical().id_salt("pages_list").max_height(max_h).show(ui, |ui| { @@ -240,8 +241,11 @@ fn pages(app: &mut FigApp, ui: &mut Ui) { } let sense = if filtering { Sense::click() } else { Sense::click_and_drag() }; let (r, resp) = ui.allocate_exact_size(vec2(w, 32.0), sense); - if scroll_to_cur && id == cur { - ui.scroll_to_rect(r, None); + if id == cur { + current_row_rendered = true; + if scroll_to_cur { + ui.scroll_to_rect(r, None); + } } if !filtering { reorder.row_by(&resp, k, r, || if divider { tr("Divider").to_string() } else { name.clone() }); @@ -306,6 +310,9 @@ fn pages(app: &mut FigApp, ui: &mut Ui) { } reorder.finish(ui) }); + if current_row_rendered { + app.ui.pages_seen_page = Some(page_key); + } // A drop outside the list's visible area (and the row gap at its edges) isn't on a page row, // even one scrolled out of view. if !ui.input(|i| i.pointer.latest_pos()).is_some_and(|p| moved.inner_rect.expand2(vec2(0.0, 3.0)).contains(p)) { diff --git a/crates/ui-egui/src/layers_behaviour_tests.rs b/crates/ui-egui/src/layers_behaviour_tests.rs index cb62540c..4d987e66 100644 --- a/crates/ui-egui/src/layers_behaviour_tests.rs +++ b/crates/ui-egui/src/layers_behaviour_tests.rs @@ -529,8 +529,37 @@ fn selecting_a_distant_page_scrolls_it_into_view() { for _ in 0..15 { u.frame(vec![]); } - assert_eq!(u.app.ui.pages_seen_page, Some(last)); + assert_eq!(u.app.ui.pages_seen_page, Some((u.app.session.ds().unwrap().uid, last))); let opened = pages_down_to(&mut u, layer.y - 60.0); let top = pages.iter().position(|p| Some(p) == opened.first()).unwrap(); assert!(top > 20, "the list scrolled past its first pages to reveal the selection (top row: page {top})"); } + +/// If the active page is filtered out by search, it is not marked as seen +/// until the filter is cleared and its row actually renders. +#[test] +fn filtered_out_active_page_is_seen_only_after_rendering() { + let mut s = Session::new(); + let p1 = s.doc().unwrap().pages[0]; + let p2 = id(s.execute("page.add", &json!({})).unwrap()); + s.execute("page.rename", &json!({"id": p1, "name": "Alpha"})).unwrap(); + s.execute("page.rename", &json!({"id": p2, "name": "Beta"})).unwrap(); + s.execute("page.select", &json!({"id": p1})).unwrap(); + let uid = s.ds().unwrap().uid; + let mut u = Ui::new(s); + assert_eq!(u.app.ui.pages_seen_page, Some((uid, p1))); + + // Filter to only "Alpha", then select "Beta" (p2). + u.app.ui.page_search = true; + u.app.ui.page_filter = "Alpha".into(); + u.app.run("page.select", json!({"id": p2})); + u.frame(vec![]); + + // Beta was filtered out and not rendered, so pages_seen_page has not recorded it. + assert_ne!(u.app.ui.pages_seen_page, Some((uid, p2))); + + // Clear filter: Beta renders now and is recorded. + u.app.ui.page_filter.clear(); + u.frame(vec![]); + assert_eq!(u.app.ui.pages_seen_page, Some((uid, p2))); +} diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index fb44b60a..8801238d 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -495,8 +495,8 @@ pub struct UiState { pub page_filter: String, /// The height asked for the pages list (`None`: a share of the panel). pub pages_height: Option, - /// The active page when last drawn in the pages list, to scroll it into view when selected. - pub pages_seen_page: Option, + /// The document and active page last drawn in the pages list. + pub pages_seen_page: Option<(u64, NodeId)>, pub expanded: BTreeSet, /// The layer being renamed in the layers panel. pub rename: Option,