diff options
Diffstat (limited to '')
-rw-r--r-- | examples/scrollable/src/main.rs | 4 | ||||
-rw-r--r-- | native/src/widget/operation/scrollable.rs | 50 | ||||
-rw-r--r-- | native/src/widget/scrollable.rs | 55 | ||||
-rw-r--r-- | src/widget.rs | 4 |
4 files changed, 90 insertions, 23 deletions
diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 2e99b1ac..be8d0f52 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -36,7 +36,7 @@ enum Message { ScrollerWidthChanged(u16), ScrollToBeginning, ScrollToEnd, - Scrolled(scrollable::RelativeOffset), + Scrolled(scrollable::CurrentOffset), } impl Application for ScrollableDemo { @@ -105,7 +105,7 @@ impl Application for ScrollableDemo { ) } Message::Scrolled(offset) => { - self.current_scroll_offset = offset; + self.current_scroll_offset = offset.relative; Command::none() } diff --git a/native/src/widget/operation/scrollable.rs b/native/src/widget/operation/scrollable.rs index 3b20631f..62f4c91e 100644 --- a/native/src/widget/operation/scrollable.rs +++ b/native/src/widget/operation/scrollable.rs @@ -5,6 +5,9 @@ use crate::widget::{Id, Operation}; pub trait Scrollable { /// Snaps the scroll of the widget to the given `percentage` along the horizontal & vertical axis. fn snap_to(&mut self, offset: RelativeOffset); + + /// Scroll the widget to the given [`AbsoluteOffset`] along the horizontal & vertical axis. + fn scroll_to(&mut self, offset: AbsoluteOffset); } /// Produces an [`Operation`] that snaps the widget with the given [`Id`] to @@ -34,7 +37,52 @@ pub fn snap_to<T>(target: Id, offset: RelativeOffset) -> impl Operation<T> { SnapTo { target, offset } } -/// The amount of offset in each direction of a [`Scrollable`]. +/// Produces an [`Operation`] that scrolls the widget with the given [`Id`] to +/// the provided [`AbsoluteOffset`]. +pub fn scroll_to<T>(target: Id, offset: AbsoluteOffset) -> impl Operation<T> { + struct ScrollTo { + target: Id, + offset: AbsoluteOffset, + } + + impl<T> Operation<T> for ScrollTo { + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), + ) { + operate_on_children(self) + } + + fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) { + if Some(&self.target) == id { + state.scroll_to(self.offset); + } + } + } + + ScrollTo { target, offset } +} + +/// The current absolute & relative offset of a [`Scrollable`] +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct CurrentOffset { + /// The [`AbsoluteOffset`] of a [`Scrollable`] + pub absolute: AbsoluteOffset, + /// The [`RelativeOffset`] of a [`Scrollable`] + pub relative: RelativeOffset, +} + +/// The amount of absolute offset in each direction of a [`Scrollable`]. +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct AbsoluteOffset { + /// The amount of horizontal offset + pub x: f32, + /// The amount of vertical offset + pub y: f32, +} + +/// The amount of relative offset in each direction of a [`Scrollable`]. /// /// A value of `0.0` means start, while `1.0` means end. #[derive(Debug, Clone, Copy, PartialEq, Default)] diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 78dcdca2..eedddfd0 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -15,7 +15,9 @@ use crate::{ }; pub use iced_style::scrollable::StyleSheet; -pub use operation::scrollable::RelativeOffset; +pub use operation::scrollable::{ + AbsoluteOffset, CurrentOffset, RelativeOffset, +}; pub mod style { //! The styles of a [`Scrollable`]. @@ -38,7 +40,7 @@ where vertical: Properties, horizontal: Option<Properties>, content: Element<'a, Message, Renderer>, - on_scroll: Option<Box<dyn Fn(RelativeOffset) -> Message + 'a>>, + on_scroll: Option<Box<dyn Fn(CurrentOffset) -> Message + 'a>>, style: <Renderer::Theme as StyleSheet>::Style, } @@ -93,11 +95,10 @@ where /// Sets a function to call when the [`Scrollable`] is scrolled. /// - /// The function takes the new relative x & y offset of the [`Scrollable`] - /// (e.g. `0` means beginning, while `1` means end). + /// The function takes the [`CurrentOffset`] of the [`Scrollable`] pub fn on_scroll( mut self, - f: impl Fn(RelativeOffset) -> Message + 'a, + f: impl Fn(CurrentOffset) -> Message + 'a, ) -> Self { self.on_scroll = Some(Box::new(f)); self @@ -436,7 +437,7 @@ pub fn update<Message>( shell: &mut Shell<'_, Message>, vertical: &Properties, horizontal: Option<&Properties>, - on_scroll: &Option<Box<dyn Fn(RelativeOffset) -> Message + '_>>, + on_scroll: &Option<Box<dyn Fn(CurrentOffset) -> Message + '_>>, update_content: impl FnOnce( Event, Layout<'_>, @@ -896,7 +897,7 @@ pub fn draw<Renderer>( fn notify_on_scroll<Message>( state: &mut State, - on_scroll: &Option<Box<dyn Fn(RelativeOffset) -> Message + '_>>, + on_scroll: &Option<Box<dyn Fn(CurrentOffset) -> Message + '_>>, bounds: Rectangle, content_bounds: Rectangle, shell: &mut Shell<'_, Message>, @@ -908,31 +909,39 @@ fn notify_on_scroll<Message>( return; } - let x = state.offset_x.absolute(bounds.width, content_bounds.width) - / (content_bounds.width - bounds.width); + let absolute_x = + state.offset_x.absolute(bounds.width, content_bounds.width); + let relative_x = absolute_x / (content_bounds.width - bounds.width); - let y = state + let absolute_y = state .offset_y - .absolute(bounds.height, content_bounds.height) - / (content_bounds.height - bounds.height); + .absolute(bounds.height, content_bounds.height); + let relative_y = absolute_y / (content_bounds.height - bounds.height); - let new_offset = RelativeOffset { x, y }; + let absolute = AbsoluteOffset { + x: absolute_x, + y: absolute_y, + }; + let relative = RelativeOffset { + x: relative_x, + y: relative_y, + }; // Don't publish redundant offsets to shell - if let Some(prev_offset) = state.last_notified { + if let Some(prev_relative) = state.last_notified { let unchanged = |a: f32, b: f32| { (a - b).abs() <= f32::EPSILON || (a.is_nan() && b.is_nan()) }; - if unchanged(prev_offset.x, new_offset.x) - && unchanged(prev_offset.y, new_offset.y) + if unchanged(prev_relative.x, relative.x) + && unchanged(prev_relative.y, relative.y) { return; } } - shell.publish(on_scroll(new_offset)); - state.last_notified = Some(new_offset); + shell.publish(on_scroll(CurrentOffset { absolute, relative })); + state.last_notified = Some(relative); } } @@ -966,6 +975,10 @@ impl operation::Scrollable for State { fn snap_to(&mut self, offset: RelativeOffset) { State::snap_to(self, offset); } + + fn scroll_to(&mut self, offset: AbsoluteOffset) { + State::scroll_to(self, offset) + } } #[derive(Debug, Clone, Copy)] @@ -1052,6 +1065,12 @@ impl State { self.offset_y = Offset::Relative(offset.y.clamp(0.0, 1.0)); } + /// Scroll to the provided [`AbsoluteOffset`]. + pub fn scroll_to(&mut self, offset: AbsoluteOffset) { + self.offset_x = Offset::Absolute(offset.x.max(0.0)); + self.offset_y = Offset::Absolute(offset.y.max(0.0)); + } + /// Unsnaps the current scroll position, if snapped, given the bounds of the /// [`Scrollable`] and its contents. pub fn unsnap(&mut self, bounds: Rectangle, content_bounds: Rectangle) { diff --git a/src/widget.rs b/src/widget.rs index 44027811..87e82f47 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -109,8 +109,8 @@ pub mod radio { pub mod scrollable { //! Navigate an endless amount of content with a scrollbar. pub use iced_native::widget::scrollable::{ - snap_to, style::Scrollbar, style::Scroller, Id, Properties, - RelativeOffset, StyleSheet, + snap_to, style::Scrollbar, style::Scroller, AbsoluteOffset, + CurrentOffset, Id, Properties, RelativeOffset, StyleSheet, }; /// A widget that can vertically display an infinite amount of content |