diff options
34 files changed, 364 insertions, 343 deletions
diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 9bcc827b..b317ac00 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -1,7 +1,7 @@ use iced::{ canvas::{self, Cache, Canvas, Cursor, Geometry, LineCap, Path, Stroke}, - executor, time, Application, Clipboard, Color, Command, Container, Element, - Length, Point, Rectangle, Settings, Subscription, Vector, + executor, time, Application, Color, Command, Container, Element, Length, + Point, Rectangle, Settings, Subscription, Vector, }; pub fn main() -> iced::Result { @@ -40,11 +40,7 @@ impl Application for Clock { String::from("Clock - Iced") } - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::Tick(local_time) => { let now = local_time; diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index 6f844e66..cd024926 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -1,6 +1,6 @@ use iced::{ - button, executor, Align, Application, Button, Clipboard, Column, Command, - Container, Element, Length, ProgressBar, Settings, Subscription, Text, + button, executor, Align, Application, Button, Column, Command, Container, + Element, Length, ProgressBar, Settings, Subscription, Text, }; mod download; @@ -43,11 +43,7 @@ impl Application for Example { String::from("Download progress - Iced") } - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::Add => { self.last_id = self.last_id + 1; diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs index 446c190b..911ff425 100644 --- a/examples/events/src/main.rs +++ b/examples/events/src/main.rs @@ -1,7 +1,7 @@ use iced::{ - button, executor, Align, Application, Button, Checkbox, Clipboard, Column, - Command, Container, Element, HorizontalAlignment, Length, Settings, - Subscription, Text, + button, executor, Align, Application, Button, Checkbox, Column, Command, + Container, Element, HorizontalAlignment, Length, Settings, Subscription, + Text, }; use iced_native::{window, Event}; @@ -40,11 +40,7 @@ impl Application for Events { String::from("Events - Iced") } - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::EventOccurred(event) if self.enabled => { self.last.push(event); diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index c3e16e8b..2d46634f 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -12,8 +12,8 @@ use iced::slider::{self, Slider}; use iced::time; use iced::window; use iced::{ - Align, Application, Checkbox, Clipboard, Column, Command, Container, - Element, Length, Row, Settings, Subscription, Text, + Align, Application, Checkbox, Column, Command, Container, Element, Length, + Row, Settings, Subscription, Text, }; use preset::Preset; use std::time::{Duration, Instant}; @@ -71,11 +71,7 @@ impl Application for GameOfLife { String::from("Game of Life - Iced") } - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::Grid(message, version) => { if version == self.version { diff --git a/examples/integration_opengl/src/controls.rs b/examples/integration_opengl/src/controls.rs index 13b7fbc2..ddc6827c 100644 --- a/examples/integration_opengl/src/controls.rs +++ b/examples/integration_opengl/src/controls.rs @@ -1,7 +1,7 @@ use iced_glow::Renderer; use iced_glutin::{ - slider, Align, Clipboard, Color, Column, Command, Element, Length, Program, - Row, Slider, Text, + slider, Align, Color, Column, Command, Element, Length, Program, Row, + Slider, Text, }; pub struct Controls { @@ -30,13 +30,8 @@ impl Controls { impl Program for Controls { type Renderer = Renderer; type Message = Message; - type Clipboard = Clipboard; - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::BackgroundColorChanged(color) => { self.background_color = color; diff --git a/examples/integration_wgpu/src/controls.rs b/examples/integration_wgpu/src/controls.rs index 36ee9b7e..824f9f53 100644 --- a/examples/integration_wgpu/src/controls.rs +++ b/examples/integration_wgpu/src/controls.rs @@ -1,7 +1,7 @@ use iced_wgpu::Renderer; use iced_winit::{ - slider, Align, Clipboard, Color, Column, Command, Element, Length, Program, - Row, Slider, Text, + slider, Align, Color, Column, Command, Element, Length, Program, Row, + Slider, Text, }; pub struct Controls { @@ -30,13 +30,8 @@ impl Controls { impl Program for Controls { type Renderer = Renderer; type Message = Message; - type Clipboard = Clipboard; - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::BackgroundColorChanged(color) => { self.background_color = color; diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 7403713c..810afa00 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -1,7 +1,6 @@ use iced::menu::{self, Menu}; use iced::{ - executor, Application, Clipboard, Command, Container, Element, Length, - Settings, Text, + executor, Application, Command, Container, Element, Length, Settings, Text, }; use iced_native::keyboard::{Hotkey, KeyCode, Modifiers}; @@ -92,11 +91,7 @@ impl Application for App { ]) } - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::MenuActivated(entry) => self.selected = Some(entry), } diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 3bd8aa25..5a134756 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -1,8 +1,7 @@ use iced::{ button, executor, keyboard, pane_grid, scrollable, Align, Application, - Button, Clipboard, Color, Column, Command, Container, Element, - HorizontalAlignment, Length, PaneGrid, Row, Scrollable, Settings, - Subscription, Text, + Button, Color, Column, Command, Container, Element, HorizontalAlignment, + Length, PaneGrid, Row, Scrollable, Settings, Subscription, Text, }; use iced_native::{event, subscription, Event}; @@ -51,11 +50,7 @@ impl Application for Example { String::from("Pane grid - Iced") } - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::Split(axis, pane) => { let result = self.panes.split( diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs index da1d5d5d..fdf667cc 100644 --- a/examples/pokedex/src/main.rs +++ b/examples/pokedex/src/main.rs @@ -1,6 +1,6 @@ use iced::{ - button, futures, image, Align, Application, Button, Clipboard, Column, - Command, Container, Element, Length, Row, Settings, Text, + button, futures, image, Align, Application, Button, Column, Command, + Container, Element, Length, Row, Settings, Text, }; pub fn main() -> iced::Result { @@ -48,11 +48,7 @@ impl Application for Pokedex { format!("{} - Pokédex", subtitle) } - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::PokemonFound(Ok(pokemon)) => { *self = Pokedex::Loaded { diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 8f844828..c8f74978 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -8,8 +8,8 @@ //! [1]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations#An_animated_solar_system use iced::{ canvas::{self, Cursor, Path, Stroke}, - executor, time, window, Application, Canvas, Clipboard, Color, Command, - Element, Length, Point, Rectangle, Settings, Size, Subscription, Vector, + executor, time, window, Application, Canvas, Color, Command, Element, + Length, Point, Rectangle, Settings, Size, Subscription, Vector, }; use std::time::Instant; @@ -48,11 +48,7 @@ impl Application for SolarSystem { String::from("Solar system - Iced") } - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::Tick(instant) => { self.state.update(instant); diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs index 51972e01..983cf3e6 100644 --- a/examples/stopwatch/src/main.rs +++ b/examples/stopwatch/src/main.rs @@ -1,6 +1,6 @@ use iced::{ - button, executor, time, Align, Application, Button, Clipboard, Column, - Command, Container, Element, HorizontalAlignment, Length, Row, Settings, + button, executor, time, Align, Application, Button, Column, Command, + Container, Element, HorizontalAlignment, Length, Row, Settings, Subscription, Text, }; use std::time::{Duration, Instant}; @@ -49,11 +49,7 @@ impl Application for Stopwatch { String::from("Stopwatch - Iced") } - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::Toggle => match self.state { State::Idle => { diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 97415475..7a8ecc1a 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -1,7 +1,7 @@ use iced::{ button, scrollable, text_input, Align, Application, Button, Checkbox, - Clipboard, Column, Command, Container, Element, Font, HorizontalAlignment, - Length, Row, Scrollable, Settings, Text, TextInput, + Column, Command, Container, Element, Font, HorizontalAlignment, Length, + Row, Scrollable, Settings, Text, TextInput, }; use serde::{Deserialize, Serialize}; @@ -58,11 +58,7 @@ impl Application for Todos { format!("Todos{} - Iced", if dirty { "*" } else { "" }) } - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match self { Todos::Loading => { match message { diff --git a/examples/url_handler/src/main.rs b/examples/url_handler/src/main.rs index f14e5227..ee2d249a 100644 --- a/examples/url_handler/src/main.rs +++ b/examples/url_handler/src/main.rs @@ -1,6 +1,6 @@ use iced::{ - executor, Application, Clipboard, Command, Container, Element, Length, - Settings, Subscription, Text, + executor, Application, Command, Container, Element, Length, Settings, + Subscription, Text, }; use iced_native::{ event::{MacOS, PlatformSpecific}, @@ -34,11 +34,7 @@ impl Application for App { String::from("Url - Iced") } - fn update( - &mut self, - message: Message, - _clipboard: &mut Clipboard, - ) -> Command<Message> { + fn update(&mut self, message: Message) -> Command<Message> { match message { Message::EventOccurred(event) => { if let Event::PlatformSpecific(PlatformSpecific::MacOS( diff --git a/futures/src/command.rs b/futures/src/command.rs deleted file mode 100644 index b06ab3f8..00000000 --- a/futures/src/command.rs +++ /dev/null @@ -1,138 +0,0 @@ -use crate::BoxFuture; -use futures::future::{Future, FutureExt}; - -/// A collection of async operations. -/// -/// You should be able to turn a future easily into a [`Command`], either by -/// using the `From` trait or [`Command::perform`]. -pub struct Command<T> { - futures: Vec<BoxFuture<T>>, -} - -impl<T> Command<T> { - /// Creates an empty [`Command`]. - /// - /// In other words, a [`Command`] that does nothing. - pub fn none() -> Self { - Self { - futures: Vec::new(), - } - } - - /// Creates a [`Command`] that performs the action of the given future. - #[cfg(not(target_arch = "wasm32"))] - pub fn perform<A>( - future: impl Future<Output = T> + 'static + Send, - f: impl Fn(T) -> A + 'static + Send, - ) -> Command<A> { - Command { - futures: vec![Box::pin(future.map(f))], - } - } - - /// Creates a [`Command`] that performs the action of the given future. - #[cfg(target_arch = "wasm32")] - pub fn perform<A>( - future: impl Future<Output = T> + 'static, - f: impl Fn(T) -> A + 'static + Send, - ) -> Command<A> { - Command { - futures: vec![Box::pin(future.map(f))], - } - } - - /// Applies a transformation to the result of a [`Command`]. - #[cfg(not(target_arch = "wasm32"))] - pub fn map<A>( - mut self, - f: impl Fn(T) -> A + 'static + Send + Sync, - ) -> Command<A> - where - T: 'static, - { - let f = std::sync::Arc::new(f); - - Command { - futures: self - .futures - .drain(..) - .map(|future| { - let f = f.clone(); - - Box::pin(future.map(move |result| f(result))) - as BoxFuture<A> - }) - .collect(), - } - } - - /// Applies a transformation to the result of a [`Command`]. - #[cfg(target_arch = "wasm32")] - pub fn map<A>(mut self, f: impl Fn(T) -> A + 'static) -> Command<A> - where - T: 'static, - { - let f = std::rc::Rc::new(f); - - Command { - futures: self - .futures - .drain(..) - .map(|future| { - let f = f.clone(); - - Box::pin(future.map(move |result| f(result))) - as BoxFuture<A> - }) - .collect(), - } - } - - /// Creates a [`Command`] that performs the actions of all the given - /// commands. - /// - /// Once this command is run, all the commands will be executed at once. - pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self { - Self { - futures: commands - .into_iter() - .flat_map(|command| command.futures) - .collect(), - } - } - - /// Converts a [`Command`] into its underlying list of futures. - pub fn futures(self) -> Vec<BoxFuture<T>> { - self.futures - } -} - -#[cfg(not(target_arch = "wasm32"))] -impl<T, A> From<A> for Command<T> -where - A: Future<Output = T> + 'static + Send, -{ - fn from(future: A) -> Self { - Self { - futures: vec![future.boxed()], - } - } -} - -#[cfg(target_arch = "wasm32")] -impl<T, A> From<A> for Command<T> -where - A: Future<Output = T> + 'static, -{ - fn from(future: A) -> Self { - Self { - futures: vec![future.boxed_local()], - } - } -} - -impl<T> std::fmt::Debug for Command<T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Command").finish() - } -} diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 01cf5c89..657aab37 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -10,7 +10,6 @@ pub use futures; -mod command; mod runtime; pub mod executor; @@ -35,7 +34,6 @@ pub mod subscription; )] pub mod time; -pub use command::Command; pub use executor::Executor; pub use runtime::Runtime; pub use subscription::Subscription; diff --git a/futures/src/runtime.rs b/futures/src/runtime.rs index e56a4eb0..7779e235 100644 --- a/futures/src/runtime.rs +++ b/futures/src/runtime.rs @@ -1,5 +1,6 @@ //! Run commands and keep track of subscriptions. -use crate::{subscription, Command, Executor, Subscription}; +use crate::BoxFuture; +use crate::{subscription, Executor, Subscription}; use futures::{channel::mpsc, Sink}; use std::marker::PhantomData; @@ -51,22 +52,18 @@ where /// /// The resulting `Message` will be forwarded to the `Sender` of the /// [`Runtime`]. - pub fn spawn(&mut self, command: Command<Message>) { + pub fn spawn(&mut self, future: BoxFuture<Message>) { use futures::{FutureExt, SinkExt}; - let futures = command.futures(); + let mut sender = self.sender.clone(); - for future in futures { - let mut sender = self.sender.clone(); - - let future = future.then(|message| async move { - let _ = sender.send(message).await; + let future = future.then(|message| async move { + let _ = sender.send(message).await; - () - }); + () + }); - self.executor.spawn(future); - } + self.executor.spawn(future); } /// Tracks a [`Subscription`] in the [`Runtime`]. diff --git a/glutin/src/application.rs b/glutin/src/application.rs index 4a5f4bd2..1368f7f8 100644 --- a/glutin/src/application.rs +++ b/glutin/src/application.rs @@ -48,7 +48,7 @@ where let subscription = application.subscription(); - runtime.spawn(init_command); + application::run_command(init_command, &mut runtime); runtime.track(subscription); let context = { @@ -211,7 +211,6 @@ async fn run_instance<A, E, C>( &mut application, &mut runtime, &mut debug, - &mut clipboard, &mut messages, ); diff --git a/native/src/clipboard.rs b/native/src/clipboard.rs index 081b4004..62dfc130 100644 --- a/native/src/clipboard.rs +++ b/native/src/clipboard.rs @@ -21,3 +21,20 @@ impl Clipboard for Null { fn write(&mut self, _contents: String) {} } + +pub enum Action<T> { + Read(Box<dyn Fn(Option<String>) -> T>), + Write(Box<dyn Fn(String) -> T>), +} + +impl<T> Action<T> { + pub fn map<A>(self, f: impl Fn(T) -> A + 'static + Send + Sync) -> Action<A> + where + T: 'static, + { + match self { + Self::Read(o) => Action::Read(Box::new(move |s| f(o(s)))), + Self::Write(o) => Action::Write(Box::new(move |s| f(o(s)))), + } + } +} diff --git a/native/src/command.rs b/native/src/command.rs new file mode 100644 index 00000000..16fd73ba --- /dev/null +++ b/native/src/command.rs @@ -0,0 +1,77 @@ +mod action; + +pub use action::Action; + +use std::future::Future; + +/// A set of asynchronous actions to be performed by some runtime. +pub enum Command<T> { + None, + Single(Action<T>), + Batch(Vec<Action<T>>), +} + +impl<T> Command<T> { + /// Creates an empty [`Command`]. + /// + /// In other words, a [`Command`] that does nothing. + pub fn none() -> Self { + Self::None + } + + /// Creates a [`Command`] that performs the action of the given future. + pub fn perform<A>( + future: impl Future<Output = T> + 'static + Send, + f: impl Fn(T) -> A + 'static + Send, + ) -> Command<A> { + use iced_futures::futures::FutureExt; + + Command::Single(Action::Future(Box::pin(future.map(f)))) + } + + /// Applies a transformation to the result of a [`Command`]. + pub fn map<A>( + self, + f: impl Fn(T) -> A + 'static + Send + Sync + Clone, + ) -> Command<A> + where + T: 'static, + { + match self { + Self::None => Command::None, + Self::Single(action) => Command::Single(action.map(f)), + Self::Batch(batch) => Command::Batch( + batch + .into_iter() + .map(|action| action.map(f.clone())) + .collect(), + ), + } + } + + /// Creates a [`Command`] that performs the actions of all the given + /// commands. + /// + /// Once this command is run, all the commands will be executed at once. + pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self { + let mut batch = Vec::new(); + + for command in commands { + match command { + Self::None => {} + Self::Single(command) => batch.push(command), + Self::Batch(commands) => batch.extend(commands), + } + } + + Self::Batch(batch) + } + + pub fn actions(self) -> Vec<Action<T>> { + match self { + Self::None => Vec::new(), + Self::Single(action) => vec![action], + Self::Batch(batch) => batch, + } + } +} diff --git a/native/src/command/action.rs b/native/src/command/action.rs new file mode 100644 index 00000000..23d6e96e --- /dev/null +++ b/native/src/command/action.rs @@ -0,0 +1,24 @@ +use crate::clipboard; +use crate::window; + +pub enum Action<T> { + Future(iced_futures::BoxFuture<T>), + Clipboard(clipboard::Action<T>), + Window(window::Action), +} + +impl<T> Action<T> { + /// Applies a transformation to the result of a [`Command`]. + pub fn map<A>(self, f: impl Fn(T) -> A + 'static + Send + Sync) -> Action<A> + where + T: 'static, + { + use iced_futures::futures::FutureExt; + + match self { + Self::Future(future) => Action::Future(Box::pin(future.map(f))), + Self::Clipboard(action) => Action::Clipboard(action.map(f)), + Self::Window(window) => Action::Window(window), + } + } +} diff --git a/native/src/lib.rs b/native/src/lib.rs index cbb02506..f300e7b9 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -28,12 +28,13 @@ //! [`druid`]: https://github.com/xi-editor/druid //! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle //! [renderer]: crate::renderer -#![deny(missing_docs)] -#![deny(missing_debug_implementations)] +//#![deny(missing_docs)] +//#![deny(missing_debug_implementations)] #![deny(unused_results)] #![forbid(unsafe_code)] #![forbid(rust_2018_idioms)] pub mod clipboard; +pub mod command; pub mod event; pub mod keyboard; pub mod layout; @@ -64,12 +65,13 @@ pub use iced_core::{ menu, Align, Background, Color, Font, HorizontalAlignment, Length, Menu, Padding, Point, Rectangle, Size, Vector, VerticalAlignment, }; -pub use iced_futures::{executor, futures, Command}; +pub use iced_futures::{executor, futures}; #[doc(no_inline)] pub use executor::Executor; pub use clipboard::Clipboard; +pub use command::Command; pub use debug::Debug; pub use element::Element; pub use event::Event; diff --git a/native/src/program.rs b/native/src/program.rs index 75fab094..fa83c0b1 100644 --- a/native/src/program.rs +++ b/native/src/program.rs @@ -1,5 +1,5 @@ //! Build interactive programs using The Elm Architecture. -use crate::{Clipboard, Command, Element, Renderer}; +use crate::{Command, Element, Renderer}; mod state; @@ -13,9 +13,6 @@ pub trait Program: Sized { /// The type of __messages__ your [`Program`] will produce. type Message: std::fmt::Debug + Clone + Send; - /// The type of [`Clipboard`] your [`Program`] will use. - type Clipboard: Clipboard; - /// Handles a __message__ and updates the state of the [`Program`]. /// /// This is where you define your __update logic__. All the __messages__, @@ -24,11 +21,7 @@ pub trait Program: Sized { /// /// Any [`Command`] returned will be executed immediately in the /// background by shells. - fn update( - &mut self, - message: Self::Message, - clipboard: &mut Self::Clipboard, - ) -> Command<Self::Message>; + fn update(&mut self, message: Self::Message) -> Command<Self::Message>; /// Returns the widgets to display in the [`Program`]. /// diff --git a/native/src/program/state.rs b/native/src/program/state.rs index fd1f2b52..3f5f6069 100644 --- a/native/src/program/state.rs +++ b/native/src/program/state.rs @@ -1,5 +1,6 @@ use crate::{ - Cache, Command, Debug, Event, Point, Program, Renderer, Size, UserInterface, + Cache, Clipboard, Command, Debug, Event, Point, Program, Renderer, Size, + UserInterface, }; /// The execution state of a [`Program`]. It leverages caching, event @@ -91,7 +92,7 @@ where bounds: Size, cursor_position: Point, renderer: &mut P::Renderer, - clipboard: &mut P::Clipboard, + clipboard: &mut dyn Clipboard, debug: &mut Debug, ) -> Option<Command<P::Message>> { let mut user_interface = build_user_interface( @@ -135,7 +136,7 @@ where debug.log_message(&message); debug.update_started(); - let command = self.program.update(message, clipboard); + let command = self.program.update(message); debug.update_finished(); command diff --git a/native/src/window.rs b/native/src/window.rs index 220bb3be..62487fb9 100644 --- a/native/src/window.rs +++ b/native/src/window.rs @@ -1,4 +1,6 @@ //! Build window-based GUI applications. +mod action; mod event; +pub use action::Action; pub use event::Event; diff --git a/native/src/window/action.rs b/native/src/window/action.rs new file mode 100644 index 00000000..01294e83 --- /dev/null +++ b/native/src/window/action.rs @@ -0,0 +1,18 @@ +/// An operation to be performed on some window. +#[derive(Debug)] +pub enum Action { + /// Resize the window. + Resize { + /// The new logical width of the window + width: u32, + /// The new logical height of the window + height: u32, + }, + /// Move the window. + Move { + /// The new logical x location of the window + x: i32, + /// The new logical y location of the window + y: i32, + }, +} diff --git a/native/src/window/event.rs b/native/src/window/event.rs index 64f2b8d8..691af29a 100644 --- a/native/src/window/event.rs +++ b/native/src/window/event.rs @@ -13,10 +13,9 @@ pub enum Event { /// A window was resized. Resized { - /// The new width of the window (in units) + /// The new logical width of the window width: u32, - - /// The new height of the window (in units) + /// The new logical height of the window height: u32, }, diff --git a/src/application.rs b/src/application.rs index 78280e98..81add963 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,7 +1,5 @@ use crate::window; -use crate::{ - Clipboard, Color, Command, Element, Executor, Menu, Settings, Subscription, -}; +use crate::{Color, Command, Element, Executor, Menu, Settings, Subscription}; /// An interactive cross-platform application. /// @@ -80,7 +78,7 @@ use crate::{ /// String::from("A cool application") /// } /// -/// fn update(&mut self, _message: Self::Message, _clipboard: &mut Clipboard) -> Command<Self::Message> { +/// fn update(&mut self, _message: Self::Message) -> Command<Self::Message> { /// Command::none() /// } /// @@ -129,11 +127,7 @@ pub trait Application: Sized { /// this method. /// /// Any [`Command`] returned will be executed immediately in the background. - fn update( - &mut self, - message: Self::Message, - clipboard: &mut Clipboard, - ) -> Command<Self::Message>; + fn update(&mut self, message: Self::Message) -> Command<Self::Message>; /// Returns the event [`Subscription`] for the current state of the /// application. @@ -249,14 +243,9 @@ where { type Renderer = crate::renderer::Renderer; type Message = A::Message; - type Clipboard = iced_winit::Clipboard; - - fn update( - &mut self, - message: Self::Message, - clipboard: &mut iced_winit::Clipboard, - ) -> Command<Self::Message> { - self.0.update(message, clipboard) + + fn update(&mut self, message: Self::Message) -> Command<Self::Message> { + self.0.update(message) } fn view(&mut self) -> Element<'_, Self::Message> { @@ -329,12 +318,8 @@ where self.0.title() } - fn update( - &mut self, - message: Self::Message, - clipboard: &mut Clipboard, - ) -> Command<Self::Message> { - self.0.update(message, clipboard) + fn update(&mut self, message: Self::Message) -> Command<Self::Message> { + self.0.update(message) } fn subscription(&self) -> Subscription<Self::Message> { diff --git a/src/sandbox.rs b/src/sandbox.rs index cb3cf624..a0bb316e 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -1,6 +1,5 @@ use crate::{ - Application, Clipboard, Color, Command, Element, Error, Settings, - Subscription, + Application, Color, Command, Element, Error, Settings, Subscription, }; /// A sandboxed [`Application`]. @@ -162,11 +161,7 @@ where T::title(self) } - fn update( - &mut self, - message: T::Message, - _clipboard: &mut Clipboard, - ) -> Command<T::Message> { + fn update(&mut self, message: T::Message) -> Command<T::Message> { T::update(self, message); Command::none() diff --git a/web/src/clipboard.rs b/web/src/clipboard.rs deleted file mode 100644 index 167a1e53..00000000 --- a/web/src/clipboard.rs +++ /dev/null @@ -1,21 +0,0 @@ -/// A buffer for short-term storage and transfer within and between -/// applications. -#[derive(Debug, Clone, Copy)] -pub struct Clipboard; - -impl Clipboard { - /// Creates a new [`Clipboard`]. - pub fn new() -> Self { - Self - } - - /// Reads the current content of the [`Clipboard`] as text. - pub fn read(&self) -> Option<String> { - unimplemented! {} - } - - /// Writes the given text contents to the [`Clipboard`]. - pub fn write(&mut self, _contents: String) { - unimplemented! {} - } -} diff --git a/web/src/command.rs b/web/src/command.rs new file mode 100644 index 00000000..606097de --- /dev/null +++ b/web/src/command.rs @@ -0,0 +1,77 @@ +mod action; + +pub use action::Action; + +#[cfg(target_arch = "wasm32")] +use std::future::Future; + +/// A set of asynchronous actions to be performed by some runtime. +pub enum Command<T> { + None, + Single(Action<T>), + Batch(Vec<Action<T>>), +} + +impl<T> Command<T> { + /// Creates an empty [`Command`]. + /// + /// In other words, a [`Command`] that does nothing. + pub fn none() -> Self { + Self::None + } + + /// Creates a [`Command`] that performs the action of the given future. + #[cfg(target_arch = "wasm32")] + pub fn perform<A>( + future: impl Future<Output = T> + 'static, + f: impl Fn(T) -> A + 'static + Send, + ) -> Command<A> { + use iced_futures::futures::FutureExt; + + Command::Single(Action::Future(Box::pin(future.map(f)))) + } + + /// Applies a transformation to the result of a [`Command`]. + #[cfg(target_arch = "wasm32")] + pub fn map<A>(mut self, f: impl Fn(T) -> A + 'static + Clone) -> Command<A> + where + T: 'static, + { + match self { + Self::None => Command::None, + Self::Single(action) => Command::Single(action.map(f)), + Self::Batch(batch) => Command::Batch( + batch + .into_iter() + .map(|action| action.map(f.clone())) + .collect(), + ), + } + } + + /// Creates a [`Command`] that performs the actions of all the given + /// commands. + /// + /// Once this command is run, all the commands will be executed at once. + pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self { + let mut batch = Vec::new(); + + for command in commands { + match command { + Self::None => {} + Self::Single(command) => batch.push(command), + Self::Batch(commands) => batch.extend(commands), + } + } + + Self::Batch(batch) + } + + pub fn actions(self) -> Vec<Action<T>> { + match self { + Self::None => Vec::new(), + Self::Single(action) => vec![action], + Self::Batch(batch) => batch, + } + } +} diff --git a/web/src/command/action.rs b/web/src/command/action.rs new file mode 100644 index 00000000..cf384f07 --- /dev/null +++ b/web/src/command/action.rs @@ -0,0 +1,18 @@ +pub enum Action<T> { + Future(iced_futures::BoxFuture<T>), +} + +impl<T> Action<T> { + /// Applies a transformation to the result of a [`Command`]. + #[cfg(target_arch = "wasm32")] + pub fn map<A>(self, f: impl Fn(T) -> A + 'static) -> Action<A> + where + T: 'static, + { + use iced_futures::futures::FutureExt; + + match self { + Self::Future(future) => Action::Future(Box::pin(future.map(f))), + } + } +} diff --git a/web/src/lib.rs b/web/src/lib.rs index bb09bb0d..ec2bd178 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -50,8 +50,8 @@ //! [`wasm-pack`]: https://github.com/rustwasm/wasm-pack //! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen //! [`tour` example]: https://github.com/hecrj/iced/tree/0.3/examples/tour -#![deny(missing_docs)] -#![deny(missing_debug_implementations)] +//#![deny(missing_docs)] +//#![deny(missing_debug_implementations)] #![deny(unused_results)] #![forbid(unsafe_code)] #![forbid(rust_2018_idioms)] @@ -59,7 +59,7 @@ use dodrio::bumpalo; use std::{cell::RefCell, rc::Rc}; mod bus; -mod clipboard; +mod command; mod element; mod hasher; @@ -68,7 +68,7 @@ pub mod subscription; pub mod widget; pub use bus::Bus; -pub use clipboard::Clipboard; +pub use command::Command; pub use css::Css; pub use dodrio; pub use element::Element; @@ -77,7 +77,7 @@ pub use iced_core::{ keyboard, menu, mouse, Align, Background, Color, Font, HorizontalAlignment, Length, Menu, Padding, Point, Rectangle, Size, Vector, VerticalAlignment, }; -pub use iced_futures::{executor, futures, Command}; +pub use iced_futures::{executor, futures}; pub use subscription::Subscription; #[doc(no_inline)] @@ -128,11 +128,7 @@ pub trait Application { /// this method. /// /// Any [`Command`] returned will be executed immediately in the background. - fn update( - &mut self, - message: Self::Message, - clipboard: &mut Clipboard, - ) -> Command<Self::Message>; + fn update(&mut self, message: Self::Message) -> Command<Self::Message>; /// Returns the widgets to display in the [`Application`]. /// @@ -162,8 +158,6 @@ pub trait Application { let document = window.document().unwrap(); let body = document.body().unwrap(); - let mut clipboard = Clipboard::new(); - let (sender, receiver) = iced_futures::futures::channel::mpsc::unbounded(); @@ -177,7 +171,7 @@ pub trait Application { let mut title = app.title(); document.set_title(&title); - runtime.spawn(command); + run_command(command, &mut runtime); let application = Rc::new(RefCell::new(app)); @@ -190,8 +184,7 @@ pub trait Application { let event_loop = receiver.for_each(move |message| { let (command, subscription) = runtime.enter(|| { - let command = - application.borrow_mut().update(message, &mut clipboard); + let command = application.borrow_mut().update(message); let subscription = application.borrow().subscription(); (command, subscription) @@ -199,7 +192,7 @@ pub trait Application { let new_title = application.borrow().title(); - runtime.spawn(command); + run_command(command, &mut runtime); runtime.track(subscription); if title != new_title { @@ -350,8 +343,7 @@ pub trait Embedded { ); let (app, command) = runtime.enter(|| Self::new(flags)); - - runtime.spawn(command); + run_command(command, &mut runtime); let application = Rc::new(RefCell::new(app)); @@ -370,7 +362,7 @@ pub trait Embedded { (command, subscription) }); - runtime.spawn(command); + run_command(command, &mut runtime); runtime.track(subscription); vdom.weak().schedule_render(); @@ -382,6 +374,25 @@ pub trait Embedded { } } +fn run_command<Message: 'static + Send, E: Executor>( + command: Command<Message>, + runtime: &mut iced_futures::Runtime< + Hasher, + (), + E, + iced_futures::futures::channel::mpsc::UnboundedSender<Message>, + Message, + >, +) { + for action in command.actions() { + match action { + command::Action::Future(future) => { + runtime.spawn(future); + } + } + } +} + struct EmbeddedInstance<A: Embedded> { application: Rc<RefCell<A>>, bus: Bus<A::Message>, diff --git a/winit/src/application.rs b/winit/src/application.rs index b683e592..99118ad1 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -30,7 +30,7 @@ use std::mem::ManuallyDrop; /// /// When using an [`Application`] with the `debug` feature enabled, a debug view /// can be toggled by pressing `F12`. -pub trait Application: Program<Clipboard = Clipboard> { +pub trait Application: Program { /// The data needed to initialize your [`Application`]. type Flags; @@ -143,7 +143,7 @@ where let subscription = application.subscription(); - runtime.spawn(init_command); + run_command(init_command, &mut runtime); runtime.track(subscription); let window = settings @@ -290,7 +290,6 @@ async fn run_instance<A, E, C>( &mut application, &mut runtime, &mut debug, - &mut clipboard, &mut messages, ); @@ -492,19 +491,36 @@ pub fn update<A: Application, E: Executor>( application: &mut A, runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>, debug: &mut Debug, - clipboard: &mut A::Clipboard, messages: &mut Vec<A::Message>, ) { for message in messages.drain(..) { debug.log_message(&message); debug.update_started(); - let command = runtime.enter(|| application.update(message, clipboard)); + let command = runtime.enter(|| application.update(message)); debug.update_finished(); - runtime.spawn(command); + run_command(command, runtime); } let subscription = application.subscription(); runtime.track(subscription); } + +/// Runs the actions of a [`Command`]. +pub fn run_command<Message: 'static + Send, E: Executor>( + command: Command<Message>, + runtime: &mut Runtime<E, Proxy<Message>, Message>, +) { + use iced_native::command; + + for action in command.actions() { + match action { + command::Action::Future(future) => { + runtime.spawn(future); + } + command::Action::Clipboard(_action) => unimplemented! {}, + command::Action::Window(_action) => unimplemented! {}, + } + } +} diff --git a/winit/src/window.rs b/winit/src/window.rs new file mode 100644 index 00000000..8ccb13ed --- /dev/null +++ b/winit/src/window.rs @@ -0,0 +1,7 @@ +pub use iced_native::window::*; + +/// The window of an [`Application`]. +/// +/// [`Application`]: crate::Application +#[derive(Debug)] +pub struct Window {} |