Simplify theming for Application

This commit is contained in:
Héctor Ramón Jiménez 2024-03-06 21:27:03 +01:00
parent 34e7c6593a
commit 7c4bf70023
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
14 changed files with 382 additions and 392 deletions

View file

@ -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)
}
}
}

View file

@ -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> {

View file

@ -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> {

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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> {

View file

@ -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,
}

View file

@ -16,7 +16,6 @@
)]
pub use iced_core as core;
pub mod application;
pub mod theme;
pub use theme::Theme;

View file

@ -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)
}
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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()

View file

@ -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);
}
}

View file

@ -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