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,
navigation_message_label, navigation_message_label,
navigation_message_generation: Rc::new(Cell::new(0)), navigation_message_generation: Rc::new(Cell::new(0)),
continuous_programmatic_scroll: Cell::new(false),
state: RefCell::new(ViewerState { state: RefCell::new(ViewerState {
pages: Vec::new(), pages: Vec::new(),
anchor: 0, anchor: 0,
@ -333,6 +334,7 @@ struct ViewerController {
navigation_message: gtk::Box, navigation_message: gtk::Box,
navigation_message_label: gtk::Label, navigation_message_label: gtk::Label,
navigation_message_generation: Rc<Cell<u64>>, navigation_message_generation: Rc<Cell<u64>>,
continuous_programmatic_scroll: Cell<bool>,
state: RefCell<ViewerState>, state: RefCell<ViewerState>,
} }
@ -948,13 +950,16 @@ impl ViewerController {
} }
fn continuous_scrolled(self: &Rc<Self>) { 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; return;
} }
let visible = self.visible_indices(); let visible = self.visible_indices();
let new_anchor = visible.first().copied(); let new_anchor = visible.first().copied();
let changed = new_anchor.is_some_and(|index| index != self.state.borrow().anchor); let update_anchor = !self.continuous_programmatic_scroll.get();
if let Some(index) = new_anchor { 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.state.borrow_mut().anchor = index;
} }
self.render_continuous(&visible); self.render_continuous(&visible);
@ -1014,16 +1019,28 @@ impl ViewerController {
} }
fn scroll_to_anchor(&self) { fn scroll_to_anchor(&self) {
let frame = {
let state = self.state.borrow(); let state = self.state.borrow();
let Some(row) = state.continuous_rows.get(state.anchor) else { let Some(row) = state.continuous_rows.get(state.anchor) else {
return; 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; return;
}; };
let adjustment = self.continuous_scrolled.vadjustment(); let adjustment = self.continuous_scrolled.vadjustment();
let maximum = (adjustment.upper() - adjustment.page_size()).max(adjustment.lower()); 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) { fn clear_continuous_paintables(&self) {
@ -1629,6 +1646,16 @@ fn intersecting_rows(bounds: &[Option<(f64, f64)>], top: f64, bottom: f64) -> Ve
.collect() .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> { fn page_seek_index(value: f64, count: usize) -> Option<usize> {
(count > 0).then(|| (value.round().max(0.0) as usize).min(count - 1)) (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]); 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] #[test]
fn fast_loading_prefetches_a_larger_window() { fn fast_loading_prefetches_a_larger_window() {
assert_eq!(prefetch_window(&[10], 30, false), [10, 11, 12, 9]); assert_eq!(prefetch_window(&[10], 30, false), [10, 11, 12, 9]);