diff options
74 files changed, 1336 insertions, 1752 deletions
| diff --git a/core/src/element.rs b/core/src/element.rs index 7d918a2e..385d8295 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -10,7 +10,6 @@ use crate::{      Widget,  }; -use std::any::Any;  use std::borrow::Borrow;  /// A generic [`Widget`]. @@ -305,63 +304,9 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<B>, +        operation: &mut dyn widget::Operation<()>,      ) { -        struct MapOperation<'a, B> { -            operation: &'a mut dyn widget::Operation<B>, -        } - -        impl<'a, T, B> widget::Operation<T> for MapOperation<'a, B> { -            fn container( -                &mut self, -                id: Option<&widget::Id>, -                bounds: Rectangle, -                operate_on_children: &mut dyn FnMut( -                    &mut dyn widget::Operation<T>, -                ), -            ) { -                self.operation.container(id, bounds, &mut |operation| { -                    operate_on_children(&mut MapOperation { operation }); -                }); -            } - -            fn focusable( -                &mut self, -                state: &mut dyn widget::operation::Focusable, -                id: Option<&widget::Id>, -            ) { -                self.operation.focusable(state, id); -            } - -            fn scrollable( -                &mut self, -                state: &mut dyn widget::operation::Scrollable, -                id: Option<&widget::Id>, -                bounds: Rectangle, -                translation: Vector, -            ) { -                self.operation.scrollable(state, id, bounds, translation); -            } - -            fn text_input( -                &mut self, -                state: &mut dyn widget::operation::TextInput, -                id: Option<&widget::Id>, -            ) { -                self.operation.text_input(state, id); -            } - -            fn custom(&mut self, state: &mut dyn Any, id: Option<&widget::Id>) { -                self.operation.custom(state, id); -            } -        } - -        self.widget.operate( -            tree, -            layout, -            renderer, -            &mut MapOperation { operation }, -        ); +        self.widget.operate(tree, layout, renderer, operation);      }      fn on_event( @@ -495,7 +440,7 @@ where          state: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          self.element              .widget diff --git a/core/src/lib.rs b/core/src/lib.rs index 32156441..db67219c 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -35,6 +35,7 @@ mod color;  mod content_fit;  mod element;  mod length; +mod maybe;  mod padding;  mod pixels;  mod point; @@ -59,6 +60,7 @@ pub use font::Font;  pub use gradient::Gradient;  pub use layout::Layout;  pub use length::Length; +pub use maybe::{MaybeSend, MaybeSync};  pub use overlay::Overlay;  pub use padding::Padding;  pub use pixels::Pixels; diff --git a/futures/src/maybe.rs b/core/src/maybe.rs index c6a507c1..c6a507c1 100644 --- a/futures/src/maybe.rs +++ b/core/src/maybe.rs diff --git a/core/src/overlay.rs b/core/src/overlay.rs index 3a57fe16..16f867da 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -41,7 +41,7 @@ where          &mut self,          _layout: Layout<'_>,          _renderer: &Renderer, -        _operation: &mut dyn widget::Operation<Message>, +        _operation: &mut dyn widget::Operation<()>,      ) {      } diff --git a/core/src/overlay/element.rs b/core/src/overlay/element.rs index 695b88b3..61e75e8a 100644 --- a/core/src/overlay/element.rs +++ b/core/src/overlay/element.rs @@ -5,9 +5,7 @@ use crate::layout;  use crate::mouse;  use crate::renderer;  use crate::widget; -use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size, Vector}; - -use std::any::Any; +use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size};  /// A generic [`Overlay`].  #[allow(missing_debug_implementations)] @@ -94,7 +92,7 @@ where          &mut self,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          self.overlay.operate(layout, renderer, operation);      } @@ -146,59 +144,9 @@ where          &mut self,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<B>, +        operation: &mut dyn widget::Operation<()>,      ) { -        struct MapOperation<'a, B> { -            operation: &'a mut dyn widget::Operation<B>, -        } - -        impl<'a, T, B> widget::Operation<T> for MapOperation<'a, B> { -            fn container( -                &mut self, -                id: Option<&widget::Id>, -                bounds: Rectangle, -                operate_on_children: &mut dyn FnMut( -                    &mut dyn widget::Operation<T>, -                ), -            ) { -                self.operation.container(id, bounds, &mut |operation| { -                    operate_on_children(&mut MapOperation { operation }); -                }); -            } - -            fn focusable( -                &mut self, -                state: &mut dyn widget::operation::Focusable, -                id: Option<&widget::Id>, -            ) { -                self.operation.focusable(state, id); -            } - -            fn scrollable( -                &mut self, -                state: &mut dyn widget::operation::Scrollable, -                id: Option<&widget::Id>, -                bounds: Rectangle, -                translation: Vector, -            ) { -                self.operation.scrollable(state, id, bounds, translation); -            } - -            fn text_input( -                &mut self, -                state: &mut dyn widget::operation::TextInput, -                id: Option<&widget::Id>, -            ) { -                self.operation.text_input(state, id); -            } - -            fn custom(&mut self, state: &mut dyn Any, id: Option<&widget::Id>) { -                self.operation.custom(state, id); -            } -        } - -        self.content -            .operate(layout, renderer, &mut MapOperation { operation }); +        self.content.operate(layout, renderer, operation);      }      fn on_event( diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index 7e4bebd0..cd12eac9 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -132,7 +132,7 @@ where          &mut self,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          operation.container(None, layout.bounds(), &mut |operation| {              self.children.iter_mut().zip(layout.children()).for_each( diff --git a/core/src/widget.rs b/core/src/widget.rs index b02e3a4f..0d12deba 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -105,7 +105,7 @@ where          _state: &mut Tree,          _layout: Layout<'_>,          _renderer: &Renderer, -        _operation: &mut dyn Operation<Message>, +        _operation: &mut dyn Operation<()>,      ) {      } diff --git a/core/src/widget/operation.rs b/core/src/widget/operation.rs index b91cf9ac..1fa924a4 100644 --- a/core/src/widget/operation.rs +++ b/core/src/widget/operation.rs @@ -8,15 +8,15 @@ pub use scrollable::Scrollable;  pub use text_input::TextInput;  use crate::widget::Id; -use crate::{Rectangle, Vector}; +use crate::{MaybeSend, Rectangle, Vector};  use std::any::Any;  use std::fmt; -use std::rc::Rc; +use std::sync::Arc;  /// A piece of logic that can traverse the widget tree of an application in  /// order to query or update some widget state. -pub trait Operation<T> { +pub trait Operation<T>: MaybeSend {      /// Operates on a widget that contains other widgets.      ///      /// The `operate_on_children` function can be called to return control to @@ -81,7 +81,7 @@ where  /// Maps the output of an [`Operation`] using the given function.  pub fn map<A, B>(      operation: Box<dyn Operation<A>>, -    f: impl Fn(A) -> B + 'static, +    f: impl Fn(A) -> B + Send + Sync + 'static,  ) -> impl Operation<B>  where      A: 'static, @@ -90,7 +90,7 @@ where      #[allow(missing_debug_implementations)]      struct Map<A, B> {          operation: Box<dyn Operation<A>>, -        f: Rc<dyn Fn(A) -> B>, +        f: Arc<dyn Fn(A) -> B + Send + Sync>,      }      impl<A, B> Operation<B> for Map<A, B> @@ -197,7 +197,7 @@ where      Map {          operation, -        f: Rc::new(f), +        f: Arc::new(f),      }  } diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index c20a7d67..ec65e2fa 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -4,7 +4,7 @@ use iced::widget::{      button, column, container, horizontal_space, pick_list, row, text,      text_editor, tooltip,  }; -use iced::{Alignment, Command, Element, Font, Length, Subscription, Theme}; +use iced::{Alignment, Element, Font, Length, Subscription, Task, Theme};  use std::ffi;  use std::io; @@ -51,26 +51,26 @@ impl Editor {          }      } -    fn load() -> Command<Message> { -        Command::perform( +    fn load() -> Task<Message> { +        Task::perform(              load_file(format!("{}/src/main.rs", env!("CARGO_MANIFEST_DIR"))),              Message::FileOpened,          )      } -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::ActionPerformed(action) => {                  self.is_dirty = self.is_dirty || action.is_edit();                  self.content.perform(action); -                Command::none() +                Task::none()              }              Message::ThemeSelected(theme) => {                  self.theme = theme; -                Command::none() +                Task::none()              }              Message::NewFile => {                  if !self.is_loading { @@ -78,15 +78,15 @@ impl Editor {                      self.content = text_editor::Content::new();                  } -                Command::none() +                Task::none()              }              Message::OpenFile => {                  if self.is_loading { -                    Command::none() +                    Task::none()                  } else {                      self.is_loading = true; -                    Command::perform(open_file(), Message::FileOpened) +                    Task::perform(open_file(), Message::FileOpened)                  }              }              Message::FileOpened(result) => { @@ -98,15 +98,15 @@ impl Editor {                      self.content = text_editor::Content::with_text(&contents);                  } -                Command::none() +                Task::none()              }              Message::SaveFile => {                  if self.is_loading { -                    Command::none() +                    Task::none()                  } else {                      self.is_loading = true; -                    Command::perform( +                    Task::perform(                          save_file(self.file.clone(), self.content.text()),                          Message::FileSaved,                      ) @@ -120,7 +120,7 @@ impl Editor {                      self.is_dirty = false;                  } -                Command::none() +                Task::none()              }          }      } diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs index bacd8e6e..504ed5d8 100644 --- a/examples/events/src/main.rs +++ b/examples/events/src/main.rs @@ -2,7 +2,7 @@ use iced::alignment;  use iced::event::{self, Event};  use iced::widget::{button, center, checkbox, text, Column};  use iced::window; -use iced::{Alignment, Command, Element, Length, Subscription}; +use iced::{Alignment, Element, Length, Subscription, Task};  pub fn main() -> iced::Result {      iced::program("Events - Iced", Events::update, Events::view) @@ -25,7 +25,7 @@ enum Message {  }  impl Events { -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::EventOccurred(event) if self.enabled => {                  self.last.push(event); @@ -34,19 +34,19 @@ impl Events {                      let _ = self.last.remove(0);                  } -                Command::none() +                Task::none()              }              Message::EventOccurred(event) => {                  if let Event::Window(window::Event::CloseRequested) = event {                      window::close(window::Id::MAIN)                  } else { -                    Command::none() +                    Task::none()                  }              }              Message::Toggled(enabled) => {                  self.enabled = enabled; -                Command::none() +                Task::none()              }              Message::Exit => window::close(window::Id::MAIN),          } diff --git a/examples/exit/src/main.rs b/examples/exit/src/main.rs index 2de97e20..8ba180a5 100644 --- a/examples/exit/src/main.rs +++ b/examples/exit/src/main.rs @@ -1,6 +1,6 @@  use iced::widget::{button, center, column};  use iced::window; -use iced::{Alignment, Command, Element}; +use iced::{Alignment, Element, Task};  pub fn main() -> iced::Result {      iced::program("Exit - Iced", Exit::update, Exit::view).run() @@ -18,13 +18,13 @@ enum Message {  }  impl Exit { -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::Confirm => window::close(window::Id::MAIN),              Message::Exit => {                  self.show_confirm = true; -                Command::none() +                Task::none()              }          }      } diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index 8f1f7a54..7e6d461d 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -9,7 +9,7 @@ use iced::time;  use iced::widget::{      button, checkbox, column, container, pick_list, row, slider, text,  }; -use iced::{Alignment, Command, Element, Length, Subscription, Theme}; +use iced::{Alignment, Element, Length, Subscription, Task, Theme};  use std::time::Duration;  pub fn main() -> iced::Result { @@ -56,7 +56,7 @@ impl GameOfLife {          }      } -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::Grid(message, version) => {                  if version == self.version { @@ -75,7 +75,7 @@ impl GameOfLife {                      let version = self.version; -                    return Command::perform(task, move |message| { +                    return Task::perform(task, move |message| {                          Message::Grid(message, version)                      });                  } @@ -103,7 +103,7 @@ impl GameOfLife {              }          } -        Command::none() +        Task::none()      }      fn subscription(&self) -> Subscription<Message> { diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs index 1958b2f3..d0654996 100644 --- a/examples/integration/src/controls.rs +++ b/examples/integration/src/controls.rs @@ -2,7 +2,7 @@ use iced_wgpu::Renderer;  use iced_widget::{column, container, row, slider, text, text_input};  use iced_winit::core::alignment;  use iced_winit::core::{Color, Element, Length, Theme}; -use iced_winit::runtime::{Command, Program}; +use iced_winit::runtime::{Program, Task};  pub struct Controls {      background_color: Color, @@ -33,7 +33,7 @@ impl Program for Controls {      type Message = Message;      type Renderer = Renderer; -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::BackgroundColorChanged(color) => {                  self.background_color = color; @@ -43,7 +43,7 @@ impl Program for Controls {              }          } -        Command::none() +        Task::none()      }      fn view(&self) -> Element<Message, Theme, Renderer> { diff --git a/examples/loading_spinners/src/easing.rs b/examples/loading_spinners/src/easing.rs index 665b3329..45089ef6 100644 --- a/examples/loading_spinners/src/easing.rs +++ b/examples/loading_spinners/src/easing.rs @@ -119,10 +119,7 @@ impl Builder {      fn point(p: impl Into<Point>) -> lyon_algorithms::geom::Point<f32> {          let p: Point = p.into(); -        lyon_algorithms::geom::point( -            p.x.min(1.0).max(0.0), -            p.y.min(1.0).max(0.0), -        ) +        lyon_algorithms::geom::point(p.x.clamp(0.0, 1.0), p.y.clamp(0.0, 1.0))      }  } diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index a012c310..d185cf3b 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -5,7 +5,7 @@ use iced::widget::{      self, button, center, column, container, horizontal_space, mouse_area,      opaque, pick_list, row, stack, text, text_input,  }; -use iced::{Alignment, Color, Command, Element, Length, Subscription}; +use iced::{Alignment, Color, Element, Length, Subscription, Task};  use std::fmt; @@ -39,7 +39,7 @@ impl App {          event::listen().map(Message::Event)      } -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::ShowModal => {                  self.show_modal = true; @@ -47,26 +47,26 @@ impl App {              }              Message::HideModal => {                  self.hide_modal(); -                Command::none() +                Task::none()              }              Message::Email(email) => {                  self.email = email; -                Command::none() +                Task::none()              }              Message::Password(password) => {                  self.password = password; -                Command::none() +                Task::none()              }              Message::Plan(plan) => {                  self.plan = plan; -                Command::none() +                Task::none()              }              Message::Submit => {                  if !self.email.is_empty() && !self.password.is_empty() {                      self.hide_modal();                  } -                Command::none() +                Task::none()              }              Message::Event(event) => match event {                  Event::Keyboard(keyboard::Event::KeyPressed { @@ -85,9 +85,9 @@ impl App {                      ..                  }) => {                      self.hide_modal(); -                    Command::none() +                    Task::none()                  } -                _ => Command::none(), +                _ => Task::none(),              },          }      } diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs index eb74c94a..e15f8759 100644 --- a/examples/multi_window/src/main.rs +++ b/examples/multi_window/src/main.rs @@ -6,7 +6,7 @@ use iced::widget::{  };  use iced::window;  use iced::{ -    Alignment, Command, Element, Length, Point, Settings, Subscription, Theme, +    Alignment, Element, Length, Point, Settings, Subscription, Task, Theme,      Vector,  }; @@ -48,13 +48,13 @@ impl multi_window::Application for Example {      type Theme = Theme;      type Flags = (); -    fn new(_flags: ()) -> (Self, Command<Message>) { +    fn new(_flags: ()) -> (Self, Task<Message>) {          (              Example {                  windows: HashMap::from([(window::Id::MAIN, Window::new(1))]),                  next_window_pos: window::Position::Default,              }, -            Command::none(), +            Task::none(),          )      } @@ -65,14 +65,14 @@ impl multi_window::Application for Example {              .unwrap_or("Example".to_string())      } -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::ScaleInputChanged(id, scale) => {                  let window =                      self.windows.get_mut(&id).expect("Window not found!");                  window.scale_input = scale; -                Command::none() +                Task::none()              }              Message::ScaleChanged(id, scale) => {                  let window = @@ -83,7 +83,7 @@ impl multi_window::Application for Example {                      .unwrap_or(window.current_scale)                      .clamp(0.5, 5.0); -                Command::none() +                Task::none()              }              Message::TitleChanged(id, title) => {                  let window = @@ -91,12 +91,12 @@ impl multi_window::Application for Example {                  window.title = title; -                Command::none() +                Task::none()              }              Message::CloseWindow(id) => window::close(id),              Message::WindowClosed(id) => {                  self.windows.remove(&id); -                Command::none() +                Task::none()              }              Message::WindowOpened(id, position) => {                  if let Some(position) = position { @@ -108,13 +108,13 @@ impl multi_window::Application for Example {                  if let Some(window) = self.windows.get(&id) {                      text_input::focus(window.input_id.clone())                  } else { -                    Command::none() +                    Task::none()                  }              }              Message::NewWindow => {                  let count = self.windows.len() + 1; -                let (id, spawn_window) = window::spawn(window::Settings { +                let (id, spawn_window) = window::open(window::Settings {                      position: self.next_window_pos,                      exit_on_close_request: count % 2 == 0,                      ..Default::default() diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs index cffa3727..e62ed70b 100644 --- a/examples/pokedex/src/main.rs +++ b/examples/pokedex/src/main.rs @@ -1,6 +1,6 @@  use iced::futures;  use iced::widget::{self, center, column, image, row, text}; -use iced::{Alignment, Command, Element, Length}; +use iced::{Alignment, Element, Length, Task};  pub fn main() -> iced::Result {      iced::program(Pokedex::title, Pokedex::update, Pokedex::view) @@ -25,8 +25,8 @@ enum Message {  }  impl Pokedex { -    fn search() -> Command<Message> { -        Command::perform(Pokemon::search(), Message::PokemonFound) +    fn search() -> Task<Message> { +        Task::perform(Pokemon::search(), Message::PokemonFound)      }      fn title(&self) -> String { @@ -39,20 +39,20 @@ impl Pokedex {          format!("{subtitle} - Pokédex")      } -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::PokemonFound(Ok(pokemon)) => {                  *self = Pokedex::Loaded { pokemon }; -                Command::none() +                Task::none()              }              Message::PokemonFound(Err(_error)) => {                  *self = Pokedex::Errored; -                Command::none() +                Task::none()              }              Message::Search => match self { -                Pokedex::Loading => Command::none(), +                Pokedex::Loading => Task::none(),                  _ => {                      *self = Pokedex::Loading; diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index fb19e556..9b9162d0 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -4,7 +4,7 @@ use iced::widget::{button, column, container, image, row, text, text_input};  use iced::window;  use iced::window::screenshot::{self, Screenshot};  use iced::{ -    Alignment, Command, ContentFit, Element, Length, Rectangle, Subscription, +    Alignment, ContentFit, Element, Length, Rectangle, Subscription, Task,  };  use ::image as img; @@ -44,13 +44,11 @@ enum Message {  }  impl Example { -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::Screenshot => { -                return iced::window::screenshot( -                    window::Id::MAIN, -                    Message::ScreenshotData, -                ); +                return iced::window::screenshot(window::Id::MAIN) +                    .map(Message::ScreenshotData);              }              Message::ScreenshotData(screenshot) => {                  self.screenshot = Some(screenshot); @@ -59,7 +57,7 @@ impl Example {                  if let Some(screenshot) = &self.screenshot {                      self.png_saving = true; -                    return Command::perform( +                    return Task::perform(                          save_to_png(screenshot.clone()),                          Message::PngSaved,                      ); @@ -103,7 +101,7 @@ impl Example {              }          } -        Command::none() +        Task::none()      }      fn view(&self) -> Element<'_, Message> { diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index bbb6497f..a0dcf82c 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -3,7 +3,7 @@ use iced::widget::{      button, column, container, horizontal_space, progress_bar, radio, row,      scrollable, slider, text, vertical_space, Scrollable,  }; -use iced::{Alignment, Border, Color, Command, Element, Length, Theme}; +use iced::{Alignment, Border, Color, Element, Length, Task, Theme};  use once_cell::sync::Lazy; @@ -59,7 +59,7 @@ impl ScrollableDemo {          }      } -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::SwitchDirection(direction) => {                  self.current_scroll_offset = scrollable::RelativeOffset::START; @@ -82,17 +82,17 @@ impl ScrollableDemo {              Message::ScrollbarWidthChanged(width) => {                  self.scrollbar_width = width; -                Command::none() +                Task::none()              }              Message::ScrollbarMarginChanged(margin) => {                  self.scrollbar_margin = margin; -                Command::none() +                Task::none()              }              Message::ScrollerWidthChanged(width) => {                  self.scroller_width = width; -                Command::none() +                Task::none()              }              Message::ScrollToBeginning => {                  self.current_scroll_offset = scrollable::RelativeOffset::START; @@ -113,7 +113,7 @@ impl ScrollableDemo {              Message::Scrolled(viewport) => {                  self.current_scroll_offset = viewport.relative_offset(); -                Command::none() +                Task::none()              }          }      } diff --git a/examples/system_information/src/main.rs b/examples/system_information/src/main.rs index 8ce12e1c..e2808edd 100644 --- a/examples/system_information/src/main.rs +++ b/examples/system_information/src/main.rs @@ -1,5 +1,5 @@  use iced::widget::{button, center, column, text}; -use iced::{system, Command, Element}; +use iced::{system, Element, Task};  pub fn main() -> iced::Result {      iced::program("System Information - Iced", Example::update, Example::view) @@ -24,19 +24,20 @@ enum Message {  }  impl Example { -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::Refresh => {                  *self = Self::Loading; -                return system::fetch_information(Message::InformationReceived); +                return system::fetch_information() +                    .map(Message::InformationReceived);              }              Message::InformationReceived(information) => {                  *self = Self::Loaded { information };              }          } -        Command::none() +        Task::none()      }      fn view(&self) -> Element<Message> { diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index 700b6b10..aee2479e 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -4,7 +4,7 @@ use iced::keyboard::key;  use iced::widget::{      self, button, center, column, pick_list, row, slider, text, text_input,  }; -use iced::{Alignment, Command, Element, Length, Subscription}; +use iced::{Alignment, Element, Length, Subscription, Task};  use toast::{Status, Toast}; @@ -49,7 +49,7 @@ impl App {          event::listen().map(Message::Event)      } -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::Add => {                  if !self.editing.title.is_empty() @@ -57,27 +57,27 @@ impl App {                  {                      self.toasts.push(std::mem::take(&mut self.editing));                  } -                Command::none() +                Task::none()              }              Message::Close(index) => {                  self.toasts.remove(index); -                Command::none() +                Task::none()              }              Message::Title(title) => {                  self.editing.title = title; -                Command::none() +                Task::none()              }              Message::Body(body) => {                  self.editing.body = body; -                Command::none() +                Task::none()              }              Message::Status(status) => {                  self.editing.status = status; -                Command::none() +                Task::none()              }              Message::Timeout(timeout) => {                  self.timeout_secs = timeout as u64; -                Command::none() +                Task::none()              }              Message::Event(Event::Keyboard(keyboard::Event::KeyPressed {                  key: keyboard::Key::Named(key::Named::Tab), @@ -88,7 +88,7 @@ impl App {                  key: keyboard::Key::Named(key::Named::Tab),                  ..              })) => widget::focus_next(), -            Message::Event(_) => Command::none(), +            Message::Event(_) => Task::none(),          }      } @@ -347,7 +347,7 @@ mod toast {              state: &mut Tree,              layout: Layout<'_>,              renderer: &Renderer, -            operation: &mut dyn Operation<Message>, +            operation: &mut dyn Operation<()>,          ) {              operation.container(None, layout.bounds(), &mut |operation| {                  self.content.as_widget().operate( @@ -589,7 +589,7 @@ mod toast {              &mut self,              layout: Layout<'_>,              renderer: &Renderer, -            operation: &mut dyn widget::Operation<Message>, +            operation: &mut dyn widget::Operation<()>,          ) {              operation.container(None, layout.bounds(), &mut |operation| {                  self.toasts diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index dd1e5213..c21e1a96 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -5,7 +5,7 @@ use iced::widget::{      scrollable, text, text_input, Text,  };  use iced::window; -use iced::{Command, Element, Font, Length, Subscription}; +use iced::{Element, Font, Length, Subscription, Task as Command};  use once_cell::sync::Lazy;  use serde::{Deserialize, Serialize}; diff --git a/examples/visible_bounds/src/main.rs b/examples/visible_bounds/src/main.rs index 8030f5b4..b43c0cca 100644 --- a/examples/visible_bounds/src/main.rs +++ b/examples/visible_bounds/src/main.rs @@ -5,8 +5,8 @@ use iced::widget::{  };  use iced::window;  use iced::{ -    Alignment, Color, Command, Element, Font, Length, Point, Rectangle, -    Subscription, Theme, +    Alignment, Color, Element, Font, Length, Point, Rectangle, Subscription, +    Task, Theme,  };  pub fn main() -> iced::Result { @@ -33,14 +33,14 @@ enum Message {  }  impl Example { -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::MouseMoved(position) => {                  self.mouse_position = Some(position); -                Command::none() +                Task::none()              } -            Message::Scrolled | Message::WindowResized => Command::batch(vec![ +            Message::Scrolled | Message::WindowResized => Task::batch(vec![                  container::visible_bounds(OUTER_CONTAINER.clone())                      .map(Message::OuterBoundsFetched),                  container::visible_bounds(INNER_CONTAINER.clone()) @@ -49,12 +49,12 @@ impl Example {              Message::OuterBoundsFetched(outer_bounds) => {                  self.outer_bounds = outer_bounds; -                Command::none() +                Task::none()              }              Message::InnerBoundsFetched(inner_bounds) => {                  self.inner_bounds = inner_bounds; -                Command::none() +                Task::none()              }          }      } diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index ba1e1029..8c0fa1d0 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -4,7 +4,7 @@ use iced::alignment::{self, Alignment};  use iced::widget::{      self, button, center, column, row, scrollable, text, text_input,  }; -use iced::{color, Command, Element, Length, Subscription}; +use iced::{color, Element, Length, Subscription, Task};  use once_cell::sync::Lazy;  pub fn main() -> iced::Result { @@ -30,19 +30,19 @@ enum Message {  }  impl WebSocket { -    fn load() -> Command<Message> { -        Command::batch([ -            Command::perform(echo::server::run(), |_| Message::Server), +    fn load() -> Task<Message> { +        Task::batch([ +            Task::perform(echo::server::run(), |_| Message::Server),              widget::focus_next(),          ])      } -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update(&mut self, message: Message) -> Task<Message> {          match message {              Message::NewMessageChanged(new_message) => {                  self.new_message = new_message; -                Command::none() +                Task::none()              }              Message::Send(message) => match &mut self.state {                  State::Connected(connection) => { @@ -50,9 +50,9 @@ impl WebSocket {                      connection.send(message); -                    Command::none() +                    Task::none()                  } -                State::Disconnected => Command::none(), +                State::Disconnected => Task::none(),              },              Message::Echo(event) => match event {                  echo::Event::Connected(connection) => { @@ -60,14 +60,14 @@ impl WebSocket {                      self.messages.push(echo::Message::connected()); -                    Command::none() +                    Task::none()                  }                  echo::Event::Disconnected => {                      self.state = State::Disconnected;                      self.messages.push(echo::Message::disconnected()); -                    Command::none() +                    Task::none()                  }                  echo::Event::MessageReceived(message) => {                      self.messages.push(message); @@ -78,7 +78,7 @@ impl WebSocket {                      )                  }              }, -            Message::Server => Command::none(), +            Message::Server => Task::none(),          }      } diff --git a/futures/src/event.rs b/futures/src/event.rs index 72ea78ad..ab895fcd 100644 --- a/futures/src/event.rs +++ b/futures/src/event.rs @@ -1,8 +1,8 @@  //! Listen to runtime events.  use crate::core::event::{self, Event};  use crate::core::window; +use crate::core::MaybeSend;  use crate::subscription::{self, Subscription}; -use crate::MaybeSend;  /// Returns a [`Subscription`] to all the ignored runtime events.  /// diff --git a/futures/src/executor.rs b/futures/src/executor.rs index 5ac76081..a9dde465 100644 --- a/futures/src/executor.rs +++ b/futures/src/executor.rs @@ -1,5 +1,5 @@  //! Choose your preferred executor to power a runtime. -use crate::MaybeSend; +use crate::core::MaybeSend;  use futures::Future;  /// A type that can run futures. diff --git a/futures/src/keyboard.rs b/futures/src/keyboard.rs index f0d7d757..c86e2169 100644 --- a/futures/src/keyboard.rs +++ b/futures/src/keyboard.rs @@ -2,8 +2,8 @@  use crate::core;  use crate::core::event;  use crate::core::keyboard::{Event, Key, Modifiers}; +use crate::core::MaybeSend;  use crate::subscription::{self, Subscription}; -use crate::MaybeSend;  /// Listens to keyboard key presses and calls the given function  /// map them into actual messages. diff --git a/futures/src/lib.rs b/futures/src/lib.rs index a874a618..01b56306 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -8,7 +8,6 @@  pub use futures;  pub use iced_core as core; -mod maybe;  mod runtime;  pub mod backend; @@ -18,7 +17,6 @@ pub mod keyboard;  pub mod subscription;  pub use executor::Executor; -pub use maybe::{MaybeSend, MaybeSync};  pub use platform::*;  pub use runtime::Runtime;  pub use subscription::Subscription; diff --git a/futures/src/runtime.rs b/futures/src/runtime.rs index 157e2c67..045fde6c 100644 --- a/futures/src/runtime.rs +++ b/futures/src/runtime.rs @@ -1,6 +1,7 @@  //! Run commands and keep track of subscriptions. +use crate::core::MaybeSend;  use crate::subscription; -use crate::{BoxFuture, BoxStream, Executor, MaybeSend}; +use crate::{BoxFuture, BoxStream, Executor};  use futures::{channel::mpsc, Sink};  use std::marker::PhantomData; diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs index 316fc44d..85a8a787 100644 --- a/futures/src/subscription.rs +++ b/futures/src/subscription.rs @@ -5,8 +5,9 @@ pub use tracker::Tracker;  use crate::core::event;  use crate::core::window; +use crate::core::MaybeSend;  use crate::futures::{Future, Stream}; -use crate::{BoxStream, MaybeSend}; +use crate::BoxStream;  use futures::channel::mpsc;  use futures::never::Never; diff --git a/futures/src/subscription/tracker.rs b/futures/src/subscription/tracker.rs index f17e3ea3..c5a7bb99 100644 --- a/futures/src/subscription/tracker.rs +++ b/futures/src/subscription/tracker.rs @@ -1,5 +1,6 @@ +use crate::core::MaybeSend;  use crate::subscription::{Event, Hasher, Recipe}; -use crate::{BoxFuture, MaybeSend}; +use crate::BoxFuture;  use futures::channel::mpsc;  use futures::sink::{Sink, SinkExt}; diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs index 47521eb0..4ac90f92 100644 --- a/graphics/src/compositor.rs +++ b/graphics/src/compositor.rs @@ -1,7 +1,6 @@  //! A compositor is responsible for initializing a renderer and managing window  //! surfaces. -use crate::core::Color; -use crate::futures::{MaybeSend, MaybeSync}; +use crate::core::{Color, MaybeSend, MaybeSync};  use crate::{Error, Settings, Viewport};  use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; diff --git a/runtime/src/clipboard.rs b/runtime/src/clipboard.rs index dd47c47d..19950d01 100644 --- a/runtime/src/clipboard.rs +++ b/runtime/src/clipboard.rs @@ -1,80 +1,62 @@  //! Access the clipboard. -use crate::command::{self, Command};  use crate::core::clipboard::Kind; -use crate::futures::MaybeSend; +use crate::futures::futures::channel::oneshot; +use crate::Task; -use std::fmt; - -/// A clipboard action to be performed by some [`Command`]. +/// A clipboard action to be performed by some [`Task`].  /// -/// [`Command`]: crate::Command -pub enum Action<T> { +/// [`Task`]: crate::Task +#[derive(Debug)] +pub enum Action {      /// Read the clipboard and produce `T` with the result. -    Read(Box<dyn Fn(Option<String>) -> T>, Kind), +    Read { +        /// The clipboard target. +        target: Kind, +        /// The channel to send the read contents. +        channel: oneshot::Sender<Option<String>>, +    },      /// Write the given contents to the clipboard. -    Write(String, Kind), -} - -impl<T> Action<T> { -    /// Maps the output of a clipboard [`Action`] using the provided closure. -    pub fn map<A>( -        self, -        f: impl Fn(T) -> A + 'static + MaybeSend + Sync, -    ) -> Action<A> -    where -        T: 'static, -    { -        match self { -            Self::Read(o, target) => { -                Action::Read(Box::new(move |s| f(o(s))), target) -            } -            Self::Write(content, target) => Action::Write(content, target), -        } -    } -} - -impl<T> fmt::Debug for Action<T> { -    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -        match self { -            Self::Read(_, target) => write!(f, "Action::Read{target:?}"), -            Self::Write(_, target) => write!(f, "Action::Write({target:?})"), -        } -    } +    Write { +        /// The clipboard target. +        target: Kind, +        /// The contents to be written. +        contents: String, +    },  }  /// Read the current contents of the clipboard. -pub fn read<Message>( -    f: impl Fn(Option<String>) -> Message + 'static, -) -> Command<Message> { -    Command::single(command::Action::Clipboard(Action::Read( -        Box::new(f), -        Kind::Standard, -    ))) +pub fn read() -> Task<Option<String>> { +    Task::oneshot(|channel| { +        crate::Action::Clipboard(Action::Read { +            target: Kind::Standard, +            channel, +        }) +    })  }  /// Read the current contents of the primary clipboard. -pub fn read_primary<Message>( -    f: impl Fn(Option<String>) -> Message + 'static, -) -> Command<Message> { -    Command::single(command::Action::Clipboard(Action::Read( -        Box::new(f), -        Kind::Primary, -    ))) +pub fn read_primary() -> Task<Option<String>> { +    Task::oneshot(|channel| { +        crate::Action::Clipboard(Action::Read { +            target: Kind::Primary, +            channel, +        }) +    })  }  /// Write the given contents to the clipboard. -pub fn write<Message>(contents: String) -> Command<Message> { -    Command::single(command::Action::Clipboard(Action::Write( +pub fn write<T>(contents: String) -> Task<T> { +    Task::effect(crate::Action::Clipboard(Action::Write { +        target: Kind::Standard,          contents, -        Kind::Standard, -    ))) +    }))  }  /// Write the given contents to the primary clipboard. -pub fn write_primary<Message>(contents: String) -> Command<Message> { -    Command::single(command::Action::Clipboard(Action::Write( +pub fn write_primary<Message>(contents: String) -> Task<Message> { +    Task::effect(crate::Action::Clipboard(Action::Write { +        target: Kind::Primary,          contents, -        Kind::Primary, -    ))) +    }))  } diff --git a/runtime/src/command.rs b/runtime/src/command.rs deleted file mode 100644 index f7a746fe..00000000 --- a/runtime/src/command.rs +++ /dev/null @@ -1,147 +0,0 @@ -//! Run asynchronous actions. -mod action; - -pub use action::Action; - -use crate::core::widget; -use crate::futures::futures; -use crate::futures::MaybeSend; - -use futures::channel::mpsc; -use futures::Stream; -use std::fmt; -use std::future::Future; - -/// A set of asynchronous actions to be performed by some runtime. -#[must_use = "`Command` must be returned to runtime to take effect"] -pub struct Command<T>(Internal<Action<T>>); - -#[derive(Debug)] -enum Internal<T> { -    None, -    Single(T), -    Batch(Vec<T>), -} - -impl<T> Command<T> { -    /// Creates an empty [`Command`]. -    /// -    /// In other words, a [`Command`] that does nothing. -    pub const fn none() -> Self { -        Self(Internal::None) -    } - -    /// Creates a [`Command`] that performs a single [`Action`]. -    pub const fn single(action: Action<T>) -> Self { -        Self(Internal::Single(action)) -    } - -    /// Creates a [`Command`] that performs a [`widget::Operation`]. -    pub fn widget(operation: impl widget::Operation<T> + 'static) -> Self { -        Self::single(Action::Widget(Box::new(operation))) -    } - -    /// Creates a [`Command`] that performs the action of the given future. -    pub fn perform<A>( -        future: impl Future<Output = A> + 'static + MaybeSend, -        f: impl FnOnce(A) -> T + 'static + MaybeSend, -    ) -> Command<T> { -        use futures::FutureExt; - -        Command::single(Action::Future(Box::pin(future.map(f)))) -    } - -    /// Creates a [`Command`] that runs the given stream to completion. -    pub fn run<A>( -        stream: impl Stream<Item = A> + 'static + MaybeSend, -        f: impl Fn(A) -> T + 'static + MaybeSend, -    ) -> Command<T> { -        use futures::StreamExt; - -        Command::single(Action::Stream(Box::pin(stream.map(f)))) -    } - -    /// 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(command) in commands { -            match command { -                Internal::None => {} -                Internal::Single(command) => batch.push(command), -                Internal::Batch(commands) => batch.extend(commands), -            } -        } - -        Self(Internal::Batch(batch)) -    } - -    /// Applies a transformation to the result of a [`Command`]. -    pub fn map<A>( -        self, -        f: impl Fn(T) -> A + 'static + MaybeSend + Sync + Clone, -    ) -> Command<A> -    where -        T: 'static, -        A: 'static, -    { -        match self.0 { -            Internal::None => Command::none(), -            Internal::Single(action) => Command::single(action.map(f)), -            Internal::Batch(batch) => Command(Internal::Batch( -                batch -                    .into_iter() -                    .map(|action| action.map(f.clone())) -                    .collect(), -            )), -        } -    } - -    /// Returns all of the actions of the [`Command`]. -    pub fn actions(self) -> Vec<Action<T>> { -        let Command(command) = self; - -        match command { -            Internal::None => Vec::new(), -            Internal::Single(action) => vec![action], -            Internal::Batch(batch) => batch, -        } -    } -} - -impl<Message> From<()> for Command<Message> { -    fn from(_value: ()) -> Self { -        Self::none() -    } -} - -impl<T> fmt::Debug for Command<T> { -    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -        let Command(command) = self; - -        command.fmt(f) -    } -} - -/// Creates a [`Command`] that produces the `Message`s published from a [`Future`] -/// to an [`mpsc::Sender`] with the given bounds. -pub fn channel<Fut, Message>( -    size: usize, -    f: impl FnOnce(mpsc::Sender<Message>) -> Fut + MaybeSend + 'static, -) -> Command<Message> -where -    Fut: Future<Output = ()> + MaybeSend + 'static, -    Message: 'static + MaybeSend, -{ -    use futures::future; -    use futures::stream::{self, StreamExt}; - -    let (sender, receiver) = mpsc::channel(size); - -    let runner = stream::once(f(sender)).filter_map(|_| future::ready(None)); - -    Command::single(Action::Stream(Box::pin(stream::select(receiver, runner)))) -} diff --git a/runtime/src/command/action.rs b/runtime/src/command/action.rs deleted file mode 100644 index c9ffe801..00000000 --- a/runtime/src/command/action.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crate::clipboard; -use crate::core::widget; -use crate::font; -use crate::futures::MaybeSend; -use crate::system; -use crate::window; - -use std::any::Any; -use std::borrow::Cow; -use std::fmt; - -/// An action that a [`Command`] can perform. -/// -/// [`Command`]: crate::Command -pub enum Action<T> { -    /// Run a [`Future`] to completion. -    /// -    /// [`Future`]: iced_futures::BoxFuture -    Future(iced_futures::BoxFuture<T>), - -    /// Run a [`Stream`] to completion. -    /// -    /// [`Stream`]: iced_futures::BoxStream -    Stream(iced_futures::BoxStream<T>), - -    /// Run a clipboard action. -    Clipboard(clipboard::Action<T>), - -    /// Run a window action. -    Window(window::Action<T>), - -    /// Run a system action. -    System(system::Action<T>), - -    /// Run a widget action. -    Widget(Box<dyn widget::Operation<T>>), - -    /// Load a font from its bytes. -    LoadFont { -        /// The bytes of the font to load. -        bytes: Cow<'static, [u8]>, - -        /// The message to produce when the font has been loaded. -        tagger: Box<dyn Fn(Result<(), font::Error>) -> T>, -    }, - -    /// A custom action supported by a specific runtime. -    Custom(Box<dyn Any>), -} - -impl<T> Action<T> { -    /// Applies a transformation to the result of a [`Command`]. -    /// -    /// [`Command`]: crate::Command -    pub fn map<A>( -        self, -        f: impl Fn(T) -> A + 'static + MaybeSend + Sync, -    ) -> Action<A> -    where -        A: 'static, -        T: 'static, -    { -        use iced_futures::futures::{FutureExt, StreamExt}; - -        match self { -            Self::Future(future) => Action::Future(Box::pin(future.map(f))), -            Self::Stream(stream) => Action::Stream(Box::pin(stream.map(f))), -            Self::Clipboard(action) => Action::Clipboard(action.map(f)), -            Self::Window(window) => Action::Window(window.map(f)), -            Self::System(system) => Action::System(system.map(f)), -            Self::Widget(operation) => { -                Action::Widget(Box::new(widget::operation::map(operation, f))) -            } -            Self::LoadFont { bytes, tagger } => Action::LoadFont { -                bytes, -                tagger: Box::new(move |result| f(tagger(result))), -            }, -            Self::Custom(custom) => Action::Custom(custom), -        } -    } -} - -impl<T> fmt::Debug for Action<T> { -    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -        match self { -            Self::Future(_) => write!(f, "Action::Future"), -            Self::Stream(_) => write!(f, "Action::Stream"), -            Self::Clipboard(action) => { -                write!(f, "Action::Clipboard({action:?})") -            } -            Self::Window(action) => { -                write!(f, "Action::Window({action:?})") -            } -            Self::System(action) => write!(f, "Action::System({action:?})"), -            Self::Widget(_action) => write!(f, "Action::Widget"), -            Self::LoadFont { .. } => write!(f, "Action::LoadFont"), -            Self::Custom(_) => write!(f, "Action::Custom"), -        } -    } -} diff --git a/runtime/src/font.rs b/runtime/src/font.rs index 15359694..d54eb6a8 100644 --- a/runtime/src/font.rs +++ b/runtime/src/font.rs @@ -1,7 +1,5 @@  //! Load and use fonts. -pub use iced_core::font::*; - -use crate::command::{self, Command}; +use crate::{Action, Task};  use std::borrow::Cow;  /// An error while loading a font. @@ -9,11 +7,9 @@ use std::borrow::Cow;  pub enum Error {}  /// Load a font from its bytes. -pub fn load( -    bytes: impl Into<Cow<'static, [u8]>>, -) -> Command<Result<(), Error>> { -    Command::single(command::Action::LoadFont { +pub fn load(bytes: impl Into<Cow<'static, [u8]>>) -> Task<Result<(), Error>> { +    Task::oneshot(|channel| Action::LoadFont {          bytes: bytes.into(), -        tagger: Box::new(std::convert::identity), +        channel,      })  } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5f054c46..5fde3039 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -10,7 +10,6 @@  )]  #![cfg_attr(docsrs, feature(doc_auto_cfg))]  pub mod clipboard; -pub mod command;  pub mod font;  pub mod keyboard;  pub mod overlay; @@ -22,6 +21,8 @@ pub mod window;  #[cfg(feature = "multi-window")]  pub mod multi_window; +mod task; +  // We disable debug capabilities on release builds unless the `debug` feature  // is explicitly enabled.  #[cfg(feature = "debug")] @@ -34,8 +35,81 @@ mod debug;  pub use iced_core as core;  pub use iced_futures as futures; -pub use command::Command;  pub use debug::Debug; -pub use font::Font;  pub use program::Program; +pub use task::Task;  pub use user_interface::UserInterface; + +use crate::core::widget; +use crate::futures::futures::channel::oneshot; + +use std::borrow::Cow; +use std::fmt; + +/// An action that the iced runtime can perform. +pub enum Action<T> { +    /// Output some value. +    Output(T), + +    /// Load a font from its bytes. +    LoadFont { +        /// The bytes of the font to load. +        bytes: Cow<'static, [u8]>, +        /// The channel to send back the load result. +        channel: oneshot::Sender<Result<(), font::Error>>, +    }, + +    /// Run a widget operation. +    Widget(Box<dyn widget::Operation<()> + Send>), + +    /// Run a clipboard action. +    Clipboard(clipboard::Action), + +    /// Run a window action. +    Window(window::Action), + +    /// Run a system action. +    System(system::Action), +} + +impl<T> Action<T> { +    /// Creates a new [`Action::Widget`] with the given [`widget::Operation`]. +    pub fn widget(operation: impl widget::Operation<()> + 'static) -> Self { +        Self::Widget(Box::new(operation)) +    } + +    fn output<O>(self) -> Result<T, Action<O>> { +        match self { +            Action::Output(output) => Ok(output), +            Action::LoadFont { bytes, channel } => { +                Err(Action::LoadFont { bytes, channel }) +            } +            Action::Widget(operation) => Err(Action::Widget(operation)), +            Action::Clipboard(action) => Err(Action::Clipboard(action)), +            Action::Window(action) => Err(Action::Window(action)), +            Action::System(action) => Err(Action::System(action)), +        } +    } +} + +impl<T> fmt::Debug for Action<T> +where +    T: fmt::Debug, +{ +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +        match self { +            Action::Output(output) => write!(f, "Action::Output({output:?})"), +            Action::LoadFont { .. } => { +                write!(f, "Action::LoadFont") +            } +            Action::Widget { .. } => { +                write!(f, "Action::Widget") +            } +            Action::Clipboard(action) => { +                write!(f, "Action::Clipboard({action:?})") +            } +            Action::Window(_) => write!(f, "Action::Window"), +            Action::System(action) => write!(f, "Action::System({action:?})"), +        } +    } +} diff --git a/runtime/src/multi_window/program.rs b/runtime/src/multi_window/program.rs index 963a09d7..e8c71b26 100644 --- a/runtime/src/multi_window/program.rs +++ b/runtime/src/multi_window/program.rs @@ -2,7 +2,7 @@  use crate::core::text;  use crate::core::window;  use crate::core::{Element, Renderer}; -use crate::Command; +use crate::Task;  /// The core of a user interface for a multi-window application following The Elm Architecture.  pub trait Program: Sized { @@ -21,9 +21,9 @@ pub trait Program: Sized {      /// produced by either user interactions or commands, will be handled by      /// this method.      /// -    /// Any [`Command`] returned will be executed immediately in the -    /// background by shells. -    fn update(&mut self, message: Self::Message) -> Command<Self::Message>; +    /// Any [`Task`] returned will be executed immediately in the background by the +    /// runtime. +    fn update(&mut self, message: Self::Message) -> Task<Self::Message>;      /// Returns the widgets to display in the [`Program`] for the `window`.      /// diff --git a/runtime/src/multi_window/state.rs b/runtime/src/multi_window/state.rs index 10366ec0..72ce6933 100644 --- a/runtime/src/multi_window/state.rs +++ b/runtime/src/multi_window/state.rs @@ -5,7 +5,7 @@ use crate::core::renderer;  use crate::core::widget::operation::{self, Operation};  use crate::core::{Clipboard, Size};  use crate::user_interface::{self, UserInterface}; -use crate::{Command, Debug, Program}; +use crate::{Debug, Program, Task};  /// The execution state of a multi-window [`Program`]. It leverages caching, event  /// processing, and rendering primitive storage. @@ -85,7 +85,7 @@ where      /// the widgets of the linked [`Program`] if necessary.      ///      /// Returns a list containing the instances of [`Event`] that were not -    /// captured by any widget, and the [`Command`] obtained from [`Program`] +    /// captured by any widget, and the [`Task`] obtained from [`Program`]      /// after updating it, only if an update was necessary.      pub fn update(          &mut self, @@ -96,7 +96,7 @@ where          style: &renderer::Style,          clipboard: &mut dyn Clipboard,          debug: &mut Debug, -    ) -> (Vec<Event>, Option<Command<P::Message>>) { +    ) -> (Vec<Event>, Option<Task<P::Message>>) {          let mut user_interfaces = build_user_interfaces(              &self.program,              self.caches.take().unwrap(), @@ -163,14 +163,14 @@ where              drop(user_interfaces); -            let commands = Command::batch(messages.into_iter().map(|msg| { +            let commands = Task::batch(messages.into_iter().map(|msg| {                  debug.log_message(&msg);                  debug.update_started(); -                let command = self.program.update(msg); +                let task = self.program.update(msg);                  debug.update_finished(); -                command +                task              }));              let mut user_interfaces = build_user_interfaces( @@ -205,7 +205,7 @@ where      pub fn operate(          &mut self,          renderer: &mut P::Renderer, -        operations: impl Iterator<Item = Box<dyn Operation<P::Message>>>, +        operations: impl Iterator<Item = Box<dyn Operation<()>>>,          bounds: Size,          debug: &mut Debug,      ) { @@ -227,9 +227,7 @@ where                  match operation.finish() {                      operation::Outcome::None => {} -                    operation::Outcome::Some(message) => { -                        self.queued_messages.push(message); -                    } +                    operation::Outcome::Some(()) => {}                      operation::Outcome::Chain(next) => {                          current_operation = Some(next);                      } diff --git a/runtime/src/overlay/nested.rs b/runtime/src/overlay/nested.rs index ddb9532b..11eee41c 100644 --- a/runtime/src/overlay/nested.rs +++ b/runtime/src/overlay/nested.rs @@ -131,13 +131,13 @@ where          &mut self,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          fn recurse<Message, Theme, Renderer>(              element: &mut overlay::Element<'_, Message, Theme, Renderer>,              layout: Layout<'_>,              renderer: &Renderer, -            operation: &mut dyn widget::Operation<Message>, +            operation: &mut dyn widget::Operation<()>,          ) where              Renderer: renderer::Renderer,          { diff --git a/runtime/src/program.rs b/runtime/src/program.rs index 0ea94d3b..77acf497 100644 --- a/runtime/src/program.rs +++ b/runtime/src/program.rs @@ -1,5 +1,5 @@  //! Build interactive programs using The Elm Architecture. -use crate::Command; +use crate::Task;  use iced_core::text;  use iced_core::Element; @@ -25,9 +25,9 @@ pub trait Program: Sized {      /// produced by either user interactions or commands, will be handled by      /// this method.      /// -    /// Any [`Command`] returned will be executed immediately in the +    /// Any [`Task`] returned will be executed immediately in the      /// background by shells. -    fn update(&mut self, message: Self::Message) -> Command<Self::Message>; +    fn update(&mut self, message: Self::Message) -> Task<Self::Message>;      /// Returns the widgets to display in the [`Program`].      /// diff --git a/runtime/src/program/state.rs b/runtime/src/program/state.rs index c6589c22..e51ad0cb 100644 --- a/runtime/src/program/state.rs +++ b/runtime/src/program/state.rs @@ -4,7 +4,7 @@ use crate::core::renderer;  use crate::core::widget::operation::{self, Operation};  use crate::core::{Clipboard, Size};  use crate::user_interface::{self, UserInterface}; -use crate::{Command, Debug, Program}; +use crate::{Debug, Program, Task};  /// The execution state of a [`Program`]. It leverages caching, event  /// processing, and rendering primitive storage. @@ -84,7 +84,7 @@ where      /// the widgets of the linked [`Program`] if necessary.      ///      /// Returns a list containing the instances of [`Event`] that were not -    /// captured by any widget, and the [`Command`] obtained from [`Program`] +    /// captured by any widget, and the [`Task`] obtained from [`Program`]      /// after updating it, only if an update was necessary.      pub fn update(          &mut self, @@ -95,7 +95,7 @@ where          style: &renderer::Style,          clipboard: &mut dyn Clipboard,          debug: &mut Debug, -    ) -> (Vec<Event>, Option<Command<P::Message>>) { +    ) -> (Vec<Event>, Option<Task<P::Message>>) {          let mut user_interface = build_user_interface(              &mut self.program,              self.cache.take().unwrap(), @@ -129,7 +129,7 @@ where          messages.append(&mut self.queued_messages);          debug.event_processing_finished(); -        let command = if messages.is_empty() { +        let task = if messages.is_empty() {              debug.draw_started();              self.mouse_interaction =                  user_interface.draw(renderer, theme, style, cursor); @@ -143,16 +143,15 @@ where              // for now :^)              let temp_cache = user_interface.into_cache(); -            let commands = -                Command::batch(messages.into_iter().map(|message| { -                    debug.log_message(&message); +            let tasks = Task::batch(messages.into_iter().map(|message| { +                debug.log_message(&message); -                    debug.update_started(); -                    let command = self.program.update(message); -                    debug.update_finished(); +                debug.update_started(); +                let task = self.program.update(message); +                debug.update_finished(); -                    command -                })); +                task +            }));              let mut user_interface = build_user_interface(                  &mut self.program, @@ -169,17 +168,17 @@ where              self.cache = Some(user_interface.into_cache()); -            Some(commands) +            Some(tasks)          }; -        (uncaptured_events, command) +        (uncaptured_events, task)      }      /// Applies [`Operation`]s to the [`State`]      pub fn operate(          &mut self,          renderer: &mut P::Renderer, -        operations: impl Iterator<Item = Box<dyn Operation<P::Message>>>, +        operations: impl Iterator<Item = Box<dyn Operation<()>>>,          bounds: Size,          debug: &mut Debug,      ) { @@ -199,9 +198,7 @@ where                  match operation.finish() {                      operation::Outcome::None => {} -                    operation::Outcome::Some(message) => { -                        self.queued_messages.push(message); -                    } +                    operation::Outcome::Some(()) => {}                      operation::Outcome::Chain(next) => {                          current_operation = Some(next);                      } diff --git a/runtime/src/system.rs b/runtime/src/system.rs index 61c8ff29..b6fb4fdf 100644 --- a/runtime/src/system.rs +++ b/runtime/src/system.rs @@ -1,6 +1,39 @@  //! Access the native system. -mod action; -mod information; +use crate::futures::futures::channel::oneshot; -pub use action::Action; -pub use information::Information; +/// An operation to be performed on the system. +#[derive(Debug)] +pub enum Action { +    /// Query system information and produce `T` with the result. +    QueryInformation(oneshot::Sender<Information>), +} + +/// Contains informations about the system (e.g. system name, processor, memory, graphics adapter). +#[derive(Clone, Debug)] +pub struct Information { +    /// The operating system name +    pub system_name: Option<String>, +    /// Operating system kernel version +    pub system_kernel: Option<String>, +    /// Long operating system version +    /// +    /// Examples: +    /// - MacOS 10.15 Catalina +    /// - Windows 10 Pro +    /// - Ubuntu 20.04 LTS (Focal Fossa) +    pub system_version: Option<String>, +    /// Short operating system version number +    pub system_short_version: Option<String>, +    /// Detailed processor model information +    pub cpu_brand: String, +    /// The number of physical cores on the processor +    pub cpu_cores: Option<usize>, +    /// Total RAM size, in bytes +    pub memory_total: u64, +    /// Memory used by this process, in bytes +    pub memory_used: Option<u64>, +    /// Underlying graphics backend for rendering +    pub graphics_backend: String, +    /// Model information for the active graphics adapter +    pub graphics_adapter: String, +} diff --git a/runtime/src/system/action.rs b/runtime/src/system/action.rs deleted file mode 100644 index dea9536f..00000000 --- a/runtime/src/system/action.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::system; - -use iced_futures::MaybeSend; -use std::fmt; - -/// An operation to be performed on the system. -pub enum Action<T> { -    /// Query system information and produce `T` with the result. -    QueryInformation(Box<dyn Closure<T>>), -} - -pub trait Closure<T>: Fn(system::Information) -> T + MaybeSend {} - -impl<T, O> Closure<O> for T where T: Fn(system::Information) -> O + MaybeSend {} - -impl<T> Action<T> { -    /// Maps the output of a system [`Action`] using the provided closure. -    pub fn map<A>( -        self, -        f: impl Fn(T) -> A + 'static + MaybeSend + Sync, -    ) -> Action<A> -    where -        T: 'static, -    { -        match self { -            Self::QueryInformation(o) => { -                Action::QueryInformation(Box::new(move |s| f(o(s)))) -            } -        } -    } -} - -impl<T> fmt::Debug for Action<T> { -    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -        match self { -            Self::QueryInformation(_) => write!(f, "Action::QueryInformation"), -        } -    } -} diff --git a/runtime/src/system/information.rs b/runtime/src/system/information.rs deleted file mode 100644 index 0f78f5e9..00000000 --- a/runtime/src/system/information.rs +++ /dev/null @@ -1,29 +0,0 @@ -/// Contains informations about the system (e.g. system name, processor, memory, graphics adapter). -#[derive(Clone, Debug)] -pub struct Information { -    /// The operating system name -    pub system_name: Option<String>, -    /// Operating system kernel version -    pub system_kernel: Option<String>, -    /// Long operating system version -    /// -    /// Examples: -    /// - MacOS 10.15 Catalina -    /// - Windows 10 Pro -    /// - Ubuntu 20.04 LTS (Focal Fossa) -    pub system_version: Option<String>, -    /// Short operating system version number -    pub system_short_version: Option<String>, -    /// Detailed processor model information -    pub cpu_brand: String, -    /// The number of physical cores on the processor -    pub cpu_cores: Option<usize>, -    /// Total RAM size, in bytes -    pub memory_total: u64, -    /// Memory used by this process, in bytes -    pub memory_used: Option<u64>, -    /// Underlying graphics backend for rendering -    pub graphics_backend: String, -    /// Model information for the active graphics adapter -    pub graphics_adapter: String, -} diff --git a/runtime/src/task.rs b/runtime/src/task.rs new file mode 100644 index 00000000..ac28a4e7 --- /dev/null +++ b/runtime/src/task.rs @@ -0,0 +1,214 @@ +use crate::core::widget; +use crate::core::MaybeSend; +use crate::futures::futures::channel::mpsc; +use crate::futures::futures::channel::oneshot; +use crate::futures::futures::future::{self, FutureExt}; +use crate::futures::futures::never::Never; +use crate::futures::futures::stream::{self, Stream, StreamExt}; +use crate::futures::{boxed_stream, BoxStream}; +use crate::Action; + +use std::future::Future; + +/// A set of concurrent actions to be performed by the iced runtime. +/// +/// A [`Task`] _may_ produce a bunch of values of type `T`. +#[allow(missing_debug_implementations)] +pub struct Task<T>(Option<BoxStream<Action<T>>>); + +impl<T> Task<T> { +    /// Creates a [`Task`] that does nothing. +    pub fn none() -> Self { +        Self(None) +    } + +    /// Creates a new [`Task`] that instantly produces the given value. +    pub fn done(value: T) -> Self +    where +        T: MaybeSend + 'static, +    { +        Self::future(future::ready(value)) +    } + +    /// Creates a new [`Task`] that runs the given [`Future`] and produces +    /// its output. +    pub fn future(future: impl Future<Output = T> + MaybeSend + 'static) -> Self +    where +        T: 'static, +    { +        Self::stream(stream::once(future)) +    } + +    /// Creates a new [`Task`] that runs the given [`Stream`] and produces +    /// each of its items. +    pub fn stream(stream: impl Stream<Item = T> + MaybeSend + 'static) -> Self +    where +        T: 'static, +    { +        Self(Some(boxed_stream(stream.map(Action::Output)))) +    } + +    /// Creates a new [`Task`] that runs the given [`widget::Operation`] and produces +    /// its output. +    pub fn widget(operation: impl widget::Operation<T> + 'static) -> Task<T> +    where +        T: MaybeSend + 'static, +    { +        Self::channel(move |sender| { +            let operation = +                widget::operation::map(Box::new(operation), move |value| { +                    let _ = sender.clone().try_send(value); +                }); + +            Action::Widget(Box::new(operation)) +        }) +    } + +    /// Creates a new [`Task`] that executes the [`Action`] returned by the closure and +    /// produces the value fed to the [`oneshot::Sender`]. +    pub fn oneshot(f: impl FnOnce(oneshot::Sender<T>) -> Action<T>) -> Task<T> +    where +        T: MaybeSend + 'static, +    { +        let (sender, receiver) = oneshot::channel(); + +        let action = f(sender); + +        Self(Some(boxed_stream( +            stream::once(async move { action }).chain( +                receiver.into_stream().filter_map(|result| async move { +                    Some(Action::Output(result.ok()?)) +                }), +            ), +        ))) +    } + +    /// Creates a new [`Task`] that executes the [`Action`] returned by the closure and +    /// produces the values fed to the [`mpsc::Sender`]. +    pub fn channel(f: impl FnOnce(mpsc::Sender<T>) -> Action<T>) -> Task<T> +    where +        T: MaybeSend + 'static, +    { +        let (sender, receiver) = mpsc::channel(1); + +        let action = f(sender); + +        Self(Some(boxed_stream( +            stream::once(async move { action }) +                .chain(receiver.map(|result| Action::Output(result))), +        ))) +    } + +    /// Creates a new [`Task`] that executes the given [`Action`] and produces no output. +    pub fn effect(action: impl Into<Action<Never>>) -> Self { +        let action = action.into(); + +        Self(Some(boxed_stream(stream::once(async move { +            action.output().expect_err("no output") +        })))) +    } + +    /// Maps the output of a [`Task`] with the given closure. +    pub fn map<O>( +        self, +        mut f: impl FnMut(T) -> O + MaybeSend + 'static, +    ) -> Task<O> +    where +        T: MaybeSend + 'static, +        O: MaybeSend + 'static, +    { +        self.then(move |output| Task::done(f(output))) +    } + +    /// Performs a new [`Task`] for every output of the current [`Task`] using the +    /// given closure. +    /// +    /// This is the monadic interface of [`Task`]—analogous to [`Future`] and +    /// [`Stream`]. +    pub fn then<O>( +        self, +        mut f: impl FnMut(T) -> Task<O> + MaybeSend + 'static, +    ) -> Task<O> +    where +        T: MaybeSend + 'static, +        O: MaybeSend + 'static, +    { +        Task(match self.0 { +            None => None, +            Some(stream) => { +                Some(boxed_stream(stream.flat_map(move |action| { +                    match action.output() { +                        Ok(output) => f(output) +                            .0 +                            .unwrap_or_else(|| boxed_stream(stream::empty())), +                        Err(action) => { +                            boxed_stream(stream::once(async move { action })) +                        } +                    } +                }))) +            } +        }) +    } + +    /// Chains a new [`Task`] to be performed once the current one finishes completely. +    pub fn chain(self, task: Self) -> Self +    where +        T: 'static, +    { +        match self.0 { +            None => task, +            Some(first) => match task.0 { +                None => Task::none(), +                Some(second) => Task(Some(boxed_stream(first.chain(second)))), +            }, +        } +    } + +    /// Creates a [`Task`] that runs the given [`Future`] to completion. +    pub fn perform<A>( +        future: impl Future<Output = A> + MaybeSend + 'static, +        f: impl Fn(A) -> T + MaybeSend + 'static, +    ) -> Self +    where +        T: MaybeSend + 'static, +        A: MaybeSend + 'static, +    { +        Self::future(future.map(f)) +    } + +    /// Creates a [`Task`] that runs the given [`Stream`] to completion. +    pub fn run<A>( +        stream: impl Stream<Item = A> + MaybeSend + 'static, +        f: impl Fn(A) -> T + 'static + MaybeSend, +    ) -> Self +    where +        T: 'static, +    { +        Self::stream(stream.map(f)) +    } + +    /// Combines the given tasks and produces a single [`Task`] that will run all of them +    /// in parallel. +    pub fn batch(tasks: impl IntoIterator<Item = Self>) -> Self +    where +        T: 'static, +    { +        Self(Some(boxed_stream(stream::select_all( +            tasks.into_iter().filter_map(|task| task.0), +        )))) +    } + +    /// Returns the underlying [`Stream`] of the [`Task`]. +    pub fn into_stream(self) -> Option<BoxStream<Action<T>>> { +        self.0 +    } +} + +impl<T> From<()> for Task<T> +where +    T: MaybeSend + 'static, +{ +    fn from(_value: ()) -> Self { +        Self::none() +    } +} diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 006225ed..858b1a2d 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -566,7 +566,7 @@ where      pub fn operate(          &mut self,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          self.root.as_widget().operate(              &mut self.state, diff --git a/runtime/src/window.rs b/runtime/src/window.rs index b68c9a71..0876ab69 100644 --- a/runtime/src/window.rs +++ b/runtime/src/window.rs @@ -1,24 +1,145 @@  //! Build window-based GUI applications. -mod action; -  pub mod screenshot; -pub use action::Action;  pub use screenshot::Screenshot; -use crate::command::{self, Command};  use crate::core::time::Instant;  use crate::core::window::{      Event, Icon, Id, Level, Mode, Settings, UserAttention,  }; -use crate::core::{Point, Size}; +use crate::core::{MaybeSend, Point, Size};  use crate::futures::event; +use crate::futures::futures::channel::oneshot;  use crate::futures::Subscription; +use crate::Task;  pub use raw_window_handle;  use raw_window_handle::WindowHandle; +/// An operation to be performed on some window. +#[allow(missing_debug_implementations)] +pub enum Action { +    /// Opens a new window with some [`Settings`]. +    Open(Id, Settings), + +    /// Close the window and exits the application. +    Close(Id), + +    /// Move the window with the left mouse button until the button is +    /// released. +    /// +    /// There’s no guarantee that this will work unless the left mouse +    /// button was pressed immediately before this function is called. +    Drag(Id), + +    /// Resize the window to the given logical dimensions. +    Resize(Id, Size), + +    /// Fetch the current logical dimensions of the window. +    FetchSize(Id, oneshot::Sender<Size>), + +    /// Fetch if the current window is maximized or not. +    FetchMaximized(Id, oneshot::Sender<bool>), + +    /// Set the window to maximized or back +    Maximize(Id, bool), + +    /// Fetch if the current window is minimized or not. +    /// +    /// ## Platform-specific +    /// - **Wayland:** Always `None`. +    FetchMinimized(Id, oneshot::Sender<Option<bool>>), + +    /// Set the window to minimized or back +    Minimize(Id, bool), + +    /// Fetch the current logical coordinates of the window. +    FetchPosition(Id, oneshot::Sender<Option<Point>>), + +    /// Move the window to the given logical coordinates. +    /// +    /// Unsupported on Wayland. +    Move(Id, Point), + +    /// Change the [`Mode`] of the window. +    ChangeMode(Id, Mode), + +    /// Fetch the current [`Mode`] of the window. +    FetchMode(Id, oneshot::Sender<Mode>), + +    /// Toggle the window to maximized or back +    ToggleMaximize(Id), + +    /// Toggle whether window has decorations. +    /// +    /// ## Platform-specific +    /// - **X11:** Not implemented. +    /// - **Web:** Unsupported. +    ToggleDecorations(Id), + +    /// Request user attention to the window, this has no effect if the application +    /// is already focused. How requesting for user attention manifests is platform dependent, +    /// see [`UserAttention`] for details. +    /// +    /// Providing `None` will unset the request for user attention. Unsetting the request for +    /// user attention might not be done automatically by the WM when the window receives input. +    /// +    /// ## Platform-specific +    /// +    /// - **iOS / Android / Web:** Unsupported. +    /// - **macOS:** `None` has no effect. +    /// - **X11:** Requests for user attention must be manually cleared. +    /// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect. +    RequestUserAttention(Id, Option<UserAttention>), + +    /// Bring the window to the front and sets input focus. Has no effect if the window is +    /// already in focus, minimized, or not visible. +    /// +    /// This method steals input focus from other applications. Do not use this method unless +    /// you are certain that's what the user wants. Focus stealing can cause an extremely disruptive +    /// user experience. +    /// +    /// ## Platform-specific +    /// +    /// - **Web / Wayland:** Unsupported. +    GainFocus(Id), + +    /// Change the window [`Level`]. +    ChangeLevel(Id, Level), + +    /// Show the system menu at cursor position. +    /// +    /// ## Platform-specific +    /// Android / iOS / macOS / Orbital / Web / X11: Unsupported. +    ShowSystemMenu(Id), + +    /// Fetch the raw identifier unique to the window. +    FetchRawId(Id, oneshot::Sender<u64>), + +    /// Change the window [`Icon`]. +    /// +    /// On Windows and X11, this is typically the small icon in the top-left +    /// corner of the titlebar. +    /// +    /// ## Platform-specific +    /// +    /// - **Web / Wayland / macOS:** Unsupported. +    /// +    /// - **Windows:** Sets `ICON_SMALL`. The base size for a window icon is 16x16, but it's +    ///   recommended to account for screen scaling and pick a multiple of that, i.e. 32x32. +    /// +    /// - **X11:** Has no universal guidelines for icon sizes, so you're at the whims of the WM. That +    ///   said, it's usually in the same ballpark as on Windows. +    ChangeIcon(Id, Icon), + +    /// Runs the closure with the native window handle of the window with the given [`Id`]. +    RunWithHandle(Id, Box<dyn FnOnce(WindowHandle<'_>) + Send>), + +    /// Screenshot the viewport of the window. +    Screenshot(Id, oneshot::Sender<Screenshot>), +} +  /// Subscribes to the frames of the window of the running application.  ///  /// The resulting [`Subscription`] will produce items at a rate equal to the @@ -34,110 +155,96 @@ pub fn frames() -> Subscription<Instant> {      })  } -/// Spawns a new window with the given `settings`. +/// Opens a new window with the given `settings`.  /// -/// Returns the new window [`Id`] alongside the [`Command`]. -pub fn spawn<Message>(settings: Settings) -> (Id, Command<Message>) { +/// Returns the new window [`Id`] alongside the [`Task`]. +pub fn open<T>(settings: Settings) -> (Id, Task<T>) {      let id = Id::unique();      (          id, -        Command::single(command::Action::Window(Action::Spawn(id, settings))), +        Task::effect(crate::Action::Window(Action::Open(id, settings))),      )  }  /// Closes the window with `id`. -pub fn close<Message>(id: Id) -> Command<Message> { -    Command::single(command::Action::Window(Action::Close(id))) +pub fn close<T>(id: Id) -> Task<T> { +    Task::effect(crate::Action::Window(Action::Close(id)))  }  /// Begins dragging the window while the left mouse button is held. -pub fn drag<Message>(id: Id) -> Command<Message> { -    Command::single(command::Action::Window(Action::Drag(id))) +pub fn drag<T>(id: Id) -> Task<T> { +    Task::effect(crate::Action::Window(Action::Drag(id)))  }  /// Resizes the window to the given logical dimensions. -pub fn resize<Message>(id: Id, new_size: Size) -> Command<Message> { -    Command::single(command::Action::Window(Action::Resize(id, new_size))) +pub fn resize<T>(id: Id, new_size: Size) -> Task<T> { +    Task::effect(crate::Action::Window(Action::Resize(id, new_size)))  }  /// Fetches the window's size in logical dimensions. -pub fn fetch_size<Message>( -    id: Id, -    f: impl FnOnce(Size) -> Message + 'static, -) -> Command<Message> { -    Command::single(command::Action::Window(Action::FetchSize(id, Box::new(f)))) +pub fn fetch_size(id: Id) -> Task<Size> { +    Task::oneshot(move |channel| { +        crate::Action::Window(Action::FetchSize(id, channel)) +    })  }  /// Fetches if the window is maximized. -pub fn fetch_maximized<Message>( -    id: Id, -    f: impl FnOnce(bool) -> Message + 'static, -) -> Command<Message> { -    Command::single(command::Action::Window(Action::FetchMaximized( -        id, -        Box::new(f), -    ))) +pub fn fetch_maximized(id: Id) -> Task<bool> { +    Task::oneshot(move |channel| { +        crate::Action::Window(Action::FetchMaximized(id, channel)) +    })  }  /// Maximizes the window. -pub fn maximize<Message>(id: Id, maximized: bool) -> Command<Message> { -    Command::single(command::Action::Window(Action::Maximize(id, maximized))) +pub fn maximize<T>(id: Id, maximized: bool) -> Task<T> { +    Task::effect(crate::Action::Window(Action::Maximize(id, maximized)))  }  /// Fetches if the window is minimized. -pub fn fetch_minimized<Message>( -    id: Id, -    f: impl FnOnce(Option<bool>) -> Message + 'static, -) -> Command<Message> { -    Command::single(command::Action::Window(Action::FetchMinimized( -        id, -        Box::new(f), -    ))) +pub fn fetch_minimized(id: Id) -> Task<Option<bool>> { +    Task::oneshot(move |channel| { +        crate::Action::Window(Action::FetchMinimized(id, channel)) +    })  }  /// Minimizes the window. -pub fn minimize<Message>(id: Id, minimized: bool) -> Command<Message> { -    Command::single(command::Action::Window(Action::Minimize(id, minimized))) +pub fn minimize<T>(id: Id, minimized: bool) -> Task<T> { +    Task::effect(crate::Action::Window(Action::Minimize(id, minimized)))  }  /// Fetches the current window position in logical coordinates. -pub fn fetch_position<Message>( -    id: Id, -    f: impl FnOnce(Option<Point>) -> Message + 'static, -) -> Command<Message> { -    Command::single(command::Action::Window(Action::FetchPosition( -        id, -        Box::new(f), -    ))) +pub fn fetch_position(id: Id) -> Task<Option<Point>> { +    Task::oneshot(move |channel| { +        crate::Action::Window(Action::FetchPosition(id, channel)) +    })  }  /// Moves the window to the given logical coordinates. -pub fn move_to<Message>(id: Id, position: Point) -> Command<Message> { -    Command::single(command::Action::Window(Action::Move(id, position))) +pub fn move_to<T>(id: Id, position: Point) -> Task<T> { +    Task::effect(crate::Action::Window(Action::Move(id, position)))  }  /// Changes the [`Mode`] of the window. -pub fn change_mode<Message>(id: Id, mode: Mode) -> Command<Message> { -    Command::single(command::Action::Window(Action::ChangeMode(id, mode))) +pub fn change_mode<T>(id: Id, mode: Mode) -> Task<T> { +    Task::effect(crate::Action::Window(Action::ChangeMode(id, mode)))  }  /// Fetches the current [`Mode`] of the window. -pub fn fetch_mode<Message>( -    id: Id, -    f: impl FnOnce(Mode) -> Message + 'static, -) -> Command<Message> { -    Command::single(command::Action::Window(Action::FetchMode(id, Box::new(f)))) +pub fn fetch_mode(id: Id) -> Task<Mode> { +    Task::oneshot(move |channel| { +        crate::Action::Window(Action::FetchMode(id, channel)) +    })  }  /// Toggles the window to maximized or back. -pub fn toggle_maximize<Message>(id: Id) -> Command<Message> { -    Command::single(command::Action::Window(Action::ToggleMaximize(id))) +pub fn toggle_maximize<T>(id: Id) -> Task<T> { +    Task::effect(crate::Action::Window(Action::ToggleMaximize(id)))  }  /// Toggles the window decorations. -pub fn toggle_decorations<Message>(id: Id) -> Command<Message> { -    Command::single(command::Action::Window(Action::ToggleDecorations(id))) +pub fn toggle_decorations<T>(id: Id) -> Task<T> { +    Task::effect(crate::Action::Window(Action::ToggleDecorations(id)))  }  /// Request user attention to the window. This has no effect if the application @@ -146,11 +253,11 @@ pub fn toggle_decorations<Message>(id: Id) -> Command<Message> {  ///  /// Providing `None` will unset the request for user attention. Unsetting the request for  /// user attention might not be done automatically by the WM when the window receives input. -pub fn request_user_attention<Message>( +pub fn request_user_attention<T>(      id: Id,      user_attention: Option<UserAttention>, -) -> Command<Message> { -    Command::single(command::Action::Window(Action::RequestUserAttention( +) -> Task<T> { +    Task::effect(crate::Action::Window(Action::RequestUserAttention(          id,          user_attention,      ))) @@ -159,59 +266,61 @@ pub fn request_user_attention<Message>(  /// Brings the window to the front and sets input focus. Has no effect if the window is  /// already in focus, minimized, or not visible.  /// -/// This [`Command`] steals input focus from other applications. Do not use this method unless +/// This [`Task`] steals input focus from other applications. Do not use this method unless  /// you are certain that's what the user wants. Focus stealing can cause an extremely disruptive  /// user experience. -pub fn gain_focus<Message>(id: Id) -> Command<Message> { -    Command::single(command::Action::Window(Action::GainFocus(id))) +pub fn gain_focus<T>(id: Id) -> Task<T> { +    Task::effect(crate::Action::Window(Action::GainFocus(id)))  }  /// Changes the window [`Level`]. -pub fn change_level<Message>(id: Id, level: Level) -> Command<Message> { -    Command::single(command::Action::Window(Action::ChangeLevel(id, level))) +pub fn change_level<T>(id: Id, level: Level) -> Task<T> { +    Task::effect(crate::Action::Window(Action::ChangeLevel(id, level)))  }  /// Show the [system menu] at cursor position.  ///  /// [system menu]: https://en.wikipedia.org/wiki/Common_menus_in_Microsoft_Windows#System_menu -pub fn show_system_menu<Message>(id: Id) -> Command<Message> { -    Command::single(command::Action::Window(Action::ShowSystemMenu(id))) +pub fn show_system_menu<T>(id: Id) -> Task<T> { +    Task::effect(crate::Action::Window(Action::ShowSystemMenu(id)))  }  /// Fetches an identifier unique to the window, provided by the underlying windowing system. This is  /// not to be confused with [`Id`]. -pub fn fetch_id<Message>( -    id: Id, -    f: impl FnOnce(u64) -> Message + 'static, -) -> Command<Message> { -    Command::single(command::Action::Window(Action::FetchId(id, Box::new(f)))) +pub fn fetch_raw_id<Message>(id: Id) -> Task<u64> { +    Task::oneshot(|channel| { +        crate::Action::Window(Action::FetchRawId(id, channel)) +    })  }  /// Changes the [`Icon`] of the window. -pub fn change_icon<Message>(id: Id, icon: Icon) -> Command<Message> { -    Command::single(command::Action::Window(Action::ChangeIcon(id, icon))) +pub fn change_icon<T>(id: Id, icon: Icon) -> Task<T> { +    Task::effect(crate::Action::Window(Action::ChangeIcon(id, icon)))  }  /// Runs the given callback with the native window handle for the window with the given id.  ///  /// Note that if the window closes before this call is processed the callback will not be run. -pub fn run_with_handle<Message>( +pub fn run_with_handle<T>(      id: Id, -    f: impl FnOnce(WindowHandle<'_>) -> Message + 'static, -) -> Command<Message> { -    Command::single(command::Action::Window(Action::RunWithHandle( -        id, -        Box::new(f), -    ))) +    f: impl FnOnce(WindowHandle<'_>) -> T + MaybeSend + 'static, +) -> Task<T> +where +    T: MaybeSend + 'static, +{ +    Task::oneshot(move |channel| { +        crate::Action::Window(Action::RunWithHandle( +            id, +            Box::new(move |handle| { +                let _ = channel.send(f(handle)); +            }), +        )) +    })  }  /// Captures a [`Screenshot`] from the window. -pub fn screenshot<Message>( -    id: Id, -    f: impl FnOnce(Screenshot) -> Message + Send + 'static, -) -> Command<Message> { -    Command::single(command::Action::Window(Action::Screenshot( -        id, -        Box::new(f), -    ))) +pub fn screenshot(id: Id) -> Task<Screenshot> { +    Task::oneshot(move |channel| { +        crate::Action::Window(Action::Screenshot(id, channel)) +    })  } diff --git a/runtime/src/window/action.rs b/runtime/src/window/action.rs deleted file mode 100644 index 07e77872..00000000 --- a/runtime/src/window/action.rs +++ /dev/null @@ -1,230 +0,0 @@ -use crate::core::window::{Icon, Id, Level, Mode, Settings, UserAttention}; -use crate::core::{Point, Size}; -use crate::futures::MaybeSend; -use crate::window::Screenshot; - -use raw_window_handle::WindowHandle; - -use std::fmt; - -/// An operation to be performed on some window. -pub enum Action<T> { -    /// Spawns a new window with some [`Settings`]. -    Spawn(Id, Settings), -    /// Close the window and exits the application. -    Close(Id), -    /// Move the window with the left mouse button until the button is -    /// released. -    /// -    /// There’s no guarantee that this will work unless the left mouse -    /// button was pressed immediately before this function is called. -    Drag(Id), -    /// Resize the window to the given logical dimensions. -    Resize(Id, Size), -    /// Fetch the current logical dimensions of the window. -    FetchSize(Id, Box<dyn FnOnce(Size) -> T + 'static>), -    /// Fetch if the current window is maximized or not. -    /// -    /// ## Platform-specific -    /// - **iOS / Android / Web:** Unsupported. -    FetchMaximized(Id, Box<dyn FnOnce(bool) -> T + 'static>), -    /// Set the window to maximized or back -    Maximize(Id, bool), -    /// Fetch if the current window is minimized or not. -    /// -    /// ## Platform-specific -    /// - **Wayland:** Always `None`. -    /// - **iOS / Android / Web:** Unsupported. -    FetchMinimized(Id, Box<dyn FnOnce(Option<bool>) -> T + 'static>), -    /// Set the window to minimized or back -    Minimize(Id, bool), -    /// Fetch the current logical coordinates of the window. -    FetchPosition(Id, Box<dyn FnOnce(Option<Point>) -> T + 'static>), -    /// Move the window to the given logical coordinates. -    /// -    /// Unsupported on Wayland. -    Move(Id, Point), -    /// Change the [`Mode`] of the window. -    ChangeMode(Id, Mode), -    /// Fetch the current [`Mode`] of the window. -    FetchMode(Id, Box<dyn FnOnce(Mode) -> T + 'static>), -    /// Toggle the window to maximized or back -    ToggleMaximize(Id), -    /// Toggle whether window has decorations. -    /// -    /// ## Platform-specific -    /// - **X11:** Not implemented. -    /// - **Web:** Unsupported. -    ToggleDecorations(Id), -    /// Request user attention to the window, this has no effect if the application -    /// is already focused. How requesting for user attention manifests is platform dependent, -    /// see [`UserAttention`] for details. -    /// -    /// Providing `None` will unset the request for user attention. Unsetting the request for -    /// user attention might not be done automatically by the WM when the window receives input. -    /// -    /// ## Platform-specific -    /// -    /// - **iOS / Android / Web:** Unsupported. -    /// - **macOS:** `None` has no effect. -    /// - **X11:** Requests for user attention must be manually cleared. -    /// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect. -    RequestUserAttention(Id, Option<UserAttention>), -    /// Bring the window to the front and sets input focus. Has no effect if the window is -    /// already in focus, minimized, or not visible. -    /// -    /// This method steals input focus from other applications. Do not use this method unless -    /// you are certain that's what the user wants. Focus stealing can cause an extremely disruptive -    /// user experience. -    /// -    /// ## Platform-specific -    /// -    /// - **Web / Wayland:** Unsupported. -    GainFocus(Id), -    /// Change the window [`Level`]. -    ChangeLevel(Id, Level), -    /// Show the system menu at cursor position. -    /// -    /// ## Platform-specific -    /// Android / iOS / macOS / Orbital / Web / X11: Unsupported. -    ShowSystemMenu(Id), -    /// Fetch the raw identifier unique to the window. -    FetchId(Id, Box<dyn FnOnce(u64) -> T + 'static>), -    /// Change the window [`Icon`]. -    /// -    /// On Windows and X11, this is typically the small icon in the top-left -    /// corner of the titlebar. -    /// -    /// ## Platform-specific -    /// -    /// - **Web / Wayland / macOS:** Unsupported. -    /// -    /// - **Windows:** Sets `ICON_SMALL`. The base size for a window icon is 16x16, but it's -    ///   recommended to account for screen scaling and pick a multiple of that, i.e. 32x32. -    /// -    /// - **X11:** Has no universal guidelines for icon sizes, so you're at the whims of the WM. That -    ///   said, it's usually in the same ballpark as on Windows. -    ChangeIcon(Id, Icon), -    /// Runs the closure with the native window handle of the window with the given [`Id`]. -    RunWithHandle(Id, Box<dyn FnOnce(WindowHandle<'_>) -> T + 'static>), -    /// Screenshot the viewport of the window. -    Screenshot(Id, Box<dyn FnOnce(Screenshot) -> T + 'static>), -} - -impl<T> Action<T> { -    /// Maps the output of a window [`Action`] using the provided closure. -    pub fn map<A>( -        self, -        f: impl Fn(T) -> A + 'static + MaybeSend + Sync, -    ) -> Action<A> -    where -        T: 'static, -    { -        match self { -            Self::Spawn(id, settings) => Action::Spawn(id, settings), -            Self::Close(id) => Action::Close(id), -            Self::Drag(id) => Action::Drag(id), -            Self::Resize(id, size) => Action::Resize(id, size), -            Self::FetchSize(id, o) => { -                Action::FetchSize(id, Box::new(move |s| f(o(s)))) -            } -            Self::FetchMaximized(id, o) => { -                Action::FetchMaximized(id, Box::new(move |s| f(o(s)))) -            } -            Self::Maximize(id, maximized) => Action::Maximize(id, maximized), -            Self::FetchMinimized(id, o) => { -                Action::FetchMinimized(id, Box::new(move |s| f(o(s)))) -            } -            Self::Minimize(id, minimized) => Action::Minimize(id, minimized), -            Self::FetchPosition(id, o) => { -                Action::FetchPosition(id, Box::new(move |s| f(o(s)))) -            } -            Self::Move(id, position) => Action::Move(id, position), -            Self::ChangeMode(id, mode) => Action::ChangeMode(id, mode), -            Self::FetchMode(id, o) => { -                Action::FetchMode(id, Box::new(move |s| f(o(s)))) -            } -            Self::ToggleMaximize(id) => Action::ToggleMaximize(id), -            Self::ToggleDecorations(id) => Action::ToggleDecorations(id), -            Self::RequestUserAttention(id, attention_type) => { -                Action::RequestUserAttention(id, attention_type) -            } -            Self::GainFocus(id) => Action::GainFocus(id), -            Self::ChangeLevel(id, level) => Action::ChangeLevel(id, level), -            Self::ShowSystemMenu(id) => Action::ShowSystemMenu(id), -            Self::FetchId(id, o) => { -                Action::FetchId(id, Box::new(move |s| f(o(s)))) -            } -            Self::ChangeIcon(id, icon) => Action::ChangeIcon(id, icon), -            Self::RunWithHandle(id, o) => { -                Action::RunWithHandle(id, Box::new(move |s| f(o(s)))) -            } -            Self::Screenshot(id, tag) => Action::Screenshot( -                id, -                Box::new(move |screenshot| f(tag(screenshot))), -            ), -        } -    } -} - -impl<T> fmt::Debug for Action<T> { -    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -        match self { -            Self::Spawn(id, settings) => { -                write!(f, "Action::Spawn({id:?}, {settings:?})") -            } -            Self::Close(id) => write!(f, "Action::Close({id:?})"), -            Self::Drag(id) => write!(f, "Action::Drag({id:?})"), -            Self::Resize(id, size) => { -                write!(f, "Action::Resize({id:?}, {size:?})") -            } -            Self::FetchSize(id, _) => write!(f, "Action::FetchSize({id:?})"), -            Self::FetchMaximized(id, _) => { -                write!(f, "Action::FetchMaximized({id:?})") -            } -            Self::Maximize(id, maximized) => { -                write!(f, "Action::Maximize({id:?}, {maximized})") -            } -            Self::FetchMinimized(id, _) => { -                write!(f, "Action::FetchMinimized({id:?})") -            } -            Self::Minimize(id, minimized) => { -                write!(f, "Action::Minimize({id:?}, {minimized}") -            } -            Self::FetchPosition(id, _) => { -                write!(f, "Action::FetchPosition({id:?})") -            } -            Self::Move(id, position) => { -                write!(f, "Action::Move({id:?}, {position})") -            } -            Self::ChangeMode(id, mode) => { -                write!(f, "Action::SetMode({id:?}, {mode:?})") -            } -            Self::FetchMode(id, _) => write!(f, "Action::FetchMode({id:?})"), -            Self::ToggleMaximize(id) => { -                write!(f, "Action::ToggleMaximize({id:?})") -            } -            Self::ToggleDecorations(id) => { -                write!(f, "Action::ToggleDecorations({id:?})") -            } -            Self::RequestUserAttention(id, _) => { -                write!(f, "Action::RequestUserAttention({id:?})") -            } -            Self::GainFocus(id) => write!(f, "Action::GainFocus({id:?})"), -            Self::ChangeLevel(id, level) => { -                write!(f, "Action::ChangeLevel({id:?}, {level:?})") -            } -            Self::ShowSystemMenu(id) => { -                write!(f, "Action::ShowSystemMenu({id:?})") -            } -            Self::FetchId(id, _) => write!(f, "Action::FetchId({id:?})"), -            Self::ChangeIcon(id, _icon) => { -                write!(f, "Action::ChangeIcon({id:?})") -            } -            Self::RunWithHandle(id, _) => { -                write!(f, "Action::RunWithHandle({id:?})") -            } -            Self::Screenshot(id, _) => write!(f, "Action::Screenshot({id:?})"), -        } -    } -} diff --git a/src/application.rs b/src/application.rs index d12ba73d..4cd4a87d 100644 --- a/src/application.rs +++ b/src/application.rs @@ -2,7 +2,7 @@  use crate::core::text;  use crate::graphics::compositor;  use crate::shell::application; -use crate::{Command, Element, Executor, Settings, Subscription}; +use crate::{Element, Executor, Settings, Subscription, Task};  pub use application::{Appearance, DefaultStyle}; @@ -16,7 +16,7 @@ pub use application::{Appearance, DefaultStyle};  ///   document.  ///  /// An [`Application`] can execute asynchronous actions by returning a -/// [`Command`] in some of its methods. +/// [`Task`] in some of its methods.  ///  /// When using an [`Application`] with the `debug` feature enabled, a debug view  /// can be toggled by pressing `F12`. @@ -62,7 +62,7 @@ pub use application::{Appearance, DefaultStyle};  /// ```no_run  /// use iced::advanced::Application;  /// use iced::executor; -/// use iced::{Command, Element, Settings, Theme, Renderer}; +/// use iced::{Task, Element, Settings, Theme, Renderer};  ///  /// pub fn main() -> iced::Result {  ///     Hello::run(Settings::default()) @@ -77,16 +77,16 @@ pub use application::{Appearance, DefaultStyle};  ///     type Theme = Theme;  ///     type Renderer = Renderer;  /// -///     fn new(_flags: ()) -> (Hello, Command<Self::Message>) { -///         (Hello, Command::none()) +///     fn new(_flags: ()) -> (Hello, Task<Self::Message>) { +///         (Hello, Task::none())  ///     }  ///  ///     fn title(&self) -> String {  ///         String::from("A cool application")  ///     }  /// -///     fn update(&mut self, _message: Self::Message) -> Command<Self::Message> { -///         Command::none() +///     fn update(&mut self, _message: Self::Message) -> Task<Self::Message> { +///         Task::none()  ///     }  ///  ///     fn view(&self) -> Element<Self::Message> { @@ -123,12 +123,12 @@ where      ///      /// Here is where you should return the initial state of your app.      /// -    /// Additionally, you can return a [`Command`] if you need to perform some +    /// Additionally, you can return a [`Task`] if you need to perform some      /// async action in the background on startup. This is useful if you want to      /// load state from a file, perform an initial HTTP request, etc.      ///      /// [`run`]: Self::run -    fn new(flags: Self::Flags) -> (Self, Command<Self::Message>); +    fn new(flags: Self::Flags) -> (Self, Task<Self::Message>);      /// Returns the current title of the [`Application`].      /// @@ -142,8 +142,8 @@ where      /// produced by either user interactions or commands, will be handled by      /// this method.      /// -    /// Any [`Command`] returned will be executed immediately in the background. -    fn update(&mut self, message: Self::Message) -> Command<Self::Message>; +    /// Any [`Task`] returned will be executed immediately in the background. +    fn update(&mut self, message: Self::Message) -> Task<Self::Message>;      /// Returns the widgets to display in the [`Application`].      /// @@ -234,7 +234,7 @@ where      type Theme = A::Theme;      type Renderer = A::Renderer; -    fn update(&mut self, message: Self::Message) -> Command<Self::Message> { +    fn update(&mut self, message: Self::Message) -> Task<Self::Message> {          self.0.update(message)      } @@ -250,7 +250,7 @@ where  {      type Flags = A::Flags; -    fn new(flags: Self::Flags) -> (Self, Command<A::Message>) { +    fn new(flags: Self::Flags) -> (Self, Task<A::Message>) {          let (app, command) = A::new(flags);          (Instance(app), command) @@ -203,6 +203,7 @@ pub use crate::core::{      Length, Padding, Pixels, Point, Radians, Rectangle, Rotation, Shadow, Size,      Theme, Transformation, Vector,  }; +pub use crate::runtime::Task;  pub mod clipboard {      //! Access the clipboard. @@ -256,11 +257,6 @@ pub mod mouse {      };  } -pub mod command { -    //! Run asynchronous actions. -    pub use crate::runtime::command::{channel, Command}; -} -  pub mod subscription {      //! Listen to external events in your application.      pub use iced_futures::subscription::{ @@ -312,7 +308,6 @@ pub mod widget {      mod runtime {}  } -pub use command::Command;  pub use error::Error;  pub use event::Event;  pub use executor::Executor; diff --git a/src/multi_window.rs b/src/multi_window.rs index b81297dc..4900bb85 100644 --- a/src/multi_window.rs +++ b/src/multi_window.rs @@ -1,6 +1,6 @@  //! Leverage multi-window support in your application.  use crate::window; -use crate::{Command, Element, Executor, Settings, Subscription}; +use crate::{Element, Executor, Settings, Subscription, Task};  pub use crate::application::{Appearance, DefaultStyle}; @@ -14,7 +14,7 @@ pub use crate::application::{Appearance, DefaultStyle};  ///   document and display only the contents of the `window::Id::MAIN` window.  ///  /// An [`Application`] can execute asynchronous actions by returning a -/// [`Command`] in some of its methods. +/// [`Task`] in some of its methods.  ///  /// When using an [`Application`] with the `debug` feature enabled, a debug view  /// can be toggled by pressing `F12`. @@ -29,7 +29,7 @@ pub use crate::application::{Appearance, DefaultStyle};  ///  /// ```no_run  /// use iced::{executor, window}; -/// use iced::{Command, Element, Settings, Theme}; +/// use iced::{Task, Element, Settings, Theme};  /// use iced::multi_window::{self, Application};  ///  /// pub fn main() -> iced::Result { @@ -44,16 +44,16 @@ pub use crate::application::{Appearance, DefaultStyle};  ///     type Message = ();  ///     type Theme = Theme;  /// -///     fn new(_flags: ()) -> (Hello, Command<Self::Message>) { -///         (Hello, Command::none()) +///     fn new(_flags: ()) -> (Hello, Task<Self::Message>) { +///         (Hello, Task::none())  ///     }  ///  ///     fn title(&self, _window: window::Id) -> String {  ///         String::from("A cool application")  ///     }  /// -///     fn update(&mut self, _message: Self::Message) -> Command<Self::Message> { -///         Command::none() +///     fn update(&mut self, _message: Self::Message) -> Task<Self::Message> { +///         Task::none()  ///     }  ///  ///     fn view(&self, _window: window::Id) -> Element<Self::Message> { @@ -89,12 +89,12 @@ where      ///      /// Here is where you should return the initial state of your app.      /// -    /// Additionally, you can return a [`Command`] if you need to perform some +    /// Additionally, you can return a [`Task`] if you need to perform some      /// async action in the background on startup. This is useful if you want to      /// load state from a file, perform an initial HTTP request, etc.      ///      /// [`run`]: Self::run -    fn new(flags: Self::Flags) -> (Self, Command<Self::Message>); +    fn new(flags: Self::Flags) -> (Self, Task<Self::Message>);      /// Returns the current title of the `window` of the [`Application`].      /// @@ -108,8 +108,8 @@ where      /// produced by either user interactions or commands, will be handled by      /// this method.      /// -    /// Any [`Command`] returned will be executed immediately in the background. -    fn update(&mut self, message: Self::Message) -> Command<Self::Message>; +    /// Any [`Task`] returned will be executed immediately in the background. +    fn update(&mut self, message: Self::Message) -> Task<Self::Message>;      /// Returns the widgets to display in the `window` of the [`Application`].      /// @@ -207,7 +207,7 @@ where      type Theme = A::Theme;      type Renderer = crate::Renderer; -    fn update(&mut self, message: Self::Message) -> Command<Self::Message> { +    fn update(&mut self, message: Self::Message) -> Task<Self::Message> {          self.0.update(message)      } @@ -226,7 +226,7 @@ where  {      type Flags = A::Flags; -    fn new(flags: Self::Flags) -> (Self, Command<A::Message>) { +    fn new(flags: Self::Flags) -> (Self, Task<A::Message>) {          let (app, command) = A::new(flags);          (Instance(app), command) diff --git a/src/program.rs b/src/program.rs index d4c2a266..ea6b0e8e 100644 --- a/src/program.rs +++ b/src/program.rs @@ -35,7 +35,7 @@ use crate::core::text;  use crate::executor::{self, Executor};  use crate::graphics::compositor;  use crate::window; -use crate::{Command, Element, Font, Result, Settings, Size, Subscription}; +use crate::{Element, Font, Result, Settings, Size, Subscription, Task};  pub use crate::application::{Appearance, DefaultStyle}; @@ -76,7 +76,7 @@ pub fn program<State, Message, Theme, Renderer>(  ) -> Program<impl Definition<State = State, Message = Message, Theme = Theme>>  where      State: 'static, -    Message: Send + std::fmt::Debug, +    Message: Send + std::fmt::Debug + 'static,      Theme: Default + DefaultStyle,      Renderer: self::Renderer,  { @@ -94,7 +94,7 @@ where      impl<State, Message, Theme, Renderer, Update, View> Definition          for Application<State, Message, Theme, Renderer, Update, View>      where -        Message: Send + std::fmt::Debug, +        Message: Send + std::fmt::Debug + 'static,          Theme: Default + DefaultStyle,          Renderer: self::Renderer,          Update: self::Update<State, Message>, @@ -106,15 +106,15 @@ where          type Renderer = Renderer;          type Executor = executor::Default; -        fn load(&self) -> Command<Self::Message> { -            Command::none() +        fn load(&self) -> Task<Self::Message> { +            Task::none()          }          fn update(              &self,              state: &mut Self::State,              message: Self::Message, -        ) -> Command<Self::Message> { +        ) -> Task<Self::Message> {              self.update.update(state, message).into()          } @@ -197,7 +197,7 @@ impl<P: Definition> Program<P> {              fn new(                  (program, initialize): Self::Flags, -            ) -> (Self, Command<Self::Message>) { +            ) -> (Self, Task<Self::Message>) {                  let state = initialize();                  let command = program.load(); @@ -218,7 +218,7 @@ impl<P: Definition> Program<P> {              fn update(                  &mut self,                  message: Self::Message, -            ) -> Command<Self::Message> { +            ) -> Task<Self::Message> {                  self.program.update(&mut self.state, message)              } @@ -357,10 +357,10 @@ impl<P: Definition> Program<P> {          }      } -    /// Runs the [`Command`] produced by the closure at startup. +    /// Runs the [`Task`] produced by the closure at startup.      pub fn load(          self, -        f: impl Fn() -> Command<P::Message>, +        f: impl Fn() -> Task<P::Message>,      ) -> Program<          impl Definition<State = P::State, Message = P::Message, Theme = P::Theme>,      > { @@ -420,7 +420,7 @@ pub trait Definition: Sized {      type State;      /// The message of the program. -    type Message: Send + std::fmt::Debug; +    type Message: Send + std::fmt::Debug + 'static;      /// The theme of the program.      type Theme: Default + DefaultStyle; @@ -431,13 +431,13 @@ pub trait Definition: Sized {      /// The executor of the program.      type Executor: Executor; -    fn load(&self) -> Command<Self::Message>; +    fn load(&self) -> Task<Self::Message>;      fn update(          &self,          state: &mut Self::State,          message: Self::Message, -    ) -> Command<Self::Message>; +    ) -> Task<Self::Message>;      fn view<'a>(          &self, @@ -484,7 +484,7 @@ fn with_title<P: Definition>(          type Renderer = P::Renderer;          type Executor = P::Executor; -        fn load(&self) -> Command<Self::Message> { +        fn load(&self) -> Task<Self::Message> {              self.program.load()          } @@ -496,7 +496,7 @@ fn with_title<P: Definition>(              &self,              state: &mut Self::State,              message: Self::Message, -        ) -> Command<Self::Message> { +        ) -> Task<Self::Message> {              self.program.update(state, message)          } @@ -532,7 +532,7 @@ fn with_title<P: Definition>(  fn with_load<P: Definition>(      program: P, -    f: impl Fn() -> Command<P::Message>, +    f: impl Fn() -> Task<P::Message>,  ) -> impl Definition<State = P::State, Message = P::Message, Theme = P::Theme> {      struct WithLoad<P, F> {          program: P, @@ -541,7 +541,7 @@ fn with_load<P: Definition>(      impl<P: Definition, F> Definition for WithLoad<P, F>      where -        F: Fn() -> Command<P::Message>, +        F: Fn() -> Task<P::Message>,      {          type State = P::State;          type Message = P::Message; @@ -549,15 +549,15 @@ fn with_load<P: Definition>(          type Renderer = P::Renderer;          type Executor = executor::Default; -        fn load(&self) -> Command<Self::Message> { -            Command::batch([self.program.load(), (self.load)()]) +        fn load(&self) -> Task<Self::Message> { +            Task::batch([self.program.load(), (self.load)()])          }          fn update(              &self,              state: &mut Self::State,              message: Self::Message, -        ) -> Command<Self::Message> { +        ) -> Task<Self::Message> {              self.program.update(state, message)          } @@ -621,7 +621,7 @@ fn with_subscription<P: Definition>(              (self.subscription)(state)          } -        fn load(&self) -> Command<Self::Message> { +        fn load(&self) -> Task<Self::Message> {              self.program.load()          } @@ -629,7 +629,7 @@ fn with_subscription<P: Definition>(              &self,              state: &mut Self::State,              message: Self::Message, -        ) -> Command<Self::Message> { +        ) -> Task<Self::Message> {              self.program.update(state, message)          } @@ -686,7 +686,7 @@ fn with_theme<P: Definition>(              (self.theme)(state)          } -        fn load(&self) -> Command<Self::Message> { +        fn load(&self) -> Task<Self::Message> {              self.program.load()          } @@ -698,7 +698,7 @@ fn with_theme<P: Definition>(              &self,              state: &mut Self::State,              message: Self::Message, -        ) -> Command<Self::Message> { +        ) -> Task<Self::Message> {              self.program.update(state, message)          } @@ -755,7 +755,7 @@ fn with_style<P: Definition>(              (self.style)(state, theme)          } -        fn load(&self) -> Command<Self::Message> { +        fn load(&self) -> Task<Self::Message> {              self.program.load()          } @@ -767,7 +767,7 @@ fn with_style<P: Definition>(              &self,              state: &mut Self::State,              message: Self::Message, -        ) -> Command<Self::Message> { +        ) -> Task<Self::Message> {              self.program.update(state, message)          } @@ -822,26 +822,26 @@ where  /// The update logic of some [`Program`].  ///  /// This trait allows the [`program`] builder to take any closure that -/// returns any `Into<Command<Message>>`. +/// returns any `Into<Task<Message>>`.  pub trait Update<State, Message> {      /// Processes the message and updates the state of the [`Program`].      fn update(          &self,          state: &mut State,          message: Message, -    ) -> impl Into<Command<Message>>; +    ) -> impl Into<Task<Message>>;  }  impl<T, State, Message, C> Update<State, Message> for T  where      T: Fn(&mut State, Message) -> C, -    C: Into<Command<Message>>, +    C: Into<Task<Message>>,  {      fn update(          &self,          state: &mut State,          message: Message, -    ) -> impl Into<Command<Message>> { +    ) -> impl Into<Task<Message>> {          self(state, message)      }  } diff --git a/widget/src/button.rs b/widget/src/button.rs index dc949671..5d446fea 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -205,7 +205,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn Operation<Message>, +        operation: &mut dyn Operation<()>,      ) {          operation.container(None, layout.bounds(), &mut |operation| {              self.content.as_widget().operate( diff --git a/widget/src/column.rs b/widget/src/column.rs index df7829b3..4699164c 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -208,7 +208,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn Operation<Message>, +        operation: &mut dyn Operation<()>,      ) {          operation.container(None, layout.bounds(), &mut |operation| {              self.children diff --git a/widget/src/container.rs b/widget/src/container.rs index 51967707..e917471f 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -13,7 +13,7 @@ use crate::core::{      Padding, Pixels, Point, Rectangle, Shadow, Shell, Size, Theme, Vector,      Widget,  }; -use crate::runtime::Command; +use crate::runtime::Task;  /// An element decorating some content.  /// @@ -258,7 +258,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn Operation<Message>, +        operation: &mut dyn Operation<()>,      ) {          operation.container(              self.id.as_ref().map(|id| &id.0), @@ -457,9 +457,9 @@ impl From<Id> for widget::Id {      }  } -/// Produces a [`Command`] that queries the visible screen bounds of the +/// Produces a [`Task`] that queries the visible screen bounds of the  /// [`Container`] with the given [`Id`]. -pub fn visible_bounds(id: Id) -> Command<Option<Rectangle>> { +pub fn visible_bounds(id: Id) -> Task<Option<Rectangle>> {      struct VisibleBounds {          target: widget::Id,          depth: usize, @@ -538,7 +538,7 @@ pub fn visible_bounds(id: Id) -> Command<Option<Rectangle>> {          }      } -    Command::widget(VisibleBounds { +    Task::widget(VisibleBounds {          target: id.into(),          depth: 0,          scrollables: Vec::new(), diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 016bafbb..62343a55 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -12,7 +12,7 @@ use crate::pick_list::{self, PickList};  use crate::progress_bar::{self, ProgressBar};  use crate::radio::{self, Radio};  use crate::rule::{self, Rule}; -use crate::runtime::Command; +use crate::runtime::{Action, Task};  use crate::scrollable::{self, Scrollable};  use crate::slider::{self, Slider};  use crate::text::{self, Text}; @@ -275,7 +275,7 @@ where              state: &mut Tree,              layout: Layout<'_>,              renderer: &Renderer, -            operation: &mut dyn operation::Operation<Message>, +            operation: &mut dyn operation::Operation<()>,          ) {              self.content                  .as_widget() @@ -477,7 +477,7 @@ where              tree: &mut Tree,              layout: Layout<'_>,              renderer: &Renderer, -            operation: &mut dyn operation::Operation<Message>, +            operation: &mut dyn operation::Operation<()>,          ) {              let children = [&self.base, &self.top]                  .into_iter() @@ -929,19 +929,13 @@ where  }  /// Focuses the previous focusable widget. -pub fn focus_previous<Message>() -> Command<Message> -where -    Message: 'static, -{ -    Command::widget(operation::focusable::focus_previous()) +pub fn focus_previous<T>() -> Task<T> { +    Task::effect(Action::widget(operation::focusable::focus_previous()))  }  /// Focuses the next focusable widget. -pub fn focus_next<Message>() -> Command<Message> -where -    Message: 'static, -{ -    Command::widget(operation::focusable::focus_next()) +pub fn focus_next<T>() -> Task<T> { +    Task::effect(Action::widget(operation::focusable::focus_next()))  }  /// A container intercepting mouse events. diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index fdaadefa..69991d1f 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -265,7 +265,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn Operation<Message>, +        operation: &mut dyn Operation<()>,      ) {          operation.container(None, layout.bounds(), &mut |operation| {              self.children diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index 04783dbe..606da22d 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -182,7 +182,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          self.with_element(|element| {              element.as_widget().operate( diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index 7ba71a02..f079c0df 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -59,7 +59,7 @@ pub trait Component<Message, Theme = crate::Theme, Renderer = crate::Renderer> {      fn operate(          &self,          _state: &mut Self::State, -        _operation: &mut dyn widget::Operation<Message>, +        _operation: &mut dyn widget::Operation<()>,      ) {      } @@ -172,7 +172,7 @@ where      fn rebuild_element_with_operation(          &self, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          let heads = self.state.borrow_mut().take().unwrap().into_heads(); @@ -358,70 +358,17 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          self.rebuild_element_with_operation(operation); -        struct MapOperation<'a, B> { -            operation: &'a mut dyn widget::Operation<B>, -        } - -        impl<'a, T, B> widget::Operation<T> for MapOperation<'a, B> { -            fn container( -                &mut self, -                id: Option<&widget::Id>, -                bounds: Rectangle, -                operate_on_children: &mut dyn FnMut( -                    &mut dyn widget::Operation<T>, -                ), -            ) { -                self.operation.container(id, bounds, &mut |operation| { -                    operate_on_children(&mut MapOperation { operation }); -                }); -            } - -            fn focusable( -                &mut self, -                state: &mut dyn widget::operation::Focusable, -                id: Option<&widget::Id>, -            ) { -                self.operation.focusable(state, id); -            } - -            fn text_input( -                &mut self, -                state: &mut dyn widget::operation::TextInput, -                id: Option<&widget::Id>, -            ) { -                self.operation.text_input(state, id); -            } - -            fn scrollable( -                &mut self, -                state: &mut dyn widget::operation::Scrollable, -                id: Option<&widget::Id>, -                bounds: Rectangle, -                translation: Vector, -            ) { -                self.operation.scrollable(state, id, bounds, translation); -            } - -            fn custom( -                &mut self, -                state: &mut dyn std::any::Any, -                id: Option<&widget::Id>, -            ) { -                self.operation.custom(state, id); -            } -        } -          let tree = tree.state.downcast_mut::<Rc<RefCell<Option<Tree>>>>();          self.with_element(|element| {              element.as_widget().operate(                  &mut tree.borrow_mut().as_mut().unwrap().children[0],                  layout,                  renderer, -                &mut MapOperation { operation }, +                operation,              );          });      } diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs index f612102e..27f52617 100644 --- a/widget/src/lazy/responsive.rs +++ b/widget/src/lazy/responsive.rs @@ -161,7 +161,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          let state = tree.state.downcast_mut::<State>();          let mut content = self.content.borrow_mut(); diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index d7235cf6..17cae53b 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -178,7 +178,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn Operation<Message>, +        operation: &mut dyn Operation<()>,      ) {          self.content.as_widget().operate(              &mut tree.children[0], diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index acfa9d44..c3da3879 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -324,7 +324,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          operation.container(None, layout.bounds(), &mut |operation| {              self.contents diff --git a/widget/src/pane_grid/content.rs b/widget/src/pane_grid/content.rs index 30ad52ca..d45fc0cd 100644 --- a/widget/src/pane_grid/content.rs +++ b/widget/src/pane_grid/content.rs @@ -214,7 +214,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          let body_layout = if let Some(title_bar) = &self.title_bar {              let mut children = layout.children(); diff --git a/widget/src/pane_grid/title_bar.rs b/widget/src/pane_grid/title_bar.rs index c2eeebb7..c05f1252 100644 --- a/widget/src/pane_grid/title_bar.rs +++ b/widget/src/pane_grid/title_bar.rs @@ -278,7 +278,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn widget::Operation<Message>, +        operation: &mut dyn widget::Operation<()>,      ) {          let mut children = layout.children();          let padded = children.next().unwrap(); diff --git a/widget/src/row.rs b/widget/src/row.rs index fa352171..00bcf601 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -197,7 +197,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn Operation<Message>, +        operation: &mut dyn Operation<()>,      ) {          operation.container(None, layout.bounds(), &mut |operation| {              self.children diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 6fc00f87..c3d08223 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -15,7 +15,7 @@ use crate::core::{      self, Background, Border, Clipboard, Color, Element, Layout, Length,      Pixels, Point, Rectangle, Shell, Size, Theme, Vector, Widget,  }; -use crate::runtime::Command; +use crate::runtime::{Action, Task};  pub use operation::scrollable::{AbsoluteOffset, RelativeOffset}; @@ -295,7 +295,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn Operation<Message>, +        operation: &mut dyn Operation<()>,      ) {          let state = tree.state.downcast_mut::<State>(); @@ -952,22 +952,18 @@ impl From<Id> for widget::Id {      }  } -/// Produces a [`Command`] that snaps the [`Scrollable`] with the given [`Id`] +/// Produces a [`Task`] that snaps the [`Scrollable`] with the given [`Id`]  /// to the provided `percentage` along the x & y axis. -pub fn snap_to<Message: 'static>( -    id: Id, -    offset: RelativeOffset, -) -> Command<Message> { -    Command::widget(operation::scrollable::snap_to(id.0, offset)) +pub fn snap_to<T>(id: Id, offset: RelativeOffset) -> Task<T> { +    Task::effect(Action::widget(operation::scrollable::snap_to(id.0, offset)))  } -/// Produces a [`Command`] that scrolls the [`Scrollable`] with the given [`Id`] +/// Produces a [`Task`] that scrolls the [`Scrollable`] with the given [`Id`]  /// to the provided [`AbsoluteOffset`] along the x & y axis. -pub fn scroll_to<Message: 'static>( -    id: Id, -    offset: AbsoluteOffset, -) -> Command<Message> { -    Command::widget(operation::scrollable::scroll_to(id.0, offset)) +pub fn scroll_to<T>(id: Id, offset: AbsoluteOffset) -> Task<T> { +    Task::effect(Action::widget(operation::scrollable::scroll_to( +        id.0, offset, +    )))  }  /// Returns [`true`] if the viewport actually changed. diff --git a/widget/src/stack.rs b/widget/src/stack.rs index 5035541b..efa9711d 100644 --- a/widget/src/stack.rs +++ b/widget/src/stack.rs @@ -189,7 +189,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn Operation<Message>, +        operation: &mut dyn Operation<()>,      ) {          operation.container(None, layout.bounds(), &mut |operation| {              self.children diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index dc4f83e0..4e89236b 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -30,7 +30,7 @@ use crate::core::{      Background, Border, Color, Element, Layout, Length, Padding, Pixels, Point,      Rectangle, Shell, Size, Theme, Vector, Widget,  }; -use crate::runtime::Command; +use crate::runtime::{Action, Task};  /// A field that can be filled with text.  /// @@ -540,7 +540,7 @@ where          tree: &mut Tree,          _layout: Layout<'_>,          _renderer: &Renderer, -        operation: &mut dyn Operation<Message>, +        operation: &mut dyn Operation<()>,      ) {          let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>(); @@ -1140,35 +1140,38 @@ impl From<Id> for widget::Id {      }  } -/// Produces a [`Command`] that focuses the [`TextInput`] with the given [`Id`]. -pub fn focus<Message: 'static>(id: Id) -> Command<Message> { -    Command::widget(operation::focusable::focus(id.0)) +/// Produces a [`Task`] that focuses the [`TextInput`] with the given [`Id`]. +pub fn focus<T>(id: Id) -> Task<T> { +    Task::effect(Action::widget(operation::focusable::focus(id.0)))  } -/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the +/// Produces a [`Task`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the  /// end. -pub fn move_cursor_to_end<Message: 'static>(id: Id) -> Command<Message> { -    Command::widget(operation::text_input::move_cursor_to_end(id.0)) +pub fn move_cursor_to_end<T>(id: Id) -> Task<T> { +    Task::effect(Action::widget(operation::text_input::move_cursor_to_end( +        id.0, +    )))  } -/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the +/// Produces a [`Task`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the  /// front. -pub fn move_cursor_to_front<Message: 'static>(id: Id) -> Command<Message> { -    Command::widget(operation::text_input::move_cursor_to_front(id.0)) +pub fn move_cursor_to_front<T>(id: Id) -> Task<T> { +    Task::effect(Action::widget(operation::text_input::move_cursor_to_front( +        id.0, +    )))  } -/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the +/// Produces a [`Task`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the  /// provided position. -pub fn move_cursor_to<Message: 'static>( -    id: Id, -    position: usize, -) -> Command<Message> { -    Command::widget(operation::text_input::move_cursor_to(id.0, position)) +pub fn move_cursor_to<T>(id: Id, position: usize) -> Task<T> { +    Task::effect(Action::widget(operation::text_input::move_cursor_to( +        id.0, position, +    )))  } -/// Produces a [`Command`] that selects all the content of the [`TextInput`] with the given [`Id`]. -pub fn select_all<Message: 'static>(id: Id) -> Command<Message> { -    Command::widget(operation::text_input::select_all(id.0)) +/// Produces a [`Task`] that selects all the content of the [`TextInput`] with the given [`Id`]. +pub fn select_all<T>(id: Id) -> Task<T> { +    Task::effect(Action::widget(operation::text_input::select_all(id.0)))  }  /// The state of a [`TextInput`]. diff --git a/widget/src/themer.rs b/widget/src/themer.rs index f4597458..9eb47d84 100644 --- a/widget/src/themer.rs +++ b/widget/src/themer.rs @@ -104,7 +104,7 @@ where          tree: &mut Tree,          layout: Layout<'_>,          renderer: &Renderer, -        operation: &mut dyn Operation<Message>, +        operation: &mut dyn Operation<()>,      ) {          self.content              .as_widget() @@ -236,7 +236,7 @@ where                  &mut self,                  layout: Layout<'_>,                  renderer: &Renderer, -                operation: &mut dyn Operation<Message>, +                operation: &mut dyn Operation<()>,              ) {                  self.content.operate(layout, renderer, operation);              } diff --git a/winit/src/application.rs b/winit/src/application.rs index d93ea42e..a08c2010 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -19,7 +19,7 @@ use crate::graphics::compositor::{self, Compositor};  use crate::runtime::clipboard;  use crate::runtime::program::Program;  use crate::runtime::user_interface::{self, UserInterface}; -use crate::runtime::{Command, Debug}; +use crate::runtime::{Action, Debug, Task};  use crate::{Clipboard, Error, Proxy, Settings};  use futures::channel::mpsc; @@ -36,7 +36,7 @@ use std::sync::Arc;  /// its own window.  ///  /// An [`Application`] can execute asynchronous actions by returning a -/// [`Command`] in some of its methods. +/// [`Task`] in some of its methods.  ///  /// When using an [`Application`] with the `debug` feature enabled, a debug view  /// can be toggled by pressing `F12`. @@ -52,10 +52,10 @@ where      ///      /// Here is where you should return the initial state of your app.      /// -    /// Additionally, you can return a [`Command`] if you need to perform some +    /// Additionally, you can return a [`Task`] if you need to perform some      /// async action in the background on startup. This is useful if you want to      /// load state from a file, perform an initial HTTP request, etc. -    fn new(flags: Self::Flags) -> (Self, Command<Self::Message>); +    fn new(flags: Self::Flags) -> (Self, Task<Self::Message>);      /// Returns the current title of the [`Application`].      /// @@ -155,19 +155,23 @@ where      let (proxy, worker) = Proxy::new(event_loop.create_proxy()); -    let runtime = { +    let mut runtime = {          let executor = E::new().map_err(Error::ExecutorCreationFailed)?;          executor.spawn(worker);          Runtime::new(executor, proxy.clone())      }; -    let (application, init_command) = { +    let (application, task) = {          let flags = settings.flags;          runtime.enter(|| A::new(flags))      }; +    if let Some(stream) = task.into_stream() { +        runtime.run(stream); +    } +      let id = settings.id;      let title = application.title(); @@ -183,7 +187,6 @@ where          boot_receiver,          event_receiver,          control_sender, -        init_command,          settings.fonts,      )); @@ -193,7 +196,7 @@ where          instance: std::pin::Pin<Box<F>>,          context: task::Context<'static>,          boot: Option<BootConfig<C>>, -        sender: mpsc::UnboundedSender<winit::event::Event<Message>>, +        sender: mpsc::UnboundedSender<winit::event::Event<Action<Message>>>,          receiver: mpsc::UnboundedReceiver<winit::event_loop::ControlFlow>,          error: Option<Error>,          #[cfg(target_arch = "wasm32")] @@ -229,7 +232,7 @@ where          queued_events: Vec::new(),      }; -    impl<Message, F, C> winit::application::ApplicationHandler<Message> +    impl<Message, F, C> winit::application::ApplicationHandler<Action<Message>>          for Runner<Message, F, C>      where          F: Future<Output = ()>, @@ -393,11 +396,11 @@ where          fn user_event(              &mut self,              event_loop: &winit::event_loop::ActiveEventLoop, -            message: Message, +            action: Action<Message>,          ) {              self.process_event(                  event_loop, -                winit::event::Event::UserEvent(message), +                winit::event::Event::UserEvent(action),              );          } @@ -416,7 +419,7 @@ where          fn process_event(              &mut self,              event_loop: &winit::event_loop::ActiveEventLoop, -            event: winit::event::Event<Message>, +            event: winit::event::Event<Action<Message>>,          ) {              // On Wasm, events may start being processed before the compositor              // boots up. We simply queue them and process them once ready. @@ -480,15 +483,14 @@ struct Boot<C> {  async fn run_instance<A, E, C>(      mut application: A, -    mut runtime: Runtime<E, Proxy<A::Message>, A::Message>, +    mut runtime: Runtime<E, Proxy<A::Message>, Action<A::Message>>,      mut proxy: Proxy<A::Message>,      mut debug: Debug,      mut boot: oneshot::Receiver<Boot<C>>,      mut event_receiver: mpsc::UnboundedReceiver< -        winit::event::Event<A::Message>, +        winit::event::Event<Action<A::Message>>,      >,      mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>, -    init_command: Command<A::Message>,      fonts: Vec<Cow<'static, [u8]>>,  ) where      A: Application + 'static, @@ -518,7 +520,7 @@ async fn run_instance<A, E, C>(      let physical_size = state.physical_size();      let mut clipboard = Clipboard::connect(&window); -    let mut cache = user_interface::Cache::default(); +    let cache = user_interface::Cache::default();      let mut surface = compositor.create_surface(          window.clone(),          physical_size.width, @@ -530,22 +532,12 @@ async fn run_instance<A, E, C>(          window.set_visible(true);      } -    run_command( -        &application, -        &mut compositor, -        &mut surface, -        &mut cache, -        &state, -        &mut renderer, -        init_command, -        &mut runtime, -        &mut clipboard, -        &mut should_exit, -        &mut proxy, -        &mut debug, -        &window, +    runtime.track( +        application +            .subscription() +            .map(Action::Output) +            .into_recipes(),      ); -    runtime.track(application.subscription().into_recipes());      let mut user_interface = ManuallyDrop::new(build_user_interface(          &application, @@ -581,8 +573,21 @@ async fn run_instance<A, E, C>(                      ),                  ));              } -            event::Event::UserEvent(message) => { -                messages.push(message); +            event::Event::UserEvent(action) => { +                run_action( +                    action, +                    &mut user_interface, +                    &mut compositor, +                    &mut surface, +                    &state, +                    &mut renderer, +                    &mut messages, +                    &mut clipboard, +                    &mut should_exit, +                    &mut debug, +                    &window, +                ); +                  user_events += 1;              }              event::Event::WindowEvent { @@ -756,21 +761,14 @@ async fn run_instance<A, E, C>(                          user_interface::State::Outdated                      )                  { -                    let mut cache = +                    let cache =                          ManuallyDrop::into_inner(user_interface).into_cache();                      // Update application                      update(                          &mut application, -                        &mut compositor, -                        &mut surface, -                        &mut cache,                          &mut state, -                        &mut renderer,                          &mut runtime, -                        &mut clipboard, -                        &mut should_exit, -                        &mut proxy,                          &mut debug,                          &mut messages,                          &window, @@ -855,282 +853,230 @@ where  }  /// Updates an [`Application`] by feeding it the provided messages, spawning any -/// resulting [`Command`], and tracking its [`Subscription`]. -pub fn update<A: Application, C, E: Executor>( +/// resulting [`Task`], and tracking its [`Subscription`]. +pub fn update<A: Application, E: Executor>(      application: &mut A, -    compositor: &mut C, -    surface: &mut C::Surface, -    cache: &mut user_interface::Cache,      state: &mut State<A>, -    renderer: &mut A::Renderer, -    runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>, -    clipboard: &mut Clipboard, -    should_exit: &mut bool, -    proxy: &mut Proxy<A::Message>, +    runtime: &mut Runtime<E, Proxy<A::Message>, Action<A::Message>>,      debug: &mut Debug,      messages: &mut Vec<A::Message>,      window: &winit::window::Window,  ) where -    C: Compositor<Renderer = A::Renderer> + 'static,      A::Theme: DefaultStyle,  {      for message in messages.drain(..) {          debug.log_message(&message);          debug.update_started(); -        let command = runtime.enter(|| application.update(message)); +        let task = runtime.enter(|| application.update(message));          debug.update_finished(); -        run_command( -            application, -            compositor, -            surface, -            cache, -            state, -            renderer, -            command, -            runtime, -            clipboard, -            should_exit, -            proxy, -            debug, -            window, -        ); +        if let Some(stream) = task.into_stream() { +            runtime.run(stream); +        }      }      state.synchronize(application, window);      let subscription = application.subscription(); -    runtime.track(subscription.into_recipes()); +    runtime.track(subscription.map(Action::Output).into_recipes());  } -/// Runs the actions of a [`Command`]. -pub fn run_command<A, C, E>( -    application: &A, +/// Runs the actions of a [`Task`]. +pub fn run_action<A, C>( +    action: Action<A::Message>, +    user_interface: &mut UserInterface<'_, A::Message, A::Theme, C::Renderer>,      compositor: &mut C,      surface: &mut C::Surface, -    cache: &mut user_interface::Cache,      state: &State<A>,      renderer: &mut A::Renderer, -    command: Command<A::Message>, -    runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>, +    messages: &mut Vec<A::Message>,      clipboard: &mut Clipboard,      should_exit: &mut bool, -    proxy: &mut Proxy<A::Message>,      debug: &mut Debug,      window: &winit::window::Window,  ) where      A: Application, -    E: Executor,      C: Compositor<Renderer = A::Renderer> + 'static,      A::Theme: DefaultStyle,  { -    use crate::runtime::command;      use crate::runtime::system;      use crate::runtime::window; -    for action in command.actions() { -        match action { -            command::Action::Future(future) => { -                runtime.spawn(future); +    match action { +        Action::Clipboard(action) => match action { +            clipboard::Action::Read { target, channel } => { +                let _ = channel.send(clipboard.read(target));              } -            command::Action::Stream(stream) => { -                runtime.run(stream); +            clipboard::Action::Write { target, contents } => { +                clipboard.write(target, contents);              } -            command::Action::Clipboard(action) => match action { -                clipboard::Action::Read(tag, kind) => { -                    let message = tag(clipboard.read(kind)); - -                    proxy.send(message); -                } -                clipboard::Action::Write(contents, kind) => { -                    clipboard.write(kind, contents); -                } -            }, -            command::Action::Window(action) => match action { -                window::Action::Close(_id) => { -                    *should_exit = true; -                } -                window::Action::Drag(_id) => { -                    let _res = window.drag_window(); -                } -                window::Action::Spawn { .. } => { -                    log::warn!( -                        "Spawning a window is only available with \ +        }, +        Action::Window(action) => match action { +            window::Action::Close(_id) => { +                *should_exit = true; +            } +            window::Action::Drag(_id) => { +                let _res = window.drag_window(); +            } +            window::Action::Open { .. } => { +                log::warn!( +                    "Spawning a window is only available with \                          multi-window applications." -                    ); -                } -                window::Action::Resize(_id, size) => { -                    let _ = -                        window.request_inner_size(winit::dpi::LogicalSize { -                            width: size.width, -                            height: size.height, -                        }); -                } -                window::Action::FetchSize(_id, callback) => { -                    let size = -                        window.inner_size().to_logical(window.scale_factor()); +                ); +            } +            window::Action::Resize(_id, size) => { +                let _ = window.request_inner_size(winit::dpi::LogicalSize { +                    width: size.width, +                    height: size.height, +                }); +            } +            window::Action::FetchSize(_id, channel) => { +                let size = +                    window.inner_size().to_logical(window.scale_factor()); -                    proxy.send(callback(Size::new(size.width, size.height))); -                } -                window::Action::FetchMaximized(_id, callback) => { -                    proxy.send(callback(window.is_maximized())); -                } -                window::Action::Maximize(_id, maximized) => { -                    window.set_maximized(maximized); -                } -                window::Action::FetchMinimized(_id, callback) => { -                    proxy.send(callback(window.is_minimized())); -                } -                window::Action::Minimize(_id, minimized) => { -                    window.set_minimized(minimized); -                } -                window::Action::FetchPosition(_id, callback) => { -                    let position = window -                        .inner_position() -                        .map(|position| { -                            let position = position -                                .to_logical::<f32>(window.scale_factor()); - -                            Point::new(position.x, position.y) -                        }) -                        .ok(); +                let _ = channel.send(Size::new(size.width, size.height)); +            } +            window::Action::FetchMaximized(_id, channel) => { +                let _ = channel.send(window.is_maximized()); +            } +            window::Action::Maximize(_id, maximized) => { +                window.set_maximized(maximized); +            } +            window::Action::FetchMinimized(_id, channel) => { +                let _ = channel.send(window.is_minimized()); +            } +            window::Action::Minimize(_id, minimized) => { +                window.set_minimized(minimized); +            } +            window::Action::FetchPosition(_id, channel) => { +                let position = window +                    .inner_position() +                    .map(|position| { +                        let position = +                            position.to_logical::<f32>(window.scale_factor()); + +                        Point::new(position.x, position.y) +                    }) +                    .ok(); + +                let _ = channel.send(position); +            } +            window::Action::Move(_id, position) => { +                window.set_outer_position(winit::dpi::LogicalPosition { +                    x: position.x, +                    y: position.y, +                }); +            } +            window::Action::ChangeMode(_id, mode) => { +                window.set_visible(conversion::visible(mode)); +                window.set_fullscreen(conversion::fullscreen( +                    window.current_monitor(), +                    mode, +                )); +            } +            window::Action::ChangeIcon(_id, icon) => { +                window.set_window_icon(conversion::icon(icon)); +            } +            window::Action::FetchMode(_id, channel) => { +                let mode = if window.is_visible().unwrap_or(true) { +                    conversion::mode(window.fullscreen()) +                } else { +                    core::window::Mode::Hidden +                }; -                    proxy.send(callback(position)); -                } -                window::Action::Move(_id, position) => { -                    window.set_outer_position(winit::dpi::LogicalPosition { -                        x: position.x, -                        y: position.y, +                let _ = channel.send(mode); +            } +            window::Action::ToggleMaximize(_id) => { +                window.set_maximized(!window.is_maximized()); +            } +            window::Action::ToggleDecorations(_id) => { +                window.set_decorations(!window.is_decorated()); +            } +            window::Action::RequestUserAttention(_id, user_attention) => { +                window.request_user_attention( +                    user_attention.map(conversion::user_attention), +                ); +            } +            window::Action::GainFocus(_id) => { +                window.focus_window(); +            } +            window::Action::ChangeLevel(_id, level) => { +                window.set_window_level(conversion::window_level(level)); +            } +            window::Action::ShowSystemMenu(_id) => { +                if let mouse::Cursor::Available(point) = state.cursor() { +                    window.show_window_menu(winit::dpi::LogicalPosition { +                        x: point.x, +                        y: point.y,                      });                  } -                window::Action::ChangeMode(_id, mode) => { -                    window.set_visible(conversion::visible(mode)); -                    window.set_fullscreen(conversion::fullscreen( -                        window.current_monitor(), -                        mode, -                    )); -                } -                window::Action::ChangeIcon(_id, icon) => { -                    window.set_window_icon(conversion::icon(icon)); -                } -                window::Action::FetchMode(_id, tag) => { -                    let mode = if window.is_visible().unwrap_or(true) { -                        conversion::mode(window.fullscreen()) -                    } else { -                        core::window::Mode::Hidden -                    }; - -                    proxy.send(tag(mode)); -                } -                window::Action::ToggleMaximize(_id) => { -                    window.set_maximized(!window.is_maximized()); -                } -                window::Action::ToggleDecorations(_id) => { -                    window.set_decorations(!window.is_decorated()); -                } -                window::Action::RequestUserAttention(_id, user_attention) => { -                    window.request_user_attention( -                        user_attention.map(conversion::user_attention), -                    ); -                } -                window::Action::GainFocus(_id) => { -                    window.focus_window(); -                } -                window::Action::ChangeLevel(_id, level) => { -                    window.set_window_level(conversion::window_level(level)); -                } -                window::Action::ShowSystemMenu(_id) => { -                    if let mouse::Cursor::Available(point) = state.cursor() { -                        window.show_window_menu(winit::dpi::LogicalPosition { -                            x: point.x, -                            y: point.y, -                        }); -                    } -                } -                window::Action::FetchId(_id, tag) => { -                    proxy.send(tag(window.id().into())); -                } -                window::Action::RunWithHandle(_id, tag) => { -                    use window::raw_window_handle::HasWindowHandle; +            } +            window::Action::FetchRawId(_id, channel) => { +                let _ = channel.send(window.id().into()); +            } +            window::Action::RunWithHandle(_id, f) => { +                use window::raw_window_handle::HasWindowHandle; -                    if let Ok(handle) = window.window_handle() { -                        proxy.send(tag(handle)); -                    } +                if let Ok(handle) = window.window_handle() { +                    f(handle);                  } +            } -                window::Action::Screenshot(_id, tag) => { -                    let bytes = compositor.screenshot( -                        renderer, -                        surface, -                        state.viewport(), -                        state.background_color(), -                        &debug.overlay(), -                    ); - -                    proxy.send(tag(window::Screenshot::new( -                        bytes, -                        state.physical_size(), -                        state.viewport().scale_factor(), -                    ))); -                } -            }, -            command::Action::System(action) => match action { -                system::Action::QueryInformation(_tag) => { -                    #[cfg(feature = "system")] -                    { -                        let graphics_info = compositor.fetch_information(); -                        let mut proxy = proxy.clone(); - -                        let _ = std::thread::spawn(move || { -                            let information = -                                crate::system::information(graphics_info); - -                            let message = _tag(information); - -                            proxy.send(message); -                        }); -                    } -                } -            }, -            command::Action::Widget(action) => { -                let mut current_cache = std::mem::take(cache); -                let mut current_operation = Some(action); - -                let mut user_interface = build_user_interface( -                    application, -                    current_cache, +            window::Action::Screenshot(_id, channel) => { +                let bytes = compositor.screenshot(                      renderer, -                    state.logical_size(), -                    debug, +                    surface, +                    state.viewport(), +                    state.background_color(), +                    &debug.overlay(),                  ); -                while let Some(mut operation) = current_operation.take() { -                    user_interface.operate(renderer, operation.as_mut()); +                let _ = channel.send(window::Screenshot::new( +                    bytes, +                    state.physical_size(), +                    state.viewport().scale_factor(), +                )); +            } +        }, +        Action::System(action) => match action { +            system::Action::QueryInformation(_channel) => { +                #[cfg(feature = "system")] +                { +                    let graphics_info = compositor.fetch_information(); -                    match operation.finish() { -                        operation::Outcome::None => {} -                        operation::Outcome::Some(message) => { -                            proxy.send(message); -                        } -                        operation::Outcome::Chain(next) => { -                            current_operation = Some(next); -                        } +                    let _ = std::thread::spawn(move || { +                        let information = +                            crate::system::information(graphics_info); + +                        let _ = _channel.send(information); +                    }); +                } +            } +        }, +        Action::Widget(operation) => { +            let mut current_operation = Some(operation); + +            while let Some(mut operation) = current_operation.take() { +                user_interface.operate(renderer, operation.as_mut()); + +                match operation.finish() { +                    operation::Outcome::None => {} +                    operation::Outcome::Some(()) => {} +                    operation::Outcome::Chain(next) => { +                        current_operation = Some(next);                      }                  } - -                current_cache = user_interface.into_cache(); -                *cache = current_cache;              } -            command::Action::LoadFont { bytes, tagger } => { -                // TODO: Error handling (?) -                compositor.load_font(bytes); +        } +        Action::LoadFont { bytes, channel } => { +            // TODO: Error handling (?) +            compositor.load_font(bytes); -                proxy.send(tagger(Ok(()))); -            } -            command::Action::Custom(_) => { -                log::warn!("Unsupported custom action in `iced_winit` shell"); -            } +            let _ = channel.send(Ok(())); +        } +        Action::Output(message) => { +            messages.push(message);          }      }  } diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 2eaf9241..d56b47eb 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -21,10 +21,10 @@ use crate::futures::{Executor, Runtime};  use crate::graphics;  use crate::graphics::{compositor, Compositor};  use crate::multi_window::window_manager::WindowManager; -use crate::runtime::command::{self, Command};  use crate::runtime::multi_window::Program;  use crate::runtime::user_interface::{self, UserInterface};  use crate::runtime::Debug; +use crate::runtime::{Action, Task};  use crate::{Clipboard, Error, Proxy, Settings};  pub use crate::application::{default, Appearance, DefaultStyle}; @@ -41,7 +41,7 @@ use std::time::Instant;  /// its own window.  ///  /// An [`Application`] can execute asynchronous actions by returning a -/// [`Command`] in some of its methods. +/// [`Task`] in some of its methods.  ///  /// When using an [`Application`] with the `debug` feature enabled, a debug view  /// can be toggled by pressing `F12`. @@ -57,10 +57,10 @@ where      ///      /// Here is where you should return the initial state of your app.      /// -    /// Additionally, you can return a [`Command`] if you need to perform some +    /// Additionally, you can return a [`Task`] if you need to perform some      /// async action in the background on startup. This is useful if you want to      /// load state from a file, perform an initial HTTP request, etc. -    fn new(flags: Self::Flags) -> (Self, Command<Self::Message>); +    fn new(flags: Self::Flags) -> (Self, Task<Self::Message>);      /// Returns the current title of the [`Application`].      /// @@ -127,19 +127,23 @@ where      let (proxy, worker) = Proxy::new(event_loop.create_proxy()); -    let runtime = { +    let mut runtime = {          let executor = E::new().map_err(Error::ExecutorCreationFailed)?;          executor.spawn(worker);          Runtime::new(executor, proxy.clone())      }; -    let (application, init_command) = { +    let (application, task) = {          let flags = settings.flags;          runtime.enter(|| A::new(flags))      }; +    if let Some(stream) = task.into_stream() { +        runtime.run(stream); +    } +      let id = settings.id;      let title = application.title(window::Id::MAIN); @@ -155,7 +159,6 @@ where          boot_receiver,          event_receiver,          control_sender, -        init_command,      ));      let context = task::Context::from_waker(task::noop_waker_ref()); @@ -448,13 +451,12 @@ enum Control {  async fn run_instance<A, E, C>(      mut application: A, -    mut runtime: Runtime<E, Proxy<A::Message>, A::Message>, +    mut runtime: Runtime<E, Proxy<A::Message>, Action<A::Message>>,      mut proxy: Proxy<A::Message>,      mut debug: Debug,      mut boot: oneshot::Receiver<Boot<C>>, -    mut event_receiver: mpsc::UnboundedReceiver<Event<A::Message>>, +    mut event_receiver: mpsc::UnboundedReceiver<Event<Action<A::Message>>>,      mut control_sender: mpsc::UnboundedSender<Control>, -    init_command: Command<A::Message>,  ) where      A: Application + 'static,      E: Executor + 'static, @@ -511,21 +513,13 @@ async fn run_instance<A, E, C>(          )]),      )); -    run_command( -        &application, -        &mut compositor, -        init_command, -        &mut runtime, -        &mut clipboard, -        &mut control_sender, -        &mut proxy, -        &mut debug, -        &mut window_manager, -        &mut ui_caches, +    runtime.track( +        application +            .subscription() +            .map(Action::Output) +            .into_recipes(),      ); -    runtime.track(application.subscription().into_recipes()); -      let mut messages = Vec::new();      let mut user_events = 0; @@ -594,8 +588,19 @@ async fn run_instance<A, E, C>(                              ),                          );                      } -                    event::Event::UserEvent(message) => { -                        messages.push(message); +                    event::Event::UserEvent(action) => { +                        run_action( +                            action, +                            &application, +                            &mut compositor, +                            &mut messages, +                            &mut clipboard, +                            &mut control_sender, +                            &mut debug, +                            &mut user_interfaces, +                            &mut window_manager, +                            &mut ui_caches, +                        );                          user_events += 1;                      }                      event::Event::WindowEvent { @@ -888,7 +893,7 @@ async fn run_instance<A, E, C>(                          // TODO mw application update returns which window IDs to update                          if !messages.is_empty() || uis_stale { -                            let mut cached_interfaces: FxHashMap< +                            let cached_interfaces: FxHashMap<                                  window::Id,                                  user_interface::Cache,                              > = ManuallyDrop::into_inner(user_interfaces) @@ -899,15 +904,9 @@ async fn run_instance<A, E, C>(                              // Update application                              update(                                  &mut application, -                                &mut compositor,                                  &mut runtime, -                                &mut clipboard, -                                &mut control_sender, -                                &mut proxy,                                  &mut debug,                                  &mut messages, -                                &mut window_manager, -                                &mut cached_interfaces,                              );                              // we must synchronize all window states with application state after an @@ -971,63 +970,46 @@ where      user_interface  } -/// Updates a multi-window [`Application`] by feeding it messages, spawning any -/// resulting [`Command`], and tracking its [`Subscription`]. -fn update<A: Application, C, E: Executor>( +fn update<A: Application, E: Executor>(      application: &mut A, -    compositor: &mut C, -    runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>, -    clipboard: &mut Clipboard, -    control_sender: &mut mpsc::UnboundedSender<Control>, -    proxy: &mut Proxy<A::Message>, +    runtime: &mut Runtime<E, Proxy<A::Message>, Action<A::Message>>,      debug: &mut Debug,      messages: &mut Vec<A::Message>, -    window_manager: &mut WindowManager<A, C>, -    ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,  ) where -    C: Compositor<Renderer = A::Renderer> + 'static,      A::Theme: DefaultStyle,  {      for message in messages.drain(..) {          debug.log_message(&message);          debug.update_started(); -        let command = runtime.enter(|| application.update(message)); +        let task = runtime.enter(|| application.update(message));          debug.update_finished(); -        run_command( -            application, -            compositor, -            command, -            runtime, -            clipboard, -            control_sender, -            proxy, -            debug, -            window_manager, -            ui_caches, -        ); +        if let Some(stream) = task.into_stream() { +            runtime.run(stream); +        }      }      let subscription = application.subscription(); -    runtime.track(subscription.into_recipes()); +    runtime.track(subscription.map(Action::Output).into_recipes());  } -/// Runs the actions of a [`Command`]. -fn run_command<A, C, E>( +fn run_action<A, C>( +    action: Action<A::Message>,      application: &A,      compositor: &mut C, -    command: Command<A::Message>, -    runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>, +    messages: &mut Vec<A::Message>,      clipboard: &mut Clipboard,      control_sender: &mut mpsc::UnboundedSender<Control>, -    proxy: &mut Proxy<A::Message>,      debug: &mut Debug, +    interfaces: &mut FxHashMap< +        window::Id, +        UserInterface<'_, A::Message, A::Theme, A::Renderer>, +    >,      window_manager: &mut WindowManager<A, C>,      ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,  ) where      A: Application, -    E: Executor,      C: Compositor<Renderer = A::Renderer> + 'static,      A::Theme: DefaultStyle,  { @@ -1035,279 +1017,252 @@ fn run_command<A, C, E>(      use crate::runtime::system;      use crate::runtime::window; -    for action in command.actions() { -        match action { -            command::Action::Future(future) => { -                runtime.spawn(Box::pin(future)); +    match action { +        Action::Output(message) => { +            messages.push(message); +        } +        Action::Clipboard(action) => match action { +            clipboard::Action::Read { target, channel } => { +                let _ = channel.send(clipboard.read(target));              } -            command::Action::Stream(stream) => { -                runtime.run(Box::pin(stream)); +            clipboard::Action::Write { target, contents } => { +                clipboard.write(target, contents);              } -            command::Action::Clipboard(action) => match action { -                clipboard::Action::Read(tag, kind) => { -                    let message = tag(clipboard.read(kind)); +        }, +        Action::Window(action) => match action { +            window::Action::Open(id, settings) => { +                let monitor = window_manager.last_monitor(); -                    proxy.send(message); -                } -                clipboard::Action::Write(contents, kind) => { -                    clipboard.write(kind, contents); -                } -            }, -            command::Action::Window(action) => match action { -                window::Action::Spawn(id, settings) => { -                    let monitor = window_manager.last_monitor(); +                control_sender +                    .start_send(Control::CreateWindow { +                        id, +                        settings, +                        title: application.title(id), +                        monitor, +                    }) +                    .expect("Send control action"); +            } +            window::Action::Close(id) => { +                let _ = window_manager.remove(id); +                let _ = ui_caches.remove(&id); +                if window_manager.is_empty() {                      control_sender -                        .start_send(Control::CreateWindow { -                            id, -                            settings, -                            title: application.title(id), -                            monitor, -                        }) +                        .start_send(Control::Exit)                          .expect("Send control action");                  } -                window::Action::Close(id) => { -                    let _ = window_manager.remove(id); -                    let _ = ui_caches.remove(&id); - -                    if window_manager.is_empty() { -                        control_sender -                            .start_send(Control::Exit) -                            .expect("Send control action"); -                    } -                } -                window::Action::Drag(id) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        let _ = window.raw.drag_window(); -                    } +            } +            window::Action::Drag(id) => { +                if let Some(window) = window_manager.get_mut(id) { +                    let _ = window.raw.drag_window();                  } -                window::Action::Resize(id, size) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        let _ = window.raw.request_inner_size( -                            winit::dpi::LogicalSize { -                                width: size.width, -                                height: size.height, -                            }, -                        ); -                    } +            } +            window::Action::Resize(id, size) => { +                if let Some(window) = window_manager.get_mut(id) { +                    let _ = window.raw.request_inner_size( +                        winit::dpi::LogicalSize { +                            width: size.width, +                            height: size.height, +                        }, +                    );                  } -                window::Action::FetchSize(id, callback) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        let size = window -                            .raw -                            .inner_size() -                            .to_logical(window.raw.scale_factor()); - -                        proxy -                            .send(callback(Size::new(size.width, size.height))); -                    } +            } +            window::Action::FetchSize(id, channel) => { +                if let Some(window) = window_manager.get_mut(id) { +                    let size = window +                        .raw +                        .inner_size() +                        .to_logical(window.raw.scale_factor()); + +                    let _ = channel.send(Size::new(size.width, size.height));                  } -                window::Action::FetchMaximized(id, callback) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        proxy.send(callback(window.raw.is_maximized())); -                    } +            } +            window::Action::FetchMaximized(id, channel) => { +                if let Some(window) = window_manager.get_mut(id) { +                    let _ = channel.send(window.raw.is_maximized());                  } -                window::Action::Maximize(id, maximized) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        window.raw.set_maximized(maximized); -                    } +            } +            window::Action::Maximize(id, maximized) => { +                if let Some(window) = window_manager.get_mut(id) { +                    window.raw.set_maximized(maximized);                  } -                window::Action::FetchMinimized(id, callback) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        proxy.send(callback(window.raw.is_minimized())); -                    } +            } +            window::Action::FetchMinimized(id, channel) => { +                if let Some(window) = window_manager.get_mut(id) { +                    let _ = channel.send(window.raw.is_minimized());                  } -                window::Action::Minimize(id, minimized) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        window.raw.set_minimized(minimized); -                    } +            } +            window::Action::Minimize(id, minimized) => { +                if let Some(window) = window_manager.get_mut(id) { +                    window.raw.set_minimized(minimized);                  } -                window::Action::FetchPosition(id, callback) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        let position = window -                            .raw -                            .inner_position() -                            .map(|position| { -                                let position = position.to_logical::<f32>( -                                    window.raw.scale_factor(), -                                ); - -                                Point::new(position.x, position.y) -                            }) -                            .ok(); +            } +            window::Action::FetchPosition(id, channel) => { +                if let Some(window) = window_manager.get_mut(id) { +                    let position = window +                        .raw +                        .inner_position() +                        .map(|position| { +                            let position = position +                                .to_logical::<f32>(window.raw.scale_factor()); + +                            Point::new(position.x, position.y) +                        }) +                        .ok(); -                        proxy.send(callback(position)); -                    } +                    let _ = channel.send(position);                  } -                window::Action::Move(id, position) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        window.raw.set_outer_position( -                            winit::dpi::LogicalPosition { -                                x: position.x, -                                y: position.y, -                            }, -                        ); -                    } +            } +            window::Action::Move(id, position) => { +                if let Some(window) = window_manager.get_mut(id) { +                    window.raw.set_outer_position( +                        winit::dpi::LogicalPosition { +                            x: position.x, +                            y: position.y, +                        }, +                    );                  } -                window::Action::ChangeMode(id, mode) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        window.raw.set_visible(conversion::visible(mode)); -                        window.raw.set_fullscreen(conversion::fullscreen( -                            window.raw.current_monitor(), -                            mode, -                        )); -                    } +            } +            window::Action::ChangeMode(id, mode) => { +                if let Some(window) = window_manager.get_mut(id) { +                    window.raw.set_visible(conversion::visible(mode)); +                    window.raw.set_fullscreen(conversion::fullscreen( +                        window.raw.current_monitor(), +                        mode, +                    ));                  } -                window::Action::ChangeIcon(id, icon) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        window.raw.set_window_icon(conversion::icon(icon)); -                    } +            } +            window::Action::ChangeIcon(id, icon) => { +                if let Some(window) = window_manager.get_mut(id) { +                    window.raw.set_window_icon(conversion::icon(icon));                  } -                window::Action::FetchMode(id, tag) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        let mode = if window.raw.is_visible().unwrap_or(true) { -                            conversion::mode(window.raw.fullscreen()) -                        } else { -                            core::window::Mode::Hidden -                        }; - -                        proxy.send(tag(mode)); -                    } +            } +            window::Action::FetchMode(id, channel) => { +                if let Some(window) = window_manager.get_mut(id) { +                    let mode = if window.raw.is_visible().unwrap_or(true) { +                        conversion::mode(window.raw.fullscreen()) +                    } else { +                        core::window::Mode::Hidden +                    }; + +                    let _ = channel.send(mode);                  } -                window::Action::ToggleMaximize(id) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        window.raw.set_maximized(!window.raw.is_maximized()); -                    } +            } +            window::Action::ToggleMaximize(id) => { +                if let Some(window) = window_manager.get_mut(id) { +                    window.raw.set_maximized(!window.raw.is_maximized());                  } -                window::Action::ToggleDecorations(id) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        window.raw.set_decorations(!window.raw.is_decorated()); -                    } +            } +            window::Action::ToggleDecorations(id) => { +                if let Some(window) = window_manager.get_mut(id) { +                    window.raw.set_decorations(!window.raw.is_decorated());                  } -                window::Action::RequestUserAttention(id, attention_type) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        window.raw.request_user_attention( -                            attention_type.map(conversion::user_attention), -                        ); -                    } +            } +            window::Action::RequestUserAttention(id, attention_type) => { +                if let Some(window) = window_manager.get_mut(id) { +                    window.raw.request_user_attention( +                        attention_type.map(conversion::user_attention), +                    );                  } -                window::Action::GainFocus(id) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        window.raw.focus_window(); -                    } +            } +            window::Action::GainFocus(id) => { +                if let Some(window) = window_manager.get_mut(id) { +                    window.raw.focus_window();                  } -                window::Action::ChangeLevel(id, level) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        window -                            .raw -                            .set_window_level(conversion::window_level(level)); -                    } +            } +            window::Action::ChangeLevel(id, level) => { +                if let Some(window) = window_manager.get_mut(id) { +                    window +                        .raw +                        .set_window_level(conversion::window_level(level));                  } -                window::Action::ShowSystemMenu(id) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        if let mouse::Cursor::Available(point) = -                            window.state.cursor() -                        { -                            window.raw.show_window_menu( -                                winit::dpi::LogicalPosition { -                                    x: point.x, -                                    y: point.y, -                                }, -                            ); -                        } +            } +            window::Action::ShowSystemMenu(id) => { +                if let Some(window) = window_manager.get_mut(id) { +                    if let mouse::Cursor::Available(point) = +                        window.state.cursor() +                    { +                        window.raw.show_window_menu( +                            winit::dpi::LogicalPosition { +                                x: point.x, +                                y: point.y, +                            }, +                        );                      }                  } -                window::Action::FetchId(id, tag) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        proxy.send(tag(window.raw.id().into())); -                    } +            } +            window::Action::FetchRawId(id, channel) => { +                if let Some(window) = window_manager.get_mut(id) { +                    let _ = channel.send(window.raw.id().into());                  } -                window::Action::RunWithHandle(id, tag) => { -                    use window::raw_window_handle::HasWindowHandle; - -                    if let Some(handle) = window_manager -                        .get_mut(id) -                        .and_then(|window| window.raw.window_handle().ok()) -                    { -                        proxy.send(tag(handle)); -                    } +            } +            window::Action::RunWithHandle(id, f) => { +                use window::raw_window_handle::HasWindowHandle; + +                if let Some(handle) = window_manager +                    .get_mut(id) +                    .and_then(|window| window.raw.window_handle().ok()) +                { +                    f(handle);                  } -                window::Action::Screenshot(id, tag) => { -                    if let Some(window) = window_manager.get_mut(id) { -                        let bytes = compositor.screenshot( -                            &mut window.renderer, -                            &mut window.surface, -                            window.state.viewport(), -                            window.state.background_color(), -                            &debug.overlay(), -                        ); +            } +            window::Action::Screenshot(id, channel) => { +                if let Some(window) = window_manager.get_mut(id) { +                    let bytes = compositor.screenshot( +                        &mut window.renderer, +                        &mut window.surface, +                        window.state.viewport(), +                        window.state.background_color(), +                        &debug.overlay(), +                    ); -                        proxy.send(tag(window::Screenshot::new( -                            bytes, -                            window.state.physical_size(), -                            window.state.viewport().scale_factor(), -                        ))); -                    } +                    let _ = channel.send(window::Screenshot::new( +                        bytes, +                        window.state.physical_size(), +                        window.state.viewport().scale_factor(), +                    ));                  } -            }, -            command::Action::System(action) => match action { -                system::Action::QueryInformation(_tag) => { -                    #[cfg(feature = "system")] -                    { -                        let graphics_info = compositor.fetch_information(); -                        let mut proxy = proxy.clone(); - -                        let _ = std::thread::spawn(move || { -                            let information = -                                crate::system::information(graphics_info); - -                            let message = _tag(information); - -                            proxy.send(message); -                        }); -                    } +            } +        }, +        Action::System(action) => match action { +            system::Action::QueryInformation(_channel) => { +                #[cfg(feature = "system")] +                { +                    let graphics_info = compositor.fetch_information(); + +                    let _ = std::thread::spawn(move || { +                        let information = +                            crate::system::information(graphics_info); + +                        let _ = _channel.send(information); +                    });                  } -            }, -            command::Action::Widget(action) => { -                let mut current_operation = Some(action); - -                let mut uis = build_user_interfaces( -                    application, -                    debug, -                    window_manager, -                    std::mem::take(ui_caches), -                ); - -                while let Some(mut operation) = current_operation.take() { -                    for (id, ui) in uis.iter_mut() { -                        if let Some(window) = window_manager.get_mut(*id) { -                            ui.operate(&window.renderer, operation.as_mut()); -                        } +            } +        }, +        Action::Widget(operation) => { +            let mut current_operation = Some(operation); + +            while let Some(mut operation) = current_operation.take() { +                for (id, ui) in interfaces.iter_mut() { +                    if let Some(window) = window_manager.get_mut(*id) { +                        ui.operate(&window.renderer, operation.as_mut());                      } +                } -                    match operation.finish() { -                        operation::Outcome::None => {} -                        operation::Outcome::Some(message) => { -                            proxy.send(message); -                        } -                        operation::Outcome::Chain(next) => { -                            current_operation = Some(next); -                        } +                match operation.finish() { +                    operation::Outcome::None => {} +                    operation::Outcome::Some(()) => {} +                    operation::Outcome::Chain(next) => { +                        current_operation = Some(next);                      }                  } - -                *ui_caches = -                    uis.drain().map(|(id, ui)| (id, ui.into_cache())).collect();              } -            command::Action::LoadFont { bytes, tagger } => { -                // TODO: Error handling (?) -                compositor.load_font(bytes.clone()); +        } +        Action::LoadFont { bytes, channel } => { +            // TODO: Error handling (?) +            compositor.load_font(bytes.clone()); -                proxy.send(tagger(Ok(()))); -            } -            command::Action::Custom(_) => { -                log::warn!("Unsupported custom action in `iced_winit` shell"); -            } +            let _ = channel.send(Ok(()));          }      }  } diff --git a/winit/src/proxy.rs b/winit/src/proxy.rs index 3edc30ad..0ab61375 100644 --- a/winit/src/proxy.rs +++ b/winit/src/proxy.rs @@ -4,17 +4,18 @@ use crate::futures::futures::{      task::{Context, Poll},      Future, Sink, StreamExt,  }; +use crate::runtime::Action;  use std::pin::Pin;  /// An event loop proxy with backpressure that implements `Sink`.  #[derive(Debug)] -pub struct Proxy<Message: 'static> { -    raw: winit::event_loop::EventLoopProxy<Message>, -    sender: mpsc::Sender<Message>, +pub struct Proxy<T: 'static> { +    raw: winit::event_loop::EventLoopProxy<Action<T>>, +    sender: mpsc::Sender<Action<T>>,      notifier: mpsc::Sender<usize>,  } -impl<Message: 'static> Clone for Proxy<Message> { +impl<T: 'static> Clone for Proxy<T> {      fn clone(&self) -> Self {          Self {              raw: self.raw.clone(), @@ -24,12 +25,12 @@ impl<Message: 'static> Clone for Proxy<Message> {      }  } -impl<Message: 'static> Proxy<Message> { +impl<T: 'static> Proxy<T> {      const MAX_SIZE: usize = 100;      /// Creates a new [`Proxy`] from an `EventLoopProxy`.      pub fn new( -        raw: winit::event_loop::EventLoopProxy<Message>, +        raw: winit::event_loop::EventLoopProxy<Action<T>>,      ) -> (Self, impl Future<Output = ()>) {          let (notifier, mut processed) = mpsc::channel(Self::MAX_SIZE);          let (sender, mut receiver) = mpsc::channel(Self::MAX_SIZE); @@ -72,16 +73,16 @@ impl<Message: 'static> Proxy<Message> {          )      } -    /// Sends a `Message` to the event loop. +    /// Sends a value to the event loop.      ///      /// Note: This skips the backpressure mechanism with an unbounded      /// channel. Use sparingly! -    pub fn send(&mut self, message: Message) +    pub fn send(&mut self, value: T)      where -        Message: std::fmt::Debug, +        T: std::fmt::Debug,      {          self.raw -            .send_event(message) +            .send_event(Action::Output(value))              .expect("Send message to event loop");      } @@ -92,7 +93,7 @@ impl<Message: 'static> Proxy<Message> {      }  } -impl<Message: 'static> Sink<Message> for Proxy<Message> { +impl<T: 'static> Sink<Action<T>> for Proxy<T> {      type Error = mpsc::SendError;      fn poll_ready( @@ -104,9 +105,9 @@ impl<Message: 'static> Sink<Message> for Proxy<Message> {      fn start_send(          mut self: Pin<&mut Self>, -        message: Message, +        action: Action<T>,      ) -> Result<(), Self::Error> { -        self.sender.start_send(message) +        self.sender.start_send(action)      }      fn poll_flush( diff --git a/winit/src/system.rs b/winit/src/system.rs index c5a5b219..7997f311 100644 --- a/winit/src/system.rs +++ b/winit/src/system.rs @@ -1,15 +1,13 @@  //! Access the native system.  use crate::graphics::compositor; -use crate::runtime::command::{self, Command};  use crate::runtime::system::{Action, Information}; +use crate::runtime::{self, Task};  /// Query for available system information. -pub fn fetch_information<Message>( -    f: impl Fn(Information) -> Message + Send + 'static, -) -> Command<Message> { -    Command::single(command::Action::System(Action::QueryInformation( -        Box::new(f), -    ))) +pub fn fetch_information() -> Task<Information> { +    Task::oneshot(|channel| { +        runtime::Action::System(Action::QueryInformation(channel)) +    })  }  pub(crate) fn information( | 
