From 02c20e6202f1c8c28753f3233cc635790707937a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 17 Nov 2019 07:09:46 +0100 Subject: [PATCH] Support async actions in `iced_winit` --- examples/scroll.rs | 14 ++++++--- examples/todos.rs | 14 ++++++--- examples/tour.rs | 36 ++++++++++++---------- src/lib.rs | 22 +++++++++----- src/winit.rs | 4 +-- winit/Cargo.toml | 1 + winit/src/application.rs | 66 +++++++++++++++++++++++++++++++--------- 7 files changed, 110 insertions(+), 47 deletions(-) diff --git a/examples/scroll.rs b/examples/scroll.rs index 50701879..61ad2a53 100644 --- a/examples/scroll.rs +++ b/examples/scroll.rs @@ -1,12 +1,12 @@ use iced::{ - button, scrollable, Align, Application, Button, Container, Element, Image, - Length, Scrollable, Text, + button, scrollable, Align, Application, Button, Command, Container, + Element, Image, Length, Scrollable, Text, }; pub fn main() { env_logger::init(); - Example::default().run() + Example::run() } #[derive(Default)] @@ -25,16 +25,22 @@ pub enum Message { impl Application for Example { type Message = Message; + fn new() -> (Example, Command) { + (Example::default(), Command::none()) + } + fn title(&self) -> String { String::from("Scroll - Iced") } - fn update(&mut self, message: Message) { + fn update(&mut self, message: Message) -> Command { match message { Message::AddItem => { self.item_count += 1; } } + + Command::none() } fn view(&mut self) -> Element { diff --git a/examples/todos.rs b/examples/todos.rs index f921a666..d97a9e08 100644 --- a/examples/todos.rs +++ b/examples/todos.rs @@ -1,11 +1,11 @@ use iced::{ button, scrollable, text::HorizontalAlignment, text_input, Align, - Application, Background, Button, Checkbox, Color, Column, Container, - Element, Font, Length, Row, Scrollable, Text, TextInput, + Application, Background, Button, Checkbox, Color, Column, Command, + Container, Element, Font, Length, Row, Scrollable, Text, TextInput, }; pub fn main() { - Todos::default().run() + Todos::run() } #[derive(Debug, Default)] @@ -29,11 +29,15 @@ pub enum Message { impl Application for Todos { type Message = Message; + fn new() -> (Todos, Command) { + (Todos::default(), Command::none()) + } + fn title(&self) -> String { String::from("Todos - Iced") } - fn update(&mut self, message: Message) { + fn update(&mut self, message: Message) -> Command { match message { Message::InputChanged(value) => { self.input_value = value; @@ -58,6 +62,8 @@ impl Application for Todos { } dbg!(self); + + Command::none() } fn view(&mut self) -> Element { diff --git a/examples/tour.rs b/examples/tour.rs index 34ad0a34..6d7a080f 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -1,13 +1,14 @@ use iced::{ button, scrollable, slider, text::HorizontalAlignment, text_input, - Application, Background, Button, Checkbox, Color, Column, Container, - Element, Image, Length, Radio, Row, Scrollable, Slider, Text, TextInput, + Application, Background, Button, Checkbox, Color, Column, Command, + Container, Element, Image, Length, Radio, Row, Scrollable, Slider, Text, + TextInput, }; pub fn main() { env_logger::init(); - Tour::new().run() + Tour::run() } pub struct Tour { @@ -18,26 +19,27 @@ pub struct Tour { debug: bool, } -impl Tour { - pub fn new() -> Tour { - Tour { - steps: Steps::new(), - scroll: scrollable::State::new(), - back_button: button::State::new(), - next_button: button::State::new(), - debug: true, - } - } -} - impl Application for Tour { type Message = Message; + fn new() -> (Tour, Command) { + ( + Tour { + steps: Steps::new(), + scroll: scrollable::State::new(), + back_button: button::State::new(), + next_button: button::State::new(), + debug: true, + }, + Command::none(), + ) + } + fn title(&self) -> String { format!("{} - Iced", self.steps.title()) } - fn update(&mut self, event: Message) { + fn update(&mut self, event: Message) -> Command { match event { Message::BackPressed => { self.steps.go_back(); @@ -49,6 +51,8 @@ impl Application for Tour { self.steps.update(step_msg, &mut self.debug); } } + + Command::none() } fn view(&mut self) -> Element { diff --git a/src/lib.rs b/src/lib.rs index 52b0ff8c..945af421 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,21 +4,23 @@ mod platform; pub use platform::*; -pub trait Application { - type Message: std::fmt::Debug; +pub trait Application: Sized { + type Message: std::fmt::Debug + Send; + + fn new() -> (Self, Command); fn title(&self) -> String; - fn update(&mut self, message: Self::Message); + fn update(&mut self, message: Self::Message) -> Command; fn view(&mut self) -> Element; - fn run(self) + fn run() where Self: 'static + Sized, { #[cfg(not(target_arch = "wasm32"))] - iced_winit::Application::run(Instance(self)); + as iced_winit::Application>::run(); #[cfg(target_arch = "wasm32")] iced_web::Application::run(Instance(self)); @@ -35,12 +37,18 @@ where type Renderer = Renderer; type Message = A::Message; + fn new() -> (Self, Command) { + let (app, command) = A::new(); + + (Instance(app), command) + } + fn title(&self) -> String { self.0.title() } - fn update(&mut self, message: Self::Message) { - self.0.update(message); + fn update(&mut self, message: Self::Message) -> Command { + self.0.update(message) } fn view(&mut self) -> Element { diff --git a/src/winit.rs b/src/winit.rs index d35a339f..c869a269 100644 --- a/src/winit.rs +++ b/src/winit.rs @@ -2,8 +2,8 @@ pub use iced_wgpu::{Primitive, Renderer}; pub use iced_winit::{ button, scrollable, slider, text, text_input, winit, Align, Background, - Checkbox, Color, Font, Image, Length, Radio, Scrollable, Slider, Text, - TextInput, + Checkbox, Color, Command, Font, Image, Length, Radio, Scrollable, Slider, + Text, TextInput, }; pub type Element<'a, Message> = iced_winit::Element<'a, Message, Renderer>; diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 2831ba2f..2a33255d 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -13,4 +13,5 @@ debug = [] [dependencies] iced_native = { version = "0.1.0-alpha", path = "../native" } winit = { version = "0.20.0-alpha3", git = "https://github.com/rust-windowing/winit", rev = "709808eb4e69044705fcb214bcc30556db761405"} +futures = { version = "0.3", features = ["thread-pool"] } log = "0.4" diff --git a/winit/src/application.rs b/winit/src/application.rs index 331bafa0..bf41d0c8 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -2,24 +2,26 @@ use crate::{ conversion, input::{keyboard, mouse}, renderer::{Target, Windowed}, - Cache, Container, Debug, Element, Event, Length, MouseCursor, + Cache, Command, Container, Debug, Element, Event, Length, MouseCursor, UserInterface, }; -pub trait Application { +pub trait Application: Sized { type Renderer: Windowed; - type Message: std::fmt::Debug; + type Message: std::fmt::Debug + Send; + + fn new() -> (Self, Command); fn title(&self) -> String; - fn update(&mut self, message: Self::Message); + fn update(&mut self, message: Self::Message) -> Command; fn view(&mut self) -> Element; - fn run(mut self) + fn run() where - Self: 'static + Sized, + Self: 'static, { use winit::{ event::{self, WindowEvent}, @@ -28,10 +30,18 @@ pub trait Application { }; let mut debug = Debug::new(); - let mut title = self.title(); debug.startup_started(); - let event_loop = EventLoop::new(); + let event_loop = EventLoop::with_user_event(); + let proxy = event_loop.create_proxy(); + let mut thread_pool = + futures::executor::ThreadPool::new().expect("Create thread pool"); + let mut external_messages = Vec::new(); + + let (mut application, init_command) = Self::new(); + spawn(init_command, &mut thread_pool, &proxy); + + let mut title = application.title(); // TODO: Ask for window settings and configure this properly let window = WindowBuilder::new() @@ -59,7 +69,7 @@ pub trait Application { debug.layout_started(); let user_interface = UserInterface::build( - document(&mut self, size, &mut debug), + document(&mut application, size, &mut debug), Cache::default(), &mut renderer, ); @@ -85,15 +95,16 @@ pub trait Application { // handled. debug.layout_started(); let mut user_interface = UserInterface::build( - document(&mut self, size, &mut debug), + document(&mut application, size, &mut debug), cache.take().unwrap(), &mut renderer, ); debug.layout_finished(); debug.event_processing_started(); - let messages = + let mut messages = user_interface.update(&renderer, events.drain(..)); + messages.extend(external_messages.drain(..)); debug.event_processing_finished(); if messages.is_empty() { @@ -113,12 +124,14 @@ pub trait Application { debug.log_message(&message); debug.update_started(); - self.update(message); + let command = application.update(message); + + spawn(command, &mut thread_pool, &proxy); debug.update_finished(); } // Update window title - let new_title = self.title(); + let new_title = application.title(); if title != new_title { window.set_title(&new_title); @@ -128,7 +141,7 @@ pub trait Application { debug.layout_started(); let user_interface = UserInterface::build( - document(&mut self, size, &mut debug), + document(&mut application, size, &mut debug), temp_cache, &mut renderer, ); @@ -143,6 +156,9 @@ pub trait Application { window.request_redraw(); } + event::Event::UserEvent(message) => { + external_messages.push(message); + } event::Event::RedrawRequested(_) => { debug.render_started(); @@ -288,3 +304,25 @@ where .height(Length::Units(size.height.round() as u16)) .into() } + +fn spawn( + command: Command, + thread_pool: &mut futures::executor::ThreadPool, + proxy: &winit::event_loop::EventLoopProxy, +) { + use futures::FutureExt; + + let futures = command.futures(); + + for future in futures { + let proxy = proxy.clone(); + + let future = future.map(move |message| { + proxy + .send_event(message) + .expect("Send command result to event loop"); + }); + + thread_pool.spawn_ok(future); + } +}