ui: scroll active page into view when selection changes

This commit is contained in:
Siade Lucas
2026-10-20 12:53:21 -03:00
parent 594dc8c6cf
commit 1c79c1646f
4 changed files with 39 additions and 0 deletions
+4
View File
@@ -24,6 +24,10 @@ display_name = "VanTan"
real_name = "Tyler Jennings"
display_name = "tylerjenningsw"
[people.SiadeLucas]
real_name = "Siade Lucas"
sync_github_name = true
[people.Silvertree2010]
display_name = "Netter"
sync_github_name = false
+5
View File
@@ -229,6 +229,8 @@ 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);
// 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| {
@@ -238,6 +240,9 @@ 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 !filtering {
reorder.row_by(&resp, k, r, || if divider { tr("Divider").to_string() } else { name.clone() });
}
@@ -507,3 +507,30 @@ fn the_pages_list_keeps_three_rows_and_leaves_the_layers_room() {
// A panel too short for both keeps the three rows.
assert_eq!(pages_list_height(Some(500.0), 100.0), 3.0 * ROW);
}
/// Selecting a page that is far down the list automatically scrolls it into view.
#[test]
fn selecting_a_distant_page_scrolls_it_into_view() {
let mut s = Session::new();
let rect = id(s.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap());
for _ in 0..40 {
s.execute("page.add", &json!({})).unwrap();
}
let pages = s.doc().unwrap().pages.clone();
let first = pages[0];
let last = *pages.last().unwrap();
s.execute("page.select", &json!({"id": first})).unwrap();
s.execute("select.none", &json!({})).unwrap();
let mut u = Ui::new(s);
let layer = u.row_at(rect);
u.frame(vec![]);
// Select the last page.
u.app.run("page.select", json!({"id": last}));
for _ in 0..15 {
u.frame(vec![]);
}
assert_eq!(u.app.ui.pages_seen_page, Some(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})");
}
+3
View File
@@ -495,6 +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<f32>,
/// The active page when last drawn in the pages list, to scroll it into view when selected.
pub pages_seen_page: Option<NodeId>,
pub expanded: BTreeSet<NodeId>,
/// The layer being renamed in the layers panel.
pub rename: Option<layers::Rename>,
@@ -667,6 +669,7 @@ impl Default for UiState {
page_search: false,
page_filter: String::new(),
pages_height: None,
pages_seen_page: None,
expanded: BTreeSet::new(),
rename: None,
rename_page: None,