summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector0193@gmail.com>2023-07-27 18:48:06 +0200
committerLibravatar GitHub <noreply@github.com>2023-07-27 18:48:06 +0200
commit18f1ab551be6fe9a603eb07dceddf231c01b5c7f (patch)
treea4fd30a4252c3a1469a618ac4754ee8a6d81a518
parente4b75ec9c76402dda7c6b6631918da240bb1c7ed (diff)
parentcbb5fcc8829e6fbe60f97cad8597c86ffd4f5b1a (diff)
downloadiced-18f1ab551be6fe9a603eb07dceddf231c01b5c7f.tar.gz
iced-18f1ab551be6fe9a603eb07dceddf231c01b5c7f.tar.bz2
iced-18f1ab551be6fe9a603eb07dceddf231c01b5c7f.zip
Merge pull request #1971 from iced-rs/visible-bounds-operation
`visible_bounds` operation for `Container`
-rw-r--r--CHANGELOG.md1
-rw-r--r--core/src/element.rs11
-rw-r--r--core/src/overlay/element.rs7
-rw-r--r--core/src/overlay/group.rs2
-rw-r--r--core/src/rectangle.rs15
-rw-r--r--core/src/widget/operation.rs32
-rw-r--r--core/src/widget/operation/focusable.rs6
-rw-r--r--core/src/widget/operation/scrollable.rs19
-rw-r--r--core/src/widget/operation/text_input.rs5
-rw-r--r--examples/toast/src/main.rs4
-rw-r--r--examples/visible_bounds/Cargo.toml10
-rw-r--r--examples/visible_bounds/src/main.rs187
-rw-r--r--widget/src/button.rs2
-rw-r--r--widget/src/column.rs2
-rw-r--r--widget/src/container.rs93
-rw-r--r--widget/src/lazy/component.rs10
-rw-r--r--widget/src/pane_grid.rs2
-rw-r--r--widget/src/row.rs2
-rw-r--r--widget/src/scrollable.rs14
19 files changed, 399 insertions, 25 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d6a989f6..c68ad2c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Helpers to change viewport alignment of a `Scrollable`. [#1953](https://github.com/iced-rs/iced/pull/1953)
- `scroll_to` widget operation. [#1796](https://github.com/iced-rs/iced/pull/1796)
- `scroll_to` helper. [#1804](https://github.com/iced-rs/iced/pull/1804)
+- `visible_bounds` widget operation for `Container`. [#1971](https://github.com/iced-rs/iced/pull/1971)
- Command to fetch window size. [#1927](https://github.com/iced-rs/iced/pull/1927)
- Conversion support from `Fn` trait to custom theme. [#1861](https://github.com/iced-rs/iced/pull/1861)
- Configurable border radii on relevant widgets. [#1869](https://github.com/iced-rs/iced/pull/1869)
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/examples/visible_bounds/Cargo.toml b/examples/visible_bounds/Cargo.toml
new file mode 100644
index 00000000..cfa56dd2
--- /dev/null
+++ b/examples/visible_bounds/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "visible_bounds"
+version = "0.1.0"
+authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced = { path = "../..", features = ["debug"] }
+once_cell = "1"
diff --git a/examples/visible_bounds/src/main.rs b/examples/visible_bounds/src/main.rs
new file mode 100644
index 00000000..8b684514
--- /dev/null
+++ b/examples/visible_bounds/src/main.rs
@@ -0,0 +1,187 @@
+use iced::executor;
+use iced::mouse;
+use iced::subscription::{self, Subscription};
+use iced::theme::{self, Theme};
+use iced::widget::{
+ column, container, horizontal_space, row, scrollable, text, vertical_space,
+};
+use iced::window;
+use iced::{
+ Alignment, Application, Color, Command, Element, Event, Font, Length,
+ Point, Rectangle, Settings,
+};
+
+pub fn main() -> iced::Result {
+ Example::run(Settings::default())
+}
+
+struct Example {
+ mouse_position: Option<Point>,
+ outer_bounds: Option<Rectangle>,
+ inner_bounds: Option<Rectangle>,
+}
+
+#[derive(Debug, Clone, Copy)]
+enum Message {
+ MouseMoved(Point),
+ WindowResized,
+ Scrolled(scrollable::Viewport),
+ OuterBoundsFetched(Option<Rectangle>),
+ InnerBoundsFetched(Option<Rectangle>),
+}
+
+impl Application for Example {
+ type Message = Message;
+ type Theme = Theme;
+ type Flags = ();
+ type Executor = executor::Default;
+
+ fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
+ (
+ Self {
+ mouse_position: None,
+ outer_bounds: None,
+ inner_bounds: None,
+ },
+ Command::none(),
+ )
+ }
+
+ fn title(&self) -> String {
+ String::from("Visible bounds - Iced")
+ }
+
+ fn update(&mut self, message: Message) -> Command<Message> {
+ match message {
+ Message::MouseMoved(position) => {
+ self.mouse_position = Some(position);
+
+ Command::none()
+ }
+ Message::Scrolled(_) | Message::WindowResized => {
+ Command::batch(vec![
+ container::visible_bounds(OUTER_CONTAINER.clone())
+ .map(Message::OuterBoundsFetched),
+ container::visible_bounds(INNER_CONTAINER.clone())
+ .map(Message::InnerBoundsFetched),
+ ])
+ }
+ Message::OuterBoundsFetched(outer_bounds) => {
+ self.outer_bounds = outer_bounds;
+
+ Command::none()
+ }
+ Message::InnerBoundsFetched(inner_bounds) => {
+ self.inner_bounds = inner_bounds;
+
+ Command::none()
+ }
+ }
+ }
+
+ fn view(&self) -> Element<Message> {
+ let data_row = |label, value, color| {
+ row![
+ text(label),
+ horizontal_space(Length::Fill),
+ text(value).font(Font::MONOSPACE).size(14).style(color),
+ ]
+ .height(40)
+ .align_items(Alignment::Center)
+ };
+
+ let view_bounds = |label, bounds: Option<Rectangle>| {
+ data_row(
+ label,
+ match bounds {
+ Some(bounds) => format!("{:?}", bounds),
+ None => "not visible".to_string(),
+ },
+ if bounds
+ .zip(self.mouse_position)
+ .map(|(bounds, mouse_position)| {
+ bounds.contains(mouse_position)
+ })
+ .unwrap_or_default()
+ {
+ Color {
+ g: 1.0,
+ ..Color::BLACK
+ }
+ .into()
+ } else {
+ theme::Text::Default
+ },
+ )
+ };
+
+ column![
+ data_row(
+ "Mouse position",
+ match self.mouse_position {
+ Some(Point { x, y }) => format!("({x}, {y})"),
+ None => "unknown".to_string(),
+ },
+ theme::Text::Default,
+ ),
+ view_bounds("Outer container", self.outer_bounds),
+ view_bounds("Inner container", self.inner_bounds),
+ scrollable(
+ column![
+ text("Scroll me!"),
+ vertical_space(400),
+ container(text("I am the outer container!"))
+ .id(OUTER_CONTAINER.clone())
+ .padding(40)
+ .style(theme::Container::Box),
+ vertical_space(400),
+ scrollable(
+ column![
+ text("Scroll me!"),
+ vertical_space(400),
+ container(text("I am the inner container!"))
+ .id(INNER_CONTAINER.clone())
+ .padding(40)
+ .style(theme::Container::Box),
+ vertical_space(400)
+ ]
+ .padding(20)
+ )
+ .on_scroll(Message::Scrolled)
+ .width(Length::Fill)
+ .height(300),
+ ]
+ .padding(20)
+ )
+ .on_scroll(Message::Scrolled)
+ .width(Length::Fill)
+ .height(300),
+ ]
+ .spacing(10)
+ .padding(20)
+ .into()
+ }
+
+ fn subscription(&self) -> Subscription<Message> {
+ subscription::events_with(|event, _| match event {
+ Event::Mouse(mouse::Event::CursorMoved { position }) => {
+ Some(Message::MouseMoved(position))
+ }
+ Event::Window(window::Event::Resized { .. }) => {
+ Some(Message::WindowResized)
+ }
+ _ => None,
+ })
+ }
+
+ fn theme(&self) -> Theme {
+ Theme::Dark
+ }
+}
+
+use once_cell::sync::Lazy;
+
+static OUTER_CONTAINER: Lazy<container::Id> =
+ Lazy::new(|| container::Id::new("outer"));
+static INNER_CONTAINER: Lazy<container::Id> =
+ Lazy::new(|| container::Id::new("inner"));
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],