Fix continuous page seeking

This commit is contained in:
burnintuna 2026-07-22 03:10:57 +09:00
parent f0d5cf3496
commit 1b3783b795

View file

@ -243,6 +243,7 @@ impl ViewerWindow {
navigation_message,
navigation_message_label,
navigation_message_generation: Rc::new(Cell::new(0)),
continuous_programmatic_scroll: Cell::new(false),
state: RefCell::new(ViewerState {
pages: Vec::new(),
anchor: 0,
@ -333,6 +334,7 @@ struct ViewerController {
navigation_message: gtk::Box,
navigation_message_label: gtk::Label,
navigation_message_generation: Rc<Cell<u64>>,
continuous_programmatic_scroll: Cell<bool>,
state: RefCell<ViewerState>,
}
@ -948,13 +950,16 @@ impl ViewerController {
}
fn continuous_scrolled(self: &Rc<Self>) {
if self.view_mode() != ViewMode::Continuous || self.state.borrow().pages.is_empty() {
let pages_empty = self.state.borrow().pages.is_empty();
if self.view_mode() != ViewMode::Continuous || pages_empty {
return;
}
let visible = self.visible_indices();
let new_anchor = visible.first().copied();
let changed = new_anchor.is_some_and(|index| index != self.state.borrow().anchor);
if let Some(index) = new_anchor {
let update_anchor = !self.continuous_programmatic_scroll.get();
let changed =
update_anchor && new_anchor.is_some_and(|index| index != self.state.borrow().anchor);
if let Some(index) = new_anchor.filter(|_| update_anchor) {
self.state.borrow_mut().anchor = index;
}
self.render_continuous(&visible);
@ -1014,16 +1019,28 @@ impl ViewerController {
}
fn scroll_to_anchor(&self) {
let frame = {
let state = self.state.borrow();
let Some(row) = state.continuous_rows.get(state.anchor) else {
return;
};
let Some(bounds) = row.frame.compute_bounds(&self.continuous_box) else {
row.frame.clone()
};
let Some(bounds) = frame.compute_bounds(&self.continuous_box) else {
return;
};
let adjustment = self.continuous_scrolled.vadjustment();
let maximum = (adjustment.upper() - adjustment.page_size()).max(adjustment.lower());
adjustment.set_value(f64::from(bounds.y()).clamp(adjustment.lower(), maximum));
let target = centered_scroll_value(
f64::from(bounds.y()),
f64::from(bounds.height()),
adjustment.page_size(),
adjustment.lower(),
maximum,
);
self.continuous_programmatic_scroll.set(true);
adjustment.set_value(target);
self.continuous_programmatic_scroll.set(false);
}
fn clear_continuous_paintables(&self) {
@ -1629,6 +1646,16 @@ fn intersecting_rows(bounds: &[Option<(f64, f64)>], top: f64, bottom: f64) -> Ve
.collect()
}
fn centered_scroll_value(
row_top: f64,
row_height: f64,
viewport_height: f64,
minimum: f64,
maximum: f64,
) -> f64 {
(row_top + row_height / 2.0 - viewport_height / 2.0).clamp(minimum, maximum)
}
fn page_seek_index(value: f64, count: usize) -> Option<usize> {
(count > 0).then(|| (value.round().max(0.0) as usize).min(count - 1))
}
@ -1673,6 +1700,19 @@ mod tests {
assert_eq!(intersecting_rows(&bounds, 3000.0, 3800.0), [2]);
}
#[test]
fn continuous_seek_centers_the_selected_row() {
assert_eq!(
centered_scroll_value(1_000.0, 800.0, 600.0, 0.0, 5_000.0),
1_100.0
);
assert_eq!(centered_scroll_value(0.0, 400.0, 800.0, 0.0, 5_000.0), 0.0);
assert_eq!(
centered_scroll_value(5_000.0, 800.0, 600.0, 0.0, 5_000.0),
5_000.0
);
}
#[test]
fn fast_loading_prefetches_a_larger_window() {
assert_eq!(prefetch_window(&[10], 30, false), [10, 11, 12, 9]);