diff options
author | 2024-03-06 21:27:03 +0100 | |
---|---|---|
committer | 2024-03-06 21:27:03 +0100 | |
commit | 7c4bf70023a8092faad9630c2c87fbf41bd6ab76 (patch) | |
tree | a02aa7fa62276e4e9b870f4f88ba448c0a264e2b | |
parent | 34e7c6593a9e0f56cee5db18b7258717cf6bc11b (diff) | |
download | iced-7c4bf70023a8092faad9630c2c87fbf41bd6ab76.tar.gz iced-7c4bf70023a8092faad9630c2c87fbf41bd6ab76.tar.bz2 iced-7c4bf70023a8092faad9630c2c87fbf41bd6ab76.zip |
Simplify theming for `Application`
-rw-r--r-- | examples/gradient/src/main.rs | 16 | ||||
-rw-r--r-- | examples/solar_system/src/main.rs | 15 | ||||
-rw-r--r-- | src/application.rs | 28 | ||||
-rw-r--r-- | src/multi_window.rs | 256 | ||||
-rw-r--r-- | src/multi_window/application.rs | 246 | ||||
-rw-r--r-- | src/sandbox.rs | 12 | ||||
-rw-r--r-- | style/src/application.rs | 23 | ||||
-rw-r--r-- | style/src/lib.rs | 1 | ||||
-rw-r--r-- | style/src/theme.rs | 45 | ||||
-rw-r--r-- | winit/src/application.rs | 72 | ||||
-rw-r--r-- | winit/src/application/state.rs | 10 | ||||
-rw-r--r-- | winit/src/multi_window.rs | 21 | ||||
-rw-r--r-- | winit/src/multi_window/state.rs | 16 | ||||
-rw-r--r-- | winit/src/multi_window/window_manager.rs | 13 |
14 files changed, 382 insertions, 392 deletions
diff --git a/examples/gradient/src/main.rs b/examples/gradient/src/main.rs index 3334bde9..6d3ff125 100644 --- a/examples/gradient/src/main.rs +++ b/examples/gradient/src/main.rs @@ -1,11 +1,11 @@ use iced::application; -use iced::theme::{self, Theme}; use iced::widget::{ checkbox, column, container, horizontal_space, row, slider, text, themer, }; use iced::{gradient, window}; use iced::{ Alignment, Background, Color, Element, Length, Radians, Sandbox, Settings, + Theme, }; pub fn main() -> iced::Result { @@ -115,16 +115,14 @@ impl Sandbox for Gradient { .into() } - fn style(&self) -> theme::Application { + fn style(&self, theme: &Theme) -> application::Appearance { if self.transparent { - theme::Application::custom(|theme: &Theme| { - application::Appearance { - background_color: Color::TRANSPARENT, - text_color: theme.palette().text, - } - }) + application::Appearance { + background_color: Color::TRANSPARENT, + text_color: theme.palette().text, + } } else { - theme::Application::Default + application::default(theme) } } } diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index a58ca683..4cc625da 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -9,7 +9,6 @@ use iced::application; use iced::executor; use iced::mouse; -use iced::theme::{self, Theme}; use iced::widget::canvas; use iced::widget::canvas::gradient; use iced::widget::canvas::stroke::{self, Stroke}; @@ -17,7 +16,7 @@ use iced::widget::canvas::Path; use iced::window; use iced::{ Application, Color, Command, Element, Length, Point, Rectangle, Renderer, - Settings, Size, Subscription, Vector, + Settings, Size, Subscription, Theme, Vector, }; use std::time::Instant; @@ -80,15 +79,11 @@ impl Application for SolarSystem { Theme::Dark } - fn style(&self) -> theme::Application { - fn dark_background(_theme: &Theme) -> application::Appearance { - application::Appearance { - background_color: Color::BLACK, - text_color: Color::WHITE, - } + fn style(&self, _theme: &Theme) -> application::Appearance { + application::Appearance { + background_color: Color::BLACK, + text_color: Color::WHITE, } - - theme::Application::custom(dark_background) } fn subscription(&self) -> Subscription<Message> { diff --git a/src/application.rs b/src/application.rs index 3d89c758..3247a97d 100644 --- a/src/application.rs +++ b/src/application.rs @@ -1,7 +1,9 @@ //! Build interactive cross-platform applications. use crate::{Command, Element, Executor, Settings, Subscription}; -pub use crate::style::application::{Appearance, StyleSheet}; +use crate::shell::application; + +pub use application::{default, Appearance, Style}; /// An interactive cross-platform application. /// @@ -91,7 +93,10 @@ pub use crate::style::application::{Appearance, StyleSheet}; /// } /// } /// ``` -pub trait Application: Sized { +pub trait Application: Sized +where + Style<Self::Theme>: Default, +{ /// The [`Executor`] that will run commands and subscriptions. /// /// The [default executor] can be a good starting point! @@ -104,7 +109,7 @@ pub trait Application: Sized { type Message: std::fmt::Debug + Send; /// The theme of your [`Application`]. - type Theme: Default + StyleSheet; + type Theme: Default; /// The data needed to initialize your [`Application`]. type Flags; @@ -151,8 +156,8 @@ pub trait Application: Sized { /// Returns the current `Style` of the [`Theme`]. /// /// [`Theme`]: Self::Theme - fn style(&self) -> <Self::Theme as StyleSheet>::Style { - <Self::Theme as StyleSheet>::Style::default() + fn style(&self, theme: &Self::Theme) -> Appearance { + Style::default().resolve(theme) } /// Returns the event [`Subscription`] for the current state of the @@ -213,11 +218,15 @@ pub trait Application: Sized { } } -struct Instance<A: Application>(A); +struct Instance<A>(A) +where + A: Application, + application::Style<A::Theme>: Default; impl<A> crate::runtime::Program for Instance<A> where A: Application, + application::Style<A::Theme>: Default, { type Message = A::Message; type Theme = A::Theme; @@ -232,9 +241,10 @@ where } } -impl<A> crate::shell::Application for Instance<A> +impl<A> application::Application for Instance<A> where A: Application, + application::Style<A::Theme>: Default, { type Flags = A::Flags; @@ -252,8 +262,8 @@ where self.0.theme() } - fn style(&self) -> <A::Theme as StyleSheet>::Style { - self.0.style() + fn style(&self, theme: &A::Theme) -> Appearance { + self.0.style(theme) } fn subscription(&self) -> Subscription<Self::Message> { diff --git a/src/multi_window.rs b/src/multi_window.rs index 5b7a00b4..bd57a746 100644 --- a/src/multi_window.rs +++ b/src/multi_window.rs @@ -1,4 +1,256 @@ //! Leverage multi-window support in your application. -mod application; +use crate::window; +use crate::{Command, Element, Executor, Settings, Subscription}; -pub use application::Application; +pub use crate::application::{default, Appearance, Style}; + +/// An interactive cross-platform multi-window application. +/// +/// This trait is the main entrypoint of Iced. Once implemented, you can run +/// your GUI application by simply calling [`run`](#method.run). +/// +/// - On native platforms, it will run in its own windows. +/// - On the web, it will take control of the `<title>` and the `<body>` of the +/// 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. If you do not intend to perform any +/// background work in your program, the [`Sandbox`] trait offers a simplified +/// interface. +/// +/// When using an [`Application`] with the `debug` feature enabled, a debug view +/// can be toggled by pressing `F12`. +/// +/// # Examples +/// See the `examples/multi-window` example to see this multi-window `Application` trait in action. +/// +/// ## A simple "Hello, world!" +/// +/// If you just want to get started, here is a simple [`Application`] that +/// says "Hello, world!": +/// +/// ```no_run +/// use iced::{executor, window}; +/// use iced::{Command, Element, Settings, Theme}; +/// use iced::multi_window::{self, Application}; +/// +/// pub fn main() -> iced::Result { +/// Hello::run(Settings::default()) +/// } +/// +/// struct Hello; +/// +/// impl multi_window::Application for Hello { +/// type Executor = executor::Default; +/// type Flags = (); +/// type Message = (); +/// type Theme = Theme; +/// +/// fn new(_flags: ()) -> (Hello, Command<Self::Message>) { +/// (Hello, Command::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 view(&self, _window: window::Id) -> Element<Self::Message> { +/// "Hello, world!".into() +/// } +/// } +/// ``` +/// +/// [`Sandbox`]: crate::Sandbox +pub trait Application: Sized +where + Style<Self::Theme>: Default, +{ + /// The [`Executor`] that will run commands and subscriptions. + /// + /// The [default executor] can be a good starting point! + /// + /// [`Executor`]: Self::Executor + /// [default executor]: crate::executor::Default + type Executor: Executor; + + /// The type of __messages__ your [`Application`] will produce. + type Message: std::fmt::Debug + Send; + + /// The theme of your [`Application`]. + type Theme: Default; + + /// The data needed to initialize your [`Application`]. + type Flags; + + /// Initializes the [`Application`] with the flags provided to + /// [`run`] as part of the [`Settings`]. + /// + /// Here is where you should return the initial state of your app. + /// + /// Additionally, you can return a [`Command`] 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>); + + /// Returns the current title of the `window` of the [`Application`]. + /// + /// This title can be dynamic! The runtime will automatically update the + /// title of your window when necessary. + fn title(&self, window: window::Id) -> String; + + /// Handles a __message__ and updates the state of the [`Application`]. + /// + /// This is where you define your __update logic__. All the __messages__, + /// 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>; + + /// Returns the widgets to display in the `window` of the [`Application`]. + /// + /// These widgets can produce __messages__ based on user interaction. + fn view( + &self, + window: window::Id, + ) -> Element<'_, Self::Message, Self::Theme, crate::Renderer>; + + /// Returns the current [`Theme`] of the `window` of the [`Application`]. + /// + /// [`Theme`]: Self::Theme + #[allow(unused_variables)] + fn theme(&self, window: window::Id) -> Self::Theme { + Self::Theme::default() + } + + /// Returns the current `Style` of the [`Theme`]. + /// + /// [`Theme`]: Self::Theme + fn style(&self, theme: &Self::Theme) -> Appearance { + Style::default().resolve(theme) + } + + /// Returns the event [`Subscription`] for the current state of the + /// application. + /// + /// A [`Subscription`] will be kept alive as long as you keep returning it, + /// and the __messages__ produced will be handled by + /// [`update`](#tymethod.update). + /// + /// By default, this method returns an empty [`Subscription`]. + fn subscription(&self) -> Subscription<Self::Message> { + Subscription::none() + } + + /// Returns the scale factor of the `window` of the [`Application`]. + /// + /// It can be used to dynamically control the size of the UI at runtime + /// (i.e. zooming). + /// + /// For instance, a scale factor of `2.0` will make widgets twice as big, + /// while a scale factor of `0.5` will shrink them to half their size. + /// + /// By default, it returns `1.0`. + #[allow(unused_variables)] + fn scale_factor(&self, window: window::Id) -> f64 { + 1.0 + } + + /// Runs the multi-window [`Application`]. + /// + /// On native platforms, this method will take control of the current thread + /// until the [`Application`] exits. + /// + /// On the web platform, this method __will NOT return__ unless there is an + /// [`Error`] during startup. + /// + /// [`Error`]: crate::Error + fn run(settings: Settings<Self::Flags>) -> crate::Result + where + Self: 'static, + { + #[allow(clippy::needless_update)] + let renderer_settings = crate::renderer::Settings { + default_font: settings.default_font, + default_text_size: settings.default_text_size, + antialiasing: if settings.antialiasing { + Some(crate::graphics::Antialiasing::MSAAx4) + } else { + None + }, + ..crate::renderer::Settings::default() + }; + + Ok(crate::shell::multi_window::run::< + Instance<Self>, + Self::Executor, + crate::renderer::Compositor, + >(settings.into(), renderer_settings)?) + } +} + +struct Instance<A>(A) +where + A: Application, + Style<A::Theme>: Default; + +impl<A> crate::runtime::multi_window::Program for Instance<A> +where + A: Application, + Style<A::Theme>: Default, +{ + type Message = A::Message; + type Theme = A::Theme; + type Renderer = crate::Renderer; + + fn update(&mut self, message: Self::Message) -> Command<Self::Message> { + self.0.update(message) + } + + fn view( + &self, + window: window::Id, + ) -> Element<'_, Self::Message, Self::Theme, Self::Renderer> { + self.0.view(window) + } +} + +impl<A> crate::shell::multi_window::Application for Instance<A> +where + A: Application, + Style<A::Theme>: Default, +{ + type Flags = A::Flags; + + fn new(flags: Self::Flags) -> (Self, Command<A::Message>) { + let (app, command) = A::new(flags); + + (Instance(app), command) + } + + fn title(&self, window: window::Id) -> String { + self.0.title(window) + } + + fn theme(&self, window: window::Id) -> A::Theme { + self.0.theme(window) + } + + fn style(&self, theme: &Self::Theme) -> Appearance { + self.0.style(theme) + } + + fn subscription(&self) -> Subscription<Self::Message> { + self.0.subscription() + } + + fn scale_factor(&self, window: window::Id) -> f64 { + self.0.scale_factor(window) + } +} diff --git a/src/multi_window/application.rs b/src/multi_window/application.rs deleted file mode 100644 index ac625281..00000000 --- a/src/multi_window/application.rs +++ /dev/null @@ -1,246 +0,0 @@ -use crate::style::application::StyleSheet; -use crate::window; -use crate::{Command, Element, Executor, Settings, Subscription}; - -/// An interactive cross-platform multi-window application. -/// -/// This trait is the main entrypoint of Iced. Once implemented, you can run -/// your GUI application by simply calling [`run`](#method.run). -/// -/// - On native platforms, it will run in its own windows. -/// - On the web, it will take control of the `<title>` and the `<body>` of the -/// 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. If you do not intend to perform any -/// background work in your program, the [`Sandbox`] trait offers a simplified -/// interface. -/// -/// When using an [`Application`] with the `debug` feature enabled, a debug view -/// can be toggled by pressing `F12`. -/// -/// # Examples -/// See the `examples/multi-window` example to see this multi-window `Application` trait in action. -/// -/// ## A simple "Hello, world!" -/// -/// If you just want to get started, here is a simple [`Application`] that -/// says "Hello, world!": -/// -/// ```no_run -/// use iced::{executor, window}; -/// use iced::{Command, Element, Settings, Theme}; -/// use iced::multi_window::{self, Application}; -/// -/// pub fn main() -> iced::Result { -/// Hello::run(Settings::default()) -/// } -/// -/// struct Hello; -/// -/// impl multi_window::Application for Hello { -/// type Executor = executor::Default; -/// type Flags = (); -/// type Message = (); -/// type Theme = Theme; -/// -/// fn new(_flags: ()) -> (Hello, Command<Self::Message>) { -/// (Hello, Command::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 view(&self, _window: window::Id) -> Element<Self::Message> { -/// "Hello, world!".into() -/// } -/// } -/// ``` -/// -/// [`Sandbox`]: crate::Sandbox -pub trait Application: Sized { - /// The [`Executor`] that will run commands and subscriptions. - /// - /// The [default executor] can be a good starting point! - /// - /// [`Executor`]: Self::Executor - /// [default executor]: crate::executor::Default - type Executor: Executor; - - /// The type of __messages__ your [`Application`] will produce. - type Message: std::fmt::Debug + Send; - - /// The theme of your [`Application`]. - type Theme: Default + StyleSheet; - - /// The data needed to initialize your [`Application`]. - type Flags; - - /// Initializes the [`Application`] with the flags provided to - /// [`run`] as part of the [`Settings`]. - /// - /// Here is where you should return the initial state of your app. - /// - /// Additionally, you can return a [`Command`] 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>); - - /// Returns the current title of the `window` of the [`Application`]. - /// - /// This title can be dynamic! The runtime will automatically update the - /// title of your window when necessary. - fn title(&self, window: window::Id) -> String; - - /// Handles a __message__ and updates the state of the [`Application`]. - /// - /// This is where you define your __update logic__. All the __messages__, - /// 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>; - - /// Returns the widgets to display in the `window` of the [`Application`]. - /// - /// These widgets can produce __messages__ based on user interaction. - fn view( - &self, - window: window::Id, - ) -> Element<'_, Self::Message, Self::Theme, crate::Renderer>; - - /// Returns the current [`Theme`] of the `window` of the [`Application`]. - /// - /// [`Theme`]: Self::Theme - #[allow(unused_variables)] - fn theme(&self, window: window::Id) -> Self::Theme { - Self::Theme::default() - } - - /// Returns the current `Style` of the [`Theme`]. - /// - /// [`Theme`]: Self::Theme - fn style(&self) -> <Self::Theme as StyleSheet>::Style { - <Self::Theme as StyleSheet>::Style::default() - } - - /// Returns the event [`Subscription`] for the current state of the - /// application. - /// - /// A [`Subscription`] will be kept alive as long as you keep returning it, - /// and the __messages__ produced will be handled by - /// [`update`](#tymethod.update). - /// - /// By default, this method returns an empty [`Subscription`]. - fn subscription(&self) -> Subscription<Self::Message> { - Subscription::none() - } - - /// Returns the scale factor of the `window` of the [`Application`]. - /// - /// It can be used to dynamically control the size of the UI at runtime - /// (i.e. zooming). - /// - /// For instance, a scale factor of `2.0` will make widgets twice as big, - /// while a scale factor of `0.5` will shrink them to half their size. - /// - /// By default, it returns `1.0`. - #[allow(unused_variables)] - fn scale_factor(&self, window: window::Id) -> f64 { - 1.0 - } - - /// Runs the multi-window [`Application`]. - /// - /// On native platforms, this method will take control of the current thread - /// until the [`Application`] exits. - /// - /// On the web platform, this method __will NOT return__ unless there is an - /// [`Error`] during startup. - /// - /// [`Error`]: crate::Error - fn run(settings: Settings<Self::Flags>) -> crate::Result - where - Self: 'static, - { - #[allow(clippy::needless_update)] - let renderer_settings = crate::renderer::Settings { - default_font: settings.default_font, - default_text_size: settings.default_text_size, - antialiasing: if settings.antialiasing { - Some(crate::graphics::Antialiasing::MSAAx4) - } else { - None - }, - ..crate::renderer::Settings::default() - }; - - Ok(crate::shell::multi_window::run::< - Instance<Self>, - Self::Executor, - crate::renderer::Compositor, - >(settings.into(), renderer_settings)?) - } -} - -struct Instance<A: Application>(A); - -impl<A> crate::runtime::multi_window::Program for Instance<A> -where - A: Application, -{ - type Message = A::Message; - type Theme = A::Theme; - type Renderer = crate::Renderer; - - fn update(&mut self, message: Self::Message) -> Command<Self::Message> { - self.0.update(message) - } - - fn view( - &self, - window: window::Id, - ) -> Element<'_, Self::Message, Self::Theme, Self::Renderer> { - self.0.view(window) - } -} - -impl<A> crate::shell::multi_window::Application for Instance<A> -where - A: Application, -{ - type Flags = A::Flags; - - fn new(flags: Self::Flags) -> (Self, Command<A::Message>) { - let (app, command) = A::new(flags); - - (Instance(app), command) - } - - fn title(&self, window: window::Id) -> String { - self.0.title(window) - } - - fn theme(&self, window: window::Id) -> A::Theme { - self.0.theme(window) - } - - fn style(&self) -> <A::Theme as StyleSheet>::Style { - self.0.style() - } - - fn subscription(&self) -> Subscription<Self::Message> { - self.0.subscription() - } - - fn scale_factor(&self, window: window::Id) -> f64 { - self.0.scale_factor(window) - } -} diff --git a/src/sandbox.rs b/src/sandbox.rs index 28461929..815652ae 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -1,5 +1,5 @@ -use crate::theme::{self, Theme}; -use crate::{Application, Command, Element, Error, Settings, Subscription}; +use crate::application::{self, Application}; +use crate::{Command, Element, Error, Settings, Subscription, Theme}; /// A sandboxed [`Application`]. /// @@ -123,8 +123,8 @@ pub trait Sandbox { /// Returns the current style variant of [`theme::Application`]. /// /// By default, it returns [`theme::Application::default`]. - fn style(&self) -> theme::Application { - theme::Application::default() + fn style(&self, theme: &Theme) -> application::Appearance { + crate::shell::application::default(theme) } /// Returns the scale factor of the [`Sandbox`]. @@ -185,8 +185,8 @@ where T::theme(self) } - fn style(&self) -> theme::Application { - T::style(self) + fn style(&self, theme: &Theme) -> application::Appearance { + T::style(self, theme) } fn subscription(&self) -> Subscription<T::Message> { diff --git a/style/src/application.rs b/style/src/application.rs deleted file mode 100644 index e9a1f4ff..00000000 --- a/style/src/application.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Change the appearance of an application. -use iced_core::Color; - -/// A set of rules that dictate the style of an application. -pub trait StyleSheet { - /// The supported style of the [`StyleSheet`]. - type Style: Default; - - /// Returns the [`Appearance`] of the application for the provided [`Style`]. - /// - /// [`Style`]: Self::Style - fn appearance(&self, style: &Self::Style) -> Appearance; -} - -/// The appearance of an application. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Appearance { - /// The background [`Color`] of the application. - pub background_color: Color, - - /// The default text [`Color`] of the application. - pub text_color: Color, -} diff --git a/style/src/lib.rs b/style/src/lib.rs index 1aea2d80..bc0e37e9 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -16,7 +16,6 @@ )] pub use iced_core as core; -pub mod application; pub mod theme; pub use theme::Theme; diff --git a/style/src/theme.rs b/style/src/theme.rs index 41e76d99..21ba2a37 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -3,8 +3,6 @@ pub mod palette; pub use palette::Palette; -use crate::application; - use std::fmt; use std::sync::Arc; @@ -221,46 +219,3 @@ impl fmt::Display for Custom { write!(f, "{}", self.name) } } - -/// The style of an application. -#[derive(Default)] -pub enum Application { - /// The default style. - #[default] - Default, - /// A custom style. - Custom(Box<dyn application::StyleSheet<Style = Theme>>), -} - -impl Application { - /// Creates a custom [`Application`] style. - pub fn custom( - custom: impl application::StyleSheet<Style = Theme> + 'static, - ) -> Self { - Self::Custom(Box::new(custom)) - } -} - -impl application::StyleSheet for Theme { - type Style = Application; - - fn appearance(&self, style: &Self::Style) -> application::Appearance { - let palette = self.extended_palette(); - - match style { - Application::Default => application::Appearance { - background_color: palette.background.base.color, - text_color: palette.background.base.text, - }, - Application::Custom(custom) => custom.appearance(self), - } - } -} - -impl<T: Fn(&Theme) -> application::Appearance> application::StyleSheet for T { - type Style = Theme; - - fn appearance(&self, style: &Self::Style) -> application::Appearance { - (self)(style) - } -} diff --git a/winit/src/application.rs b/winit/src/application.rs index 05a4f070..fbca6ee4 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -10,7 +10,7 @@ use crate::core::renderer; use crate::core::time::Instant; use crate::core::widget::operation; use crate::core::window; -use crate::core::{Event, Point, Size}; +use crate::core::{Color, Event, Point, Size}; use crate::futures::futures; use crate::futures::{Executor, Runtime, Subscription}; use crate::graphics::compositor::{self, Compositor}; @@ -18,7 +18,7 @@ use crate::runtime::clipboard; use crate::runtime::program::Program; use crate::runtime::user_interface::{self, UserInterface}; use crate::runtime::{Command, Debug}; -use crate::style::application::{Appearance, StyleSheet}; +use crate::style::Theme; use crate::{Clipboard, Error, Proxy, Settings}; use futures::channel::mpsc; @@ -39,7 +39,7 @@ use std::sync::Arc; /// can be toggled by pressing `F12`. pub trait Application: Program where - Self::Theme: StyleSheet, + Style<Self::Theme>: Default, { /// The data needed to initialize your [`Application`]. type Flags; @@ -64,8 +64,8 @@ where fn theme(&self) -> Self::Theme; /// Returns the `Style` variation of the `Theme`. - fn style(&self) -> <Self::Theme as StyleSheet>::Style { - Default::default() + fn style(&self, theme: &Self::Theme) -> Appearance { + Style::default().resolve(theme) } /// Returns the event `Subscription` for the current state of the @@ -95,6 +95,58 @@ where } } +/// The appearance of an application. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Appearance { + /// The background [`Color`] of the application. + pub background_color: Color, + + /// The default text [`Color`] of the application. + pub text_color: Color, +} + +/// The style of an [`Application`]. +#[derive(Debug, PartialEq, Eq)] +pub struct Style<Theme>(pub fn(&Theme) -> Appearance); + +impl<Theme> Style<Theme> { + /// Resolves the [`Style`] with the given `Theme` to produce + /// an [`Appearance`]. + pub fn resolve(self, theme: &Theme) -> Appearance { + (self.0)(theme) + } +} + +impl<Theme> Clone for Style<Theme> { + fn clone(&self) -> Self { + *self + } +} + +impl<Theme> Copy for Style<Theme> {} + +impl<Theme> From<fn(&Theme) -> Appearance> for Style<Theme> { + fn from(f: fn(&Theme) -> Appearance) -> Self { + Style(f) + } +} + +impl Default for Style<Theme> { + fn default() -> Self { + Style(default) + } +} + +/// The default style of an [`Application`]. +pub fn default(theme: &Theme) -> Appearance { + let palette = theme.extended_palette(); + + Appearance { + background_color: palette.background.base.color, + text_color: palette.background.base.text, + } +} + /// Runs an [`Application`] with an executor, compositor, and the provided /// settings. pub fn run<A, E, C>( @@ -105,7 +157,7 @@ where A: Application + 'static, E: Executor + 'static, C: Compositor<Renderer = A::Renderer> + 'static, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { use futures::task; use futures::Future; @@ -289,7 +341,7 @@ async fn run_instance<A, E, C>( A: Application + 'static, E: Executor + 'static, C: Compositor<Renderer = A::Renderer> + 'static, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { use futures::stream::StreamExt; use winit::event; @@ -612,7 +664,7 @@ pub fn build_user_interface<'a, A: Application>( debug: &mut Debug, ) -> UserInterface<'a, A::Message, A::Theme, A::Renderer> where - A::Theme: StyleSheet, + Style<A::Theme>: Default, { debug.view_started(); let view = application.view(); @@ -643,7 +695,7 @@ pub fn update<A: Application, C, E: Executor>( window: &winit::window::Window, ) where C: Compositor<Renderer = A::Renderer> + 'static, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { for message in messages.drain(..) { debug.log_message(&message); @@ -694,7 +746,7 @@ pub fn run_command<A, C, E>( A: Application, E: Executor, C: Compositor<Renderer = A::Renderer> + 'static, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { use crate::runtime::command; use crate::runtime::system; diff --git a/winit/src/application/state.rs b/winit/src/application/state.rs index c17a3bcc..b3bdb3a3 100644 --- a/winit/src/application/state.rs +++ b/winit/src/application/state.rs @@ -1,4 +1,4 @@ -use crate::application::{self, StyleSheet as _}; +use crate::application; use crate::conversion; use crate::core::mouse; use crate::core::{Color, Size}; @@ -14,7 +14,7 @@ use winit::window::Window; #[allow(missing_debug_implementations)] pub struct State<A: Application> where - A::Theme: application::StyleSheet, + application::Style<A::Theme>: Default, { title: String, scale_factor: f64, @@ -29,14 +29,14 @@ where impl<A: Application> State<A> where - A::Theme: application::StyleSheet, + application::Style<A::Theme>: Default, { /// Creates a new [`State`] for the provided [`Application`] and window. pub fn new(application: &A, window: &Window) -> Self { let title = application.title(); let scale_factor = application.scale_factor(); let theme = application.theme(); - let appearance = theme.appearance(&application.style()); + let appearance = application.style(&theme); let viewport = { let physical_size = window.inner_size(); @@ -216,6 +216,6 @@ where // Update theme and appearance self.theme = application.theme(); - self.appearance = self.theme.appearance(&application.style()); + self.appearance = application.style(&self.theme); } } diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 03066d6c..19951ed3 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -20,9 +20,10 @@ 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::style::application::StyleSheet; use crate::{Clipboard, Error, Proxy, Settings}; +pub use crate::application::{default, Appearance, Style}; + use std::collections::HashMap; use std::mem::ManuallyDrop; use std::sync::Arc; @@ -41,7 +42,7 @@ use std::time::Instant; /// can be toggled by pressing `F12`. pub trait Application: Program where - Self::Theme: StyleSheet, + Style<Self::Theme>: Default, { /// The data needed to initialize your [`Application`]. type Flags; @@ -66,8 +67,8 @@ where fn theme(&self, window: window::Id) -> Self::Theme; /// Returns the `Style` variation of the `Theme`. - fn style(&self) -> <Self::Theme as StyleSheet>::Style { - Default::default() + fn style(&self, theme: &Self::Theme) -> Appearance { + Style::default().resolve(theme) } /// Returns the event `Subscription` for the current state of the @@ -108,7 +109,7 @@ where A: Application + 'static, E: Executor + 'static, C: Compositor<Renderer = A::Renderer> + 'static, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { use winit::event_loop::EventLoopBuilder; @@ -349,7 +350,7 @@ async fn run_instance<A, E, C>( A: Application + 'static, E: Executor + 'static, C: Compositor<Renderer = A::Renderer> + 'static, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { use winit::event; use winit::event_loop::ControlFlow; @@ -819,7 +820,7 @@ fn build_user_interface<'a, A: Application>( id: window::Id, ) -> UserInterface<'a, A::Message, A::Theme, A::Renderer> where - A::Theme: StyleSheet, + Style<A::Theme>: Default, { debug.view_started(); let view = application.view(id); @@ -847,7 +848,7 @@ fn update<A: Application, C, E: Executor>( ui_caches: &mut HashMap<window::Id, user_interface::Cache>, ) where C: Compositor<Renderer = A::Renderer> + 'static, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { for message in messages.drain(..) { debug.log_message(&message); @@ -890,7 +891,7 @@ fn run_command<A, C, E>( A: Application, E: Executor, C: Compositor<Renderer = A::Renderer> + 'static, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { use crate::runtime::clipboard; use crate::runtime::system; @@ -1216,8 +1217,8 @@ pub fn build_user_interfaces<'a, A: Application, C: Compositor>( mut cached_user_interfaces: HashMap<window::Id, user_interface::Cache>, ) -> HashMap<window::Id, UserInterface<'a, A::Message, A::Theme, A::Renderer>> where - A::Theme: StyleSheet, C: Compositor<Renderer = A::Renderer>, + Style<A::Theme>: Default, { cached_user_interfaces .drain() diff --git a/winit/src/multi_window/state.rs b/winit/src/multi_window/state.rs index 2e97a13d..8a332176 100644 --- a/winit/src/multi_window/state.rs +++ b/winit/src/multi_window/state.rs @@ -2,18 +2,16 @@ use crate::conversion; use crate::core::{mouse, window}; use crate::core::{Color, Size}; use crate::graphics::Viewport; -use crate::multi_window::Application; -use crate::style::application; +use crate::multi_window::{self, Application}; use std::fmt::{Debug, Formatter}; -use iced_style::application::StyleSheet; use winit::event::{Touch, WindowEvent}; use winit::window::Window; /// The state of a multi-windowed [`Application`]. pub struct State<A: Application> where - A::Theme: application::StyleSheet, + multi_window::Style<A::Theme>: Default, { title: String, scale_factor: f64, @@ -22,12 +20,12 @@ where cursor_position: Option<winit::dpi::PhysicalPosition<f64>>, modifiers: winit::keyboard::ModifiersState, theme: A::Theme, - appearance: application::Appearance, + appearance: multi_window::Appearance, } impl<A: Application> Debug for State<A> where - A::Theme: application::StyleSheet, + multi_window::Style<A::Theme>: Default, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("multi_window::State") @@ -43,7 +41,7 @@ where impl<A: Application> State<A> where - A::Theme: application::StyleSheet, + multi_window::Style<A::Theme>: Default, { /// Creates a new [`State`] for the provided [`Application`]'s `window`. pub fn new( @@ -54,7 +52,7 @@ where let title = application.title(window_id); let scale_factor = application.scale_factor(window_id); let theme = application.theme(window_id); - let appearance = theme.appearance(&application.style()); + let appearance = application.style(&theme); let viewport = { let physical_size = window.inner_size(); @@ -236,6 +234,6 @@ where // Update theme and appearance self.theme = application.theme(window_id); - self.appearance = self.theme.appearance(&application.style()); + self.appearance = application.style(&self.theme); } } diff --git a/winit/src/multi_window/window_manager.rs b/winit/src/multi_window/window_manager.rs index 23f3c0ba..2800e923 100644 --- a/winit/src/multi_window/window_manager.rs +++ b/winit/src/multi_window/window_manager.rs @@ -2,8 +2,7 @@ use crate::core::mouse; use crate::core::window::Id; use crate::core::{Point, Size}; use crate::graphics::Compositor; -use crate::multi_window::{Application, State}; -use crate::style::application::StyleSheet; +use crate::multi_window::{Application, State, Style}; use std::collections::BTreeMap; use std::sync::Arc; @@ -12,8 +11,8 @@ use winit::monitor::MonitorHandle; #[allow(missing_debug_implementations)] pub struct WindowManager<A: Application, C: Compositor> where - A::Theme: StyleSheet, C: Compositor<Renderer = A::Renderer>, + Style<A::Theme>: Default, { aliases: BTreeMap<winit::window::WindowId, Id>, entries: BTreeMap<Id, Window<A, C>>, @@ -23,7 +22,7 @@ impl<A, C> WindowManager<A, C> where A: Application, C: Compositor<Renderer = A::Renderer>, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { pub fn new() -> Self { Self { @@ -109,7 +108,7 @@ impl<A, C> Default for WindowManager<A, C> where A: Application, C: Compositor<Renderer = A::Renderer>, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { fn default() -> Self { Self::new() @@ -121,7 +120,7 @@ pub struct Window<A, C> where A: Application, C: Compositor<Renderer = A::Renderer>, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { pub raw: Arc<winit::window::Window>, pub state: State<A>, @@ -136,7 +135,7 @@ impl<A, C> Window<A, C> where A: Application, C: Compositor<Renderer = A::Renderer>, - A::Theme: StyleSheet, + Style<A::Theme>: Default, { pub fn position(&self) -> Option<Point> { self.raw |