ailloli_ui_editor/input/scroll.rs
1//! Wrap-aware scroll-axis selection and bounded offset updates.
2
3use crate::EditorWrapMode;
4use ailloli_ui_core::scroll::{ScrollAxes, ScrollMetrics, ScrollState};
5use ailloli_ui_core::Offset;
6use ailloli_ui_text::TextEditState;
7
8/// Selects vertical-only scrolling for soft wrap and both axes for no wrap.
9///
10/// # Examples
11///
12/// ```
13/// use ailloli_ui_core::ScrollAxes;
14/// use ailloli_ui_editor::{input::scroll::axes_for_wrap_mode, EditorWrapMode};
15/// assert_eq!(axes_for_wrap_mode(EditorWrapMode::SoftWrap), ScrollAxes::VERTICAL);
16/// assert_eq!(axes_for_wrap_mode(EditorWrapMode::NoWrap), ScrollAxes::BOTH);
17/// ```
18pub fn axes_for_wrap_mode(wrap_mode: EditorWrapMode) -> ScrollAxes {
19 match wrap_mode {
20 EditorWrapMode::SoftWrap => ScrollAxes::VERTICAL,
21 EditorWrapMode::NoWrap => ScrollAxes::BOTH,
22 }
23}
24
25/// Applies a logical-pixel scroll delta clamped by viewport/content metrics.
26///
27/// Soft wrap always resets horizontal offset to zero. Returns `true` iff either
28/// stored axis changed; non-finite or oversize deltas inherit `ScrollState`'s
29/// normalization and clamping policy.
30///
31/// # Examples
32///
33/// ```
34/// use ailloli_ui_core::{Offset, ScrollMetrics, Size};
35/// use ailloli_ui_editor::{input::scroll::scroll_by, EditorWrapMode};
36/// use ailloli_ui_text::TextEditState;
37/// let mut edit = TextEditState::new();
38/// let changed = scroll_by(&mut edit, EditorWrapMode::NoWrap, Offset::new(0.0, 0.0), ScrollMetrics::new(Size::new(100.0, 80.0), Size::new(300.0, 200.0)));
39/// assert!(!changed);
40/// assert_eq!((edit.scroll_x, edit.scroll_y), (0.0, 0.0));
41/// ```
42pub fn scroll_by(
43 edit: &mut TextEditState,
44 wrap_mode: EditorWrapMode,
45 delta: Offset,
46 metrics: ScrollMetrics,
47) -> bool {
48 let before_x = edit.scroll_x;
49 let before_y = edit.scroll_y;
50 let axes = axes_for_wrap_mode(wrap_mode);
51 let state = ScrollState::with_offset(Offset::new(edit.scroll_x, edit.scroll_y));
52 let outcome = state.scroll_by(delta, metrics, axes);
53 edit.scroll_y = outcome.after.y;
54 edit.scroll_x = if matches!(wrap_mode, EditorWrapMode::NoWrap) {
55 outcome.after.x
56 } else {
57 0.0
58 };
59 edit.scroll_x != before_x || edit.scroll_y != before_y
60}