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/program.rs | 754 ++++++++++++++++++++------------------------------------- 1 file changed, 262 insertions(+), 492 deletions(-) (limited to 'src/program.rs') diff --git a/src/program.rs b/src/program.rs index ea6b0e8e..3f9d2d0c 100644 --- a/src/program.rs +++ b/src/program.rs @@ -1,194 +1,108 @@ -//! Create and run iced applications step by step. -//! -//! # Example -//! ```no_run -//! use iced::widget::{button, column, text, Column}; -//! use iced::Theme; -//! -//! pub fn main() -> iced::Result { -//! iced::program("A counter", update, view) -//! .theme(|_| Theme::Dark) -//! .centered() -//! .run() -//! } -//! -//! #[derive(Debug, Clone)] -//! enum Message { -//! Increment, -//! } -//! -//! fn update(value: &mut u64, message: Message) { -//! match message { -//! Message::Increment => *value += 1, -//! } -//! } -//! -//! fn view(value: &u64) -> Column { -//! column![ -//! text(value), -//! button("+").on_press(Message::Increment), -//! ] -//! } -//! ``` -use crate::application::Application; use crate::core::text; -use crate::executor::{self, Executor}; use crate::graphics::compositor; +use crate::shell; use crate::window; -use crate::{Element, Font, Result, Settings, Size, Subscription, Task}; +use crate::{Element, Executor, Result, Settings, Subscription, Task}; -pub use crate::application::{Appearance, DefaultStyle}; +pub use crate::shell::program::{Appearance, DefaultStyle}; -use std::borrow::Cow; - -/// Creates an iced [`Program`] given its title, update, and view logic. -/// -/// # Example -/// ```no_run -/// use iced::widget::{button, column, text, Column}; -/// -/// pub fn main() -> iced::Result { -/// iced::program("A counter", update, view).run() -/// } -/// -/// #[derive(Debug, Clone)] -/// enum Message { -/// Increment, -/// } -/// -/// fn update(value: &mut u64, message: Message) { -/// match message { -/// Message::Increment => *value += 1, -/// } -/// } +/// The internal definition of a [`Program`]. /// -/// fn view(value: &u64) -> Column { -/// column![ -/// text(value), -/// button("+").on_press(Message::Increment), -/// ] -/// } -/// ``` -pub fn program( - title: impl Title, - update: impl Update, - view: impl for<'a> self::View<'a, State, Message, Theme, Renderer>, -) -> Program> -where - State: 'static, - Message: Send + std::fmt::Debug + 'static, - Theme: Default + DefaultStyle, - Renderer: self::Renderer, -{ - use std::marker::PhantomData; - - struct Application { - update: Update, - view: View, - _state: PhantomData, - _message: PhantomData, - _theme: PhantomData, - _renderer: PhantomData, - } +/// You should not need to implement this trait directly. Instead, use the +/// methods available in the [`Program`] struct. +#[allow(missing_docs)] +pub trait Program: Sized { + /// The state of the program. + type State; - impl Definition - for Application - where - Message: Send + std::fmt::Debug + 'static, - Theme: Default + DefaultStyle, - Renderer: self::Renderer, - Update: self::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 = executor::Default; + /// The message of the program. + type Message: Send + std::fmt::Debug + 'static; - fn load(&self) -> Task { - Task::none() - } + /// The theme of the program. + type Theme: Default + DefaultStyle; - fn update( - &self, - state: &mut Self::State, - message: Self::Message, - ) -> Task { - self.update.update(state, message).into() - } + /// The renderer of the program. + type Renderer: Renderer; - fn view<'a>( - &self, - state: &'a Self::State, - ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { - self.view.view(state).into() - } + /// The executor of the program. + type Executor: Executor; + + fn load(&self) -> Task; + + fn update( + &self, + state: &mut Self::State, + message: Self::Message, + ) -> Task; + + fn view<'a>( + &self, + state: &'a Self::State, + window: window::Id, + ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer>; + + fn title(&self, _state: &Self::State, _window: window::Id) -> String { + String::from("A cool iced application!") } - Program { - raw: Application { - update, - view, - _state: PhantomData, - _message: PhantomData, - _theme: PhantomData, - _renderer: PhantomData, - }, - settings: Settings::default(), + fn subscription( + &self, + _state: &Self::State, + ) -> Subscription { + Subscription::none() } - .title(title) -} -/// The underlying definition and configuration of an iced application. -/// -/// 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 [`Program`] with the [`program`] helper. -/// -/// [`run`]: Program::run -#[derive(Debug)] -pub struct Program { - raw: P, - settings: Settings, -} + fn theme(&self, _state: &Self::State, _window: window::Id) -> Self::Theme { + Self::Theme::default() + } + + fn style(&self, _state: &Self::State, theme: &Self::Theme) -> Appearance { + DefaultStyle::default_style(theme) + } -impl Program

{ - /// Runs the underlying [`Application`] of the [`Program`]. + fn scale_factor(&self, _state: &Self::State, _window: window::Id) -> f64 { + 1.0 + } + + /// Runs the [`Program`]. /// /// The state of the [`Program`] must implement [`Default`]. /// If your state does not implement [`Default`], use [`run_with`] /// instead. /// /// [`run_with`]: Self::run_with - pub fn run(self) -> Result + fn run( + self, + settings: Settings, + window_settings: Option, + ) -> Result where Self: 'static, - P::State: Default, + Self::State: Default, { - self.run_with(P::State::default) + self.run_with(settings, window_settings, Self::State::default) } - /// Runs the underlying [`Application`] of the [`Program`] with a - /// closure that creates the initial state. - pub fn run_with( + /// Runs the [`Program`] with the given [`Settings`] and a closure that creates the initial state. + fn run_with( self, - initialize: impl Fn() -> P::State + Clone + 'static, + settings: Settings, + window_settings: Option, + initialize: I, ) -> Result where Self: 'static, + I: Fn() -> Self::State + Clone + 'static, { use std::marker::PhantomData; - struct Instance { + struct Instance { program: P, state: P::State, _initialize: PhantomData, } - impl P::State> Application for Instance { + impl P::State> shell::Program for Instance { type Message = P::Message; type Theme = P::Theme; type Renderer = P::Renderer; @@ -211,8 +125,8 @@ impl Program

{ ) } - fn title(&self) -> String { - self.program.title(&self.state) + fn title(&self, window: window::Id) -> String { + self.program.title(&self.state, window) } fn update( @@ -224,259 +138,73 @@ impl Program

{ fn view( &self, + window: window::Id, ) -> crate::Element<'_, Self::Message, Self::Theme, Self::Renderer> { - self.program.view(&self.state) + self.program.view(&self.state, window) } fn subscription(&self) -> Subscription { self.program.subscription(&self.state) } - fn theme(&self) -> Self::Theme { - self.program.theme(&self.state) + fn theme(&self, window: window::Id) -> Self::Theme { + self.program.theme(&self.state, window) } fn style(&self, theme: &Self::Theme) -> Appearance { self.program.style(&self.state, theme) } - } - let Self { raw, settings } = self; + fn scale_factor(&self, window: window::Id) -> f64 { + self.program.scale_factor(&self.state, window) + } + } - Instance::run(Settings { - flags: (raw, initialize), - id: settings.id, - window: settings.window, - fonts: settings.fonts, + #[allow(clippy::needless_update)] + let renderer_settings = crate::graphics::Settings { default_font: settings.default_font, default_text_size: settings.default_text_size, - antialiasing: settings.antialiasing, - }) - } - - /// Sets the [`Settings`] that will be used to run the [`Program`]. - pub fn settings(self, settings: Settings) -> Self { - Self { settings, ..self } - } - - /// Sets the [`Settings::antialiasing`] of the [`Program`]. - pub fn antialiasing(self, antialiasing: bool) -> Self { - Self { - settings: Settings { - antialiasing, - ..self.settings - }, - ..self - } - } - - /// Sets the default [`Font`] of the [`Program`]. - 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 [`Program`]. - pub fn font(mut self, font: impl Into>) -> Self { - self.settings.fonts.push(font.into()); - self - } - - /// Sets the [`window::Settings::position`] to [`window::Position::Centered`] in the [`Program`]. - pub fn centered(self) -> Self { - Self { - settings: Settings { - window: window::Settings { - position: window::Position::Centered, - ..self.settings.window - }, - ..self.settings - }, - ..self - } - } - - /// Sets the [`window::Settings::exit_on_close_request`] of the [`Program`]. - pub fn exit_on_close_request(self, exit_on_close_request: bool) -> Self { - Self { - settings: Settings { - window: window::Settings { - exit_on_close_request, - ..self.settings.window - }, - ..self.settings - }, - ..self - } - } - - /// Sets the [`window::Settings::size`] of the [`Program`]. - pub fn window_size(self, size: impl Into) -> Self { - Self { - settings: Settings { - window: window::Settings { - size: size.into(), - ..self.settings.window - }, - ..self.settings - }, - ..self - } - } - - /// Sets the [`window::Settings::transparent`] of the [`Program`]. - pub fn transparent(self, transparent: bool) -> Self { - Self { - settings: Settings { - window: window::Settings { - transparent, - ..self.settings.window - }, - ..self.settings + antialiasing: if settings.antialiasing { + Some(crate::graphics::Antialiasing::MSAAx4) + } else { + None }, - ..self - } - } - - /// Sets the [`Title`] of the [`Program`]. - pub(crate) fn title( - self, - title: impl Title, - ) -> Program< - impl Definition, - > { - Program { - raw: with_title(self.raw, title), - settings: self.settings, - } - } - - /// Runs the [`Task`] produced by the closure at startup. - pub fn load( - self, - f: impl Fn() -> Task, - ) -> Program< - impl Definition, - > { - Program { - raw: with_load(self.raw, f), - settings: self.settings, - } - } - - /// Sets the subscription logic of the [`Program`]. - pub fn subscription( - self, - f: impl Fn(&P::State) -> Subscription, - ) -> Program< - impl Definition, - > { - Program { - raw: with_subscription(self.raw, f), - settings: self.settings, - } - } - - /// Sets the theme logic of the [`Program`]. - pub fn theme( - self, - f: impl Fn(&P::State) -> P::Theme, - ) -> Program< - impl Definition, - > { - Program { - raw: with_theme(self.raw, f), - settings: self.settings, - } - } - - /// Sets the style logic of the [`Program`]. - pub fn style( - self, - f: impl Fn(&P::State, &P::Theme) -> Appearance, - ) -> Program< - impl Definition, - > { - Program { - raw: with_style(self.raw, f), - settings: self.settings, - } - } -} - -/// The internal definition of a [`Program`]. -/// -/// You should not need to implement this trait directly. Instead, use the -/// methods available in the [`Program`] struct. -#[allow(missing_docs)] -pub trait Definition: Sized { - /// The state of the program. - type State; - - /// The message of the program. - type Message: Send + std::fmt::Debug + 'static; - - /// The theme of the program. - type Theme: Default + DefaultStyle; - - /// The renderer of the program. - type Renderer: Renderer; - - /// The executor of the program. - type Executor: Executor; - - fn load(&self) -> Task; - - fn update( - &self, - state: &mut Self::State, - message: Self::Message, - ) -> Task; - - fn view<'a>( - &self, - state: &'a Self::State, - ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer>; - - fn title(&self, _state: &Self::State) -> String { - String::from("A cool iced application!") - } - - fn subscription( - &self, - _state: &Self::State, - ) -> Subscription { - Subscription::none() - } - - fn theme(&self, _state: &Self::State) -> Self::Theme { - Self::Theme::default() - } - - fn style(&self, _state: &Self::State, theme: &Self::Theme) -> Appearance { - DefaultStyle::default_style(theme) + ..crate::graphics::Settings::default() + }; + + Ok(shell::program::run::< + Instance, + ::Compositor, + >( + Settings { + id: settings.id, + fonts: settings.fonts, + default_font: settings.default_font, + default_text_size: settings.default_text_size, + antialiasing: settings.antialiasing, + } + .into(), + renderer_settings, + window_settings, + (self, initialize), + )?) } } -fn with_title( +pub fn with_title( program: P, - title: impl Title, -) -> impl Definition { + title: impl Fn(&P::State, window::Id) -> String, +) -> impl Program { struct WithTitle { program: P, title: Title, } - impl Definition for WithTitle + impl Program for WithTitle where - P: Definition, - Title: self::Title, + P: Program, + Title: Fn(&P::State, window::Id) -> String, { type State = P::State; type Message = P::Message; @@ -488,8 +216,8 @@ fn with_title( self.program.load() } - fn title(&self, state: &Self::State) -> String { - self.title.title(state) + fn title(&self, state: &Self::State, window: window::Id) -> String { + (self.title)(state, window) } fn update( @@ -503,12 +231,17 @@ fn with_title( fn view<'a>( &self, state: &'a Self::State, + window: window::Id, ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { - self.program.view(state) + self.program.view(state, window) } - fn theme(&self, state: &Self::State) -> Self::Theme { - self.program.theme(state) + fn theme( + &self, + state: &Self::State, + window: window::Id, + ) -> Self::Theme { + self.program.theme(state, window) } fn subscription( @@ -525,21 +258,25 @@ fn with_title( ) -> Appearance { self.program.style(state, theme) } + + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + self.program.scale_factor(state, window) + } } WithTitle { program, title } } -fn with_load( +pub fn with_load( program: P, f: impl Fn() -> Task, -) -> impl Definition { +) -> impl Program { struct WithLoad { program: P, load: F, } - impl Definition for WithLoad + impl Program for WithLoad where F: Fn() -> Task, { @@ -547,7 +284,7 @@ fn with_load( type Message = P::Message; type Theme = P::Theme; type Renderer = P::Renderer; - type Executor = executor::Default; + type Executor = P::Executor; fn load(&self) -> Task { Task::batch([self.program.load(), (self.load)()]) @@ -564,12 +301,13 @@ fn with_load( fn view<'a>( &self, state: &'a Self::State, + window: window::Id, ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { - self.program.view(state) + self.program.view(state, window) } - fn title(&self, state: &Self::State) -> String { - self.program.title(state) + fn title(&self, state: &Self::State, window: window::Id) -> String { + self.program.title(state, window) } fn subscription( @@ -579,8 +317,12 @@ fn with_load( self.program.subscription(state) } - fn theme(&self, state: &Self::State) -> Self::Theme { - self.program.theme(state) + fn theme( + &self, + state: &Self::State, + window: window::Id, + ) -> Self::Theme { + self.program.theme(state, window) } fn style( @@ -590,21 +332,25 @@ fn with_load( ) -> Appearance { self.program.style(state, theme) } + + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + self.program.scale_factor(state, window) + } } WithLoad { program, load: f } } -fn with_subscription( +pub fn with_subscription( program: P, f: impl Fn(&P::State) -> Subscription, -) -> impl Definition { +) -> impl Program { struct WithSubscription { program: P, subscription: F, } - impl Definition for WithSubscription + impl Program for WithSubscription where F: Fn(&P::State) -> Subscription, { @@ -612,7 +358,7 @@ fn with_subscription( type Message = P::Message; type Theme = P::Theme; type Renderer = P::Renderer; - type Executor = executor::Default; + type Executor = P::Executor; fn subscription( &self, @@ -636,16 +382,21 @@ fn with_subscription( fn view<'a>( &self, state: &'a Self::State, + window: window::Id, ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { - self.program.view(state) + self.program.view(state, window) } - fn title(&self, state: &Self::State) -> String { - self.program.title(state) + fn title(&self, state: &Self::State, window: window::Id) -> String { + self.program.title(state, window) } - fn theme(&self, state: &Self::State) -> Self::Theme { - self.program.theme(state) + fn theme( + &self, + state: &Self::State, + window: window::Id, + ) -> Self::Theme { + self.program.theme(state, window) } fn style( @@ -655,6 +406,10 @@ fn with_subscription( ) -> Appearance { self.program.style(state, theme) } + + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + self.program.scale_factor(state, window) + } } WithSubscription { @@ -663,18 +418,18 @@ fn with_subscription( } } -fn with_theme( +pub fn with_theme( program: P, - f: impl Fn(&P::State) -> P::Theme, -) -> impl Definition { + f: impl Fn(&P::State, window::Id) -> P::Theme, +) -> impl Program { struct WithTheme { program: P, theme: F, } - impl Definition for WithTheme + impl Program for WithTheme where - F: Fn(&P::State) -> P::Theme, + F: Fn(&P::State, window::Id) -> P::Theme, { type State = P::State; type Message = P::Message; @@ -682,16 +437,20 @@ fn with_theme( type Renderer = P::Renderer; type Executor = P::Executor; - fn theme(&self, state: &Self::State) -> Self::Theme { - (self.theme)(state) + fn theme( + &self, + state: &Self::State, + window: window::Id, + ) -> Self::Theme { + (self.theme)(state, window) } fn load(&self) -> Task { self.program.load() } - fn title(&self, state: &Self::State) -> String { - self.program.title(state) + fn title(&self, state: &Self::State, window: window::Id) -> String { + self.program.title(state, window) } fn update( @@ -705,8 +464,9 @@ fn with_theme( fn view<'a>( &self, state: &'a Self::State, + window: window::Id, ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { - self.program.view(state) + self.program.view(state, window) } fn subscription( @@ -723,21 +483,25 @@ fn with_theme( ) -> Appearance { self.program.style(state, theme) } + + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + self.program.scale_factor(state, window) + } } WithTheme { program, theme: f } } -fn with_style( +pub fn with_style( program: P, f: impl Fn(&P::State, &P::Theme) -> Appearance, -) -> impl Definition { +) -> impl Program { struct WithStyle { program: P, style: F, } - impl Definition for WithStyle + impl Program for WithStyle where F: Fn(&P::State, &P::Theme) -> Appearance, { @@ -759,8 +523,8 @@ fn with_style( self.program.load() } - fn title(&self, state: &Self::State) -> String { - self.program.title(state) + fn title(&self, state: &Self::State, window: window::Id) -> String { + self.program.title(state, window) } fn update( @@ -774,8 +538,9 @@ fn with_style( fn view<'a>( &self, state: &'a Self::State, + window: window::Id, ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { - self.program.view(state) + self.program.view(state, window) } fn subscription( @@ -785,91 +550,96 @@ fn with_style( self.program.subscription(state) } - fn theme(&self, state: &Self::State) -> Self::Theme { - self.program.theme(state) + fn theme( + &self, + state: &Self::State, + window: window::Id, + ) -> Self::Theme { + self.program.theme(state, window) + } + + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + self.program.scale_factor(state, window) } } WithStyle { program, style: f } } -/// The title logic of some [`Program`]. -/// -/// This trait is implemented both for `&static str` and -/// any closure `Fn(&State) -> String`. -/// -/// This trait allows the [`program`] builder to take any of them. -pub trait Title { - /// Produces the title of the [`Program`]. - fn title(&self, state: &State) -> String; -} - -impl Title for &'static str { - fn title(&self, _state: &State) -> String { - self.to_string() +pub fn with_scale_factor( + program: P, + f: impl Fn(&P::State, window::Id) -> f64, +) -> impl Program { + struct WithScaleFactor { + program: P, + scale_factor: F, } -} -impl Title for T -where - T: Fn(&State) -> String, -{ - fn title(&self, state: &State) -> String { - self(state) - } -} + impl Program for WithScaleFactor + where + F: Fn(&P::State, window::Id) -> f64, + { + type State = P::State; + type Message = P::Message; + type Theme = P::Theme; + type Renderer = P::Renderer; + type Executor = P::Executor; -/// The update logic of some [`Program`]. -/// -/// This trait allows the [`program`] builder to take any closure that -/// returns any `Into>`. -pub trait Update { - /// Processes the message and updates the state of the [`Program`]. - fn update( - &self, - state: &mut State, - message: Message, - ) -> impl Into>; -} + fn load(&self) -> Task { + self.program.load() + } -impl Update for T -where - T: Fn(&mut State, Message) -> C, - C: Into>, -{ - fn update( - &self, - state: &mut State, - message: Message, - ) -> impl Into> { - self(state, message) - } -} + fn title(&self, state: &Self::State, window: window::Id) -> String { + self.program.title(state, window) + } -/// The view logic of some [`Program`]. -/// -/// This trait allows the [`program`] builder to take any closure that -/// returns any `Into>`. -pub trait View<'a, State, Message, Theme, Renderer> { - /// Produces the widget of the [`Program`]. - fn view( - &self, - state: &'a State, - ) -> impl Into>; -} + fn update( + &self, + state: &mut Self::State, + message: Self::Message, + ) -> Task { + self.program.update(state, message) + } -impl<'a, T, State, Message, Theme, Renderer, Widget> - View<'a, State, Message, Theme, Renderer> for T -where - T: Fn(&'a State) -> Widget, - State: 'static, - Widget: Into>, -{ - fn view( - &self, - state: &'a State, - ) -> impl Into> { - self(state) + fn view<'a>( + &self, + state: &'a Self::State, + window: window::Id, + ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { + self.program.view(state, window) + } + + fn subscription( + &self, + state: &Self::State, + ) -> Subscription { + self.program.subscription(state) + } + + fn theme( + &self, + state: &Self::State, + window: window::Id, + ) -> Self::Theme { + self.program.theme(state, window) + } + + fn style( + &self, + state: &Self::State, + theme: &Self::Theme, + ) -> Appearance { + self.program.style(state, theme) + } + + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + (self.scale_factor)(state, window) + } + } + + WithScaleFactor { + program, + scale_factor: f, } } -- cgit