summaryrefslogtreecommitdiffstats
path: root/native/src/user_interface.rs
diff options
context:
space:
mode:
Diffstat (limited to 'native/src/user_interface.rs')
-rw-r--r--native/src/user_interface.rs213
1 files changed, 147 insertions, 66 deletions
diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs
index 6fc6a479..344ba4d6 100644
--- a/native/src/user_interface.rs
+++ b/native/src/user_interface.rs
@@ -1,8 +1,10 @@
//! Implement your own event loop to drive a user interface.
+use crate::application;
use crate::event::{self, Event};
use crate::layout;
use crate::mouse;
use crate::renderer;
+use crate::widget;
use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
/// A set of interactive graphical elements with a specific [`Layout`].
@@ -13,14 +15,16 @@ use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
/// charge of using this type in your system in any way you want.
///
/// # Example
-/// The [`integration` example] uses a [`UserInterface`] to integrate Iced in
-/// an existing graphical application.
+/// The [`integration_opengl`] & [`integration_wgpu`] examples use a
+/// [`UserInterface`] to integrate Iced in an existing graphical application.
///
-/// [`integration` example]: https://github.com/iced-rs/iced/tree/0.3/examples/integration
+/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.4/examples/integration_opengl
+/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.4/examples/integration_wgpu
#[allow(missing_debug_implementations)]
pub struct UserInterface<'a, Message, Renderer> {
root: Element<'a, Message, Renderer>,
base: layout::Node,
+ state: widget::Tree,
overlay: Option<layout::Node>,
bounds: Size,
}
@@ -28,6 +32,7 @@ pub struct UserInterface<'a, Message, Renderer> {
impl<'a, Message, Renderer> UserInterface<'a, Message, Renderer>
where
Renderer: crate::Renderer,
+ Renderer::Theme: application::StyleSheet,
{
/// Builds a user interface for an [`Element`].
///
@@ -86,17 +91,21 @@ where
pub fn build<E: Into<Element<'a, Message, Renderer>>>(
root: E,
bounds: Size,
- _cache: Cache,
+ cache: Cache,
renderer: &mut Renderer,
) -> Self {
let root = root.into();
+ let Cache { mut state } = cache;
+ state.diff(root.as_widget());
+
let base =
renderer.layout(&root, &layout::Limits::new(Size::ZERO, bounds));
UserInterface {
root,
base,
+ state,
overlay: None,
bounds,
}
@@ -177,40 +186,67 @@ where
clipboard: &mut dyn Clipboard,
messages: &mut Vec<Message>,
) -> (State, Vec<event::Status>) {
- let mut state = State::Updated;
+ use std::mem::ManuallyDrop;
- let (base_cursor, overlay_statuses) = if let Some(mut overlay) =
- self.root.overlay(Layout::new(&self.base), renderer)
- {
+ let mut state = State::Updated;
+ let mut manual_overlay =
+ ManuallyDrop::new(self.root.as_widget().overlay(
+ &mut self.state,
+ Layout::new(&self.base),
+ renderer,
+ ));
+
+ let (base_cursor, overlay_statuses) = if manual_overlay.is_some() {
let bounds = self.bounds;
+
+ let mut overlay = manual_overlay.as_mut().unwrap();
let mut layout = overlay.layout(renderer, bounds);
+ let mut event_statuses = Vec::new();
+
+ for event in events.iter().cloned() {
+ let mut shell = Shell::new(messages);
+
+ let event_status = overlay.on_event(
+ event,
+ Layout::new(&layout),
+ cursor_position,
+ renderer,
+ clipboard,
+ &mut shell,
+ );
+
+ event_statuses.push(event_status);
- let event_statuses = events
- .iter()
- .cloned()
- .map(|event| {
- let mut shell = Shell::new(messages);
-
- let event_status = overlay.on_event(
- event,
- Layout::new(&layout),
- cursor_position,
- renderer,
- clipboard,
- &mut shell,
+ if shell.is_layout_invalid() {
+ let _ = ManuallyDrop::into_inner(manual_overlay);
+
+ self.base = renderer.layout(
+ &self.root,
+ &layout::Limits::new(Size::ZERO, self.bounds),
);
+ manual_overlay =
+ ManuallyDrop::new(self.root.as_widget().overlay(
+ &mut self.state,
+ Layout::new(&self.base),
+ renderer,
+ ));
+
+ if manual_overlay.is_none() {
+ break;
+ }
+
+ overlay = manual_overlay.as_mut().unwrap();
+
shell.revalidate_layout(|| {
layout = overlay.layout(renderer, bounds);
});
+ }
- if shell.are_widgets_invalid() {
- state = State::Outdated;
- }
-
- event_status
- })
- .collect();
+ if shell.are_widgets_invalid() {
+ state = State::Outdated;
+ }
+ }
let base_cursor = if layout.bounds().contains(cursor_position) {
// TODO: Type-safe cursor availability
@@ -226,14 +262,21 @@ where
(cursor_position, vec![event::Status::Ignored; events.len()])
};
+ let _ = ManuallyDrop::into_inner(manual_overlay);
+
let event_statuses = events
.iter()
.cloned()
.zip(overlay_statuses.into_iter())
.map(|(event, overlay_status)| {
+ if matches!(overlay_status, event::Status::Captured) {
+ return overlay_status;
+ }
+
let mut shell = Shell::new(messages);
- let event_status = self.root.widget.on_event(
+ let event_status = self.root.as_widget_mut().on_event(
+ &mut self.state,
event,
Layout::new(&self.base),
base_cursor,
@@ -264,19 +307,20 @@ where
/// Draws the [`UserInterface`] with the provided [`Renderer`].
///
- /// It returns the some [`Renderer::Output`]. You should update the icon of
- /// the mouse cursor accordingly in your system.
+ /// It returns the current [`mouse::Interaction`]. You should update the
+ /// icon of the mouse cursor accordingly in your system.
///
/// [`Renderer`]: crate::Renderer
- /// [`Renderer::Output`]: crate::Renderer::Output
///
/// # Example
/// We can finally draw our [counter](index.html#usage) by
/// [completing the last example](#example-1):
///
/// ```no_run
- /// use iced_native::{clipboard, Size, Point};
+ /// use iced_native::clipboard;
+ /// use iced_native::renderer;
/// use iced_native::user_interface::{self, UserInterface};
+ /// use iced_native::{Size, Point, Theme};
/// use iced_wgpu::Renderer;
///
/// # mod iced_wgpu {
@@ -323,7 +367,7 @@ where
/// );
///
/// // Draw the user interface
- /// let mouse_cursor = user_interface.draw(&mut renderer, cursor_position);
+ /// let mouse_cursor = user_interface.draw(&mut renderer, &Theme::default(), &renderer::Style::default(), cursor_position);
///
/// cache = user_interface.into_cache();
///
@@ -338,6 +382,8 @@ where
pub fn draw(
&mut self,
renderer: &mut Renderer,
+ theme: &Renderer::Theme,
+ style: &renderer::Style,
cursor_position: Point,
) -> mouse::Interaction {
// TODO: Move to shell level (?)
@@ -345,9 +391,11 @@ where
let viewport = Rectangle::with_size(self.bounds);
- let base_cursor = if let Some(overlay) =
- self.root.overlay(Layout::new(&self.base), renderer)
- {
+ let base_cursor = if let Some(overlay) = self.root.as_widget().overlay(
+ &mut self.state,
+ Layout::new(&self.base),
+ renderer,
+ ) {
let overlay_layout = self
.overlay
.take()
@@ -367,15 +415,18 @@ where
cursor_position
};
- self.root.widget.draw(
+ self.root.as_widget().draw(
+ &self.state,
renderer,
- &renderer::Style::default(),
+ theme,
+ style,
Layout::new(&self.base),
base_cursor,
&viewport,
);
- let base_interaction = self.root.widget.mouse_interaction(
+ let base_interaction = self.root.as_widget().mouse_interaction(
+ &self.state,
Layout::new(&self.base),
cursor_position,
&viewport,
@@ -397,51 +448,79 @@ where
overlay
.as_ref()
.and_then(|layout| {
- root.overlay(Layout::new(&base), renderer).map(|overlay| {
- let overlay_interaction = overlay.mouse_interaction(
- Layout::new(layout),
- cursor_position,
- &viewport,
- renderer,
- );
-
- let overlay_bounds = layout.bounds();
-
- renderer.with_layer(overlay_bounds, |renderer| {
- overlay.draw(
- renderer,
- &renderer::Style::default(),
+ root.as_widget()
+ .overlay(&mut self.state, Layout::new(base), renderer)
+ .map(|overlay| {
+ let overlay_interaction = overlay.mouse_interaction(
Layout::new(layout),
cursor_position,
+ &viewport,
+ renderer,
);
- });
- if overlay_bounds.contains(cursor_position) {
- overlay_interaction
- } else {
- base_interaction
- }
- })
+ let overlay_bounds = layout.bounds();
+
+ renderer.with_layer(overlay_bounds, |renderer| {
+ overlay.draw(
+ renderer,
+ theme,
+ style,
+ Layout::new(layout),
+ cursor_position,
+ );
+ });
+
+ if overlay_bounds.contains(cursor_position) {
+ overlay_interaction
+ } else {
+ base_interaction
+ }
+ })
})
.unwrap_or(base_interaction)
}
+ /// Applies a [`widget::Operation`] to the [`UserInterface`].
+ pub fn operate(
+ &mut self,
+ renderer: &Renderer,
+ operation: &mut dyn widget::Operation<Message>,
+ ) {
+ self.root.as_widget().operate(
+ &mut self.state,
+ Layout::new(&self.base),
+ operation,
+ );
+
+ if let Some(layout) = self.overlay.as_ref() {
+ if let Some(overlay) = self.root.as_widget().overlay(
+ &mut self.state,
+ Layout::new(&self.base),
+ renderer,
+ ) {
+ overlay.operate(Layout::new(layout), operation);
+ }
+ }
+ }
+
/// Relayouts and returns a new [`UserInterface`] using the provided
/// bounds.
pub fn relayout(self, bounds: Size, renderer: &mut Renderer) -> Self {
- Self::build(self.root, bounds, Cache, renderer)
+ Self::build(self.root, bounds, Cache { state: self.state }, renderer)
}
/// Extract the [`Cache`] of the [`UserInterface`], consuming it in the
/// process.
pub fn into_cache(self) -> Cache {
- Cache
+ Cache { state: self.state }
}
}
/// Reusable data of a specific [`UserInterface`].
-#[derive(Debug, Clone)]
-pub struct Cache;
+#[derive(Debug)]
+pub struct Cache {
+ state: widget::Tree,
+}
impl Cache {
/// Creates an empty [`Cache`].
@@ -449,7 +528,9 @@ impl Cache {
/// You should use this to initialize a [`Cache`] before building your first
/// [`UserInterface`].
pub fn new() -> Cache {
- Cache
+ Cache {
+ state: widget::Tree::empty(),
+ }
}
}