summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/src/element.rs21
-rw-r--r--core/src/layout/flex.rs53
-rw-r--r--core/src/overlay.rs8
-rw-r--r--core/src/overlay/element.rs17
-rw-r--r--core/src/overlay/group.rs29
-rw-r--r--core/src/shell.rs47
-rw-r--r--core/src/widget.rs8
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`].