diff options
Diffstat (limited to 'src/daemon.rs')
-rw-r--r-- | src/daemon.rs | 298 |
1 files changed, 298 insertions, 0 deletions
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<State, Message, Theme, Renderer>( + title: impl Title<State>, + update: impl application::Update<State, Message>, + view: impl for<'a> self::View<'a, State, Message, Theme, Renderer>, +) -> Daemon<impl Program<State = State, Message = Message, Theme = Theme>> +where + State: 'static, + Message: Send + std::fmt::Debug + 'static, + Theme: Default + DefaultStyle, + Renderer: program::Renderer, +{ + use std::marker::PhantomData; + + struct Instance<State, Message, Theme, Renderer, Update, View> { + update: Update, + view: View, + _state: PhantomData<State>, + _message: PhantomData<Message>, + _theme: PhantomData<Theme>, + _renderer: PhantomData<Renderer>, + } + + impl<State, Message, Theme, Renderer, Update, View> Program + for Instance<State, Message, Theme, Renderer, Update, View> + where + Message: Send + std::fmt::Debug + 'static, + Theme: Default + DefaultStyle, + Renderer: program::Renderer, + Update: application::Update<State, Message>, + 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<Self::Message> { + Task::none() + } + + fn update( + &self, + state: &mut Self::State, + message: Self::Message, + ) -> Task<Self::Message> { + 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<P: Program> { + raw: P, + settings: Settings, +} + +impl<P: Program> Daemon<P> { + /// 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<I>(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<Cow<'static, [u8]>>) -> Self { + self.settings.fonts.push(font.into()); + self + } + + /// Sets the [`Title`] of the [`Daemon`]. + pub(crate) fn title( + self, + title: impl Title<P::State>, + ) -> Daemon< + impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, + > { + 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<P::Message>, + ) -> Daemon< + impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, + > { + 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<P::Message>, + ) -> Daemon< + impl Program<State = P::State, Message = P::Message, Theme = P::Theme>, + > { + 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<State = P::State, Message = P::Message, Theme = P::Theme>, + > { + 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<State = P::State, Message = P::Message, Theme = P::Theme>, + > { + 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<State = P::State, Message = P::Message, Theme = P::Theme>, + > { + 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<State> { + /// Produces the title of the [`Daemon`]. + fn title(&self, state: &State, window: window::Id) -> String; +} + +impl<State> Title<State> for &'static str { + fn title(&self, _state: &State, _window: window::Id) -> String { + self.to_string() + } +} + +impl<T, State> Title<State> 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<Element<'_, Message>>`. +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<Element<'a, Message, Theme, Renderer>>; +} + +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<Element<'a, Message, Theme, Renderer>>, +{ + fn view( + &self, + state: &'a State, + window: window::Id, + ) -> impl Into<Element<'a, Message, Theme, Renderer>> { + self(state, window) + } +} |