diff options
Diffstat (limited to 'widget/src/container.rs')
| -rw-r--r-- | widget/src/container.rs | 133 | 
1 files changed, 116 insertions, 17 deletions
diff --git a/widget/src/container.rs b/widget/src/container.rs index da9a31d6..ee7a4965 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -5,11 +5,13 @@ use crate::core::layout;  use crate::core::mouse;  use crate::core::overlay;  use crate::core::renderer; -use crate::core::widget::{self, Operation, Tree}; +use crate::core::widget::tree::{self, Tree}; +use crate::core::widget::{self, Operation};  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}; @@ -134,12 +136,20 @@ where      Renderer: crate::core::Renderer,      Renderer::Theme: StyleSheet,  { +    fn tag(&self) -> tree::Tag { +        self.content.as_widget().tag() +    } + +    fn state(&self) -> tree::State { +        self.content.as_widget().state() +    } +      fn children(&self) -> Vec<Tree> { -        vec![Tree::new(&self.content)] +        self.content.as_widget().children()      }      fn diff(&self, tree: &mut Tree) { -        tree.diff_children(std::slice::from_ref(&self.content)) +        self.content.as_widget().diff(tree);      }      fn width(&self) -> Length { @@ -152,11 +162,11 @@ where      fn layout(          &self, +        tree: &mut Tree,          renderer: &Renderer,          limits: &layout::Limits,      ) -> layout::Node {          layout( -            renderer,              limits,              self.width,              self.height, @@ -165,9 +175,7 @@ where              self.padding,              self.horizontal_alignment,              self.vertical_alignment, -            |renderer, limits| { -                self.content.as_widget().layout(renderer, limits) -            }, +            |limits| self.content.as_widget().layout(tree, renderer, limits),          )      } @@ -180,9 +188,10 @@ where      ) {          operation.container(              self.id.as_ref().map(|id| &id.0), +            layout.bounds(),              &mut |operation| {                  self.content.as_widget().operate( -                    &mut tree.children[0], +                    tree,                      layout.children().next().unwrap(),                      renderer,                      operation, @@ -200,15 +209,17 @@ where          renderer: &Renderer,          clipboard: &mut dyn Clipboard,          shell: &mut Shell<'_, Message>, +        viewport: &Rectangle,      ) -> event::Status {          self.content.as_widget_mut().on_event( -            &mut tree.children[0], +            tree,              event,              layout.children().next().unwrap(),              cursor,              renderer,              clipboard,              shell, +            viewport,          )      } @@ -221,7 +232,7 @@ where          renderer: &Renderer,      ) -> mouse::Interaction {          self.content.as_widget().mouse_interaction( -            &tree.children[0], +            tree,              layout.children().next().unwrap(),              cursor,              viewport, @@ -244,7 +255,7 @@ where          draw_background(renderer, &style, layout.bounds());          self.content.as_widget().draw( -            &tree.children[0], +            tree,              renderer,              theme,              &renderer::Style { @@ -265,7 +276,7 @@ where          renderer: &Renderer,      ) -> Option<overlay::Element<'b, Message, Renderer>> {          self.content.as_widget_mut().overlay( -            &mut tree.children[0], +            tree,              layout.children().next().unwrap(),              renderer,          ) @@ -287,8 +298,7 @@ where  }  /// Computes the layout of a [`Container`]. -pub fn layout<Renderer>( -    renderer: &Renderer, +pub fn layout(      limits: &layout::Limits,      width: Length,      height: Length, @@ -297,7 +307,7 @@ pub fn layout<Renderer>(      padding: Padding,      horizontal_alignment: alignment::Horizontal,      vertical_alignment: alignment::Vertical, -    layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node, +    layout_content: impl FnOnce(&layout::Limits) -> layout::Node,  ) -> layout::Node {      let limits = limits          .loose() @@ -306,7 +316,7 @@ pub fn layout<Renderer>(          .width(width)          .height(height); -    let mut content = layout_content(renderer, &limits.pad(padding).loose()); +    let mut content = layout_content(&limits.pad(padding).loose());      let padding = padding.fit(content.size(), limits.max());      let size = limits.pad(padding).resolve(content.size()); @@ -366,3 +376,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, +    }) +}  | 
