Merge branch 'master' into feature/test-recorder

This commit is contained in:
Héctor Ramón Jiménez 2025-08-29 04:25:52 +02:00
commit 9e81c2b9e8
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
88 changed files with 1225 additions and 1158 deletions

View file

@ -235,7 +235,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -246,7 +246,7 @@ where
self.height,
self.padding,
|limits| {
self.content.as_widget().layout(
self.content.as_widget_mut().layout(
&mut tree.children[0],
renderer,
limits,
@ -256,7 +256,7 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -264,7 +264,7 @@ where
) {
operation.container(None, layout.bounds());
operation.traverse(&mut |operation| {
self.content.as_widget().operate(
self.content.as_widget_mut().operate(
&mut tree.children[0],
layout.children().next().unwrap(),
renderer,

View file

@ -207,7 +207,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
_renderer: &Renderer,
limits: &layout::Limits,

View file

@ -269,7 +269,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -447,7 +447,7 @@ where
}
fn operate(
&self,
&mut self,
_state: &mut Tree,
layout: Layout<'_>,
_renderer: &Renderer,

View file

@ -206,7 +206,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -222,13 +222,13 @@ where
self.padding,
self.spacing,
self.align,
&self.children,
&mut self.children,
&mut tree.children,
)
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -237,12 +237,12 @@ where
operation.container(None, layout.bounds());
operation.traverse(&mut |operation| {
self.children
.iter()
.iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), layout)| {
child
.as_widget()
.as_widget_mut()
.operate(state, layout, renderer, operation);
});
});

View file

@ -474,7 +474,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut widget::Tree,
renderer: &Renderer,
limits: &layout::Limits,

View file

@ -258,7 +258,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -272,12 +272,14 @@ where
self.padding,
self.horizontal_alignment,
self.vertical_alignment,
|limits| self.content.as_widget().layout(tree, renderer, limits),
|limits| {
self.content.as_widget_mut().layout(tree, renderer, limits)
},
)
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -285,7 +287,7 @@ where
) {
operation.container(self.id.as_ref(), layout.bounds());
operation.traverse(&mut |operation| {
self.content.as_widget().operate(
self.content.as_widget_mut().operate(
tree,
layout.children().next().unwrap(),
renderer,

View file

@ -116,12 +116,12 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut widget::Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.content.as_widget().layout(tree, renderer, limits)
self.content.as_widget_mut().layout(tree, renderer, limits)
}
fn update(
@ -197,14 +197,14 @@ where
}
fn operate(
&self,
&mut self,
state: &mut widget::Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
self.content
.as_widget()
.as_widget_mut()
.operate(state, layout, renderer, operation);
}

View file

@ -176,7 +176,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -220,10 +220,10 @@ where
let mut row_height = 0.0f32;
for (i, (child, tree)) in
self.children.iter().zip(&mut tree.children).enumerate()
self.children.iter_mut().zip(&mut tree.children).enumerate()
{
let node = child
.as_widget()
.as_widget_mut()
.layout(tree, renderer, &cell_limits)
.move_to((x, y));
@ -251,7 +251,7 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -260,12 +260,12 @@ where
operation.container(None, layout.bounds());
operation.traverse(&mut |operation| {
self.children
.iter()
.iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), layout)| {
child
.as_widget()
.as_widget_mut()
.operate(state, layout, renderer, operation);
});
});

View file

@ -6,7 +6,7 @@ use crate::container::{self, Container};
use crate::core;
use crate::core::widget::operation::{self, Operation};
use crate::core::window;
use crate::core::{Element, Length, Pixels, Widget};
use crate::core::{Element, Length, Pixels, Size, Widget};
use crate::float::{self, Float};
use crate::keyed;
use crate::overlay;
@ -23,7 +23,9 @@ use crate::text_input::{self, TextInput};
use crate::toggler::{self, Toggler};
use crate::tooltip::{self, Tooltip};
use crate::vertical_slider::{self, VerticalSlider};
use crate::{Column, Grid, MouseArea, Pin, Row, Sensor, Space, Stack, Themer};
use crate::{
Column, Grid, MouseArea, Pin, Responsive, Row, Sensor, Space, Stack, Themer,
};
use std::borrow::Borrow;
use std::ops::RangeInclusive;
@ -607,12 +609,12 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.content.as_widget().layout(tree, renderer, limits)
self.content.as_widget_mut().layout(tree, renderer, limits)
}
fn draw(
@ -631,14 +633,14 @@ where
}
fn operate(
&self,
&mut self,
state: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn operation::Operation,
) {
self.content
.as_widget()
.as_widget_mut()
.operate(state, layout, renderer, operation);
}
@ -770,18 +772,18 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let base = self.base.as_widget().layout(
let base = self.base.as_widget_mut().layout(
&mut tree.children[0],
renderer,
limits,
);
let top = self.top.as_widget().layout(
let top = self.top.as_widget_mut().layout(
&mut tree.children[1],
renderer,
&layout::Limits::new(Size::ZERO, base.size()),
@ -832,18 +834,20 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn operation::Operation,
) {
let children = [&self.base, &self.top]
let children = [&mut self.base, &mut self.top]
.into_iter()
.zip(layout.children().zip(&mut tree.children));
for (child, (layout, tree)) in children {
child.as_widget().operate(tree, layout, renderer, operation);
child
.as_widget_mut()
.operate(tree, layout, renderer, operation);
}
}
@ -2117,3 +2121,18 @@ where
{
Float::new(content)
}
/// Creates a new [`Responsive`] widget with a closure that produces its
/// contents.
///
/// The `view` closure will receive the maximum available space for
/// the [`Responsive`] during layout. You can use this [`Size`] to
/// conditionally build the contents.
pub fn responsive<'a, Message, Theme, Renderer>(
f: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a,
) -> Responsive<'a, Message, Theme, Renderer>
where
Renderer: core::Renderer,
{
Responsive::new(f)
}

View file

@ -59,6 +59,7 @@ pub struct Image<Handle = image::Handle> {
handle: Handle,
width: Length,
height: Length,
crop: Option<Rectangle<u32>>,
content_fit: ContentFit,
filter_method: FilterMethod,
rotation: Rotation,
@ -74,6 +75,7 @@ impl<Handle> Image<Handle> {
handle: handle.into(),
width: Length::Shrink,
height: Length::Shrink,
crop: None,
content_fit: ContentFit::default(),
filter_method: FilterMethod::default(),
rotation: Rotation::default(),
@ -145,6 +147,24 @@ impl<Handle> Image<Handle> {
self.scale = scale.into();
self
}
/// Crops the [`Image`] to the given region described by the [`Rectangle`] in absolute
/// coordinates.
///
/// Cropping is done before applying any transformation or [`ContentFit`]. In practice,
/// this means that cropping an [`Image`] with this method should produce the same result
/// as cropping it externally (e.g. with an image editor) and creating a new [`Handle`]
/// for the cropped version.
///
/// However, this method is much more efficient; since it just leverages scissoring during
/// rendering and no image cropping actually takes place. Instead, it reuses the existing
/// image allocations and should be as efficient as not cropping at all!
///
/// The `region` coordinates will be clamped to the image dimensions, if necessary.
pub fn crop(mut self, region: Rectangle<u32>) -> Self {
self.crop = Some(region);
self
}
}
/// Computes the layout of an [`Image`].
@ -154,6 +174,7 @@ pub fn layout<Renderer, Handle>(
handle: &Handle,
width: Length,
height: Length,
region: Option<Rectangle<u32>>,
content_fit: ContentFit,
rotation: Rotation,
expand: bool,
@ -162,16 +183,14 @@ where
Renderer: image::Renderer<Handle = Handle>,
{
// The raw w/h of the underlying image
let image_size = renderer.measure_image(handle);
let image_size =
Size::new(image_size.width as f32, image_size.height as f32);
let image_size = crop(renderer.measure_image(handle), region);
// The rotated size of the image
let rotated_size = rotation.apply(image_size);
// The size to be available to the widget prior to `Shrink`ing
let bounds = if expand {
limits.max()
limits.width(width).height(height).max()
} else {
limits.resolve(width, height, rotated_size)
};
@ -198,6 +217,7 @@ fn drawing_bounds<Renderer, Handle>(
renderer: &Renderer,
bounds: Rectangle,
handle: &Handle,
region: Option<Rectangle<u32>>,
content_fit: ContentFit,
rotation: Rotation,
scale: f32,
@ -205,8 +225,8 @@ fn drawing_bounds<Renderer, Handle>(
where
Renderer: image::Renderer<Handle = Handle>,
{
let Size { width, height } = renderer.measure_image(handle);
let image_size = Size::new(width as f32, height as f32);
let original_size = renderer.measure_image(handle);
let image_size = crop(original_size, region);
let rotated_size = rotation.apply(image_size);
let adjusted_fit = content_fit.fit(rotated_size, bounds.size());
@ -217,6 +237,37 @@ where
let final_size = image_size * fit_scale * scale;
let (crop_offset, final_size) = if let Some(region) = region {
let x = region.x.min(original_size.width) as f32;
let y = region.y.min(original_size.height) as f32;
let width = image_size.width;
let height = image_size.height;
let ratio = Vector::new(
original_size.width as f32 / width,
original_size.height as f32 / height,
);
let final_size = final_size * ratio;
let scale = Vector::new(
final_size.width / original_size.width as f32,
final_size.height / original_size.height as f32,
);
let offset = match content_fit {
ContentFit::None => Vector::new(x * scale.x, y * scale.y),
_ => Vector::new(
((original_size.width as f32 - width) / 2.0 - x) * scale.x,
((original_size.height as f32 - height) / 2.0 - y) * scale.y,
),
};
(offset, final_size)
} else {
(Vector::ZERO, final_size)
};
let position = match content_fit {
ContentFit::None => Point::new(
bounds.x + (rotated_size.width - adjusted_fit.width) / 2.0,
@ -228,19 +279,31 @@ where
),
};
Rectangle::new(position, final_size)
Rectangle::new(position + crop_offset, final_size)
}
fn must_clip(bounds: Rectangle, drawing_bounds: Rectangle) -> bool {
drawing_bounds.width > bounds.width || drawing_bounds.height > bounds.height
}
fn crop(size: Size<u32>, region: Option<Rectangle<u32>>) -> Size<f32> {
if let Some(region) = region {
Size::new(
region.width.min(size.width) as f32,
region.height.min(size.height) as f32,
)
} else {
Size::new(size.width as f32, size.height as f32)
}
}
/// Draws an [`Image`]
pub fn draw<Renderer, Handle>(
renderer: &mut Renderer,
layout: Layout<'_>,
viewport: &Rectangle,
handle: &Handle,
crop: Option<Rectangle<u32>>,
content_fit: ContentFit,
filter_method: FilterMethod,
rotation: Rotation,
@ -251,8 +314,15 @@ pub fn draw<Renderer, Handle>(
Handle: Clone,
{
let bounds = layout.bounds();
let drawing_bounds =
drawing_bounds(renderer, bounds, handle, content_fit, rotation, scale);
let drawing_bounds = drawing_bounds(
renderer,
bounds,
handle,
crop,
content_fit,
rotation,
scale,
);
if must_clip(bounds, drawing_bounds) {
if let Some(bounds) = bounds.intersection(viewport) {
@ -316,7 +386,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -327,6 +397,7 @@ where
&self.handle,
self.width,
self.height,
self.crop,
self.content_fit,
self.rotation,
self.expand,
@ -348,6 +419,7 @@ where
layout,
viewport,
&self.handle,
self.crop,
self.content_fit,
self.filter_method,
self.rotation,

View file

@ -117,7 +117,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,

View file

@ -253,7 +253,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -272,13 +272,13 @@ where
self.padding,
self.spacing,
self.align_items,
&self.children,
&mut self.children,
&mut tree.children,
)
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -287,12 +287,12 @@ where
operation.container(None, layout.bounds());
operation.traverse(&mut |operation| {
self.children
.iter()
.iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), layout)| {
child
.as_widget()
.as_widget_mut()
.operate(state, layout, renderer, operation);
});
});

View file

@ -2,11 +2,9 @@
pub(crate) mod helpers;
pub mod component;
pub mod responsive;
#[allow(deprecated)]
pub use component::Component;
pub use responsive::Responsive;
mod cache;
@ -165,27 +163,29 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.with_element(|element| {
element
.as_widget()
.layout(&mut tree.children[0], renderer, limits)
self.with_element_mut(|element| {
element.as_widget_mut().layout(
&mut tree.children[0],
renderer,
limits,
)
})
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
self.with_element(|element| {
element.as_widget().operate(
self.with_element_mut(|element| {
element.as_widget_mut().operate(
&mut tree.children[0],
layout,
renderer,

View file

@ -298,15 +298,15 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let t = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
self.with_element(|element| {
element.as_widget().layout(
self.with_element_mut(|element| {
element.as_widget_mut().layout(
&mut t.borrow_mut().as_mut().unwrap().children[0],
renderer,
limits,
@ -377,7 +377,7 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -386,8 +386,8 @@ where
self.rebuild_element_with_operation(layout, operation);
let tree = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();
self.with_element(|element| {
element.as_widget().operate(
self.with_element_mut(|element| {
element.as_widget_mut().operate(
&mut tree.borrow_mut().as_mut().unwrap().children[0],
layout,
renderer,

View file

@ -1,10 +1,10 @@
use crate::core::{self, Element, Size};
use crate::core::{self, Element};
use crate::lazy::component;
use std::hash::Hash;
#[allow(deprecated)]
pub use crate::lazy::{Component, Lazy, Responsive};
pub use crate::lazy::{Component, Lazy};
/// Creates a new [`Lazy`] widget with the given data `Dependency` and a
/// closure that can turn this data into a widget tree.
@ -41,19 +41,3 @@ where
{
component::view(component)
}
/// Creates a new [`Responsive`] widget with a closure that produces its
/// contents.
///
/// The `view` closure will be provided with the current [`Size`] of
/// the [`Responsive`] widget and, therefore, can be used to build the
/// contents of the widget in a responsive way.
#[cfg(feature = "lazy")]
pub fn responsive<'a, Message, Theme, Renderer>(
f: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a,
) -> Responsive<'a, Message, Theme, Renderer>
where
Renderer: core::Renderer,
{
Responsive::new(f)
}

View file

@ -1,462 +0,0 @@
use crate::core::layout::{self, Layout};
use crate::core::mouse;
use crate::core::overlay;
use crate::core::renderer;
use crate::core::widget;
use crate::core::widget::tree::{self, Tree};
use crate::core::{
self, Clipboard, Element, Event, Length, Point, Rectangle, Shell, Size,
Vector, Widget,
};
use crate::horizontal_space;
use ouroboros::self_referencing;
use std::cell::{RefCell, RefMut};
use std::marker::PhantomData;
use std::ops::Deref;
/// A widget that is aware of its dimensions.
///
/// A [`Responsive`] widget will always try to fill all the available space of
/// its parent.
#[cfg(feature = "lazy")]
#[allow(missing_debug_implementations)]
pub struct Responsive<
'a,
Message,
Theme = crate::Theme,
Renderer = crate::Renderer,
> {
view: Box<dyn Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a>,
content: RefCell<Content<'a, Message, Theme, Renderer>>,
}
impl<'a, Message, Theme, Renderer> Responsive<'a, Message, Theme, Renderer>
where
Renderer: core::Renderer,
{
/// Creates a new [`Responsive`] widget with a closure that produces its
/// contents.
///
/// The `view` closure will be provided with the current [`Size`] of
/// the [`Responsive`] widget and, therefore, can be used to build the
/// contents of the widget in a responsive way.
pub fn new(
view: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a,
) -> Self {
Self {
view: Box::new(view),
content: RefCell::new(Content {
size: Size::ZERO,
layout: None,
is_layout_invalid: true,
element: Element::new(horizontal_space().width(0)),
}),
}
}
}
struct Content<'a, Message, Theme, Renderer> {
size: Size,
layout: Option<layout::Node>,
is_layout_invalid: bool,
element: Element<'a, Message, Theme, Renderer>,
}
impl<'a, Message, Theme, Renderer> Content<'a, Message, Theme, Renderer>
where
Renderer: core::Renderer,
{
fn layout(&mut self, tree: &mut Tree, renderer: &Renderer) {
if self.layout.is_none() || self.is_layout_invalid {
self.layout = Some(self.element.as_widget().layout(
tree,
renderer,
&layout::Limits::new(Size::ZERO, self.size),
));
self.is_layout_invalid = false;
}
}
fn update(
&mut self,
tree: &mut Tree,
new_size: Size,
view: &dyn Fn(Size) -> Element<'a, Message, Theme, Renderer>,
) {
if self.size != new_size {
self.element = view(new_size);
self.size = new_size;
self.layout = None;
tree.diff(&self.element);
} else {
let is_tree_empty =
tree.tag == tree::Tag::stateless() && tree.children.is_empty();
if is_tree_empty {
self.layout = None;
tree.diff(&self.element);
}
}
}
fn resolve<R, T>(
&mut self,
tree: &mut Tree,
renderer: R,
layout: Layout<'_>,
view: &dyn Fn(Size) -> Element<'a, Message, Theme, Renderer>,
f: impl FnOnce(
&mut Tree,
R,
Layout<'_>,
&mut Element<'a, Message, Theme, Renderer>,
) -> T,
) -> T
where
R: Deref<Target = Renderer>,
{
self.update(tree, layout.bounds().size(), view);
self.layout(tree, renderer.deref());
let content_layout = Layout::with_offset(
layout.position() - Point::ORIGIN,
self.layout.as_ref().unwrap(),
);
f(tree, renderer, content_layout, &mut self.element)
}
}
struct State {
tree: RefCell<Tree>,
}
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Responsive<'_, Message, Theme, Renderer>
where
Renderer: core::Renderer,
{
fn tag(&self) -> tree::Tag {
tree::Tag::of::<State>()
}
fn state(&self) -> tree::State {
tree::State::new(State {
tree: RefCell::new(Tree::empty()),
})
}
fn size(&self) -> Size<Length> {
Size {
width: Length::Fill,
height: Length::Fill,
}
}
fn layout(
&self,
_tree: &mut Tree,
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
layout::Node::new(limits.max())
}
fn operate(
&self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
let state = tree.state.downcast_mut::<State>();
let mut content = self.content.borrow_mut();
content.resolve(
&mut state.tree.borrow_mut(),
renderer,
layout,
&self.view,
|tree, renderer, layout, element| {
element
.as_widget()
.operate(tree, layout, renderer, operation);
},
);
}
fn update(
&mut self,
tree: &mut Tree,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) {
let state = tree.state.downcast_mut::<State>();
let mut content = self.content.borrow_mut();
let mut local_messages = vec![];
let mut local_shell = Shell::new(&mut local_messages);
content.resolve(
&mut state.tree.borrow_mut(),
renderer,
layout,
&self.view,
|tree, renderer, layout, element| {
element.as_widget_mut().update(
tree,
event,
layout,
cursor,
renderer,
clipboard,
&mut local_shell,
viewport,
);
},
);
if local_shell.is_layout_invalid() {
content.layout = None;
}
shell.merge(local_shell, std::convert::identity);
}
fn draw(
&self,
tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
) {
let state = tree.state.downcast_ref::<State>();
let mut content = self.content.borrow_mut();
content.resolve(
&mut state.tree.borrow_mut(),
renderer,
layout,
&self.view,
|tree, renderer, layout, element| {
element.as_widget().draw(
tree, renderer, theme, style, layout, cursor, viewport,
);
},
);
}
fn mouse_interaction(
&self,
tree: &Tree,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
let state = tree.state.downcast_ref::<State>();
let mut content = self.content.borrow_mut();
content.resolve(
&mut state.tree.borrow_mut(),
renderer,
layout,
&self.view,
|tree, renderer, layout, element| {
element
.as_widget()
.mouse_interaction(tree, layout, cursor, viewport, renderer)
},
)
}
fn overlay<'b>(
&'b mut self,
tree: &'b mut Tree,
layout: Layout<'b>,
renderer: &Renderer,
viewport: &Rectangle,
translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
use std::ops::DerefMut;
let state = tree.state.downcast_ref::<State>();
let overlay = OverlayBuilder {
content: self.content.borrow_mut(),
tree: state.tree.borrow_mut(),
types: PhantomData,
overlay_builder: |content: &mut RefMut<
'_,
Content<'_, _, _, _>,
>,
tree| {
content.update(tree, layout.bounds().size(), &self.view);
content.layout(tree, renderer);
let Content {
element,
layout: content_layout_node,
is_layout_invalid,
..
} = content.deref_mut();
let content_layout = Layout::with_offset(
layout.bounds().position() - Point::ORIGIN,
content_layout_node.as_ref().unwrap(),
);
(
element
.as_widget_mut()
.overlay(
tree,
content_layout,
renderer,
viewport,
translation,
)
.map(|overlay| {
RefCell::new(overlay::Nested::new(overlay))
}),
is_layout_invalid,
)
},
}
.build();
if overlay.with_overlay(|(overlay, _layout)| overlay.is_some()) {
Some(overlay::Element::new(Box::new(overlay)))
} else {
None
}
}
}
impl<'a, Message, Theme, Renderer>
From<Responsive<'a, Message, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer>
where
Message: 'a,
Theme: 'a,
Renderer: core::Renderer + 'a,
{
fn from(responsive: Responsive<'a, Message, Theme, Renderer>) -> Self {
Self::new(responsive)
}
}
#[self_referencing]
struct Overlay<'a, 'b, Message, Theme, Renderer> {
content: RefMut<'a, Content<'b, Message, Theme, Renderer>>,
tree: RefMut<'a, Tree>,
types: PhantomData<Message>,
#[borrows(mut content, mut tree)]
#[not_covariant]
overlay: (
Option<RefCell<overlay::Nested<'this, Message, Theme, Renderer>>>,
&'this mut bool,
),
}
impl<Message, Theme, Renderer> Overlay<'_, '_, Message, Theme, Renderer> {
fn with_overlay_maybe<T>(
&self,
f: impl FnOnce(&mut overlay::Nested<'_, Message, Theme, Renderer>) -> T,
) -> Option<T> {
self.with_overlay(|(overlay, _layout)| {
overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut()))
})
}
fn with_overlay_mut_maybe<T>(
&mut self,
f: impl FnOnce(&mut overlay::Nested<'_, Message, Theme, Renderer>) -> T,
) -> Option<T> {
self.with_overlay_mut(|(overlay, _layout)| {
overlay.as_mut().map(|nested| (f)(nested.get_mut()))
})
}
}
impl<Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
for Overlay<'_, '_, Message, Theme, Renderer>
where
Renderer: core::Renderer,
{
fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds))
.unwrap_or_default()
}
fn draw(
&self,
renderer: &mut Renderer,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
) {
let _ = self.with_overlay_maybe(|overlay| {
overlay.draw(renderer, theme, style, layout, cursor);
});
}
fn mouse_interaction(
&self,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
) -> mouse::Interaction {
self.with_overlay_maybe(|overlay| {
overlay.mouse_interaction(layout, cursor, renderer)
})
.unwrap_or_default()
}
fn update(
&mut self,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) {
let mut is_layout_invalid = false;
let _ = self.with_overlay_mut_maybe(|overlay| {
overlay.update(event, layout, cursor, renderer, clipboard, shell);
is_layout_invalid = shell.is_layout_invalid();
});
if is_layout_invalid {
self.with_overlay_mut(|(_overlay, layout)| {
**layout = true;
});
}
}
fn operate(
&mut self,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
let _ = self.with_overlay_mut_maybe(|overlay| {
overlay.operate(layout, renderer, operation);
});
}
}

View file

@ -13,6 +13,7 @@ mod action;
mod column;
mod mouse_area;
mod pin;
mod responsive;
mod space;
mod stack;
mod themer;
@ -79,6 +80,8 @@ pub use progress_bar::ProgressBar;
#[doc(no_inline)]
pub use radio::Radio;
#[doc(no_inline)]
pub use responsive::Responsive;
#[doc(no_inline)]
pub use row::Row;
#[doc(no_inline)]
pub use rule::Rule;

View file

@ -190,24 +190,26 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.content
.as_widget()
.layout(&mut tree.children[0], renderer, limits)
self.content.as_widget_mut().layout(
&mut tree.children[0],
renderer,
limits,
)
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn Operation,
) {
self.content.as_widget().operate(
self.content.as_widget_mut().operate(
&mut tree.children[0],
layout,
renderer,

View file

@ -369,7 +369,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,

View file

@ -418,7 +418,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -432,20 +432,19 @@ where
let children = self
.panes
.iter()
.copied()
.zip(&self.contents)
.iter_mut()
.zip(&mut self.contents)
.zip(tree.children.iter_mut())
.filter_map(|((pane, content), tree)| {
if self
.internal
.maximized()
.is_some_and(|maximized| maximized != pane)
.is_some_and(|maximized| maximized != *pane)
{
return Some(layout::Node::new(Size::ZERO));
}
let region = regions.get(&pane)?;
let region = regions.get(pane)?;
let size = Size::new(region.width, region.height);
let node = content.layout(
@ -462,7 +461,7 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -471,15 +470,14 @@ where
operation.container(None, layout.bounds());
operation.traverse(&mut |operation| {
self.panes
.iter()
.copied()
.zip(&self.contents)
.iter_mut()
.zip(&mut self.contents)
.zip(&mut tree.children)
.zip(layout.children())
.filter(|(((pane, _), _), _)| {
self.internal
.maximized()
.is_none_or(|maximized| *pane == maximized)
.is_none_or(|maximized| **pane == maximized)
})
.for_each(|(((_, content), state), layout)| {
content.operate(state, layout, renderer, operation);

View file

@ -165,12 +165,12 @@ where
}
pub(crate) fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
if let Some(title_bar) = &self.title_bar {
if let Some(title_bar) = &mut self.title_bar {
let max_size = limits.max();
let title_bar_layout = title_bar.layout(
@ -181,7 +181,7 @@ where
let title_bar_size = title_bar_layout.size();
let body_layout = self.body.as_widget().layout(
let body_layout = self.body.as_widget_mut().layout(
&mut tree.children[0],
renderer,
&layout::Limits::new(
@ -201,7 +201,7 @@ where
],
)
} else {
self.body.as_widget().layout(
self.body.as_widget_mut().layout(
&mut tree.children[0],
renderer,
limits,
@ -210,13 +210,13 @@ where
}
pub(crate) fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
let body_layout = if let Some(title_bar) = &self.title_bar {
let body_layout = if let Some(title_bar) = &mut self.title_bar {
let mut children = layout.children();
title_bar.operate(
@ -231,7 +231,7 @@ where
layout
};
self.body.as_widget().operate(
self.body.as_widget_mut().operate(
&mut tree.children[0],
body_layout,
renderer,

View file

@ -274,7 +274,7 @@ where
}
pub(crate) fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -282,7 +282,7 @@ where
let limits = limits.shrink(self.padding);
let max_size = limits.max();
let title_layout = self.content.as_widget().layout(
let title_layout = self.content.as_widget_mut().layout(
&mut tree.children[0],
renderer,
&layout::Limits::new(Size::ZERO, max_size),
@ -290,8 +290,8 @@ where
let title_size = title_layout.size();
let node = if let Some(controls) = &self.controls {
let controls_layout = controls.full.as_widget().layout(
let node = if let Some(controls) = &mut self.controls {
let controls_layout = controls.full.as_widget_mut().layout(
&mut tree.children[1],
renderer,
&layout::Limits::new(Size::ZERO, max_size),
@ -300,8 +300,8 @@ where
if title_layout.bounds().width + controls_layout.bounds().width
> max_size.width
{
if let Some(compact) = controls.compact.as_ref() {
let compact_layout = compact.as_widget().layout(
if let Some(compact) = controls.compact.as_mut() {
let compact_layout = compact.as_widget_mut().layout(
&mut tree.children[2],
renderer,
&layout::Limits::new(Size::ZERO, max_size),
@ -369,7 +369,7 @@ where
}
pub(crate) fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -382,16 +382,16 @@ where
let title_layout = children.next().unwrap();
let mut show_title = true;
if let Some(controls) = &self.controls {
if let Some(controls) = &mut self.controls {
let controls_layout = children.next().unwrap();
if title_layout.bounds().width + controls_layout.bounds().width
> padded.bounds().width
{
if let Some(compact) = controls.compact.as_ref() {
if let Some(compact) = controls.compact.as_mut() {
let compact_layout = children.next().unwrap();
compact.as_widget().operate(
compact.as_widget_mut().operate(
&mut tree.children[2],
compact_layout,
renderer,
@ -400,7 +400,7 @@ where
} else {
show_title = false;
controls.full.as_widget().operate(
controls.full.as_widget_mut().operate(
&mut tree.children[1],
controls_layout,
renderer,
@ -408,7 +408,7 @@ where
);
}
} else {
controls.full.as_widget().operate(
controls.full.as_widget_mut().operate(
&mut tree.children[1],
controls_layout,
renderer,
@ -418,7 +418,7 @@ where
};
if show_title {
self.content.as_widget().operate(
self.content.as_widget_mut().operate(
&mut tree.children[0],
title_layout,
renderer,

View file

@ -348,7 +348,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,

View file

@ -139,7 +139,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut widget::Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -151,7 +151,7 @@ where
let node = self
.content
.as_widget()
.as_widget_mut()
.layout(tree, renderer, &layout::Limits::new(Size::ZERO, available))
.move_to(self.position);
@ -160,13 +160,13 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut widget::Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
self.content.as_widget().operate(
self.content.as_widget_mut().operate(
tree,
layout.children().next().unwrap(),
renderer,

View file

@ -157,7 +157,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
_renderer: &Renderer,
limits: &layout::Limits,

View file

@ -136,7 +136,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
_renderer: &Renderer,
_limits: &layout::Limits,

View file

@ -290,7 +290,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,

208
widget/src/responsive.rs Normal file
View file

@ -0,0 +1,208 @@
use crate::core::layout::{self, Layout};
use crate::core::mouse;
use crate::core::overlay;
use crate::core::renderer;
use crate::core::widget;
use crate::core::widget::Tree;
use crate::core::{
self, Clipboard, Element, Event, Length, Rectangle, Shell, Size, Vector,
Widget,
};
use crate::horizontal_space;
/// A widget that is aware of its dimensions.
///
/// A [`Responsive`] widget will always try to fill all the available space of
/// its parent.
#[allow(missing_debug_implementations)]
pub struct Responsive<
'a,
Message,
Theme = crate::Theme,
Renderer = crate::Renderer,
> {
view: Box<dyn Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a>,
width: Length,
height: Length,
content: Element<'a, Message, Theme, Renderer>,
}
impl<'a, Message, Theme, Renderer> Responsive<'a, Message, Theme, Renderer>
where
Renderer: core::Renderer,
{
/// Creates a new [`Responsive`] widget with a closure that produces its
/// contents.
///
/// The `view` closure will receive the maximum available space for
/// the [`Responsive`] during layout. You can use this [`Size`] to
/// conditionally build the contents.
pub fn new(
view: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a,
) -> Self {
Self {
view: Box::new(view),
width: Length::Fill,
height: Length::Fill,
content: Element::new(horizontal_space().width(0)),
}
}
/// Sets the width of the [`Responsive`].
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.width = width.into();
self
}
/// Sets the height of the [`Responsive`].
pub fn height(mut self, height: impl Into<Length>) -> Self {
self.height = height.into();
self
}
}
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Responsive<'_, Message, Theme, Renderer>
where
Renderer: core::Renderer,
{
fn diff(&self, _tree: &mut Tree) {
// Diff is deferred to layout
}
fn size(&self) -> Size<Length> {
Size {
width: self.width,
height: self.height,
}
}
fn layout(
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let limits = limits.width(self.width).height(self.height);
let size = limits.max();
self.content = (self.view)(size);
tree.diff_children(std::slice::from_ref(&self.content));
let node = self.content.as_widget_mut().layout(
&mut tree.children[0],
renderer,
&limits.loose(),
);
let size = limits.resolve(self.width, self.height, node.size());
layout::Node::with_children(size, vec![node])
}
fn update(
&mut self,
tree: &mut Tree,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) {
self.content.as_widget_mut().update(
&mut tree.children[0],
event,
layout.children().next().unwrap(),
cursor,
renderer,
clipboard,
shell,
viewport,
);
}
fn draw(
&self,
tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
) {
self.content.as_widget().draw(
&tree.children[0],
renderer,
theme,
style,
layout.children().next().unwrap(),
cursor,
viewport,
);
}
fn mouse_interaction(
&self,
tree: &Tree,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
self.content.as_widget().mouse_interaction(
&tree.children[0],
layout.children().next().unwrap(),
cursor,
viewport,
renderer,
)
}
fn operate(
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
self.content.as_widget_mut().operate(
&mut tree.children[0],
layout.children().next().unwrap(),
renderer,
operation,
);
}
fn overlay<'a>(
&'a mut self,
tree: &'a mut Tree,
layout: Layout<'a>,
renderer: &Renderer,
viewport: &Rectangle,
translation: Vector,
) -> Option<overlay::Element<'a, Message, Theme, Renderer>> {
self.content.as_widget_mut().overlay(
&mut tree.children[0],
layout.children().next().unwrap(),
renderer,
viewport,
translation,
)
}
}
impl<'a, Message, Theme, Renderer>
From<Responsive<'a, Message, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer>
where
Message: 'a,
Theme: 'a,
Renderer: core::Renderer + 'a,
{
fn from(responsive: Responsive<'a, Message, Theme, Renderer>) -> Self {
Self::new(responsive)
}
}

View file

@ -208,7 +208,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -222,13 +222,13 @@ where
self.padding,
self.spacing,
self.align,
&self.children,
&mut self.children,
&mut tree.children,
)
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -237,12 +237,12 @@ where
operation.container(None, layout.bounds());
operation.traverse(&mut |operation| {
self.children
.iter()
.iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), layout)| {
child
.as_widget()
.as_widget_mut()
.operate(state, layout, renderer, operation);
});
});
@ -408,7 +408,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -418,6 +418,7 @@ where
.height(self.row.height)
.shrink(self.row.padding);
let child_limits = limits.loose();
let spacing = self.row.spacing;
let vertical_spacing = self.vertical_spacing.unwrap_or(spacing);
let max_width = limits.max().width;
@ -450,11 +451,11 @@ where
}
};
for (i, child) in self.row.children.iter().enumerate() {
let node = child.as_widget().layout(
for (i, child) in self.row.children.iter_mut().enumerate() {
let node = child.as_widget_mut().layout(
&mut tree.children[i],
renderer,
&limits,
&child_limits,
);
let child_size = node.size();
@ -529,7 +530,7 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,

View file

@ -112,7 +112,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
_renderer: &Renderer,
limits: &layout::Limits,

View file

@ -109,22 +109,12 @@ where
class: Theme::default(),
last_status: None,
}
.validate()
.enclose()
}
fn validate(mut self) -> Self {
fn enclose(mut self) -> Self {
let size_hint = self.content.as_widget().size_hint();
debug_assert!(
self.direction.vertical().is_none() || !size_hint.height.is_fill(),
"scrollable content must not fill its vertical scrolling axis"
);
debug_assert!(
self.direction.horizontal().is_none() || !size_hint.width.is_fill(),
"scrollable content must not fill its horizontal scrolling axis"
);
if self.direction.horizontal().is_none() {
self.width = self.width.enclose(size_hint.width);
}
@ -144,7 +134,7 @@ where
/// Sets the [`Direction`] of the [`Scrollable`].
pub fn direction(mut self, direction: impl Into<Direction>) -> Self {
self.direction = direction.into();
self.validate()
self.enclose()
}
/// Sets the [`widget::Id`] of the [`Scrollable`].
@ -419,7 +409,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -435,23 +425,27 @@ where
..Padding::ZERO
},
|limits| {
let child_limits = layout::Limits::new(
let is_horizontal = self.direction.horizontal().is_some();
let is_vertical = self.direction.vertical().is_some();
let child_limits = layout::Limits::with_compression(
limits.min(),
Size::new(
if self.direction.horizontal().is_some() {
if is_horizontal {
f32::INFINITY
} else {
limits.max().width
},
if self.direction.vertical().is_some() {
if is_vertical {
f32::INFINITY
} else {
limits.max().height
},
),
Size::new(is_horizontal, is_vertical),
);
self.content.as_widget().layout(
self.content.as_widget_mut().layout(
&mut tree.children[0],
renderer,
&child_limits,
@ -525,7 +519,7 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -548,7 +542,7 @@ where
);
operation.traverse(&mut |operation| {
self.content.as_widget().operate(
self.content.as_widget_mut().operate(
&mut tree.children[0],
layout.children().next().unwrap(),
renderer,

View file

@ -284,14 +284,16 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.content
.as_widget()
.layout(&mut tree.children[0], renderer, limits)
self.content.as_widget_mut().layout(
&mut tree.children[0],
renderer,
limits,
)
}
fn draw(
@ -316,13 +318,13 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: core::Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
self.content.as_widget().operate(
self.content.as_widget_mut().operate(
&mut tree.children[0],
layout,
renderer,

View file

@ -77,7 +77,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
_renderer: &Renderer,
limits: &layout::Limits,

View file

@ -234,7 +234,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
_renderer: &Renderer,
limits: &layout::Limits,

View file

@ -65,7 +65,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
_renderer: &Renderer,
limits: &layout::Limits,

View file

@ -23,6 +23,7 @@ pub struct Stack<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
width: Length,
height: Length,
children: Vec<Element<'a, Message, Theme, Renderer>>,
clip: bool,
}
impl<'a, Message, Theme, Renderer> Stack<'a, Message, Theme, Renderer>
@ -62,6 +63,7 @@ where
width: Length::Shrink,
height: Length::Shrink,
children,
clip: false,
}
}
@ -114,6 +116,16 @@ where
) -> Self {
children.into_iter().fold(self, Self::push)
}
/// Sets whether the [`Stack`] should clip overflowing content.
///
/// It has a slight performance overhead during presentation.
///
/// By default, it is set to `false`.
pub fn clip(mut self, clip: bool) -> Self {
self.clip = clip;
self
}
}
impl<Message, Renderer> Default for Stack<'_, Message, Renderer>
@ -146,7 +158,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -161,7 +173,7 @@ where
));
}
let base = self.children[0].as_widget().layout(
let base = self.children[0].as_widget_mut().layout(
&mut tree.children[0],
renderer,
&limits,
@ -171,18 +183,21 @@ where
let limits = layout::Limits::new(Size::ZERO, size);
let nodes = std::iter::once(base)
.chain(self.children[1..].iter().zip(&mut tree.children[1..]).map(
|(layer, tree)| {
layer.as_widget().layout(tree, renderer, &limits)
},
))
.chain(
self.children[1..]
.iter_mut()
.zip(&mut tree.children[1..])
.map(|(layer, tree)| {
layer.as_widget_mut().layout(tree, renderer, &limits)
}),
)
.collect();
layout::Node::with_children(size, nodes)
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -191,12 +206,12 @@ where
operation.container(None, layout.bounds());
operation.traverse(&mut |operation| {
self.children
.iter()
.iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), layout)| {
child
.as_widget()
.as_widget_mut()
.operate(state, layout, renderer, operation);
});
});
@ -278,6 +293,12 @@ where
viewport: &Rectangle,
) {
if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
let viewport = if self.clip {
&clipped_viewport
} else {
viewport
};
let layers_below = if cursor.is_over(layout.bounds()) {
self.children
.iter()
@ -313,26 +334,16 @@ where
layout,
cursor| {
if i > 0 {
renderer.with_layer(clipped_viewport, |renderer| {
renderer.with_layer(*viewport, |renderer| {
layer.as_widget().draw(
state,
renderer,
theme,
style,
layout,
cursor,
&clipped_viewport,
state, renderer, theme, style, layout, cursor,
viewport,
);
});
} else {
layer.as_widget().draw(
state,
renderer,
theme,
style,
layout,
cursor,
&clipped_viewport,
state, renderer, theme, style, layout, cursor,
viewport,
);
}
};

View file

@ -162,7 +162,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,

View file

@ -232,7 +232,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut widget::Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -265,7 +265,7 @@ where
let mut y = self.padding_y;
for (i, (cell, state)) in
self.cells.iter().zip(&mut tree.children).enumerate()
self.cells.iter_mut().zip(&mut tree.children).enumerate()
{
let row = i / columns;
let column = i % columns;
@ -306,7 +306,7 @@ where
)
.width(width);
let layout = cell.as_widget().layout(state, renderer, &limits);
let layout = cell.as_widget_mut().layout(state, renderer, &limits);
let size = limits.resolve(width, Length::Shrink, layout.size());
metrics.columns[column] = metrics.columns[column].max(size.width);
@ -344,7 +344,7 @@ where
let mut y = self.padding_y;
for (i, (cell, state)) in
self.cells.iter().zip(&mut tree.children).enumerate()
self.cells.iter_mut().zip(&mut tree.children).enumerate()
{
let row = i / columns;
let column = i % columns;
@ -396,7 +396,7 @@ where
)
.width(width);
let layout = cell.as_widget().layout(state, renderer, &limits);
let layout = cell.as_widget_mut().layout(state, renderer, &limits);
let size = limits.resolve(
if let Length::Fixed(_) = width {
width
@ -581,7 +581,7 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut widget::Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -589,11 +589,12 @@ where
) {
for ((cell, state), layout) in self
.cells
.iter()
.iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
{
cell.as_widget().operate(state, layout, renderer, operation);
cell.as_widget_mut()
.operate(state, layout, renderer, operation);
}
}

View file

@ -225,7 +225,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,

View file

@ -597,7 +597,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut widget::Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -1051,7 +1051,7 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut widget::Tree,
layout: Layout<'_>,
_renderer: &Renderer,

View file

@ -295,7 +295,7 @@ where
///
/// [`Renderer`]: text::Renderer
pub fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -669,7 +669,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
@ -678,7 +678,7 @@ where
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
_renderer: &Renderer,

View file

@ -82,23 +82,23 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.content.as_widget().layout(tree, renderer, limits)
self.content.as_widget_mut().layout(tree, renderer, limits)
}
fn operate(
&self,
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn Operation,
) {
self.content
.as_widget()
.as_widget_mut()
.operate(tree, layout, renderer, operation);
}

View file

@ -270,7 +270,7 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,

View file

@ -179,14 +179,16 @@ where
}
fn layout(
&self,
&mut self,
tree: &mut widget::Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.content
.as_widget()
.layout(&mut tree.children[0], renderer, limits)
self.content.as_widget_mut().layout(
&mut tree.children[0],
renderer,
limits,
)
}
fn update(
@ -202,6 +204,7 @@ where
) {
let state = tree.state.downcast_mut::<State>();
let previous_state = *state;
let was_idle = *state == State::Idle;
*state = cursor
@ -214,7 +217,9 @@ where
if was_idle != is_idle {
shell.invalidate_layout();
shell.request_redraw();
} else if !is_idle && self.position == Position::FollowCursor {
} else if self.position == Position::FollowCursor
&& previous_state != *state
{
shell.request_redraw();
}
@ -291,7 +296,7 @@ where
let tooltip = if let State::Hovered { cursor_position } = *state {
Some(overlay::Element::new(Box::new(Overlay {
position: layout.position() + translation,
tooltip: &self.tooltip,
tooltip: &mut self.tooltip,
state: children.next().unwrap(),
cursor_position,
content_bounds: layout.bounds(),
@ -363,7 +368,7 @@ where
Renderer: text::Renderer,
{
position: Point,
tooltip: &'b Element<'a, Message, Theme, Renderer>,
tooltip: &'b mut Element<'a, Message, Theme, Renderer>,
state: &'b mut widget::Tree,
cursor_position: Point,
content_bounds: Rectangle,
@ -383,7 +388,7 @@ where
fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
let viewport = Rectangle::with_size(bounds);
let tooltip_layout = self.tooltip.as_widget().layout(
let tooltip_layout = self.tooltip.as_widget_mut().layout(
self.state,
renderer,
&layout::Limits::new(
@ -391,7 +396,7 @@ where
if self.snap_within_viewport {
viewport.size()
} else {
Size::INFINITY
Size::INFINITE
},
)
.shrink(Padding::new(self.padding)),
@ -505,7 +510,7 @@ where
&defaults,
layout.children().next().unwrap(),
cursor_position,
&Rectangle::with_size(Size::INFINITY),
&Rectangle::with_size(Size::INFINITE),
);
}
}

View file

@ -238,7 +238,7 @@ where
}
fn layout(
&self,
&mut self,
_tree: &mut Tree,
_renderer: &Renderer,
limits: &layout::Limits,