From 341c9a3c12aa9d327ef1d8f168ea0adb9b5ad10b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 19 Jun 2024 01:53:40 +0200 Subject: Introduce `daemon` API and unify shell runtimes --- src/daemon.rs | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 src/daemon.rs (limited to 'src/daemon.rs') diff --git a/src/daemon.rs b/src/daemon.rs new file mode 100644 index 00000000..58293949 --- /dev/null +++ b/src/daemon.rs @@ -0,0 +1,298 @@ +//! Create and run daemons that run in the background. +use crate::application; +use crate::program::{self, Program}; +use crate::window; +use crate::{Element, Font, Result, Settings, Subscription, Task}; + +use std::borrow::Cow; + +pub use crate::shell::program::{Appearance, DefaultStyle}; + +/// Creates an iced [`Daemon`] given its title, update, and view logic. +/// +/// A [`Daemon`] will not open a window by default, but will run silently +/// instead until a [`Task`] from [`window::open`] is returned by its update logic. +/// +/// Furthermore, a [`Daemon`] will not stop running when all its windows are closed. +/// In order to completely terminate a [`Daemon`], its process must be interrupted or +/// its update logic must produce a [`Task`] from [`exit`]. +/// +/// [`exit`]: crate::exit +pub fn daemon( + title: impl Title, + update: impl application::Update, + view: impl for<'a> self::View<'a, State, Message, Theme, Renderer>, +) -> Daemon> +where + State: 'static, + Message: Send + std::fmt::Debug + 'static, + Theme: Default + DefaultStyle, + Renderer: program::Renderer, +{ + use std::marker::PhantomData; + + struct Instance { + update: Update, + view: View, + _state: PhantomData, + _message: PhantomData, + _theme: PhantomData, + _renderer: PhantomData, + } + + impl Program + for Instance + where + Message: Send + std::fmt::Debug + 'static, + Theme: Default + DefaultStyle, + Renderer: program::Renderer, + Update: application::Update, + View: for<'a> self::View<'a, State, Message, Theme, Renderer>, + { + type State = State; + type Message = Message; + type Theme = Theme; + type Renderer = Renderer; + type Executor = iced_futures::backend::default::Executor; + + fn load(&self) -> Task { + Task::none() + } + + fn update( + &self, + state: &mut Self::State, + message: Self::Message, + ) -> Task { + self.update.update(state, message).into() + } + + fn view<'a>( + &self, + state: &'a Self::State, + window: window::Id, + ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { + self.view.view(state, window).into() + } + } + + Daemon { + raw: Instance { + update, + view, + _state: PhantomData, + _message: PhantomData, + _theme: PhantomData, + _renderer: PhantomData, + }, + settings: Settings::default(), + } + .title(title) +} + +/// The underlying definition and configuration of an iced daemon. +/// +/// You can use this API to create and run iced applications +/// step by step—without coupling your logic to a trait +/// or a specific type. +/// +/// You can create a [`Daemon`] with the [`daemon`] helper. +#[derive(Debug)] +pub struct Daemon { + raw: P, + settings: Settings, +} + +impl Daemon

{ + /// Runs the [`Daemon`]. + /// + /// The state of the [`Daemon`] must implement [`Default`]. + /// If your state does not implement [`Default`], use [`run_with`] + /// instead. + /// + /// [`run_with`]: Self::run_with + pub fn run(self) -> Result + where + Self: 'static, + P::State: Default, + { + self.run_with(P::State::default) + } + + /// Runs the [`Daemon`] with a closure that creates the initial state. + pub fn run_with(self, initialize: I) -> Result + where + Self: 'static, + I: Fn() -> P::State + Clone + 'static, + { + self.raw.run_with(self.settings, None, initialize) + } + + /// Sets the [`Settings`] that will be used to run the [`Daemon`]. + pub fn settings(self, settings: Settings) -> Self { + Self { settings, ..self } + } + + /// Sets the [`Settings::antialiasing`] of the [`Daemon`]. + pub fn antialiasing(self, antialiasing: bool) -> Self { + Self { + settings: Settings { + antialiasing, + ..self.settings + }, + ..self + } + } + + /// Sets the default [`Font`] of the [`Daemon`]. + pub fn default_font(self, default_font: Font) -> Self { + Self { + settings: Settings { + default_font, + ..self.settings + }, + ..self + } + } + + /// Adds a font to the list of fonts that will be loaded at the start of the [`Daemon`]. + pub fn font(mut self, font: impl Into>) -> Self { + self.settings.fonts.push(font.into()); + self + } + + /// Sets the [`Title`] of the [`Daemon`]. + pub(crate) fn title( + self, + title: impl Title, + ) -> Daemon< + impl Program, + > { + Daemon { + raw: program::with_title(self.raw, move |state, window| { + title.title(state, window) + }), + settings: self.settings, + } + } + + /// Runs the [`Task`] produced by the closure at startup. + pub fn load( + self, + f: impl Fn() -> Task, + ) -> Daemon< + impl Program, + > { + Daemon { + raw: program::with_load(self.raw, f), + settings: self.settings, + } + } + + /// Sets the subscription logic of the [`Daemon`]. + pub fn subscription( + self, + f: impl Fn(&P::State) -> Subscription, + ) -> Daemon< + impl Program, + > { + Daemon { + raw: program::with_subscription(self.raw, f), + settings: self.settings, + } + } + + /// Sets the theme logic of the [`Daemon`]. + pub fn theme( + self, + f: impl Fn(&P::State, window::Id) -> P::Theme, + ) -> Daemon< + impl Program, + > { + Daemon { + raw: program::with_theme(self.raw, f), + settings: self.settings, + } + } + + /// Sets the style logic of the [`Daemon`]. + pub fn style( + self, + f: impl Fn(&P::State, &P::Theme) -> Appearance, + ) -> Daemon< + impl Program, + > { + Daemon { + raw: program::with_style(self.raw, f), + settings: self.settings, + } + } + + /// Sets the scale factor of the [`Daemon`]. + pub fn scale_factor( + self, + f: impl Fn(&P::State, window::Id) -> f64, + ) -> Daemon< + impl Program, + > { + Daemon { + raw: program::with_scale_factor(self.raw, f), + settings: self.settings, + } + } +} + +/// The title logic of some [`Daemon`]. +/// +/// This trait is implemented both for `&static str` and +/// any closure `Fn(&State, window::Id) -> String`. +/// +/// This trait allows the [`daemon`] builder to take any of them. +pub trait Title { + /// Produces the title of the [`Daemon`]. + fn title(&self, state: &State, window: window::Id) -> String; +} + +impl Title for &'static str { + fn title(&self, _state: &State, _window: window::Id) -> String { + self.to_string() + } +} + +impl Title for T +where + T: Fn(&State, window::Id) -> String, +{ + fn title(&self, state: &State, window: window::Id) -> String { + self(state, window) + } +} + +/// The view logic of some [`Daemon`]. +/// +/// This trait allows the [`daemon`] builder to take any closure that +/// returns any `Into>`. +pub trait View<'a, State, Message, Theme, Renderer> { + /// Produces the widget of the [`Daemon`]. + fn view( + &self, + state: &'a State, + window: window::Id, + ) -> impl Into>; +} + +impl<'a, T, State, Message, Theme, Renderer, Widget> + View<'a, State, Message, Theme, Renderer> for T +where + T: Fn(&'a State, window::Id) -> Widget, + State: 'static, + Widget: Into>, +{ + fn view( + &self, + state: &'a State, + window: window::Id, + ) -> impl Into> { + self(state, window) + } +} -- cgit