summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector0193@gmail.com>2020-11-14 02:17:21 +0100
committerLibravatar GitHub <noreply@github.com>2020-11-14 02:17:21 +0100
commit62295f554b885b8d486b666bc10dc4ecdc78c7d6 (patch)
tree4758f6f17301b1a6c252a5a32248ae5498d1bb11
parent73811c394a39c3816c67bffd2cf7d7a93c8803a9 (diff)
parentbf2d2561b8dde3e160438428b59c03c38a5f752a (diff)
downloadiced-62295f554b885b8d486b666bc10dc4ecdc78c7d6.tar.gz
iced-62295f554b885b8d486b666bc10dc4ecdc78c7d6.tar.bz2
iced-62295f554b885b8d486b666bc10dc4ecdc78c7d6.zip
Merge pull request #614 from hecrj/feature/event-capturing
Event capturing
-rw-r--r--examples/bezier_tool/src/main.rs75
-rw-r--r--examples/game_of_life/src/main.rs74
-rw-r--r--examples/pane_grid/src/main.rs20
-rw-r--r--glutin/src/application.rs21
-rw-r--r--graphics/src/widget/canvas.rs21
-rw-r--r--graphics/src/widget/canvas/event.rs3
-rw-r--r--graphics/src/widget/canvas/program.rs9
-rw-r--r--native/src/element.rs15
-rw-r--r--native/src/event.rs41
-rw-r--r--native/src/lib.rs2
-rw-r--r--native/src/overlay.rs7
-rw-r--r--native/src/overlay/element.rs12
-rw-r--r--native/src/overlay/menu.rs20
-rw-r--r--native/src/program/state.rs7
-rw-r--r--native/src/runtime.rs12
-rw-r--r--native/src/subscription.rs24
-rw-r--r--native/src/subscription/events.rs15
-rw-r--r--native/src/user_interface.rs82
-rw-r--r--native/src/widget.rs10
-rw-r--r--native/src/widget/button.rs27
-rw-r--r--native/src/widget/checkbox.rs16
-rw-r--r--native/src/widget/column.rs16
-rw-r--r--native/src/widget/container.rs8
-rw-r--r--native/src/widget/pane_grid.rs60
-rw-r--r--native/src/widget/pane_grid/content.rs13
-rw-r--r--native/src/widget/pane_grid/title_bar.rs11
-rw-r--r--native/src/widget/pick_list.rs30
-rw-r--r--native/src/widget/radio.rs16
-rw-r--r--native/src/widget/row.rs16
-rw-r--r--native/src/widget/scrollable.rs100
-rw-r--r--native/src/widget/slider.rs16
-rw-r--r--native/src/widget/text_input.rs312
-rw-r--r--winit/src/application.rs25
33 files changed, 711 insertions, 425 deletions
diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs
index cb5247aa..97832e01 100644
--- a/examples/bezier_tool/src/main.rs
+++ b/examples/bezier_tool/src/main.rs
@@ -69,7 +69,8 @@ impl Sandbox for Example {
mod bezier {
use iced::{
- canvas::{self, Canvas, Cursor, Event, Frame, Geometry, Path, Stroke},
+ canvas::event::{self, Event},
+ canvas::{self, Canvas, Cursor, Frame, Geometry, Path, Stroke},
mouse, Element, Length, Point, Rectangle,
};
@@ -109,41 +110,51 @@ mod bezier {
event: Event,
bounds: Rectangle,
cursor: Cursor,
- ) -> Option<Curve> {
- let cursor_position = cursor.position_in(&bounds)?;
+ ) -> (event::Status, Option<Curve>) {
+ let cursor_position =
+ if let Some(position) = cursor.position_in(&bounds) {
+ position
+ } else {
+ return (event::Status::Ignored, None);
+ };
match event {
- Event::Mouse(mouse_event) => match mouse_event {
- mouse::Event::ButtonPressed(mouse::Button::Left) => {
- match self.state.pending {
- None => {
- self.state.pending = Some(Pending::One {
- from: cursor_position,
- });
- None
- }
- Some(Pending::One { from }) => {
- self.state.pending = Some(Pending::Two {
- from,
- to: cursor_position,
- });
-
- None
- }
- Some(Pending::Two { from, to }) => {
- self.state.pending = None;
-
- Some(Curve {
- from,
- to,
- control: cursor_position,
- })
+ Event::Mouse(mouse_event) => {
+ let message = match mouse_event {
+ mouse::Event::ButtonPressed(mouse::Button::Left) => {
+ match self.state.pending {
+ None => {
+ self.state.pending = Some(Pending::One {
+ from: cursor_position,
+ });
+
+ None
+ }
+ Some(Pending::One { from }) => {
+ self.state.pending = Some(Pending::Two {
+ from,
+ to: cursor_position,
+ });
+
+ None
+ }
+ Some(Pending::Two { from, to }) => {
+ self.state.pending = None;
+
+ Some(Curve {
+ from,
+ to,
+ control: cursor_position,
+ })
+ }
}
}
- }
- _ => None,
- },
- _ => None,
+ _ => None,
+ };
+
+ (event::Status::Captured, message)
+ }
+ _ => (event::Status::Ignored, None),
}
}
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 3f087f88..e18bd6e0 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -153,9 +153,8 @@ impl Application for GameOfLife {
mod grid {
use crate::Preset;
use iced::{
- canvas::{
- self, Cache, Canvas, Cursor, Event, Frame, Geometry, Path, Text,
- },
+ canvas::event::{self, Event},
+ canvas::{self, Cache, Canvas, Cursor, Frame, Geometry, Path, Text},
mouse, Color, Element, HorizontalAlignment, Length, Point, Rectangle,
Size, Vector, VerticalAlignment,
};
@@ -328,12 +327,18 @@ mod grid {
event: Event,
bounds: Rectangle,
cursor: Cursor,
- ) -> Option<Message> {
+ ) -> (event::Status, Option<Message>) {
if let Event::Mouse(mouse::Event::ButtonReleased(_)) = event {
self.interaction = Interaction::None;
}
- let cursor_position = cursor.position_in(&bounds)?;
+ let cursor_position =
+ if let Some(position) = cursor.position_in(&bounds) {
+ position
+ } else {
+ return (event::Status::Ignored, None);
+ };
+
let cell = Cell::at(self.project(cursor_position, bounds.size()));
let is_populated = self.state.contains(&cell);
@@ -345,28 +350,32 @@ mod grid {
match event {
Event::Mouse(mouse_event) => match mouse_event {
- mouse::Event::ButtonPressed(button) => match button {
- mouse::Button::Left => {
- self.interaction = if is_populated {
- Interaction::Erasing
- } else {
- Interaction::Drawing
- };
-
- populate.or(unpopulate)
- }
- mouse::Button::Right => {
- self.interaction = Interaction::Panning {
- translation: self.translation,
- start: cursor_position,
- };
+ mouse::Event::ButtonPressed(button) => {
+ let message = match button {
+ mouse::Button::Left => {
+ self.interaction = if is_populated {
+ Interaction::Erasing
+ } else {
+ Interaction::Drawing
+ };
+
+ populate.or(unpopulate)
+ }
+ mouse::Button::Right => {
+ self.interaction = Interaction::Panning {
+ translation: self.translation,
+ start: cursor_position,
+ };
- None
- }
- _ => None,
- },
+ None
+ }
+ _ => None,
+ };
+
+ (event::Status::Captured, message)
+ }
mouse::Event::CursorMoved { .. } => {
- match self.interaction {
+ let message = match self.interaction {
Interaction::Drawing => populate,
Interaction::Erasing => unpopulate,
Interaction::Panning { translation, start } => {
@@ -380,7 +389,14 @@ mod grid {
None
}
_ => None,
- }
+ };
+
+ let event_status = match self.interaction {
+ Interaction::None => event::Status::Ignored,
+ _ => event::Status::Captured,
+ };
+
+ (event_status, message)
}
mouse::Event::WheelScrolled { delta } => match delta {
mouse::ScrollDelta::Lines { y, .. }
@@ -413,12 +429,12 @@ mod grid {
self.grid_cache.clear();
}
- None
+ (event::Status::Captured, None)
}
},
- _ => None,
+ _ => (event::Status::Ignored, None),
},
- _ => None,
+ _ => (event::Status::Ignored, None),
}
}
diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs
index e786eadd..f986cc8d 100644
--- a/examples/pane_grid/src/main.rs
+++ b/examples/pane_grid/src/main.rs
@@ -3,7 +3,7 @@ use iced::{
Button, Column, Command, Container, Element, HorizontalAlignment, Length,
PaneGrid, Scrollable, Settings, Subscription, Text,
};
-use iced_native::{subscription, Event};
+use iced_native::{event, subscription, Event};
pub fn main() -> iced::Result {
Example::run(Settings::default())
@@ -119,12 +119,18 @@ impl Application for Example {
}
fn subscription(&self) -> Subscription<Message> {
- subscription::events_with(|event| match event {
- Event::Keyboard(keyboard::Event::KeyPressed {
- modifiers,
- key_code,
- }) if modifiers.is_command_pressed() => handle_hotkey(key_code),
- _ => None,
+ subscription::events_with(|event, status| {
+ if let event::Status::Captured = status {
+ return None;
+ }
+
+ match event {
+ Event::Keyboard(keyboard::Event::KeyPressed {
+ modifiers,
+ key_code,
+ }) if modifiers.is_command_pressed() => handle_hotkey(key_code),
+ _ => None,
+ }
})
}
diff --git a/glutin/src/application.rs b/glutin/src/application.rs
index 68612978..e593f0ae 100644
--- a/glutin/src/application.rs
+++ b/glutin/src/application.rs
@@ -156,29 +156,33 @@ async fn run_instance<A, E, C>(
let mut mouse_interaction = mouse::Interaction::default();
let mut events = Vec::new();
- let mut external_messages = Vec::new();
+ let mut messages = Vec::new();
debug.startup_finished();
while let Some(event) = receiver.next().await {
match event {
event::Event::MainEventsCleared => {
- if events.is_empty() && external_messages.is_empty() {
+ if events.is_empty() && messages.is_empty() {
continue;
}
debug.event_processing_started();
- let mut messages = user_interface.update(
+
+ let statuses = user_interface.update(
&events,
state.cursor_position(),
clipboard.as_ref().map(|c| c as _),
&mut renderer,
+ &mut messages,
);
- messages.extend(external_messages.drain(..));
- events.clear();
debug.event_processing_finished();
+ for event in events.drain(..).zip(statuses.into_iter()) {
+ runtime.broadcast(event);
+ }
+
if !messages.is_empty() {
let cache =
ManuallyDrop::into_inner(user_interface).into_cache();
@@ -188,7 +192,7 @@ async fn run_instance<A, E, C>(
&mut application,
&mut runtime,
&mut debug,
- messages,
+ &mut messages,
);
// Update window
@@ -212,7 +216,7 @@ async fn run_instance<A, E, C>(
context.window().request_redraw();
}
event::Event::UserEvent(message) => {
- external_messages.push(message);
+ messages.push(message);
}
event::Event::RedrawRequested(_) => {
debug.render_started();
@@ -283,8 +287,7 @@ async fn run_instance<A, E, C>(
state.scale_factor(),
state.modifiers(),
) {
- events.push(event.clone());
- runtime.broadcast(event);
+ events.push(event);
}
}
_ => {}
diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs
index 73778d16..ae0d87a4 100644
--- a/graphics/src/widget/canvas.rs
+++ b/graphics/src/widget/canvas.rs
@@ -7,18 +7,20 @@
//! [`Canvas`]: struct.Canvas.html
//! [`Frame`]: struct.Frame.html
use crate::{Backend, Defaults, Primitive, Renderer};
+use iced_native::layout;
+use iced_native::mouse;
use iced_native::{
- layout, mouse, Clipboard, Element, Hasher, Layout, Length, Point,
- Rectangle, Size, Vector, Widget,
+ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Vector,
+ Widget,
};
use std::hash::Hash;
use std::marker::PhantomData;
+pub mod event;
pub mod path;
mod cache;
mod cursor;
-mod event;
mod fill;
mod frame;
mod geometry;
@@ -166,7 +168,7 @@ where
messages: &mut Vec<Message>,
_renderer: &Renderer<B>,
_clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
let bounds = layout.bounds();
let canvas_event = match event {
@@ -182,12 +184,17 @@ where
let cursor = Cursor::from_window_position(cursor_position);
if let Some(canvas_event) = canvas_event {
- if let Some(message) =
- self.program.update(canvas_event, bounds, cursor)
- {
+ let (event_status, message) =
+ self.program.update(canvas_event, bounds, cursor);
+
+ if let Some(message) = message {
messages.push(message);
}
+
+ return event_status;
}
+
+ event::Status::Ignored
}
fn draw(
diff --git a/graphics/src/widget/canvas/event.rs b/graphics/src/widget/canvas/event.rs
index 0e66f0ff..ede2fd73 100644
--- a/graphics/src/widget/canvas/event.rs
+++ b/graphics/src/widget/canvas/event.rs
@@ -1,6 +1,9 @@
+//! Handle events of a canvas.
use iced_native::keyboard;
use iced_native::mouse;
+pub use iced_native::event::Status;
+
/// A [`Canvas`] event.
///
/// [`Canvas`]: struct.Event.html
diff --git a/graphics/src/widget/canvas/program.rs b/graphics/src/widget/canvas/program.rs
index 725d9d72..e8f43380 100644
--- a/graphics/src/widget/canvas/program.rs
+++ b/graphics/src/widget/canvas/program.rs
@@ -1,4 +1,5 @@
-use crate::canvas::{Cursor, Event, Geometry};
+use crate::canvas::event::{self, Event};
+use crate::canvas::{Cursor, Geometry};
use iced_native::{mouse, Rectangle};
/// The state and logic of a [`Canvas`].
@@ -27,8 +28,8 @@ pub trait Program<Message> {
_event: Event,
_bounds: Rectangle,
_cursor: Cursor,
- ) -> Option<Message> {
- None
+ ) -> (event::Status, Option<Message>) {
+ (event::Status::Ignored, None)
}
/// Draws the state of the [`Program`], producing a bunch of [`Geometry`].
@@ -67,7 +68,7 @@ where
event: Event,
bounds: Rectangle,
cursor: Cursor,
- ) -> Option<Message> {
+ ) -> (event::Status, Option<Message>) {
T::update(self, event, bounds, cursor)
}
diff --git a/native/src/element.rs b/native/src/element.rs
index 10e1b5fb..9703a7db 100644
--- a/native/src/element.rs
+++ b/native/src/element.rs
@@ -1,7 +1,8 @@
+use crate::event::{self, Event};
use crate::layout;
use crate::overlay;
use crate::{
- Clipboard, Color, Event, Hasher, Layout, Length, Point, Rectangle, Widget,
+ Clipboard, Color, Hasher, Layout, Length, Point, Rectangle, Widget,
};
/// A generic [`Widget`].
@@ -240,7 +241,7 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
self.widget.on_event(
event,
layout,
@@ -248,7 +249,7 @@ where
messages,
renderer,
clipboard,
- );
+ )
}
/// Draws the [`Element`] and its children using the given [`Layout`].
@@ -335,10 +336,10 @@ where
messages: &mut Vec<B>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
let mut original_messages = Vec::new();
- self.widget.on_event(
+ let status = self.widget.on_event(
event,
layout,
cursor_position,
@@ -350,6 +351,8 @@ where
original_messages
.drain(..)
.for_each(|message| messages.push((self.mapper)(message)));
+
+ status
}
fn draw(
@@ -423,7 +426,7 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
self.element.widget.on_event(
event,
layout,
diff --git a/native/src/event.rs b/native/src/event.rs
index 606a71d6..9c079151 100644
--- a/native/src/event.rs
+++ b/native/src/event.rs
@@ -1,3 +1,4 @@
+//! Handle events of a user interface.
use crate::{keyboard, mouse, window};
/// A user interface event.
@@ -6,7 +7,7 @@ use crate::{keyboard, mouse, window};
/// additional events, feel free to [open an issue] and share your use case!_
///
/// [open an issue]: https://github.com/hecrj/iced/issues
-#[derive(PartialEq, Clone, Debug)]
+#[derive(Debug, Clone, PartialEq)]
pub enum Event {
/// A keyboard event
Keyboard(keyboard::Event),
@@ -17,3 +18,41 @@ pub enum Event {
/// A window event
Window(window::Event),
}
+
+/// The status of an [`Event`] after being processed.
+///
+/// [`Event`]: enum.Event.html
+/// [`UserInterface`]: ../struct.UserInterface.html
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Status {
+ /// The [`Event`] was **NOT** handled by any widget.
+ ///
+ /// [`Event`]: enum.Event.html
+ Ignored,
+
+ /// The [`Event`] was handled and processed by a widget.
+ ///
+ /// [`Event`]: enum.Event.html
+ Captured,
+}
+
+impl Status {
+ /// Merges two [`Status`] into one.
+ ///
+ /// `Captured` takes precedence over `Ignored`:
+ ///
+ /// ```
+ /// use iced_native::event::Status;
+ ///
+ /// assert_eq!(Status::Ignored.merge(Status::Ignored), Status::Ignored);
+ /// assert_eq!(Status::Ignored.merge(Status::Captured), Status::Captured);
+ /// assert_eq!(Status::Captured.merge(Status::Ignored), Status::Captured);
+ /// assert_eq!(Status::Captured.merge(Status::Captured), Status::Captured);
+ /// ```
+ pub fn merge(self, b: Self) -> Self {
+ match self {
+ Status::Ignored => b,
+ Status::Captured => Status::Captured,
+ }
+ }
+}
diff --git a/native/src/lib.rs b/native/src/lib.rs
index 067e3c0a..d1252eaf 100644
--- a/native/src/lib.rs
+++ b/native/src/lib.rs
@@ -35,6 +35,7 @@
#![deny(unused_results)]
#![forbid(unsafe_code)]
#![forbid(rust_2018_idioms)]
+pub mod event;
pub mod keyboard;
pub mod layout;
pub mod mouse;
@@ -47,7 +48,6 @@ pub mod window;
mod clipboard;
mod element;
-mod event;
mod hasher;
mod runtime;
mod user_interface;
diff --git a/native/src/overlay.rs b/native/src/overlay.rs
index 7c3bec32..56d055d3 100644
--- a/native/src/overlay.rs
+++ b/native/src/overlay.rs
@@ -6,7 +6,9 @@ pub mod menu;
pub use element::Element;
pub use menu::Menu;
-use crate::{layout, Clipboard, Event, Hasher, Layout, Point, Size};
+use crate::event::{self, Event};
+use crate::layout;
+use crate::{Clipboard, Hasher, Layout, Point, Size};
/// An interactive component that can be displayed on top of other widgets.
pub trait Overlay<Message, Renderer>
@@ -79,6 +81,7 @@ where
_messages: &mut Vec<Message>,
_renderer: &Renderer,
_clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
+ event::Status::Ignored
}
}
diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs
index e1fd9b88..3f346695 100644
--- a/native/src/overlay/element.rs
+++ b/native/src/overlay/element.rs
@@ -1,6 +1,8 @@
pub use crate::Overlay;
-use crate::{layout, Clipboard, Event, Hasher, Layout, Point, Size, Vector};
+use crate::event::{self, Event};
+use crate::layout;
+use crate::{Clipboard, Hasher, Layout, Point, Size, Vector};
/// A generic [`Overlay`].
///
@@ -67,7 +69,7 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
self.overlay.on_event(
event,
layout,
@@ -136,10 +138,10 @@ where
messages: &mut Vec<B>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
let mut original_messages = Vec::new();
- self.content.on_event(
+ let event_status = self.content.on_event(
event,
layout,
cursor_position,
@@ -151,6 +153,8 @@ where
original_messages
.drain(..)
.for_each(|message| messages.push((self.mapper)(message)));
+
+ event_status
}
fn draw(
diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs
index 4b392a8e..d99b5940 100644
--- a/native/src/overlay/menu.rs
+++ b/native/src/overlay/menu.rs
@@ -1,8 +1,14 @@
//! Build and show dropdown menus.
+use crate::container;
+use crate::event::{self, Event};
+use crate::layout;
+use crate::mouse;
+use crate::overlay;
+use crate::scrollable;
+use crate::text;
use crate::{
- container, layout, mouse, overlay, scrollable, text, Clipboard, Container,
- Element, Event, Hasher, Layout, Length, Point, Rectangle, Scrollable, Size,
- Vector, Widget,
+ Clipboard, Container, Element, Hasher, Layout, Length, Point, Rectangle,
+ Scrollable, Size, Vector, Widget,
};
/// A list of selectable options.
@@ -235,7 +241,7 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
self.container.on_event(
event.clone(),
layout,
@@ -243,7 +249,7 @@ where
messages,
renderer,
clipboard,
- );
+ )
}
fn draw(
@@ -336,7 +342,7 @@ where
_messages: &mut Vec<Message>,
renderer: &Renderer,
_clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
let bounds = layout.bounds();
@@ -364,6 +370,8 @@ where
}
_ => {}
}
+
+ event::Status::Ignored
}
fn draw(
diff --git a/native/src/program/state.rs b/native/src/program/state.rs
index 95e6b60c..76283e30 100644
--- a/native/src/program/state.rs
+++ b/native/src/program/state.rs
@@ -120,14 +120,17 @@ where
);
debug.event_processing_started();
- let mut messages = user_interface.update(
+ let mut messages = Vec::new();
+
+ let _ = user_interface.update(
&self.queued_events,
cursor_position,
clipboard,
renderer,
+ &mut messages,
);
- messages.extend(self.queued_messages.drain(..));
+ messages.extend(self.queued_messages.drain(..));
self.queued_events.clear();
debug.event_processing_finished();
diff --git a/native/src/runtime.rs b/native/src/runtime.rs
index 9fa031f4..bd814a0b 100644
--- a/native/src/runtime.rs
+++ b/native/src/runtime.rs
@@ -1,5 +1,6 @@
//! Run commands and subscriptions.
-use crate::{Event, Hasher};
+use crate::event::{self, Event};
+use crate::Hasher;
/// A native runtime with a generic executor and receiver of results.
///
@@ -8,5 +9,10 @@ use crate::{Event, Hasher};
///
/// [`Command`]: ../struct.Command.html
/// [`Subscription`]: ../struct.Subscription.html
-pub type Runtime<Executor, Receiver, Message> =
- iced_futures::Runtime<Hasher, Event, Executor, Receiver, Message>;
+pub type Runtime<Executor, Receiver, Message> = iced_futures::Runtime<
+ Hasher,
+ (Event, event::Status),
+ Executor,
+ Receiver,
+ Message,
+>;
diff --git a/native/src/subscription.rs b/native/src/subscription.rs
index 18750abf..3cc04188 100644
--- a/native/src/subscription.rs
+++ b/native/src/subscription.rs
@@ -1,5 +1,6 @@
//! Listen to external events in your application.
-use crate::{Event, Hasher};
+use crate::event::{self, Event};
+use crate::Hasher;
use iced_futures::futures::stream::BoxStream;
/// A request to listen to external events.
@@ -15,19 +16,21 @@ use iced_futures::futures::stream::BoxStream;
///
/// [`Command`]: ../struct.Command.html
/// [`Subscription`]: struct.Subscription.html
-pub type Subscription<T> = iced_futures::Subscription<Hasher, Event, T>;
+pub type Subscription<T> =
+ iced_futures::Subscription<Hasher, (Event, event::Status), T>;
/// A stream of runtime events.
///
/// It is the input of a [`Subscription`] in the native runtime.
///
/// [`Subscription`]: type.Subscription.html
-pub type EventStream = BoxStream<'static, Event>;
+pub type EventStream = BoxStream<'static, (Event, event::Status)>;
/// A native [`Subscription`] tracker.
///
/// [`Subscription`]: type.Subscription.html
-pub type Tracker = iced_futures::subscription::Tracker<Hasher, Event>;
+pub type Tracker =
+ iced_futures::subscription::Tracker<Hasher, (Event, event::Status)>;
pub use iced_futures::subscription::Recipe;
@@ -37,13 +40,18 @@ use events::Events;
/// Returns a [`Subscription`] to all the runtime events.
///
-/// This subscription will notify your application of any [`Event`] handled by
-/// the runtime.
+/// This subscription will notify your application of any [`Event`] that was
+/// not captured by any widget.
///
/// [`Subscription`]: type.Subscription.html
/// [`Event`]: ../enum.Event.html
pub fn events() -> Subscription<Event> {
- Subscription::from_recipe(Events { f: Some })
+ Subscription::from_recipe(Events {
+ f: |event, status| match status {
+ event::Status::Ignored => Some(event),
+ event::Status::Captured => None,
+ },
+ })
}
/// Returns a [`Subscription`] that filters all the runtime events with the
@@ -58,7 +66,7 @@ pub fn events() -> Subscription<Event> {
/// [`Subscription`]: type.Subscription.html
/// [`Event`]: ../enum.Event.html
pub fn events_with<Message>(
- f: fn(Event) -> Option<Message>,
+ f: fn(Event, event::Status) -> Option<Message>,
) -> Subscription<Message>
where
Message: 'static + Send,
diff --git a/native/src/subscription/events.rs b/native/src/subscription/events.rs
index a1ae6051..f689f3af 100644
--- a/native/src/subscription/events.rs
+++ b/native/src/subscription/events.rs
@@ -1,16 +1,15 @@
-use crate::{
- subscription::{EventStream, Recipe},
- Event, Hasher,
-};
+use crate::event::{self, Event};
+use crate::subscription::{EventStream, Recipe};
+use crate::Hasher;
use iced_futures::futures::future;
use iced_futures::futures::StreamExt;
use iced_futures::BoxStream;
pub struct Events<Message> {
- pub(super) f: fn(Event) -> Option<Message>,
+ pub(super) f: fn(Event, event::Status) -> Option<Message>,
}
-impl<Message> Recipe<Hasher, Event> for Events<Message>
+impl<Message> Recipe<Hasher, (Event, event::Status)> for Events<Message>
where
Message: 'static + Send,
{
@@ -29,7 +28,9 @@ where
event_stream: EventStream,
) -> BoxStream<Self::Output> {
event_stream
- .filter_map(move |event| future::ready((self.f)(event)))
+ .filter_map(move |(event, status)| {
+ future::ready((self.f)(event, status))
+ })
.boxed()
}
}
diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs
index 504dbe0f..31bb6b99 100644
--- a/native/src/user_interface.rs
+++ b/native/src/user_interface.rs
@@ -1,6 +1,7 @@
+use crate::event::{self, Event};
use crate::layout;
use crate::overlay;
-use crate::{Clipboard, Element, Event, Layout, Point, Rectangle, Size};
+use crate::{Clipboard, Element, Layout, Point, Rectangle, Size};
use std::hash::Hasher;
@@ -169,9 +170,10 @@ where
///
/// // Initialize our event storage
/// let mut events = Vec::new();
+ /// let mut messages = Vec::new();
///
/// loop {
- /// // Process system events...
+ /// // Obtain system events...
///
/// let mut user_interface = UserInterface::build(
/// counter.view(),
@@ -181,17 +183,18 @@ where
/// );
///
/// // Update the user interface
- /// let messages = user_interface.update(
+ /// let event_statuses = user_interface.update(
/// &events,
/// cursor_position,
/// None,
/// &renderer,
+ /// &mut messages
/// );
///
/// cache = user_interface.into_cache();
///
/// // Process the produced messages
- /// for message in messages {
+ /// for message in messages.drain(..) {
/// counter.update(message);
/// }
/// }
@@ -202,10 +205,9 @@ where
cursor_position: Point,
clipboard: Option<&dyn Clipboard>,
renderer: &Renderer,
- ) -> Vec<Message> {
- let mut messages = Vec::new();
-
- let base_cursor = if let Some(mut overlay) =
+ messages: &mut Vec<Message>,
+ ) -> Vec<event::Status> {
+ let (base_cursor, overlay_statuses) = if let Some(mut overlay) =
self.root.overlay(Layout::new(&self.base.layout))
{
let layer = Self::overlay_layer(
@@ -215,16 +217,20 @@ where
renderer,
);
- for event in events {
- overlay.on_event(
- event.clone(),
- Layout::new(&layer.layout),
- cursor_position,
- &mut messages,
- renderer,
- clipboard,
- );
- }
+ let event_statuses = events
+ .iter()
+ .cloned()
+ .map(|event| {
+ overlay.on_event(
+ event,
+ Layout::new(&layer.layout),
+ cursor_position,
+ messages,
+ renderer,
+ clipboard,
+ )
+ })
+ .collect();
let base_cursor = if layer.layout.bounds().contains(cursor_position)
{
@@ -236,23 +242,28 @@ where
self.overlay = Some(layer);
- base_cursor
+ (base_cursor, event_statuses)
} else {
- cursor_position
+ (cursor_position, vec![event::Status::Ignored; events.len()])
};
- for event in events {
- self.root.widget.on_event(
- event.clone(),
- Layout::new(&self.base.layout),
- base_cursor,
- &mut messages,
- renderer,
- clipboard,
- );
- }
+ events
+ .iter()
+ .cloned()
+ .zip(overlay_statuses.into_iter())
+ .map(|(event, overlay_status)| {
+ let event_status = self.root.widget.on_event(
+ event,
+ Layout::new(&self.base.layout),
+ base_cursor,
+ messages,
+ renderer,
+ clipboard,
+ );
- messages
+ event_status.merge(overlay_status)
+ })
+ .collect()
}
/// Draws the [`UserInterface`] with the provided [`Renderer`].
@@ -293,9 +304,10 @@ where
/// let mut window_size = Size::new(1024.0, 768.0);
/// let mut cursor_position = Point::default();
/// let mut events = Vec::new();
+ /// let mut messages = Vec::new();
///
/// loop {
- /// // Process system events...
+ /// // Obtain system events...
///
/// let mut user_interface = UserInterface::build(
/// counter.view(),
@@ -304,11 +316,13 @@ where
/// &mut renderer,
/// );
///
- /// let messages = user_interface.update(
+ /// // Update the user interface
+ /// let event_statuses = user_interface.update(
/// &events,
/// cursor_position,
/// None,
/// &renderer,
+ /// &mut messages
/// );
///
/// // Draw the user interface
@@ -316,7 +330,7 @@ where
///
/// cache = user_interface.into_cache();
///
- /// for message in messages {
+ /// for message in messages.drain(..) {
/// counter.update(message);
/// }
///
diff --git a/native/src/widget.rs b/native/src/widget.rs
index 8687ce6f..d3ffe9c2 100644
--- a/native/src/widget.rs
+++ b/native/src/widget.rs
@@ -73,9 +73,10 @@ pub use text::Text;
#[doc(no_inline)]
pub use text_input::TextInput;
-use crate::{
- layout, overlay, Clipboard, Event, Hasher, Layout, Length, Point, Rectangle,
-};
+use crate::event::{self, Event};
+use crate::layout;
+use crate::overlay;
+use crate::{Clipboard, Hasher, Layout, Length, Point, Rectangle};
/// A component that displays information and allows interaction.
///
@@ -182,7 +183,8 @@ where
_messages: &mut Vec<Message>,
_renderer: &Renderer,
_clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
+ event::Status::Ignored
}
/// Returns the overlay of the [`Element`], if there is any.
diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs
index 995ba7bc..466f6ac5 100644
--- a/native/src/widget/button.rs
+++ b/native/src/widget/button.rs
@@ -4,9 +4,11 @@
//!
//! [`Button`]: struct.Button.html
//! [`State`]: struct.State.html
+use crate::event::{self, Event};
+use crate::layout;
+use crate::mouse;
use crate::{
- layout, mouse, Clipboard, Element, Event, Hasher, Layout, Length, Point,
- Rectangle, Widget,
+ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Widget,
};
use std::hash::Hash;
@@ -184,31 +186,38 @@ where
messages: &mut Vec<Message>,
_renderer: &Renderer,
_clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
if self.on_press.is_some() {
let bounds = layout.bounds();
- self.state.is_pressed = bounds.contains(cursor_position);
+ if bounds.contains(cursor_position) {
+ self.state.is_pressed = true;
+
+ return event::Status::Captured;
+ }
}
}
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => {
if let Some(on_press) = self.on_press.clone() {
let bounds = layout.bounds();
- let is_clicked = self.state.is_pressed
- && bounds.contains(cursor_position);
+ if self.state.is_pressed {
+ self.state.is_pressed = false;
- self.state.is_pressed = false;
+ if bounds.contains(cursor_position) {
+ messages.push(on_press);
+ }
- if is_clicked {
- messages.push(on_press);
+ return event::Status::Captured;
}
}
}
_ => {}
}
+
+ event::Status::Ignored
}
fn draw(
diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs
index e389427e..42e52aef 100644
--- a/native/src/widget/checkbox.rs
+++ b/native/src/widget/checkbox.rs
@@ -1,10 +1,14 @@
//! Show toggle controls using checkboxes.
use std::hash::Hash;
+use crate::event::{self, Event};
+use crate::layout;
+use crate::mouse;
+use crate::row;
+use crate::text;
use crate::{
- layout, mouse, row, text, Align, Clipboard, Element, Event, Hasher,
- HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text,
- VerticalAlignment, Widget,
+ Align, Clipboard, Element, Hasher, HorizontalAlignment, Layout, Length,
+ Point, Rectangle, Row, Text, VerticalAlignment, Widget,
};
/// A box that can be checked.
@@ -161,17 +165,21 @@ where
messages: &mut Vec<Message>,
_renderer: &Renderer,
_clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
let mouse_over = layout.bounds().contains(cursor_position);
if mouse_over {
messages.push((self.on_toggle)(!self.is_checked));
+
+ return event::Status::Captured;
}
}
_ => {}
}
+
+ event::Status::Ignored
}
fn draw(
diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs
index e874ad42..42a9e734 100644
--- a/native/src/widget/column.rs
+++ b/native/src/widget/column.rs
@@ -1,11 +1,11 @@
//! Distribute content vertically.
use std::hash::Hash;
+use crate::event::{self, Event};
use crate::layout;
use crate::overlay;
use crate::{
- Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle,
- Widget,
+ Align, Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Widget,
};
use std::u32;
@@ -162,9 +162,11 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
- self.children.iter_mut().zip(layout.children()).for_each(
- |(child, layout)| {
+ ) -> event::Status {
+ self.children
+ .iter_mut()
+ .zip(layout.children())
+ .map(|(child, layout)| {
child.widget.on_event(
event.clone(),
layout,
@@ -173,8 +175,8 @@ where
renderer,
clipboard,
)
- },
- );
+ })
+ .fold(event::Status::Ignored, event::Status::merge)
}
fn draw(
diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs
index 5b04d699..419060db 100644
--- a/native/src/widget/container.rs
+++ b/native/src/widget/container.rs
@@ -1,9 +1,11 @@
//! Decorate content and apply alignment.
use std::hash::Hash;
+use crate::event::{self, Event};
+use crate::layout;
+use crate::overlay;
use crate::{
- layout, overlay, Align, Clipboard, Element, Event, Hasher, Layout, Length,
- Point, Rectangle, Widget,
+ Align, Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Widget,
};
use std::u32;
@@ -174,7 +176,7 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
self.content.widget.on_event(
event,
layout.children().next().unwrap(),
diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs
index 9d36bae6..43e752cb 100644
--- a/native/src/widget/pane_grid.rs
+++ b/native/src/widget/pane_grid.rs
@@ -28,9 +28,16 @@ pub use split::Split;
pub use state::{Focus, State};
pub use title_bar::TitleBar;
+use crate::container;
+use crate::event::{self, Event};
+use crate::layout;
+use crate::mouse;
+use crate::overlay;
+use crate::row;
+use crate::text;
use crate::{
- container, layout, mouse, overlay, row, text, Clipboard, Element, Event,
- Hasher, Layout, Length, Point, Rectangle, Size, Vector, Widget,
+ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Vector,
+ Widget,
};
/// A collection of panes distributed using either vertical or horizontal splits
@@ -242,7 +249,7 @@ where
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
- ) {
+ ) -> event::Status {
if let Some((_, on_resize)) = &self.on_resize {
if let Some((split, _)) = self.state.picked_split() {
let bounds = layout.bounds();
@@ -269,9 +276,13 @@ where
};
messages.push(on_resize(ResizeEvent { split, ratio }));
+
+ return event::Status::Captured;
}
}
}
+
+ event::Status::Ignored
}
}
@@ -386,13 +397,17 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
+ let mut event_status = event::Status::Ignored;
+
match event {
Event::Mouse(mouse_event) => match mouse_event {
mouse::Event::ButtonPressed(mouse::Button::Left) => {
let bounds = layout.bounds();
if bounds.contains(cursor_position) {
+ event_status = event::Status::Captured;
+
match self.on_resize {
Some((leeway, _)) => {
let relative_cursor = Point::new(
@@ -456,12 +471,17 @@ where
}
self.state.idle();
+
+ event_status = event::Status::Captured;
} else if self.state.picked_split().is_some() {
self.state.idle();
+
+ event_status = event::Status::Captured;
}
}
mouse::Event::CursorMoved { .. } => {
- self.trigger_resize(layout, cursor_position, messages);
+ event_status =
+ self.trigger_resize(layout, cursor_position, messages);
}
_ => {}
},
@@ -469,20 +489,22 @@ where
}
if self.state.picked_pane().is_none() {
- {
- self.elements.iter_mut().zip(layout.children()).for_each(
- |((_, pane), layout)| {
- pane.on_event(
- event.clone(),
- layout,
- cursor_position,
- messages,
- renderer,
- clipboard,
- )
- },
- );
- }
+ self.elements
+ .iter_mut()
+ .zip(layout.children())
+ .map(|((_, pane), layout)| {
+ pane.on_event(
+ event.clone(),
+ layout,
+ cursor_position,
+ messages,
+ renderer,
+ clipboard,
+ )
+ })
+ .fold(event_status, event::Status::merge)
+ } else {
+ event::Status::Captured
}
}
diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs
index 5bfbb9ed..2dac7060 100644
--- a/native/src/widget/pane_grid/content.rs
+++ b/native/src/widget/pane_grid/content.rs
@@ -1,8 +1,9 @@
use crate::container;
+use crate::event::{self, Event};
use crate::layout;
use crate::overlay;
use crate::pane_grid::{self, TitleBar};
-use crate::{Clipboard, Element, Event, Hasher, Layout, Point, Size};
+use crate::{Clipboard, Element, Hasher, Layout, Point, Size};
/// The content of a [`Pane`].
///
@@ -154,11 +155,13 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
+ let mut event_status = event::Status::Ignored;
+
let body_layout = if let Some(title_bar) = &mut self.title_bar {
let mut children = layout.children();
- title_bar.on_event(
+ event_status = title_bar.on_event(
event.clone(),
children.next().unwrap(),
cursor_position,
@@ -172,7 +175,7 @@ where
layout
};
- self.body.on_event(
+ let body_status = self.body.on_event(
event,
body_layout,
cursor_position,
@@ -180,6 +183,8 @@ where
renderer,
clipboard,
);
+
+ event_status.merge(body_status)
}
pub(crate) fn hash_layout(&self, state: &mut Hasher) {
diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs
index 9dfb9ae4..f8ff43eb 100644
--- a/native/src/widget/pane_grid/title_bar.rs
+++ b/native/src/widget/pane_grid/title_bar.rs
@@ -1,8 +1,7 @@
+use crate::event::{self, Event};
use crate::layout;
use crate::pane_grid;
-use crate::{
- Clipboard, Element, Event, Hasher, Layout, Point, Rectangle, Size,
-};
+use crate::{Clipboard, Element, Hasher, Layout, Point, Rectangle, Size};
/// The title bar of a [`Pane`].
///
@@ -245,7 +244,7 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
if let Some(controls) = &mut self.controls {
let mut children = layout.children();
let padded = children.next().unwrap();
@@ -261,7 +260,9 @@ where
messages,
renderer,
clipboard,
- );
+ )
+ } else {
+ event::Status::Ignored
}
}
}
diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs
index e086e367..113197f7 100644
--- a/native/src/widget/pick_list.rs
+++ b/native/src/widget/pick_list.rs
@@ -1,9 +1,13 @@
//! Display a dropdown list of selectable values.
+use crate::event::{self, Event};
+use crate::layout;
+use crate::mouse;
+use crate::overlay;
+use crate::overlay::menu::{self, Menu};
+use crate::scrollable;
+use crate::text;
use crate::{
- layout, mouse, overlay,
- overlay::menu::{self, Menu},
- scrollable, text, Clipboard, Element, Event, Hasher, Layout, Length, Point,
- Rectangle, Size, Widget,
+ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget,
};
use std::borrow::Cow;
@@ -223,13 +227,15 @@ where
messages: &mut Vec<Message>,
_renderer: &Renderer,
_clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
- if *self.is_open {
+ let event_status = if *self.is_open {
// TODO: Encode cursor availability in the type system
*self.is_open =
cursor_position.x < 0.0 || cursor_position.y < 0.0;
+
+ event::Status::Captured
} else if layout.bounds().contains(cursor_position) {
let selected = self.selected.as_ref();
@@ -238,15 +244,23 @@ where
.options
.iter()
.position(|option| Some(option) == selected);
- }
+
+ event::Status::Captured
+ } else {
+ event::Status::Ignored
+ };
if let Some(last_selection) = self.last_selection.take() {
messages.push((self.on_selected)(last_selection));
*self.is_open = false;
+
+ event::Status::Captured
+ } else {
+ event_status
}
}
- _ => {}
+ _ => event::Status::Ignored,
}
}
diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs
index 06d3f846..781fffb1 100644
--- a/native/src/widget/radio.rs
+++ b/native/src/widget/radio.rs
@@ -1,8 +1,12 @@
//! Create choices using radio buttons.
+use crate::event::{self, Event};
+use crate::layout;
+use crate::mouse;
+use crate::row;
+use crate::text;
use crate::{
- layout, mouse, row, text, Align, Clipboard, Element, Event, Hasher,
- HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text,
- VerticalAlignment, Widget,
+ Align, Clipboard, Element, Hasher, HorizontalAlignment, Layout, Length,
+ Point, Rectangle, Row, Text, VerticalAlignment, Widget,
};
use std::hash::Hash;
@@ -166,15 +170,19 @@ where
messages: &mut Vec<Message>,
_renderer: &Renderer,
_clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
if layout.bounds().contains(cursor_position) {
messages.push(self.on_click.clone());
+
+ return event::Status::Captured;
}
}
_ => {}
}
+
+ event::Status::Ignored
}
fn draw(
diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs
index bc8a3df1..6b09d0c8 100644
--- a/native/src/widget/row.rs
+++ b/native/src/widget/row.rs
@@ -1,9 +1,9 @@
//! Distribute content horizontally.
+use crate::event::{self, Event};
use crate::layout;
use crate::overlay;
use crate::{
- Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle,
- Widget,
+ Align, Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Widget,
};
use std::hash::Hash;
@@ -162,9 +162,11 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
- self.children.iter_mut().zip(layout.children()).for_each(
- |(child, layout)| {
+ ) -> event::Status {
+ self.children
+ .iter_mut()
+ .zip(layout.children())
+ .map(|(child, layout)| {
child.widget.on_event(
event.clone(),
layout,
@@ -173,8 +175,8 @@ where
renderer,
clipboard,
)
- },
- );
+ })
+ .fold(event::Status::Ignored, event::Status::merge)
}
fn draw(
diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
index 60ec2d7d..92671ddd 100644
--- a/native/src/widget/scrollable.rs
+++ b/native/src/widget/scrollable.rs
@@ -1,7 +1,12 @@
//! Navigate an endless amount of content with a scrollbar.
+use crate::column;
+use crate::event::{self, Event};
+use crate::layout;
+use crate::mouse;
+use crate::overlay;
use crate::{
- column, layout, mouse, overlay, Align, Clipboard, Column, Element, Event,
- Hasher, Layout, Length, Point, Rectangle, Size, Vector, Widget,
+ Align, Clipboard, Column, Element, Hasher, Layout, Length, Point,
+ Rectangle, Size, Vector, Widget,
};
use std::{f32, hash::Hash, u32};
@@ -184,14 +189,56 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
let bounds = layout.bounds();
let is_mouse_over = bounds.contains(cursor_position);
let content = layout.children().next().unwrap();
let content_bounds = content.bounds();
- // TODO: Event capture. Nested scrollables should capture scroll events.
+ let offset = self.state.offset(bounds, content_bounds);
+ let scrollbar = renderer.scrollbar(
+ bounds,
+ content_bounds,
+ offset,
+ self.scrollbar_width,
+ self.scrollbar_margin,
+ self.scroller_width,
+ );
+ let is_mouse_over_scrollbar = scrollbar
+ .as_ref()
+ .map(|scrollbar| scrollbar.is_mouse_over(cursor_position))
+ .unwrap_or(false);
+
+ let event_status = {
+ let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar {
+ Point::new(
+ cursor_position.x,
+ cursor_position.y
+ + self.state.offset(bounds, content_bounds) as f32,
+ )
+ } else {
+ // TODO: Make `cursor_position` an `Option<Point>` so we can encode
+ // cursor availability.
+ // This will probably happen naturally once we add multi-window
+ // support.
+ Point::new(cursor_position.x, -1.0)
+ };
+
+ self.content.on_event(
+ event.clone(),
+ content,
+ cursor_position,
+ messages,
+ renderer,
+ clipboard,
+ )
+ };
+
+ if let event::Status::Captured = event_status {
+ return event::Status::Captured;
+ }
+
if is_mouse_over {
match event {
Event::Mouse(mouse::Event::WheelScrolled { delta }) => {
@@ -204,31 +251,21 @@ where
self.state.scroll(y, bounds, content_bounds);
}
}
+
+ return event::Status::Captured;
}
_ => {}
}
}
- let offset = self.state.offset(bounds, content_bounds);
- let scrollbar = renderer.scrollbar(
- bounds,
- content_bounds,
- offset,
- self.scrollbar_width,
- self.scrollbar_margin,
- self.scroller_width,
- );
- let is_mouse_over_scrollbar = scrollbar
- .as_ref()
- .map(|scrollbar| scrollbar.is_mouse_over(cursor_position))
- .unwrap_or(false);
-
if self.state.is_scroller_grabbed() {
match event {
Event::Mouse(mouse::Event::ButtonReleased(
mouse::Button::Left,
)) => {
self.state.scroller_grabbed_at = None;
+
+ return event::Status::Captured;
}
Event::Mouse(mouse::Event::CursorMoved { .. }) => {
if let (Some(scrollbar), Some(scroller_grabbed_at)) =
@@ -242,6 +279,8 @@ where
bounds,
content_bounds,
);
+
+ return event::Status::Captured;
}
}
_ => {}
@@ -266,6 +305,8 @@ where
self.state.scroller_grabbed_at =
Some(scroller_grabbed_at);
+
+ return event::Status::Captured;
}
}
}
@@ -273,28 +314,7 @@ where
}
}
- let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar {
- Point::new(
- cursor_position.x,
- cursor_position.y
- + self.state.offset(bounds, content_bounds) as f32,
- )
- } else {
- // TODO: Make `cursor_position` an `Option<Point>` so we can encode
- // cursor availability.
- // This will probably happen naturally once we add multi-window
- // support.
- Point::new(cursor_position.x, -1.0)
- };
-
- self.content.on_event(
- event,
- content,
- cursor_position,
- messages,
- renderer,
- clipboard,
- )
+ event::Status::Ignored
}
fn draw(
diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs
index d6e366aa..4e38fb86 100644
--- a/native/src/widget/slider.rs
+++ b/native/src/widget/slider.rs
@@ -4,9 +4,11 @@
//!
//! [`Slider`]: struct.Slider.html
//! [`State`]: struct.State.html
+use crate::event::{self, Event};
+use crate::layout;
+use crate::mouse;
use crate::{
- layout, mouse, Clipboard, Element, Event, Hasher, Layout, Length, Point,
- Rectangle, Size, Widget,
+ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget,
};
use std::{hash::Hash, ops::RangeInclusive};
@@ -202,7 +204,7 @@ where
messages: &mut Vec<Message>,
_renderer: &Renderer,
_clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
let mut change = || {
let bounds = layout.bounds();
if cursor_position.x <= bounds.x {
@@ -232,6 +234,8 @@ where
if layout.bounds().contains(cursor_position) {
change();
self.state.is_dragging = true;
+
+ return event::Status::Captured;
}
}
mouse::Event::ButtonReleased(mouse::Button::Left) => {
@@ -240,17 +244,23 @@ where
messages.push(on_release);
}
self.state.is_dragging = false;
+
+ return event::Status::Captured;
}
}
mouse::Event::CursorMoved { .. } => {
if self.state.is_dragging {
change();
+
+ return event::Status::Captured;
}
}
_ => {}
},
_ => {}
}
+
+ event::Status::Ignored
}
fn draw(
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index 470e92ed..cf95e7e8 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -14,11 +14,13 @@ pub use value::Value;
use editor::Editor;
+use crate::event::{self, Event};
+use crate::keyboard;
+use crate::layout;
+use crate::mouse::{self, click};
+use crate::text;
use crate::{
- keyboard, layout,
- mouse::{self, click},
- text, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle,
- Size, Widget,
+ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget,
};
use std::u32;
@@ -218,11 +220,14 @@ where
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
- ) {
+ ) -> event::Status {
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
let is_clicked = layout.bounds().contains(cursor_position);
+ self.state.is_dragging = is_clicked;
+ self.state.is_focused = is_clicked;
+
if is_clicked {
let text_layout = layout.children().next().unwrap();
let target = cursor_position.x - text_layout.bounds().x;
@@ -280,10 +285,9 @@ where
}
self.state.last_click = Some(click);
- }
- self.state.is_dragging = is_clicked;
- self.state.is_focused = is_clicked;
+ return event::Status::Captured;
+ }
}
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => {
self.state.is_dragging = false;
@@ -314,6 +318,8 @@ where
position,
);
}
+
+ return event::Status::Captured;
}
}
Event::Keyboard(keyboard::Event::CharacterReceived(c))
@@ -328,167 +334,203 @@ where
let message = (self.on_change)(editor.contents());
messages.push(message);
+
+ return event::Status::Captured;
}
Event::Keyboard(keyboard::Event::KeyPressed {
key_code,
modifiers,
- }) if self.state.is_focused => match key_code {
- keyboard::KeyCode::Enter => {
- if let Some(on_submit) = self.on_submit.clone() {
- messages.push(on_submit);
- }
- }
- keyboard::KeyCode::Backspace => {
- if platform::is_jump_modifier_pressed(modifiers)
- && self.state.cursor.selection(&self.value).is_none()
- {
- if self.is_secure {
- let cursor_pos = self.state.cursor.end(&self.value);
- self.state.cursor.select_range(0, cursor_pos);
- } else {
- self.state.cursor.select_left_by_words(&self.value);
+ }) if self.state.is_focused => {
+ match key_code {
+ keyboard::KeyCode::Enter => {
+ if let Some(on_submit) = self.on_submit.clone() {
+ messages.push(on_submit);
}
}
+ keyboard::KeyCode::Backspace => {
+ if platform::is_jump_modifier_pressed(modifiers)
+ && self
+ .state
+ .cursor
+ .selection(&self.value)
+ .is_none()
+ {
+ if self.is_secure {
+ let cursor_pos =
+ self.state.cursor.end(&self.value);
+ self.state.cursor.select_range(0, cursor_pos);
+ } else {
+ self.state
+ .cursor
+ .select_left_by_words(&self.value);
+ }
+ }
- let mut editor =
- Editor::new(&mut self.value, &mut self.state.cursor);
+ let mut editor = Editor::new(
+ &mut self.value,
+ &mut self.state.cursor,
+ );
- editor.backspace();
+ editor.backspace();
- let message = (self.on_change)(editor.contents());
- messages.push(message);
- }
- keyboard::KeyCode::Delete => {
- if platform::is_jump_modifier_pressed(modifiers)
- && self.state.cursor.selection(&self.value).is_none()
- {
- if self.is_secure {
- let cursor_pos = self.state.cursor.end(&self.value);
- self.state
- .cursor
- .select_range(cursor_pos, self.value.len());
- } else {
- self.state
+ let message = (self.on_change)(editor.contents());
+ messages.push(message);
+ }
+ keyboard::KeyCode::Delete => {
+ if platform::is_jump_modifier_pressed(modifiers)
+ && self
+ .state
.cursor
- .select_right_by_words(&self.value);
+ .selection(&self.value)
+ .is_none()
+ {
+ if self.is_secure {
+ let cursor_pos =
+ self.state.cursor.end(&self.value);
+ self.state
+ .cursor
+ .select_range(cursor_pos, self.value.len());
+ } else {
+ self.state
+ .cursor
+ .select_right_by_words(&self.value);
+ }
}
- }
- let mut editor =
- Editor::new(&mut self.value, &mut self.state.cursor);
+ let mut editor = Editor::new(
+ &mut self.value,
+ &mut self.state.cursor,
+ );
- editor.delete();
+ editor.delete();
- let message = (self.on_change)(editor.contents());
- messages.push(message);
- }
- keyboard::KeyCode::Left => {
- if platform::is_jump_modifier_pressed(modifiers)
- && !self.is_secure
- {
- if modifiers.shift {
- self.state.cursor.select_left_by_words(&self.value);
+ let message = (self.on_change)(editor.contents());
+ messages.push(message);
+ }
+ keyboard::KeyCode::Left => {
+ if platform::is_jump_modifier_pressed(modifiers)
+ && !self.is_secure
+ {
+ if modifiers.shift {
+ self.state
+ .cursor
+ .select_left_by_words(&self.value);
+ } else {
+ self.state
+ .cursor
+ .move_left_by_words(&self.value);
+ }
+ } else if modifiers.shift {
+ self.state.cursor.select_left(&self.value)
} else {
- self.state.cursor.move_left_by_words(&self.value);
+ self.state.cursor.move_left(&self.value);
}
- } else if modifiers.shift {
- self.state.cursor.select_left(&self.value)
- } else {
- self.state.cursor.move_left(&self.value);
}
- }
- keyboard::KeyCode::Right => {
- if platform::is_jump_modifier_pressed(modifiers)
- && !self.is_secure
- {
- if modifiers.shift {
- self.state
- .cursor
- .select_right_by_words(&self.value);
+ keyboard::KeyCode::Right => {
+ if platform::is_jump_modifier_pressed(modifiers)
+ && !self.is_secure
+ {
+ if modifiers.shift {
+ self.state
+ .cursor
+ .select_right_by_words(&self.value);
+ } else {
+ self.state
+ .cursor
+ .move_right_by_words(&self.value);
+ }
+ } else if modifiers.shift {
+ self.state.cursor.select_right(&self.value)
} else {
- self.state.cursor.move_right_by_words(&self.value);
+ self.state.cursor.move_right(&self.value);
}
- } else if modifiers.shift {
- self.state.cursor.select_right(&self.value)
- } else {
- self.state.cursor.move_right(&self.value);
- }
- }
- keyboard::KeyCode::Home => {
- if modifiers.shift {
- self.state.cursor.select_range(
- self.state.cursor.start(&self.value),
- 0,
- );
- } else {
- self.state.cursor.move_to(0);
}
- }
- keyboard::KeyCode::End => {
- if modifiers.shift {
- self.state.cursor.select_range(
- self.state.cursor.start(&self.value),
- self.value.len(),
- );
- } else {
- self.state.cursor.move_to(self.value.len());
+ keyboard::KeyCode::Home => {
+ if modifiers.shift {
+ self.state.cursor.select_range(
+ self.state.cursor.start(&self.value),
+ 0,
+ );
+ } else {
+ self.state.cursor.move_to(0);
+ }
}
- }
- keyboard::KeyCode::V => {
- if platform::is_copy_paste_modifier_pressed(modifiers) {
- if let Some(clipboard) = clipboard {
- let content = match self.state.is_pasting.take() {
- Some(content) => content,
- None => {
- let content: String = clipboard
- .content()
- .unwrap_or(String::new())
- .chars()
- .filter(|c| !c.is_control())
- .collect();
-
- Value::new(&content)
- }
- };
-
- let mut editor = Editor::new(
- &mut self.value,
- &mut self.state.cursor,
+ keyboard::KeyCode::End => {
+ if modifiers.shift {
+ self.state.cursor.select_range(
+ self.state.cursor.start(&self.value),
+ self.value.len(),
);
+ } else {
+ self.state.cursor.move_to(self.value.len());
+ }
+ }
+ keyboard::KeyCode::V => {
+ if platform::is_copy_paste_modifier_pressed(modifiers) {
+ if let Some(clipboard) = clipboard {
+ let content = match self.state.is_pasting.take()
+ {
+ Some(content) => content,
+ None => {
+ let content: String = clipboard
+ .content()
+ .unwrap_or(String::new())
+ .chars()
+ .filter(|c| !c.is_control())
+ .collect();
+
+ Value::new(&content)
+ }
+ };
- editor.paste(content.clone());
+ let mut editor = Editor::new(
+ &mut self.value,
+ &mut self.state.cursor,
+ );
+
+ editor.paste(content.clone());
- let message = (self.on_change)(editor.contents());
- messages.push(message);
+ let message =
+ (self.on_change)(editor.contents());
+ messages.push(message);
- self.state.is_pasting = Some(content);
+ self.state.is_pasting = Some(content);
+ }
+ } else {
+ self.state.is_pasting = None;
}
- } else {
- self.state.is_pasting = None;
}
- }
- keyboard::KeyCode::A => {
- if platform::is_copy_paste_modifier_pressed(modifiers) {
- self.state.cursor.select_all(&self.value);
+ keyboard::KeyCode::A => {
+ if platform::is_copy_paste_modifier_pressed(modifiers) {
+ self.state.cursor.select_all(&self.value);
+ }
}
+ keyboard::KeyCode::Escape => {
+ self.state.is_focused = false;
+ self.state.is_dragging = false;
+ self.state.is_pasting = None;
+ }
+ _ => {}
}
- keyboard::KeyCode::Escape => {
- self.state.is_focused = false;
- self.state.is_dragging = false;
- self.state.is_pasting = None;
- }
- _ => {}
- },
+
+ return event::Status::Captured;
+ }
Event::Keyboard(keyboard::Event::KeyReleased {
key_code, ..
- }) => match key_code {
- keyboard::KeyCode::V => {
- self.state.is_pasting = None;
+ }) if self.state.is_focused => {
+ match key_code {
+ keyboard::KeyCode::V => {
+ self.state.is_pasting = None;
+ }
+ _ => {}
}
- _ => {}
- },
+
+ return event::Status::Captured;
+ }
_ => {}
}
+
+ event::Status::Ignored
}
fn draw(
diff --git a/winit/src/application.rs b/winit/src/application.rs
index c1d86471..ded60366 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -242,29 +242,33 @@ async fn run_instance<A, E, C>(
let mut mouse_interaction = mouse::Interaction::default();
let mut events = Vec::new();
- let mut external_messages = Vec::new();
+ let mut messages = Vec::new();
debug.startup_finished();
while let Some(event) = receiver.next().await {
match event {
event::Event::MainEventsCleared => {
- if events.is_empty() && external_messages.is_empty() {
+ if events.is_empty() && messages.is_empty() {
continue;
}
debug.event_processing_started();
- let mut messages = user_interface.update(
+
+ let statuses = user_interface.update(
&events,
state.cursor_position(),
clipboard.as_ref().map(|c| c as _),
&mut renderer,
+ &mut messages,
);
- messages.extend(external_messages.drain(..));
- events.clear();
debug.event_processing_finished();
+ for event in events.drain(..).zip(statuses.into_iter()) {
+ runtime.broadcast(event);
+ }
+
if !messages.is_empty() {
let cache =
ManuallyDrop::into_inner(user_interface).into_cache();
@@ -274,7 +278,7 @@ async fn run_instance<A, E, C>(
&mut application,
&mut runtime,
&mut debug,
- messages,
+ &mut messages,
);
// Update window
@@ -297,7 +301,7 @@ async fn run_instance<A, E, C>(
window.request_redraw();
}
event::Event::UserEvent(message) => {
- external_messages.push(message);
+ messages.push(message);
}
event::Event::RedrawRequested(_) => {
debug.render_started();
@@ -365,8 +369,7 @@ async fn run_instance<A, E, C>(
state.scale_factor(),
state.modifiers(),
) {
- events.push(event.clone());
- runtime.broadcast(event);
+ events.push(event);
}
}
_ => {}
@@ -437,9 +440,9 @@ pub fn update<A: Application, E: Executor>(
application: &mut A,
runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
debug: &mut Debug,
- messages: Vec<A::Message>,
+ messages: &mut Vec<A::Message>,
) {
- for message in messages {
+ for message in messages.drain(..) {
debug.log_message(&message);
debug.update_started();