Skip to main content

iced_core/widget/operation/
scrollable.rs

1//! Operate on widgets that can be scrolled.
2use crate::widget::operation::Animation;
3use crate::widget::{Id, Operation};
4use crate::{Rectangle, Size, Vector};
5
6/// The internal state of a widget that can be scrolled.
7pub trait Scrollable {
8    /// Snaps the scroll of the widget to the given `percentage` along the horizontal & vertical axis.
9    fn snap_to(
10        &mut self,
11        offset: RelativeOffset<Option<f32>>,
12        animation: Animation,
13        bounds: Rectangle,
14        content: Size,
15    );
16
17    /// Scroll the widget to the given [`AbsoluteOffset`] along the horizontal & vertical axis.
18    fn scroll_to(
19        &mut self,
20        offset: AbsoluteOffset<Option<f32>>,
21        animation: Animation,
22        bounds: Rectangle,
23        content: Size,
24    );
25
26    /// Scroll the widget by the given [`AbsoluteOffset`] along the horizontal & vertical axis.
27    fn scroll_by(
28        &mut self,
29        offset: AbsoluteOffset,
30        animation: Animation,
31        bounds: Rectangle,
32        content: Size,
33    );
34}
35
36/// Produces an [`Operation`] that snaps the widget with the given [`Id`] to
37/// the provided `percentage`.
38pub fn snap_to<T>(
39    target: Id,
40    offset: RelativeOffset<Option<f32>>,
41    animation: Animation,
42) -> impl Operation<T> {
43    struct SnapTo {
44        target: Id,
45        offset: RelativeOffset<Option<f32>>,
46        animation: Animation,
47    }
48
49    impl<T> Operation<T> for SnapTo {
50        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
51            operate(self);
52        }
53
54        fn scrollable(
55            &mut self,
56            id: Option<&Id>,
57            bounds: Rectangle,
58            content: Size,
59            _translation: Vector,
60            state: &mut dyn Scrollable,
61        ) {
62            if Some(&self.target) == id {
63                state.snap_to(self.offset, self.animation, bounds, content);
64            }
65        }
66    }
67
68    SnapTo {
69        target,
70        offset,
71        animation,
72    }
73}
74
75/// Produces an [`Operation`] that scrolls the widget with the given [`Id`] to
76/// the provided [`AbsoluteOffset`].
77pub fn scroll_to<T>(
78    target: Id,
79    offset: AbsoluteOffset<Option<f32>>,
80    animation: Animation,
81) -> impl Operation<T> {
82    struct ScrollTo {
83        target: Id,
84        offset: AbsoluteOffset<Option<f32>>,
85        animation: Animation,
86    }
87
88    impl<T> Operation<T> for ScrollTo {
89        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
90            operate(self);
91        }
92
93        fn scrollable(
94            &mut self,
95            id: Option<&Id>,
96            bounds: Rectangle,
97            content: Size,
98            _translation: Vector,
99            state: &mut dyn Scrollable,
100        ) {
101            if Some(&self.target) == id {
102                state.scroll_to(self.offset, self.animation, bounds, content);
103            }
104        }
105    }
106
107    ScrollTo {
108        target,
109        offset,
110        animation,
111    }
112}
113
114/// Produces an [`Operation`] that scrolls the widget with the given [`Id`] by
115/// the provided [`AbsoluteOffset`].
116pub fn scroll_by<T>(target: Id, offset: AbsoluteOffset, animation: Animation) -> impl Operation<T> {
117    struct ScrollBy {
118        target: Id,
119        offset: AbsoluteOffset,
120        animation: Animation,
121    }
122
123    impl<T> Operation<T> for ScrollBy {
124        fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
125            operate(self);
126        }
127
128        fn scrollable(
129            &mut self,
130            id: Option<&Id>,
131            bounds: Rectangle,
132            content: Size,
133            _translation: Vector,
134            state: &mut dyn Scrollable,
135        ) {
136            if Some(&self.target) == id {
137                state.scroll_by(self.offset, self.animation, bounds, content);
138            }
139        }
140    }
141
142    ScrollBy {
143        target,
144        offset,
145        animation,
146    }
147}
148
149/// The amount of absolute offset in each direction of a [`Scrollable`].
150#[derive(Debug, Clone, Copy, PartialEq, Default)]
151pub struct AbsoluteOffset<T = f32> {
152    /// The amount of horizontal offset
153    pub x: T,
154    /// The amount of vertical offset
155    pub y: T,
156}
157
158impl From<AbsoluteOffset> for AbsoluteOffset<Option<f32>> {
159    fn from(offset: AbsoluteOffset) -> Self {
160        Self {
161            x: Some(offset.x),
162            y: Some(offset.y),
163        }
164    }
165}
166
167impl std::ops::Add<Vector> for AbsoluteOffset {
168    type Output = Self;
169
170    fn add(self, translation: Vector) -> Self::Output {
171        Self {
172            x: self.x + translation.x,
173            y: self.y + translation.y,
174        }
175    }
176}
177
178/// The amount of relative offset in each direction of a [`Scrollable`].
179///
180/// A value of `0.0` means start, while `1.0` means end.
181#[derive(Debug, Clone, Copy, PartialEq, Default)]
182pub struct RelativeOffset<T = f32> {
183    /// The amount of horizontal offset
184    pub x: T,
185    /// The amount of vertical offset
186    pub y: T,
187}
188
189impl RelativeOffset {
190    /// A relative offset that points to the top-left of a [`Scrollable`].
191    pub const START: Self = Self { x: 0.0, y: 0.0 };
192
193    /// A relative offset that points to the bottom-right of a [`Scrollable`].
194    pub const END: Self = Self { x: 1.0, y: 1.0 };
195}
196
197impl From<RelativeOffset> for RelativeOffset<Option<f32>> {
198    fn from(offset: RelativeOffset) -> Self {
199        Self {
200            x: Some(offset.x),
201            y: Some(offset.y),
202        }
203    }
204}