Introduce keyboard::on_key_press and on_key_release

Also rename `subscription::events*` to `event::listen*`.
This commit is contained in:
Héctor Ramón Jiménez 2023-09-07 02:45:15 +02:00
parent a56b25b909
commit 08a031cbe5
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
14 changed files with 193 additions and 138 deletions

View file

@ -1,9 +1,8 @@
use iced::alignment; use iced::alignment;
use iced::event::{self, Event};
use iced::executor; use iced::executor;
use iced::subscription;
use iced::widget::{button, checkbox, container, text, Column}; use iced::widget::{button, checkbox, container, text, Column};
use iced::window; use iced::window;
use iced::Event;
use iced::{ use iced::{
Alignment, Application, Command, Element, Length, Settings, Subscription, Alignment, Application, Command, Element, Length, Settings, Subscription,
Theme, Theme,
@ -71,7 +70,7 @@ impl Application for Events {
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
subscription::events().map(Message::EventOccurred) event::listen().map(Message::EventOccurred)
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {

View file

@ -1,12 +1,14 @@
use iced::event::{self, Event};
use iced::executor; use iced::executor;
use iced::keyboard; use iced::keyboard;
use iced::subscription::{self, Subscription};
use iced::theme; use iced::theme;
use iced::widget::{ use iced::widget::{
self, button, column, container, horizontal_space, pick_list, row, text, self, button, column, container, horizontal_space, pick_list, row, text,
text_input, text_input,
}; };
use iced::{Alignment, Application, Command, Element, Event, Length, Settings}; use iced::{
Alignment, Application, Command, Element, Length, Settings, Subscription,
};
use modal::Modal; use modal::Modal;
use std::fmt; use std::fmt;
@ -49,7 +51,7 @@ impl Application for App {
} }
fn subscription(&self) -> Subscription<Self::Message> { fn subscription(&self) -> Subscription<Self::Message> {
subscription::events().map(Message::Event) event::listen().map(Message::Event)
} }
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> Command<Message> {

View file

@ -1,8 +1,6 @@
use iced::alignment::{self, Alignment}; use iced::alignment::{self, Alignment};
use iced::event::{self, Event};
use iced::executor; use iced::executor;
use iced::keyboard; use iced::keyboard;
use iced::subscription;
use iced::theme::{self, Theme}; use iced::theme::{self, Theme};
use iced::widget::pane_grid::{self, PaneGrid}; use iced::widget::pane_grid::{self, PaneGrid};
use iced::widget::{ use iced::widget::{
@ -146,18 +144,12 @@ impl Application for Example {
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
subscription::events_with(|event, status| { keyboard::on_key_press(|key_code, modifiers| {
if let event::Status::Captured = status { if !modifiers.command() {
return None; return None;
} }
match event { handle_hotkey(key_code)
Event::Keyboard(keyboard::Event::KeyPressed {
modifiers,
key_code,
}) if modifiers.command() => handle_hotkey(key_code),
_ => None,
}
}) })
} }

View file

@ -4,9 +4,8 @@ use iced::theme::{Button, Container};
use iced::widget::{button, column, container, image, row, text, text_input}; use iced::widget::{button, column, container, image, row, text, text_input};
use iced::window::screenshot::{self, Screenshot}; use iced::window::screenshot::{self, Screenshot};
use iced::{ use iced::{
event, executor, keyboard, subscription, Alignment, Application, Command, event, executor, keyboard, Alignment, Application, Command, ContentFit,
ContentFit, Element, Event, Length, Rectangle, Renderer, Subscription, Element, Event, Length, Rectangle, Renderer, Subscription, Theme,
Theme,
}; };
use ::image as img; use ::image as img;
@ -254,7 +253,7 @@ impl Application for Example {
} }
fn subscription(&self) -> Subscription<Self::Message> { fn subscription(&self) -> Subscription<Self::Message> {
subscription::events_with(|event, status| { event::listen_with(|event, status| {
if let event::Status::Captured = status { if let event::Status::Captured = status {
return None; return None;
} }

View file

@ -1,10 +1,12 @@
use iced::event::{self, Event};
use iced::executor; use iced::executor;
use iced::keyboard; use iced::keyboard;
use iced::subscription::{self, Subscription};
use iced::widget::{ use iced::widget::{
self, button, column, container, pick_list, row, slider, text, text_input, self, button, column, container, pick_list, row, slider, text, text_input,
}; };
use iced::{Alignment, Application, Command, Element, Event, Length, Settings}; use iced::{
Alignment, Application, Command, Element, Length, Settings, Subscription,
};
use toast::{Status, Toast}; use toast::{Status, Toast};
@ -57,7 +59,7 @@ impl Application for App {
} }
fn subscription(&self) -> Subscription<Self::Message> { fn subscription(&self) -> Subscription<Self::Message> {
subscription::events().map(Message::Event) event::listen().map(Message::Event)
} }
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> Command<Message> {

View file

@ -1,8 +1,6 @@
use iced::alignment::{self, Alignment}; use iced::alignment::{self, Alignment};
use iced::event::{self, Event};
use iced::font::{self, Font}; use iced::font::{self, Font};
use iced::keyboard::{self, KeyCode, Modifiers}; use iced::keyboard;
use iced::subscription;
use iced::theme::{self, Theme}; use iced::theme::{self, Theme};
use iced::widget::{ use iced::widget::{
self, button, checkbox, column, container, row, scrollable, text, self, button, checkbox, column, container, row, scrollable, text,
@ -52,7 +50,7 @@ enum Message {
FilterChanged(Filter), FilterChanged(Filter),
TaskMessage(usize, TaskMessage), TaskMessage(usize, TaskMessage),
TabPressed { shift: bool }, TabPressed { shift: bool },
ToggleFullscreen(window::Mode), ChangeWindowMode(window::Mode),
} }
impl Application for Todos { impl Application for Todos {
@ -163,7 +161,7 @@ impl Application for Todos {
widget::focus_next() widget::focus_next()
} }
} }
Message::ToggleFullscreen(mode) => { Message::ChangeWindowMode(mode) => {
window::change_mode(mode) window::change_mode(mode)
} }
_ => Command::none(), _ => Command::none(),
@ -262,33 +260,19 @@ impl Application for Todos {
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
subscription::events_with(|event, status| match (event, status) { keyboard::on_key_press(|key_code, modifiers| {
( match (key_code, modifiers) {
Event::Keyboard(keyboard::Event::KeyPressed { (keyboard::KeyCode::Tab, _) => Some(Message::TabPressed {
key_code: keyboard::KeyCode::Tab, shift: modifiers.shift(),
modifiers,
..
}), }),
event::Status::Ignored, (keyboard::KeyCode::Up, keyboard::Modifiers::SHIFT) => {
) => Some(Message::TabPressed { Some(Message::ChangeWindowMode(window::Mode::Fullscreen))
shift: modifiers.shift(),
}),
(
Event::Keyboard(keyboard::Event::KeyPressed {
key_code,
modifiers: Modifiers::SHIFT,
}),
event::Status::Ignored,
) => match key_code {
KeyCode::Up => {
Some(Message::ToggleFullscreen(window::Mode::Fullscreen))
} }
KeyCode::Down => { (keyboard::KeyCode::Down, keyboard::Modifiers::SHIFT) => {
Some(Message::ToggleFullscreen(window::Mode::Windowed)) Some(Message::ChangeWindowMode(window::Mode::Windowed))
} }
_ => None, _ => None,
}, }
_ => None,
}) })
} }
} }

View file

@ -1,6 +1,5 @@
use iced::event::{Event, MacOS, PlatformSpecific}; use iced::event::{self, Event};
use iced::executor; use iced::executor;
use iced::subscription;
use iced::widget::{container, text}; use iced::widget::{container, text};
use iced::{ use iced::{
Application, Command, Element, Length, Settings, Subscription, Theme, Application, Command, Element, Length, Settings, Subscription, Theme,
@ -37,9 +36,11 @@ impl Application for App {
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> Command<Message> {
match message { match message {
Message::EventOccurred(event) => { Message::EventOccurred(event) => {
if let Event::PlatformSpecific(PlatformSpecific::MacOS( if let Event::PlatformSpecific(
MacOS::ReceivedUrl(url), event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl(
)) = event url,
)),
) = event
{ {
self.url = Some(url); self.url = Some(url);
} }
@ -50,7 +51,7 @@ impl Application for App {
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
subscription::events().map(Message::EventOccurred) event::listen().map(Message::EventOccurred)
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {

View file

@ -1,14 +1,14 @@
use iced::event::{self, Event};
use iced::executor; use iced::executor;
use iced::mouse; use iced::mouse;
use iced::subscription::{self, Subscription};
use iced::theme::{self, Theme}; use iced::theme::{self, Theme};
use iced::widget::{ use iced::widget::{
column, container, horizontal_space, row, scrollable, text, vertical_space, column, container, horizontal_space, row, scrollable, text, vertical_space,
}; };
use iced::window; use iced::window;
use iced::{ use iced::{
Alignment, Application, Color, Command, Element, Event, Font, Length, Alignment, Application, Color, Command, Element, Font, Length, Point,
Point, Rectangle, Settings, Rectangle, Settings, Subscription,
}; };
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
@ -163,7 +163,7 @@ impl Application for Example {
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
subscription::events_with(|event, _| match event { event::listen_with(|event, _| match event {
Event::Mouse(mouse::Event::CursorMoved { position }) => { Event::Mouse(mouse::Event::CursorMoved { position }) => {
Some(Message::MouseMoved(position)) Some(Message::MouseMoved(position))
} }

59
futures/src/event.rs Normal file
View file

@ -0,0 +1,59 @@
//! Listen to runtime events.
use crate::core::event::{self, Event};
use crate::core::window;
use crate::subscription::{self, Subscription};
use crate::MaybeSend;
/// Returns a [`Subscription`] to all the ignored runtime events.
///
/// This subscription will notify your application of any [`Event`] that was
/// not captured by any widget.
pub fn listen() -> Subscription<Event> {
listen_with(|event, status| match status {
event::Status::Ignored => Some(event),
event::Status::Captured => None,
})
}
/// Creates a [`Subscription`] that listens and filters all the runtime events
/// with the provided function, producing messages accordingly.
///
/// This subscription will call the provided function for every [`Event`]
/// handled by the runtime. If the function:
///
/// - Returns `None`, the [`Event`] will be discarded.
/// - Returns `Some` message, the `Message` will be produced.
pub fn listen_with<Message>(
f: fn(Event, event::Status) -> Option<Message>,
) -> Subscription<Message>
where
Message: 'static + MaybeSend,
{
#[derive(Hash)]
struct EventsWith;
subscription::filter_map(
(EventsWith, f),
move |event, status| match event {
Event::Window(window::Event::RedrawRequested(_)) => None,
_ => f(event, status),
},
)
}
/// Creates a [`Subscription`] that produces a message for every runtime event,
/// including the redraw request events.
///
/// **Warning:** This [`Subscription`], if unfiltered, may produce messages in
/// an infinite loop.
pub fn listen_raw<Message>(
f: fn(Event, event::Status) -> Option<Message>,
) -> Subscription<Message>
where
Message: 'static + MaybeSend,
{
#[derive(Hash)]
struct RawEvents;
subscription::filter_map((RawEvents, f), f)
}

61
futures/src/keyboard.rs Normal file
View file

@ -0,0 +1,61 @@
//! Listen to keyboard events.
use crate::core;
use crate::core::keyboard::{Event, KeyCode, Modifiers};
use crate::subscription::{self, Subscription};
use crate::MaybeSend;
/// Listens to keyboard key presses and calls the given function
/// map them into actual messages.
///
/// If the function returns `None`, the key press will be simply
/// ignored.
pub fn on_key_press<Message>(
f: fn(KeyCode, Modifiers) -> Option<Message>,
) -> Subscription<Message>
where
Message: MaybeSend + 'static,
{
#[derive(Hash)]
struct OnKeyPress;
subscription::filter_map((OnKeyPress, f), move |event, status| {
match (event, status) {
(
core::Event::Keyboard(Event::KeyPressed {
key_code,
modifiers,
}),
core::event::Status::Ignored,
) => f(key_code, modifiers),
_ => None,
}
})
}
/// Listens to keyboard key releases and calls the given function
/// map them into actual messages.
///
/// If the function returns `None`, the key release will be simply
/// ignored.
pub fn on_key_release<Message>(
f: fn(KeyCode, Modifiers) -> Option<Message>,
) -> Subscription<Message>
where
Message: MaybeSend + 'static,
{
#[derive(Hash)]
struct OnKeyPress;
subscription::filter_map((OnKeyPress, f), move |event, status| {
match (event, status) {
(
core::Event::Keyboard(Event::KeyReleased {
key_code,
modifiers,
}),
core::event::Status::Ignored,
) => f(key_code, modifiers),
_ => None,
}
})
}

View file

@ -24,7 +24,9 @@ mod maybe_send;
mod runtime; mod runtime;
pub mod backend; pub mod backend;
pub mod event;
pub mod executor; pub mod executor;
pub mod keyboard;
pub mod subscription; pub mod subscription;
pub use executor::Executor; pub use executor::Executor;

View file

@ -4,7 +4,6 @@ mod tracker;
pub use tracker::Tracker; pub use tracker::Tracker;
use crate::core::event::{self, Event}; use crate::core::event::{self, Event};
use crate::core::window;
use crate::core::Hasher; use crate::core::Hasher;
use crate::futures::{Future, Stream}; use crate::futures::{Future, Stream};
use crate::{BoxStream, MaybeSend}; use crate::{BoxStream, MaybeSend};
@ -215,77 +214,6 @@ where
} }
} }
/// Returns a [`Subscription`] to all the ignored runtime events.
///
/// This subscription will notify your application of any [`Event`] that was
/// not captured by any widget.
pub fn events() -> Subscription<Event> {
events_with(|event, status| match status {
event::Status::Ignored => Some(event),
event::Status::Captured => None,
})
}
/// Returns a [`Subscription`] that filters all the runtime events with the
/// provided function, producing messages accordingly.
///
/// This subscription will call the provided function for every [`Event`]
/// handled by the runtime. If the function:
///
/// - Returns `None`, the [`Event`] will be discarded.
/// - Returns `Some` message, the `Message` will be produced.
pub fn events_with<Message>(
f: fn(Event, event::Status) -> Option<Message>,
) -> Subscription<Message>
where
Message: 'static + MaybeSend,
{
#[derive(Hash)]
struct EventsWith;
Subscription::from_recipe(Runner {
id: (EventsWith, f),
spawn: move |events| {
use futures::future;
use futures::stream::StreamExt;
events.filter_map(move |(event, status)| {
future::ready(match event {
Event::Window(window::Event::RedrawRequested(_)) => None,
_ => f(event, status),
})
})
},
})
}
/// Returns a [`Subscription`] that produces a message for every runtime event,
/// including the redraw request events.
///
/// **Warning:** This [`Subscription`], if unfiltered, may produce messages in
/// an infinite loop.
pub fn raw_events<Message>(
f: fn(Event, event::Status) -> Option<Message>,
) -> Subscription<Message>
where
Message: 'static + MaybeSend,
{
#[derive(Hash)]
struct RawEvents;
Subscription::from_recipe(Runner {
id: (RawEvents, f),
spawn: move |events| {
use futures::future;
use futures::stream::StreamExt;
events.filter_map(move |(event, status)| {
future::ready(f(event, status))
})
},
})
}
/// Returns a [`Subscription`] that will call the given function to create and /// Returns a [`Subscription`] that will call the given function to create and
/// asynchronously run the given [`Stream`]. /// asynchronously run the given [`Stream`].
pub fn run<S, Message>(builder: fn() -> S) -> Subscription<Message> pub fn run<S, Message>(builder: fn() -> S) -> Subscription<Message>
@ -338,6 +266,25 @@ where
) )
} }
pub(crate) fn filter_map<I, F, Message>(id: I, f: F) -> Subscription<Message>
where
I: Hash + 'static,
F: Fn(Event, event::Status) -> Option<Message> + MaybeSend + 'static,
Message: 'static + MaybeSend,
{
Subscription::from_recipe(Runner {
id,
spawn: |events| {
use futures::future;
use futures::stream::StreamExt;
events.filter_map(move |(event, status)| {
future::ready(f(event, status))
})
},
})
}
/// Creates a [`Subscription`] that publishes the events sent from a [`Future`] /// Creates a [`Subscription`] that publishes the events sent from a [`Future`]
/// to an [`mpsc::Sender`] with the given bounds. /// to an [`mpsc::Sender`] with the given bounds.
/// ///

View file

@ -10,7 +10,8 @@ use crate::command::{self, Command};
use crate::core::time::Instant; use crate::core::time::Instant;
use crate::core::window::{Event, Icon, Level, Mode, UserAttention}; use crate::core::window::{Event, Icon, Level, Mode, UserAttention};
use crate::core::Size; use crate::core::Size;
use crate::futures::subscription::{self, Subscription}; use crate::futures::event;
use crate::futures::Subscription;
/// Subscribes to the frames of the window of the running application. /// Subscribes to the frames of the window of the running application.
/// ///
@ -21,7 +22,7 @@ use crate::futures::subscription::{self, Subscription};
/// In any case, this [`Subscription`] is useful to smoothly draw application-driven /// In any case, this [`Subscription`] is useful to smoothly draw application-driven
/// animations without missing any frames. /// animations without missing any frames.
pub fn frames() -> Subscription<Instant> { pub fn frames() -> Subscription<Instant> {
subscription::raw_events(|event, _status| match event { event::listen_raw(|event, _status| match event {
iced_core::Event::Window(Event::RedrawRequested(at)) => Some(at), iced_core::Event::Window(Event::RedrawRequested(at)) => Some(at),
_ => None, _ => None,
}) })

View file

@ -187,7 +187,6 @@ pub mod advanced;
pub use style::theme; pub use style::theme;
pub use crate::core::alignment; pub use crate::core::alignment;
pub use crate::core::event;
pub use crate::core::gradient; pub use crate::core::gradient;
pub use crate::core::{ pub use crate::core::{
color, Alignment, Background, BorderRadius, Color, ContentFit, Degrees, color, Alignment, Background, BorderRadius, Color, ContentFit, Degrees,
@ -223,9 +222,16 @@ pub mod font {
pub use crate::runtime::font::*; pub use crate::runtime::font::*;
} }
pub mod event {
//! Handle events of a user interface.
pub use crate::core::event::{Event, MacOS, PlatformSpecific, Status};
pub use iced_futures::event::{listen, listen_raw, listen_with};
}
pub mod keyboard { pub mod keyboard {
//! Listen and react to keyboard events. //! Listen and react to keyboard events.
pub use crate::core::keyboard::{Event, KeyCode, Modifiers}; pub use crate::core::keyboard::{Event, KeyCode, Modifiers};
pub use iced_futures::keyboard::{on_key_press, on_key_release};
} }
pub mod mouse { pub mod mouse {
@ -238,7 +244,7 @@ pub mod mouse {
pub mod subscription { pub mod subscription {
//! Listen to external events in your application. //! Listen to external events in your application.
pub use iced_futures::subscription::{ pub use iced_futures::subscription::{
channel, events, events_with, run, run_with_id, unfold, Subscription, channel, run, run_with_id, unfold, Subscription,
}; };
} }