Skip to content

Commit

Permalink
Introduce Shell type in iced_native
Browse files Browse the repository at this point in the history
Widgets now can invalidate the current layout of the application on demand.
  • Loading branch information
hecrj committed Nov 29, 2021
1 parent f7792d8 commit bbd9355
Show file tree
Hide file tree
Showing 26 changed files with 218 additions and 130 deletions.
8 changes: 4 additions & 4 deletions graphics/src/widget/canvas.rs
Expand Up @@ -9,8 +9,8 @@ use crate::{Backend, Primitive};
use iced_native::layout;
use iced_native::mouse;
use iced_native::{
Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Vector,
Widget,
Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Shell, Size,
Vector, Widget,
};
use std::hash::Hash;
use std::marker::PhantomData;
Expand Down Expand Up @@ -158,7 +158,7 @@ where
cursor_position: Point,
_renderer: &Renderer<B>,
_clipboard: &mut dyn Clipboard,
messages: &mut Vec<Message>,
shell: &mut Shell<'_, Message>,
) -> event::Status {
let bounds = layout.bounds();

Expand All @@ -179,7 +179,7 @@ where
self.program.update(canvas_event, bounds, cursor);

if let Some(message) = message {
messages.push(message);
shell.publish(message);
}

return event_status;
Expand Down
20 changes: 11 additions & 9 deletions lazy/src/component.rs
Expand Up @@ -4,7 +4,7 @@ use iced_native::mouse;
use iced_native::overlay;
use iced_native::renderer;
use iced_native::{
Clipboard, Element, Hasher, Length, Point, Rectangle, Widget,
Clipboard, Element, Hasher, Length, Point, Rectangle, Shell, Widget,
};

use ouroboros::self_referencing;
Expand Down Expand Up @@ -91,9 +91,10 @@ where
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
messages: &mut Vec<Message>,
shell: &mut Shell<'_, Message>,
) -> event::Status {
let mut local_messages = Vec::new();
let mut local_shell = Shell::new(&mut local_messages);

let event_status =
self.state.as_mut().unwrap().with_cache_mut(|cache| {
Expand All @@ -103,19 +104,20 @@ where
cursor_position,
renderer,
clipboard,
&mut local_messages,
&mut local_shell,
)
});

if !local_messages.is_empty() {
let mut component =
self.state.take().unwrap().into_heads().component;

messages.extend(
local_messages
.into_iter()
.filter_map(|message| component.update(message)),
);
for message in local_messages
.into_iter()
.filter_map(|message| component.update(message))
{
shell.publish(message);
}

self.state = Some(
StateBuilder {
Expand All @@ -128,7 +130,7 @@ where
.build(),
);

// TODO: Invalidate layout (!)
shell.invalidate_layout();
}

event_status
Expand Down
21 changes: 10 additions & 11 deletions native/src/element.rs
Expand Up @@ -4,7 +4,7 @@ use crate::mouse;
use crate::overlay;
use crate::renderer;
use crate::{
Clipboard, Color, Hasher, Layout, Length, Point, Rectangle, Widget,
Clipboard, Color, Hasher, Layout, Length, Point, Rectangle, Shell, Widget,
};

/// A generic [`Widget`].
Expand Down Expand Up @@ -228,15 +228,15 @@ where
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
messages: &mut Vec<Message>,
shell: &mut Shell<'_, Message>,
) -> event::Status {
self.widget.on_event(
event,
layout,
cursor_position,
renderer,
clipboard,
messages,
shell,
)
}

Expand Down Expand Up @@ -327,22 +327,21 @@ where
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
messages: &mut Vec<B>,
shell: &mut Shell<'_, B>,
) -> event::Status {
let mut original_messages = Vec::new();
let mut local_messages = Vec::new();
let mut local_shell = Shell::new(&mut local_messages);

let status = self.widget.on_event(
event,
layout,
cursor_position,
renderer,
clipboard,
&mut original_messages,
&mut local_shell,
);

original_messages
.drain(..)
.for_each(|message| messages.push((self.mapper)(message)));
shell.merge(local_shell, &self.mapper);

status
}
Expand Down Expand Up @@ -427,15 +426,15 @@ where
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
messages: &mut Vec<Message>,
shell: &mut Shell<'_, Message>,
) -> event::Status {
self.element.widget.on_event(
event,
layout,
cursor_position,
renderer,
clipboard,
messages,
shell,
)
}

Expand Down
2 changes: 2 additions & 0 deletions native/src/lib.rs
Expand Up @@ -53,6 +53,7 @@ pub mod window;
mod element;
mod hasher;
mod runtime;
mod shell;
mod user_interface;

// We disable debug capabilities on release builds unless the `debug` feature
Expand Down Expand Up @@ -85,6 +86,7 @@ pub use overlay::Overlay;
pub use program::Program;
pub use renderer::Renderer;
pub use runtime::Runtime;
pub use shell::Shell;
pub use subscription::Subscription;
pub use user_interface::{Cache, UserInterface};
pub use widget::Widget;
4 changes: 2 additions & 2 deletions native/src/overlay.rs
Expand Up @@ -10,7 +10,7 @@ use crate::event::{self, Event};
use crate::layout;
use crate::mouse;
use crate::renderer;
use crate::{Clipboard, Hasher, Layout, Point, Rectangle, Size};
use crate::{Clipboard, Hasher, Layout, Point, Rectangle, Shell, Size};

/// An interactive component that can be displayed on top of other widgets.
pub trait Overlay<Message, Renderer>
Expand Down Expand Up @@ -71,7 +71,7 @@ where
_cursor_position: Point,
_renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
_messages: &mut Vec<Message>,
_shell: &mut Shell<'_, Message>,
) -> event::Status {
event::Status::Ignored
}
Expand Down
17 changes: 8 additions & 9 deletions native/src/overlay/element.rs
Expand Up @@ -4,7 +4,7 @@ use crate::event::{self, Event};
use crate::layout;
use crate::mouse;
use crate::renderer;
use crate::{Clipboard, Hasher, Layout, Point, Rectangle, Size, Vector};
use crate::{Clipboard, Hasher, Layout, Point, Rectangle, Shell, Size, Vector};

/// A generic [`Overlay`].
#[allow(missing_debug_implementations)]
Expand Down Expand Up @@ -62,15 +62,15 @@ where
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
messages: &mut Vec<Message>,
shell: &mut Shell<'_, Message>,
) -> event::Status {
self.overlay.on_event(
event,
layout,
cursor_position,
renderer,
clipboard,
messages,
shell,
)
}

Expand Down Expand Up @@ -136,22 +136,21 @@ where
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
messages: &mut Vec<B>,
shell: &mut Shell<'_, B>,
) -> event::Status {
let mut original_messages = Vec::new();
let mut local_messages = Vec::new();
let mut local_shell = Shell::new(&mut local_messages);

let event_status = self.content.on_event(
event,
layout,
cursor_position,
renderer,
clipboard,
&mut original_messages,
&mut local_shell,
);

original_messages
.drain(..)
.for_each(|message| messages.push((self.mapper)(message)));
shell.merge(local_shell, self.mapper);

event_status
}
Expand Down
8 changes: 4 additions & 4 deletions native/src/overlay/menu.rs
Expand Up @@ -11,7 +11,7 @@ use crate::widget::scrollable::{self, Scrollable};
use crate::widget::Container;
use crate::{
Clipboard, Color, Element, Hasher, Layout, Length, Padding, Point,
Rectangle, Size, Vector, Widget,
Rectangle, Shell, Size, Vector, Widget,
};

pub use iced_style::menu::Style;
Expand Down Expand Up @@ -222,15 +222,15 @@ where
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
messages: &mut Vec<Message>,
shell: &mut Shell<'_, Message>,
) -> event::Status {
self.container.on_event(
event.clone(),
layout,
cursor_position,
renderer,
clipboard,
messages,
shell,
)
}

Expand Down Expand Up @@ -333,7 +333,7 @@ where
cursor_position: Point,
renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
_messages: &mut Vec<Message>,
_shell: &mut Shell<'_, Message>,
) -> event::Status {
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
Expand Down
54 changes: 54 additions & 0 deletions native/src/shell.rs
@@ -0,0 +1,54 @@
/// A connection to the state of a shell.
///
/// A [`Widget`] can leverage a [`Shell`] to trigger changes in an application,
/// like publishing messages or invalidating the current layout.
///
/// [`Widget`]: crate::Widget
#[derive(Debug)]
pub struct Shell<'a, Message> {
messages: &'a mut Vec<Message>,
is_layout_invalid: bool,
}

impl<'a, Message> Shell<'a, Message> {
/// Creates a new [`Shell`] with the provided buffer of messages.
pub fn new(messages: &'a mut Vec<Message>) -> Self {
Self {
messages,
is_layout_invalid: false,
}
}

/// Triggers the given function if the layout is invalid, cleaning it in the
/// process.
pub fn with_invalid_layout(&mut self, f: impl FnOnce()) {
if self.is_layout_invalid {
self.is_layout_invalid = false;

f()
}
}

/// Publish the given `Message` for an application to process it.
pub fn publish(&mut self, message: Message) {
self.messages.push(message);
}

/// Invalidates the current application layout.
///
/// The shell will relayout the application widgets.
pub fn invalidate_layout(&mut self) {
self.is_layout_invalid = true;
}

/// Merges the current [`Shell`] with another one by applying the given
/// function to the messages of the latter.
///
/// This method is useful for composition.
pub fn merge<B>(&mut self, other: Shell<'_, B>, f: impl Fn(B) -> Message) {
self.messages.extend(other.messages.drain(..).map(f));

self.is_layout_invalid =
self.is_layout_invalid || other.is_layout_invalid;
}
}

0 comments on commit bbd9355

Please sign in to comment.