diff options
author | 2024-11-13 17:08:26 +0100 | |
---|---|---|
committer | 2024-11-13 17:08:26 +0100 | |
commit | a11fcf8f2dde551335c6f34788393fa2e8f8a362 (patch) | |
tree | 1e423c847a6505a2582bec19908866dba94c436d /core/src | |
parent | 42a2cb6d4f78343f43d6a68a28e5502d9426ed2c (diff) | |
parent | 28ec6df8f0ebf96966bee61caf5a325695314b7a (diff) | |
download | iced-a11fcf8f2dde551335c6f34788393fa2e8f8a362.tar.gz iced-a11fcf8f2dde551335c6f34788393fa2e8f8a362.tar.bz2 iced-a11fcf8f2dde551335c6f34788393fa2e8f8a362.zip |
Merge pull request #2662 from iced-rs/reactive-rendering
Reactive Rendering
Diffstat (limited to 'core/src')
-rw-r--r-- | core/src/element.rs | 21 | ||||
-rw-r--r-- | core/src/layout/flex.rs | 53 | ||||
-rw-r--r-- | core/src/overlay.rs | 8 | ||||
-rw-r--r-- | core/src/overlay/element.rs | 17 | ||||
-rw-r--r-- | core/src/overlay/group.rs | 29 | ||||
-rw-r--r-- | core/src/shell.rs | 47 | ||||
-rw-r--r-- | core/src/widget.rs | 8 |
7 files changed, 126 insertions, 57 deletions
diff --git a/core/src/element.rs b/core/src/element.rs index 6ebb8a15..03e56b43 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -1,4 +1,3 @@ -use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::overlay; @@ -6,8 +5,8 @@ use crate::renderer; use crate::widget; use crate::widget::tree::{self, Tree}; use crate::{ - Border, Clipboard, Color, Layout, Length, Rectangle, Shell, Size, Vector, - Widget, + Border, Clipboard, Color, Event, Layout, Length, Rectangle, Shell, Size, + Vector, Widget, }; use std::borrow::Borrow; @@ -309,7 +308,7 @@ where self.widget.operate(tree, layout, renderer, operation); } - fn on_event( + fn update( &mut self, tree: &mut Tree, event: Event, @@ -319,11 +318,11 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, B>, viewport: &Rectangle, - ) -> event::Status { + ) { let mut local_messages = Vec::new(); let mut local_shell = Shell::new(&mut local_messages); - let status = self.widget.on_event( + self.widget.update( tree, event, layout, @@ -335,8 +334,6 @@ where ); shell.merge(local_shell, &self.mapper); - - status } fn draw( @@ -447,7 +444,7 @@ where .operate(state, layout, renderer, operation); } - fn on_event( + fn update( &mut self, state: &mut Tree, event: Event, @@ -457,10 +454,10 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, - ) -> event::Status { - self.element.widget.on_event( + ) { + self.element.widget.update( state, event, layout, cursor, renderer, clipboard, shell, viewport, - ) + ); } fn draw( diff --git a/core/src/layout/flex.rs b/core/src/layout/flex.rs index ac80d393..2cff5bfd 100644 --- a/core/src/layout/flex.rs +++ b/core/src/layout/flex.rs @@ -79,6 +79,7 @@ where let max_cross = axis.cross(limits.max()); let mut fill_main_sum = 0; + let mut some_fill_cross = false; let (mut cross, cross_compress) = match axis { Axis::Vertical if width == Length::Shrink => (0.0, true), Axis::Horizontal if height == Length::Shrink => (0.0, true), @@ -90,6 +91,10 @@ where let mut nodes: Vec<Node> = Vec::with_capacity(items.len()); nodes.resize(items.len(), Node::default()); + // FIRST PASS + // We lay out non-fluid elements in the main axis. + // If we need to compress the cross axis, then we skip any of these elements + // that are also fluid in the cross axis. for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() { let (fill_main_factor, fill_cross_factor) = { let size = child.as_widget().size(); @@ -121,6 +126,41 @@ where nodes[i] = layout; } else { fill_main_sum += fill_main_factor; + some_fill_cross = some_fill_cross || fill_cross_factor != 0; + } + } + + // SECOND PASS (conditional) + // If we must compress the cross axis and there are fluid elements in the + // cross axis, we lay out any of these elements that are also non-fluid in + // the main axis (i.e. the ones we deliberately skipped in the first pass). + // + // We use the maximum cross length obtained in the first pass as the maximum + // cross limit. + if cross_compress && some_fill_cross { + for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() + { + let (fill_main_factor, fill_cross_factor) = { + let size = child.as_widget().size(); + + axis.pack(size.width.fill_factor(), size.height.fill_factor()) + }; + + if fill_main_factor == 0 && fill_cross_factor != 0 { + let (max_width, max_height) = axis.pack(available, cross); + + let child_limits = + Limits::new(Size::ZERO, Size::new(max_width, max_height)); + + let layout = + child.as_widget().layout(tree, renderer, &child_limits); + let size = layout.size(); + + available -= axis.main(size); + cross = cross.max(axis.cross(size)); + + nodes[i] = layout; + } } } @@ -135,6 +175,9 @@ where }, }; + // THIRD PASS + // We only have the elements that are fluid in the main axis left. + // We use the remaining space to evenly allocate space based on fill factors. for (i, (child, tree)) in items.iter().zip(trees).enumerate() { let (fill_main_factor, fill_cross_factor) = { let size = child.as_widget().size(); @@ -142,10 +185,16 @@ where axis.pack(size.width.fill_factor(), size.height.fill_factor()) }; - if fill_main_factor != 0 || (cross_compress && fill_cross_factor != 0) { + if fill_main_factor != 0 { let max_main = remaining * fill_main_factor as f32 / fill_main_sum as f32; + let max_main = if max_main.is_nan() { + f32::INFINITY + } else { + max_main + }; + let min_main = if max_main.is_infinite() { 0.0 } else { @@ -178,6 +227,8 @@ where let pad = axis.pack(padding.left, padding.top); let mut main = pad.0; + // FOURTH PASS + // We align all the laid out nodes in the cross axis, if needed. for (i, node) in nodes.iter_mut().enumerate() { if i > 0 { main += spacing; diff --git a/core/src/overlay.rs b/core/src/overlay.rs index f09de831..383663af 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -5,13 +5,12 @@ mod group; pub use element::Element; pub use group::Group; -use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::renderer; use crate::widget; use crate::widget::Tree; -use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size, Vector}; +use crate::{Clipboard, Event, Layout, Point, Rectangle, Shell, Size, Vector}; /// An interactive component that can be displayed on top of other widgets. pub trait Overlay<Message, Theme, Renderer> @@ -57,7 +56,7 @@ where /// * a [`Clipboard`], if available /// /// By default, it does nothing. - fn on_event( + fn update( &mut self, _event: Event, _layout: Layout<'_>, @@ -65,8 +64,7 @@ where _renderer: &Renderer, _clipboard: &mut dyn Clipboard, _shell: &mut Shell<'_, Message>, - ) -> event::Status { - event::Status::Ignored + ) { } /// Returns the current [`mouse::Interaction`] of the [`Overlay`]. diff --git a/core/src/overlay/element.rs b/core/src/overlay/element.rs index 32e987a3..ed870feb 100644 --- a/core/src/overlay/element.rs +++ b/core/src/overlay/element.rs @@ -1,11 +1,10 @@ pub use crate::Overlay; -use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::renderer; use crate::widget; -use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size}; +use crate::{Clipboard, Event, Layout, Point, Rectangle, Shell, Size}; /// A generic [`Overlay`]. #[allow(missing_debug_implementations)] @@ -50,7 +49,7 @@ where } /// Processes a runtime [`Event`]. - pub fn on_event( + pub fn update( &mut self, event: Event, layout: Layout<'_>, @@ -58,9 +57,9 @@ where renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - ) -> event::Status { + ) { self.overlay - .on_event(event, layout, cursor, renderer, clipboard, shell) + .update(event, layout, cursor, renderer, clipboard, shell); } /// Returns the current [`mouse::Interaction`] of the [`Element`]. @@ -149,7 +148,7 @@ where self.content.operate(layout, renderer, operation); } - fn on_event( + fn update( &mut self, event: Event, layout: Layout<'_>, @@ -157,11 +156,11 @@ where renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, B>, - ) -> event::Status { + ) { let mut local_messages = Vec::new(); let mut local_shell = Shell::new(&mut local_messages); - let event_status = self.content.on_event( + self.content.update( event, layout, cursor, @@ -171,8 +170,6 @@ where ); shell.merge(local_shell, self.mapper); - - event_status } fn mouse_interaction( diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index 6541d311..2b374252 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -1,4 +1,3 @@ -use crate::event; use crate::layout; use crate::mouse; use crate::overlay; @@ -73,7 +72,7 @@ where ) } - fn on_event( + fn update( &mut self, event: Event, layout: Layout<'_>, @@ -81,21 +80,17 @@ where renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, - ) -> event::Status { - self.children - .iter_mut() - .zip(layout.children()) - .map(|(child, layout)| { - child.on_event( - event.clone(), - layout, - cursor, - renderer, - clipboard, - shell, - ) - }) - .fold(event::Status::Ignored, event::Status::merge) + ) { + for (child, layout) in self.children.iter_mut().zip(layout.children()) { + child.update( + event.clone(), + layout, + cursor, + renderer, + clipboard, + shell, + ); + } } fn draw( diff --git a/core/src/shell.rs b/core/src/shell.rs index 2952ceff..12ebbaa8 100644 --- a/core/src/shell.rs +++ b/core/src/shell.rs @@ -1,3 +1,5 @@ +use crate::event; +use crate::time::Instant; use crate::window; /// A connection to the state of a shell. @@ -9,6 +11,7 @@ use crate::window; #[derive(Debug)] pub struct Shell<'a, Message> { messages: &'a mut Vec<Message>, + event_status: event::Status, redraw_request: Option<window::RedrawRequest>, is_layout_invalid: bool, are_widgets_invalid: bool, @@ -19,6 +22,7 @@ impl<'a, Message> Shell<'a, Message> { pub fn new(messages: &'a mut Vec<Message>) -> Self { Self { messages, + event_status: event::Status::Ignored, redraw_request: None, is_layout_invalid: false, are_widgets_invalid: false, @@ -35,14 +39,37 @@ impl<'a, Message> Shell<'a, Message> { self.messages.push(message); } - /// Requests a new frame to be drawn. - pub fn request_redraw(&mut self, request: window::RedrawRequest) { + /// Marks the current event as captured. Prevents "event bubbling". + /// + /// A widget should capture an event when no ancestor should + /// handle it. + pub fn capture_event(&mut self) { + self.event_status = event::Status::Captured; + } + + /// Returns the current [`event::Status`] of the [`Shell`]. + pub fn event_status(&self) -> event::Status { + self.event_status + } + + /// Returns whether the current event has been captured. + pub fn is_event_captured(&self) -> bool { + self.event_status == event::Status::Captured + } + + /// Requests a new frame to be drawn as soon as possible. + pub fn request_redraw(&mut self) { + self.redraw_request = Some(window::RedrawRequest::NextFrame); + } + + /// Requests a new frame to be drawn at the given [`Instant`]. + pub fn request_redraw_at(&mut self, at: Instant) { match self.redraw_request { None => { - self.redraw_request = Some(request); + self.redraw_request = Some(window::RedrawRequest::At(at)); } - Some(current) if request < current => { - self.redraw_request = Some(request); + Some(window::RedrawRequest::At(current)) if at < current => { + self.redraw_request = Some(window::RedrawRequest::At(at)); } _ => {} } @@ -95,8 +122,12 @@ impl<'a, Message> Shell<'a, Message> { pub fn merge<B>(&mut self, other: Shell<'_, B>, f: impl Fn(B) -> Message) { self.messages.extend(other.messages.drain(..).map(f)); - if let Some(at) = other.redraw_request { - self.request_redraw(at); + if let Some(new) = other.redraw_request { + self.redraw_request = Some( + self.redraw_request + .map(|current| if current < new { current } else { new }) + .unwrap_or(new), + ); } self.is_layout_invalid = @@ -104,5 +135,7 @@ impl<'a, Message> Shell<'a, Message> { self.are_widgets_invalid = self.are_widgets_invalid || other.are_widgets_invalid; + + self.event_status = self.event_status.merge(other.event_status); } } diff --git a/core/src/widget.rs b/core/src/widget.rs index 9cfff83d..2a40f823 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -10,12 +10,11 @@ pub use operation::Operation; pub use text::Text; pub use tree::Tree; -use crate::event::{self, Event}; use crate::layout::{self, Layout}; use crate::mouse; use crate::overlay; use crate::renderer; -use crate::{Clipboard, Length, Rectangle, Shell, Size, Vector}; +use crate::{Clipboard, Event, Length, Rectangle, Shell, Size, Vector}; /// A component that displays information and allows interaction. /// @@ -112,7 +111,7 @@ where /// Processes a runtime [`Event`]. /// /// By default, it does nothing. - fn on_event( + fn update( &mut self, _state: &mut Tree, _event: Event, @@ -122,8 +121,7 @@ where _clipboard: &mut dyn Clipboard, _shell: &mut Shell<'_, Message>, _viewport: &Rectangle, - ) -> event::Status { - event::Status::Ignored + ) { } /// Returns the current [`mouse::Interaction`] of the [`Widget`]. |