Skip to content

Commit

Permalink
Implement a scroll_by operation for scrollables
Browse files Browse the repository at this point in the history
scroll_by allows scrolling an absolute offset that is applied to the current scrolling position.
  • Loading branch information
lufte committed May 10, 2024
1 parent fa458a3 commit 2a7cab9
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 4 deletions.
3 changes: 2 additions & 1 deletion core/src/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,9 +338,10 @@ where
state: &mut dyn widget::operation::Scrollable,
id: Option<&widget::Id>,
bounds: Rectangle,
content_bounds: Rectangle,

Check failure on line 341 in core/src/element.rs

View workflow job for this annotation

GitHub Actions / all

Diff in /home/runner/work/iced/iced/core/src/element.rs
translation: Vector,
) {
self.operation.scrollable(state, id, bounds, translation);
self.operation.scrollable(state, id, bounds, content_bounds, translation);
}

fn text_input(
Expand Down
3 changes: 2 additions & 1 deletion core/src/overlay/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,10 @@ where
state: &mut dyn widget::operation::Scrollable,
id: Option<&widget::Id>,
bounds: Rectangle,
content_bounds: Rectangle,

Check failure on line 182 in core/src/overlay/element.rs

View workflow job for this annotation

GitHub Actions / all

Diff in /home/runner/work/iced/iced/core/src/overlay/element.rs
translation: Vector,
) {
self.operation.scrollable(state, id, bounds, translation);
self.operation.scrollable(state, id, bounds, content_bounds, translation);
}

fn text_input(
Expand Down
7 changes: 5 additions & 2 deletions core/src/widget/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub trait Operation<T> {
_state: &mut dyn Scrollable,
_id: Option<&Id>,
_bounds: Rectangle,
_content_bounds: Rectangle,
_translation: Vector,
) {
}
Expand Down Expand Up @@ -127,9 +128,10 @@ where
state: &mut dyn Scrollable,
id: Option<&Id>,
bounds: Rectangle,
content_bounds: Rectangle,

Check failure on line 131 in core/src/widget/operation.rs

View workflow job for this annotation

GitHub Actions / all

Diff in /home/runner/work/iced/iced/core/src/widget/operation.rs
translation: Vector,
) {
self.operation.scrollable(state, id, bounds, translation);
self.operation.scrollable(state, id, bounds, content_bounds, translation);
}

fn focusable(
Expand Down Expand Up @@ -170,9 +172,10 @@ where
state: &mut dyn Scrollable,
id: Option<&Id>,
bounds: Rectangle,
content_bounds: Rectangle,

Check failure on line 175 in core/src/widget/operation.rs

View workflow job for this annotation

GitHub Actions / all

Diff in /home/runner/work/iced/iced/core/src/widget/operation.rs
translation: Vector,
) {
self.operation.scrollable(state, id, bounds, translation);
self.operation.scrollable(state, id, bounds, content_bounds, translation);
}

fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
Expand Down
40 changes: 40 additions & 0 deletions core/src/widget/operation/scrollable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ pub trait Scrollable {

/// Scroll the widget to the given [`AbsoluteOffset`] along the horizontal & vertical axis.
fn scroll_to(&mut self, offset: AbsoluteOffset);

Check failure on line 11 in core/src/widget/operation/scrollable.rs

View workflow job for this annotation

GitHub Actions / all

Diff in /home/runner/work/iced/iced/core/src/widget/operation/scrollable.rs

/// Scroll the widget by the given [`AbsoluteOffset`] along the horizontal & vertical axis.
fn scroll_by(&mut self, offset: AbsoluteOffset, bounds: Rectangle, content_bounds: Rectangle);
}

/// Produces an [`Operation`] that snaps the widget with the given [`Id`] to
Expand All @@ -34,6 +37,7 @@ pub fn snap_to<T>(target: Id, offset: RelativeOffset) -> impl Operation<T> {
state: &mut dyn Scrollable,
id: Option<&Id>,
_bounds: Rectangle,
_content_bounds: Rectangle,
_translation: Vector,
) {
if Some(&self.target) == id {
Expand Down Expand Up @@ -68,6 +72,7 @@ pub fn scroll_to<T>(target: Id, offset: AbsoluteOffset) -> impl Operation<T> {
state: &mut dyn Scrollable,
id: Option<&Id>,
_bounds: Rectangle,
_content_bounds: Rectangle,
_translation: Vector,
) {
if Some(&self.target) == id {
Expand All @@ -79,6 +84,41 @@ pub fn scroll_to<T>(target: Id, offset: AbsoluteOffset) -> impl Operation<T> {
ScrollTo { target, offset }
}

/// Produces an [`Operation`] that scrolls the widget with the given [`Id`] by
/// the provided [`AbsoluteOffset`].
pub fn scroll_by<T>(target: Id, offset: AbsoluteOffset) -> impl Operation<T> {
struct ScrollBy {
target: Id,
offset: AbsoluteOffset,
}

impl<T> Operation<T> for ScrollBy {
fn container(
&mut self,
_id: Option<&Id>,
_bounds: Rectangle,
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>,
bounds: Rectangle,
content_bounds: Rectangle,
_translation: Vector,
) {
if Some(&self.target) == id {
state.scroll_by(self.offset, bounds, content_bounds);
}
}
}

ScrollBy { target, offset }
}

/// The amount of absolute offset in each direction of a [`Scrollable`].
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct AbsoluteOffset {
Expand Down
1 change: 1 addition & 0 deletions widget/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ pub fn visible_bounds(id: Id) -> Command<Option<Rectangle>> {
_state: &mut dyn widget::operation::Scrollable,
_id: Option<&widget::Id>,
bounds: Rectangle,
_content_bounds: Rectangle,
translation: Vector,
) {
match self.scrollables.last() {
Expand Down
39 changes: 39 additions & 0 deletions widget/src/scrollable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ where
state,
self.id.as_ref().map(|id| &id.0),
bounds,
content_bounds,
translation,
);

Expand Down Expand Up @@ -970,6 +971,15 @@ pub fn scroll_to<Message: 'static>(
Command::widget(operation::scrollable::scroll_to(id.0, offset))
}

/// Produces a [`Command`] that scrolls the [`Scrollable`] with the given [`Id`]
/// by the provided [`AbsoluteOffset`] along the x & y axis.
pub fn scroll_by<Message: 'static>(
id: Id,
offset: AbsoluteOffset,
) -> Command<Message> {
Command::widget(operation::scrollable::scroll_by(id.0, offset))
}

/// Returns [`true`] if the viewport actually changed.
fn notify_on_scroll<Message>(
state: &mut State,
Expand Down Expand Up @@ -1053,6 +1063,10 @@ impl operation::Scrollable for State {
fn scroll_to(&mut self, offset: AbsoluteOffset) {
State::scroll_to(self, offset);

Check failure on line 1064 in widget/src/scrollable.rs

View workflow job for this annotation

GitHub Actions / all

Diff in /home/runner/work/iced/iced/widget/src/scrollable.rs
}

fn scroll_by(&mut self, offset: AbsoluteOffset, bounds: Rectangle, content_bounds: Rectangle) {
State::scroll_by(self, offset, bounds, content_bounds);
}
}

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -1237,6 +1251,31 @@ impl State {
self.offset_y = Offset::Absolute(offset.y.max(0.0));
}

/// Scroll by the provided [`AbsoluteOffset`].
pub fn scroll_by(
&mut self,
offset: AbsoluteOffset,
bounds: Rectangle,
content_bounds: Rectangle,

Check failure on line 1259 in widget/src/scrollable.rs

View workflow job for this annotation

GitHub Actions / all

Diff in /home/runner/work/iced/iced/widget/src/scrollable.rs
) {
self.offset_x = match self.offset_x {
Offset::Absolute(v) => Offset::Absolute((v + offset.x).max(0.0).min(
content_bounds.width - bounds.width
)),
rel => Offset::Absolute(
(rel.absolute(bounds.width, content_bounds.width) + offset.x).max(0.0)
),
};
self.offset_y = match self.offset_y {
Offset::Absolute(v) => Offset::Absolute((v + offset.y).max(0.0).min(

Check failure on line 1270 in widget/src/scrollable.rs

View workflow job for this annotation

GitHub Actions / all

Diff in /home/runner/work/iced/iced/widget/src/scrollable.rs
content_bounds.height - bounds.height
)),
rel => Offset::Absolute(
(rel.absolute(bounds.height, content_bounds.height) + 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) {
Expand Down

0 comments on commit 2a7cab9

Please sign in to comment.