Make Widget::layout and operate mutable
... and bless `responsive`!
This commit is contained in:
parent
9490d735c5
commit
31bc6d48cd
54 changed files with 284 additions and 519 deletions
|
|
@ -5,16 +5,10 @@ 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,
|
||||
self, Clipboard, Element, Event, Length, Rectangle, Shell, Size, Vector,
|
||||
Widget,
|
||||
};
|
||||
use crate::horizontal_space;
|
||||
use crate::runtime::overlay::Nested;
|
||||
|
||||
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.
|
||||
///
|
||||
|
|
@ -29,7 +23,7 @@ pub struct Responsive<
|
|||
Renderer = crate::Renderer,
|
||||
> {
|
||||
view: Box<dyn Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a>,
|
||||
content: RefCell<Content<'a, Message, Theme, Renderer>>,
|
||||
content: Element<'a, Message, Theme, Renderer>,
|
||||
}
|
||||
|
||||
impl<'a, Message, Theme, Renderer> Responsive<'a, Message, Theme, Renderer>
|
||||
|
|
@ -47,91 +41,13 @@ where
|
|||
) -> 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)),
|
||||
}),
|
||||
content: 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>,
|
||||
tree: Tree,
|
||||
}
|
||||
|
||||
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||
|
|
@ -145,7 +61,7 @@ where
|
|||
|
||||
fn state(&self) -> tree::State {
|
||||
tree::State::new(State {
|
||||
tree: RefCell::new(Tree::empty()),
|
||||
tree: Tree::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -157,35 +73,22 @@ where
|
|||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
_tree: &mut Tree,
|
||||
_renderer: &Renderer,
|
||||
&mut 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();
|
||||
let size = limits.max();
|
||||
|
||||
content.resolve(
|
||||
&mut state.tree.borrow_mut(),
|
||||
self.content = (self.view)(size);
|
||||
state.tree.diff(&self.content);
|
||||
|
||||
self.content.as_widget_mut().layout(
|
||||
&mut state.tree,
|
||||
renderer,
|
||||
layout,
|
||||
&self.view,
|
||||
|tree, renderer, layout, element| {
|
||||
element
|
||||
.as_widget()
|
||||
.operate(tree, layout, renderer, operation);
|
||||
},
|
||||
);
|
||||
&limits.loose(),
|
||||
)
|
||||
}
|
||||
|
||||
fn update(
|
||||
|
|
@ -200,35 +103,17 @@ where
|
|||
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,
|
||||
self.content.as_widget_mut().update(
|
||||
&mut state.tree,
|
||||
event,
|
||||
layout,
|
||||
&self.view,
|
||||
|tree, renderer, layout, element| {
|
||||
element.as_widget_mut().update(
|
||||
tree,
|
||||
event,
|
||||
layout,
|
||||
cursor,
|
||||
renderer,
|
||||
clipboard,
|
||||
&mut local_shell,
|
||||
viewport,
|
||||
);
|
||||
},
|
||||
cursor,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
viewport,
|
||||
);
|
||||
|
||||
if local_shell.is_layout_invalid() {
|
||||
content.layout = None;
|
||||
}
|
||||
|
||||
shell.merge(local_shell, std::convert::identity);
|
||||
}
|
||||
|
||||
fn draw(
|
||||
|
|
@ -242,18 +127,15 @@ where
|
|||
viewport: &Rectangle,
|
||||
) {
|
||||
let state = tree.state.downcast_ref::<State>();
|
||||
let mut content = self.content.borrow_mut();
|
||||
|
||||
content.resolve(
|
||||
&mut state.tree.borrow_mut(),
|
||||
self.content.as_widget().draw(
|
||||
&state.tree,
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout,
|
||||
&self.view,
|
||||
|tree, renderer, layout, element| {
|
||||
element.as_widget().draw(
|
||||
tree, renderer, theme, style, layout, cursor, viewport,
|
||||
);
|
||||
},
|
||||
cursor,
|
||||
viewport,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -266,79 +148,50 @@ where
|
|||
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,
|
||||
self.content.as_widget().mouse_interaction(
|
||||
&state.tree,
|
||||
layout,
|
||||
&self.view,
|
||||
|tree, renderer, layout, element| {
|
||||
element
|
||||
.as_widget()
|
||||
.mouse_interaction(tree, layout, cursor, viewport, renderer)
|
||||
},
|
||||
cursor,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'b>,
|
||||
fn operate(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
operation: &mut dyn widget::Operation,
|
||||
) {
|
||||
let state = tree.state.downcast_mut::<State>();
|
||||
|
||||
self.content.as_widget_mut().operate(
|
||||
&mut state.tree,
|
||||
layout,
|
||||
renderer,
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn overlay<'a>(
|
||||
&'a mut self,
|
||||
tree: &'a mut Tree,
|
||||
layout: Layout<'a>,
|
||||
renderer: &Renderer,
|
||||
viewport: &Rectangle,
|
||||
translation: Vector,
|
||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||
use std::ops::DerefMut;
|
||||
) -> Option<overlay::Element<'a, Message, Theme, Renderer>> {
|
||||
let state = tree.state.downcast_mut::<State>();
|
||||
|
||||
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(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
|
||||
}
|
||||
self.content.as_widget_mut().overlay(
|
||||
&mut state.tree,
|
||||
layout,
|
||||
renderer,
|
||||
viewport,
|
||||
translation,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -354,108 +207,3 @@ where
|
|||
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<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 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 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue