diff options
author | 2023-07-27 01:02:28 +0200 | |
---|---|---|
committer | 2023-07-27 01:04:18 +0200 | |
commit | e2ba7ece83f141c149659747977147392df008f4 (patch) | |
tree | 5fa04524d0ca95c6289c4d5962af7a97098ec811 | |
parent | e29754f32d03efc4b075e9b63cc554d128bc2ccf (diff) | |
download | iced-e2ba7ece83f141c149659747977147392df008f4.tar.gz iced-e2ba7ece83f141c149659747977147392df008f4.tar.bz2 iced-e2ba7ece83f141c149659747977147392df008f4.zip |
Introduce `visible_bounds` operation for `Container`
-rw-r--r-- | core/src/element.rs | 11 | ||||
-rw-r--r-- | core/src/overlay/element.rs | 7 | ||||
-rw-r--r-- | core/src/overlay/group.rs | 2 | ||||
-rw-r--r-- | core/src/rectangle.rs | 15 | ||||
-rw-r--r-- | core/src/widget/operation.rs | 32 | ||||
-rw-r--r-- | core/src/widget/operation/focusable.rs | 6 | ||||
-rw-r--r-- | core/src/widget/operation/scrollable.rs | 19 | ||||
-rw-r--r-- | core/src/widget/operation/text_input.rs | 5 | ||||
-rw-r--r-- | examples/toast/src/main.rs | 4 | ||||
-rw-r--r-- | widget/src/button.rs | 2 | ||||
-rw-r--r-- | widget/src/column.rs | 2 | ||||
-rw-r--r-- | widget/src/container.rs | 93 | ||||
-rw-r--r-- | widget/src/lazy/component.rs | 10 | ||||
-rw-r--r-- | widget/src/pane_grid.rs | 2 | ||||
-rw-r--r-- | widget/src/row.rs | 2 | ||||
-rw-r--r-- | widget/src/scrollable.rs | 14 |
16 files changed, 201 insertions, 25 deletions
diff --git a/core/src/element.rs b/core/src/element.rs index b9b76247..d2c6358b 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -5,7 +5,9 @@ use crate::overlay; use crate::renderer; use crate::widget; use crate::widget::tree::{self, Tree}; -use crate::{Clipboard, Color, Layout, Length, Rectangle, Shell, Widget}; +use crate::{ + Clipboard, Color, Layout, Length, Rectangle, Shell, Vector, Widget, +}; use std::any::Any; use std::borrow::Borrow; @@ -325,11 +327,12 @@ where fn container( &mut self, id: Option<&widget::Id>, + bounds: Rectangle, operate_on_children: &mut dyn FnMut( &mut dyn widget::Operation<T>, ), ) { - self.operation.container(id, &mut |operation| { + self.operation.container(id, bounds, &mut |operation| { operate_on_children(&mut MapOperation { operation }); }); } @@ -346,8 +349,10 @@ where &mut self, state: &mut dyn widget::operation::Scrollable, id: Option<&widget::Id>, + bounds: Rectangle, + translation: Vector, ) { - self.operation.scrollable(state, id); + self.operation.scrollable(state, id, bounds, translation); } fn text_input( diff --git a/core/src/overlay/element.rs b/core/src/overlay/element.rs index c2134343..29b404b8 100644 --- a/core/src/overlay/element.rs +++ b/core/src/overlay/element.rs @@ -172,11 +172,12 @@ where fn container( &mut self, id: Option<&widget::Id>, + bounds: Rectangle, operate_on_children: &mut dyn FnMut( &mut dyn widget::Operation<T>, ), ) { - self.operation.container(id, &mut |operation| { + self.operation.container(id, bounds, &mut |operation| { operate_on_children(&mut MapOperation { operation }); }); } @@ -193,8 +194,10 @@ where &mut self, state: &mut dyn widget::operation::Scrollable, id: Option<&widget::Id>, + bounds: Rectangle, + translation: Vector, ) { - self.operation.scrollable(state, id); + self.operation.scrollable(state, id, bounds, translation); } fn text_input( diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index deffaad0..691686cd 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -138,7 +138,7 @@ where renderer: &Renderer, operation: &mut dyn widget::Operation<Message>, ) { - operation.container(None, &mut |operation| { + operation.container(None, layout.bounds(), &mut |operation| { self.children.iter_mut().zip(layout.children()).for_each( |(child, layout)| { child.operate(layout, renderer, operation); diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index 7ff324cb..db56aa18 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -197,3 +197,18 @@ where } } } + +impl<T> std::ops::Sub<Vector<T>> for Rectangle<T> +where + T: std::ops::Sub<Output = T>, +{ + type Output = Rectangle<T>; + + fn sub(self, translation: Vector<T>) -> Self { + Rectangle { + x: self.x - translation.x, + y: self.y - translation.y, + ..self + } + } +} diff --git a/core/src/widget/operation.rs b/core/src/widget/operation.rs index ad188c36..b91cf9ac 100644 --- a/core/src/widget/operation.rs +++ b/core/src/widget/operation.rs @@ -8,6 +8,7 @@ pub use scrollable::Scrollable; pub use text_input::TextInput; use crate::widget::Id; +use crate::{Rectangle, Vector}; use std::any::Any; use std::fmt; @@ -23,6 +24,7 @@ pub trait Operation<T> { fn container( &mut self, id: Option<&Id>, + bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), ); @@ -30,7 +32,14 @@ pub trait Operation<T> { fn focusable(&mut self, _state: &mut dyn Focusable, _id: Option<&Id>) {} /// Operates on a widget that can be scrolled. - fn scrollable(&mut self, _state: &mut dyn Scrollable, _id: Option<&Id>) {} + fn scrollable( + &mut self, + _state: &mut dyn Scrollable, + _id: Option<&Id>, + _bounds: Rectangle, + _translation: Vector, + ) { + } /// Operates on a widget that has text input. fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {} @@ -92,6 +101,7 @@ where fn container( &mut self, id: Option<&Id>, + bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>), ) { struct MapRef<'a, A> { @@ -102,11 +112,12 @@ where fn container( &mut self, id: Option<&Id>, + bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>), ) { let Self { operation, .. } = self; - operation.container(id, &mut |operation| { + operation.container(id, bounds, &mut |operation| { operate_on_children(&mut MapRef { operation }); }); } @@ -115,8 +126,10 @@ where &mut self, state: &mut dyn Scrollable, id: Option<&Id>, + bounds: Rectangle, + translation: Vector, ) { - self.operation.scrollable(state, id); + self.operation.scrollable(state, id, bounds, translation); } fn focusable( @@ -145,15 +158,21 @@ where MapRef { operation: operation.as_mut(), } - .container(id, operate_on_children); + .container(id, bounds, operate_on_children); } fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) { self.operation.focusable(state, id); } - fn scrollable(&mut self, state: &mut dyn Scrollable, id: Option<&Id>) { - self.operation.scrollable(state, id); + fn scrollable( + &mut self, + state: &mut dyn Scrollable, + id: Option<&Id>, + bounds: Rectangle, + translation: Vector, + ) { + self.operation.scrollable(state, id, bounds, translation); } fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) { @@ -197,6 +216,7 @@ pub fn scope<T: 'static>( fn container( &mut self, id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<Message>), ) { if id == Some(&self.target) { diff --git a/core/src/widget/operation/focusable.rs b/core/src/widget/operation/focusable.rs index 312e4894..ab1b677e 100644 --- a/core/src/widget/operation/focusable.rs +++ b/core/src/widget/operation/focusable.rs @@ -1,6 +1,7 @@ //! Operate on widgets that can be focused. use crate::widget::operation::{Operation, Outcome}; use crate::widget::Id; +use crate::Rectangle; /// The internal state of a widget that can be focused. pub trait Focusable { @@ -45,6 +46,7 @@ pub fn focus<T>(target: Id) -> impl Operation<T> { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), ) { operate_on_children(self) @@ -80,6 +82,7 @@ where fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), ) { operate_on_children(self) @@ -126,6 +129,7 @@ pub fn focus_previous<T>() -> impl Operation<T> { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), ) { operate_on_children(self) @@ -159,6 +163,7 @@ pub fn focus_next<T>() -> impl Operation<T> { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), ) { operate_on_children(self) @@ -185,6 +190,7 @@ pub fn find_focused() -> impl Operation<Id> { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<Id>), ) { operate_on_children(self) diff --git a/core/src/widget/operation/scrollable.rs b/core/src/widget/operation/scrollable.rs index f947344d..4f8b2a98 100644 --- a/core/src/widget/operation/scrollable.rs +++ b/core/src/widget/operation/scrollable.rs @@ -1,5 +1,6 @@ //! Operate on widgets that can be scrolled. use crate::widget::{Id, Operation}; +use crate::{Rectangle, Vector}; /// The internal state of a widget that can be scrolled. pub trait Scrollable { @@ -22,12 +23,19 @@ pub fn snap_to<T>(target: Id, offset: RelativeOffset) -> impl Operation<T> { 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>) { + fn scrollable( + &mut self, + state: &mut dyn Scrollable, + id: Option<&Id>, + _bounds: Rectangle, + _translation: Vector, + ) { if Some(&self.target) == id { state.snap_to(self.offset); } @@ -49,12 +57,19 @@ pub fn scroll_to<T>(target: Id, offset: AbsoluteOffset) -> impl Operation<T> { 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>) { + fn scrollable( + &mut self, + state: &mut dyn Scrollable, + id: Option<&Id>, + _bounds: Rectangle, + _translation: Vector, + ) { if Some(&self.target) == id { state.scroll_to(self.offset); } diff --git a/core/src/widget/operation/text_input.rs b/core/src/widget/operation/text_input.rs index 4c773e99..a9ea2e81 100644 --- a/core/src/widget/operation/text_input.rs +++ b/core/src/widget/operation/text_input.rs @@ -1,6 +1,7 @@ //! Operate on widgets that have text input. use crate::widget::operation::Operation; use crate::widget::Id; +use crate::Rectangle; /// The internal state of a widget that has text input. pub trait TextInput { @@ -34,6 +35,7 @@ pub fn move_cursor_to_front<T>(target: Id) -> impl Operation<T> { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), ) { operate_on_children(self) @@ -63,6 +65,7 @@ pub fn move_cursor_to_end<T>(target: Id) -> impl Operation<T> { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), ) { operate_on_children(self) @@ -93,6 +96,7 @@ pub fn move_cursor_to<T>(target: Id, position: usize) -> impl Operation<T> { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), ) { operate_on_children(self) @@ -121,6 +125,7 @@ pub fn select_all<T>(target: Id) -> impl Operation<T> { fn container( &mut self, _id: Option<&Id>, + _bounds: Rectangle, operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), ) { operate_on_children(self) diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index 5d29e895..42f6c348 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -381,7 +381,7 @@ mod toast { renderer: &Renderer, operation: &mut dyn Operation<Message>, ) { - operation.container(None, &mut |operation| { + operation.container(None, layout.bounds(), &mut |operation| { self.content.as_widget().operate( &mut state.children[0], layout, @@ -622,7 +622,7 @@ mod toast { renderer: &Renderer, operation: &mut dyn widget::Operation<Message>, ) { - operation.container(None, &mut |operation| { + operation.container(None, layout.bounds(), &mut |operation| { self.toasts .iter() .zip(self.state.iter_mut()) diff --git a/widget/src/button.rs b/widget/src/button.rs index 1312095f..5727c631 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -181,7 +181,7 @@ where renderer: &Renderer, operation: &mut dyn Operation<Message>, ) { - operation.container(None, &mut |operation| { + operation.container(None, layout.bounds(), &mut |operation| { self.content.as_widget().operate( &mut tree.children[0], layout.children().next().unwrap(), diff --git a/widget/src/column.rs b/widget/src/column.rs index 9271d5ef..c16477f3 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -148,7 +148,7 @@ where renderer: &Renderer, operation: &mut dyn Operation<Message>, ) { - operation.container(None, &mut |operation| { + operation.container(None, layout.bounds(), &mut |operation| { self.children .iter() .zip(&mut tree.children) diff --git a/widget/src/container.rs b/widget/src/container.rs index 64cf5cd5..1f1df861 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -8,8 +8,9 @@ use crate::core::renderer; use crate::core::widget::{self, Operation, Tree}; use crate::core::{ Background, Clipboard, Color, Element, Layout, Length, Padding, Pixels, - Point, Rectangle, Shell, Widget, + Point, Rectangle, Shell, Size, Vector, Widget, }; +use crate::runtime::Command; pub use iced_style::container::{Appearance, StyleSheet}; @@ -180,6 +181,7 @@ where ) { operation.container( self.id.as_ref().map(|id| &id.0), + layout.bounds(), &mut |operation| { self.content.as_widget().operate( &mut tree.children[0], @@ -368,3 +370,92 @@ impl From<Id> for widget::Id { id.0 } } + +/// Produces a [`Command`] that queries the visible screen bounds of the +/// [`Container`] with the given [`Id`]. +pub fn visible_bounds(id: Id) -> Command<Option<Rectangle>> { + struct VisibleBounds { + target: widget::Id, + depth: usize, + scrollables: Vec<(Vector, Rectangle, usize)>, + bounds: Option<Rectangle>, + } + + impl Operation<Option<Rectangle>> for VisibleBounds { + fn scrollable( + &mut self, + _state: &mut dyn widget::operation::Scrollable, + _id: Option<&widget::Id>, + bounds: Rectangle, + translation: Vector, + ) { + match self.scrollables.last() { + Some((last_translation, last_viewport, _depth)) => { + let viewport = last_viewport + .intersection(&(bounds - *last_translation)) + .unwrap_or(Rectangle::new(Point::ORIGIN, Size::ZERO)); + + self.scrollables.push(( + translation + *last_translation, + viewport, + self.depth, + )); + } + None => { + self.scrollables.push((translation, bounds, self.depth)); + } + } + } + + fn container( + &mut self, + id: Option<&widget::Id>, + bounds: Rectangle, + operate_on_children: &mut dyn FnMut( + &mut dyn Operation<Option<Rectangle>>, + ), + ) { + if self.bounds.is_some() { + return; + } + + if id == Some(&self.target) { + match self.scrollables.last() { + Some((translation, viewport, _)) => { + self.bounds = + viewport.intersection(&(bounds - *translation)); + } + None => { + self.bounds = Some(bounds); + } + } + + return; + } + + self.depth += 1; + + operate_on_children(self); + + self.depth -= 1; + + match self.scrollables.last() { + Some((_, _, depth)) if self.depth == *depth => { + let _ = self.scrollables.pop(); + } + _ => {} + } + } + + fn finish(&self) -> widget::operation::Outcome<Option<Rectangle>> { + widget::operation::Outcome::Some(self.bounds) + } + } + + Command::widget(VisibleBounds { + target: id.into(), + depth: 0, + scrollables: Vec::new(), + bounds: None, + }) +} diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index bc0e23df..19df2792 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -7,7 +7,8 @@ use crate::core::renderer; use crate::core::widget; use crate::core::widget::tree::{self, Tree}; use crate::core::{ - self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Widget, + self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Vector, + Widget, }; use crate::runtime::overlay::Nested; @@ -340,11 +341,12 @@ where fn container( &mut self, id: Option<&widget::Id>, + bounds: Rectangle, operate_on_children: &mut dyn FnMut( &mut dyn widget::Operation<T>, ), ) { - self.operation.container(id, &mut |operation| { + self.operation.container(id, bounds, &mut |operation| { operate_on_children(&mut MapOperation { operation }); }); } @@ -369,8 +371,10 @@ where &mut self, state: &mut dyn widget::operation::Scrollable, id: Option<&widget::Id>, + bounds: Rectangle, + translation: Vector, ) { - self.operation.scrollable(state, id); + self.operation.scrollable(state, id, bounds, translation); } fn custom( diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 4f6dfbe8..0f4ab9eb 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -297,7 +297,7 @@ where renderer: &Renderer, operation: &mut dyn widget::Operation<Message>, ) { - operation.container(None, &mut |operation| { + operation.container(None, layout.bounds(), &mut |operation| { self.contents .iter() .zip(&mut tree.children) diff --git a/widget/src/row.rs b/widget/src/row.rs index 7baaaae3..99b2a0bf 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -137,7 +137,7 @@ where renderer: &Renderer, operation: &mut dyn Operation<Message>, ) { - operation.container(None, &mut |operation| { + operation.container(None, layout.bounds(), &mut |operation| { self.children .iter() .zip(&mut tree.children) diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index f621fb26..103e3944 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -254,10 +254,22 @@ where ) { let state = tree.state.downcast_mut::<State>(); - operation.scrollable(state, self.id.as_ref().map(|id| &id.0)); + let bounds = layout.bounds(); + let content_layout = layout.children().next().unwrap(); + let content_bounds = content_layout.bounds(); + let translation = + state.translation(self.direction, bounds, content_bounds); + + operation.scrollable( + state, + self.id.as_ref().map(|id| &id.0), + bounds, + translation, + ); operation.container( self.id.as_ref().map(|id| &id.0), + bounds, &mut |operation| { self.content.as_widget().operate( &mut tree.children[0], |