From 1aeb317f2dbfb63215e6226073e67878ffa6503b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 6 Dec 2024 04:06:41 +0100 Subject: [PATCH] Add image and hash snapshot-based testing to `iced_test` --- Cargo.toml | 2 + core/src/theme.rs | 34 ++++ core/src/window.rs | 2 + {runtime => core}/src/window/screenshot.rs | 2 +- examples/gradient/src/main.rs | 10 +- .../todos/snapshots/creates_a_new_task.sha256 | 1 + examples/todos/src/main.rs | 15 +- runtime/src/window.rs | 6 +- src/application.rs | 9 +- src/daemon.rs | 9 +- src/lib.rs | 2 +- src/program.rs | 29 ++- test/Cargo.toml | 3 + test/src/lib.rs | 167 +++++++++++++++++- winit/src/program.rs | 54 ++---- winit/src/program/state.rs | 27 +-- winit/src/program/window_manager.rs | 13 +- 17 files changed, 280 insertions(+), 105 deletions(-) rename {runtime => core}/src/window/screenshot.rs (98%) create mode 100644 examples/todos/snapshots/creates_a_new_task.sha256 diff --git a/Cargo.toml b/Cargo.toml index 9268781278..0b9833d605 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -166,11 +166,13 @@ num-traits = "0.2" once_cell = "1.0" ouroboros = "0.18" palette = "0.7" +png = "0.17" pulldown-cmark = "0.11" qrcode = { version = "0.13", default-features = false } raw-window-handle = "0.6" resvg = "0.42" rustc-hash = "2.0" +sha2 = "0.10" smol = "1.0" smol_str = "0.2" softbuffer = "0.4" diff --git a/core/src/theme.rs b/core/src/theme.rs index 6b2c04da4f..23480cec9b 100644 --- a/core/src/theme.rs +++ b/core/src/theme.rs @@ -3,6 +3,8 @@ pub mod palette; pub use palette::Palette; +use crate::Color; + use std::fmt; use std::sync::Arc; @@ -246,3 +248,35 @@ impl fmt::Display for Custom { write!(f, "{}", self.name) } } + +/// The base style of a [`Theme`]. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Style { + /// The background [`Color`] of the application. + pub background_color: Color, + + /// The default text [`Color`] of the application. + pub text_color: Color, +} + +/// The default blank style of a [`Theme`]. +pub trait Base { + /// Returns the default base [`Style`] of a [`Theme`]. + fn base(&self) -> Style; +} + +impl Base for Theme { + fn base(&self) -> Style { + default(self) + } +} + +/// The default [`Style`] of a built-in [`Theme`]. +pub fn default(theme: &Theme) -> Style { + let palette = theme.extended_palette(); + + Style { + background_color: palette.background.base.color, + text_color: palette.background.base.text, + } +} diff --git a/core/src/window.rs b/core/src/window.rs index 448ffc4503..a338999805 100644 --- a/core/src/window.rs +++ b/core/src/window.rs @@ -1,5 +1,6 @@ //! Build window-based GUI applications. pub mod icon; +pub mod screenshot; pub mod settings; mod event; @@ -17,5 +18,6 @@ pub use level::Level; pub use mode::Mode; pub use position::Position; pub use redraw_request::RedrawRequest; +pub use screenshot::Screenshot; pub use settings::Settings; pub use user_attention::UserAttention; diff --git a/runtime/src/window/screenshot.rs b/core/src/window/screenshot.rs similarity index 98% rename from runtime/src/window/screenshot.rs rename to core/src/window/screenshot.rs index d9adbc010a..424168bb91 100644 --- a/runtime/src/window/screenshot.rs +++ b/core/src/window/screenshot.rs @@ -1,5 +1,5 @@ //! Take screenshots of a window. -use crate::core::{Rectangle, Size}; +use crate::{Rectangle, Size}; use bytes::Bytes; use std::fmt::{Debug, Formatter}; diff --git a/examples/gradient/src/main.rs b/examples/gradient/src/main.rs index b2de069f67..910ea9fceb 100644 --- a/examples/gradient/src/main.rs +++ b/examples/gradient/src/main.rs @@ -1,5 +1,5 @@ -use iced::application; use iced::gradient; +use iced::theme; use iced::widget::{ checkbox, column, container, horizontal_space, row, slider, text, }; @@ -95,16 +95,14 @@ impl Gradient { .into() } - fn style(&self, theme: &Theme) -> application::Appearance { - use application::DefaultStyle; - + fn style(&self, theme: &Theme) -> theme::Style { if self.transparent { - application::Appearance { + theme::Style { background_color: Color::TRANSPARENT, text_color: theme.palette().text, } } else { - Theme::default_style(theme) + theme::default(theme) } } } diff --git a/examples/todos/snapshots/creates_a_new_task.sha256 b/examples/todos/snapshots/creates_a_new_task.sha256 new file mode 100644 index 0000000000..fcfee31548 --- /dev/null +++ b/examples/todos/snapshots/creates_a_new_task.sha256 @@ -0,0 +1 @@ +5047c10c4ea050393b686185b8d03b3c3738b47182f9892fb68df589ec46bd4b \ No newline at end of file diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 51d099625d..ff38c6ce8f 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -15,7 +15,7 @@ pub fn main() -> iced::Result { iced::application(Todos::title, Todos::update, Todos::view) .subscription(Todos::subscription) - .font(include_bytes!("../fonts/icons.ttf").as_slice()) + .font(Todos::ICON_FONT) .window_size((500.0, 800.0)) .run_with(Todos::new) } @@ -48,6 +48,8 @@ enum Message { } impl Todos { + const ICON_FONT: &[u8] = include_bytes!("../fonts/icons.ttf"); + fn new() -> (Self, Command) { ( Self::Loading, @@ -449,11 +451,10 @@ fn empty_message(message: &str) -> Element<'_, Message> { } // Fonts -const ICONS: Font = Font::with_name("Iced-Todos-Icons"); fn icon(unicode: char) -> Text<'static> { text(unicode.to_string()) - .font(ICONS) + .font(Font::with_name("Iced-Todos-Icons")) .width(20) .align_x(Center) } @@ -594,6 +595,8 @@ mod tests { #[test] fn it_creates_a_new_task() -> Result<(), test::Error> { + test::load_font(Todos::ICON_FONT)?; + let (mut todos, _command) = Todos::new(); let _command = todos.update(Message::Loaded(Err(LoadError::File))); @@ -610,6 +613,12 @@ mod tests { let mut interface = test::interface(todos.view()); let _ = interface.find(selector::text("Create the universe"))?; + let snapshot = interface.snapshot()?; + assert!( + snapshot.matches_hash("snapshots/creates_a_new_task")?, + "snapshots should match!" + ); + Ok(()) } } diff --git a/runtime/src/window.rs b/runtime/src/window.rs index 382f4518d4..0ebdba2f5c 100644 --- a/runtime/src/window.rs +++ b/runtime/src/window.rs @@ -1,11 +1,7 @@ //! Build window-based GUI applications. -pub mod screenshot; - -pub use screenshot::Screenshot; - use crate::core::time::Instant; use crate::core::window::{ - Event, Icon, Id, Level, Mode, Settings, UserAttention, + Event, Icon, Id, Level, Mode, Screenshot, Settings, UserAttention, }; use crate::core::{Point, Size}; use crate::futures::event; diff --git a/src/application.rs b/src/application.rs index 2ba764bed6..c79ed62be4 100644 --- a/src/application.rs +++ b/src/application.rs @@ -31,6 +31,7 @@ //! } //! ``` use crate::program::{self, Program}; +use crate::theme; use crate::window; use crate::{ Element, Executor, Font, Result, Settings, Size, Subscription, Task, @@ -38,8 +39,6 @@ use crate::{ use std::borrow::Cow; -pub use crate::shell::program::{Appearance, DefaultStyle}; - /// Creates an iced [`Application`] given its title, update, and view logic. /// /// # Example @@ -76,7 +75,7 @@ pub fn application( where State: 'static, Message: Send + std::fmt::Debug + 'static, - Theme: Default + DefaultStyle, + Theme: Default + theme::Base, Renderer: program::Renderer, { use std::marker::PhantomData; @@ -94,7 +93,7 @@ where for Instance where Message: Send + std::fmt::Debug + 'static, - Theme: Default + DefaultStyle, + Theme: Default + theme::Base, Renderer: program::Renderer, Update: self::Update, View: for<'a> self::View<'a, State, Message, Theme, Renderer>, @@ -352,7 +351,7 @@ impl Application

{ /// Sets the style logic of the [`Application`]. pub fn style( self, - f: impl Fn(&P::State, &P::Theme) -> Appearance, + f: impl Fn(&P::State, &P::Theme) -> theme::Style, ) -> Application< impl Program, > { diff --git a/src/daemon.rs b/src/daemon.rs index 81254bf947..fd6d027861 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -1,13 +1,12 @@ //! Create and run daemons that run in the background. use crate::application; use crate::program::{self, Program}; +use crate::theme; use crate::window; use crate::{Element, Executor, 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 @@ -26,7 +25,7 @@ pub fn daemon( where State: 'static, Message: Send + std::fmt::Debug + 'static, - Theme: Default + DefaultStyle, + Theme: Default + theme::Base, Renderer: program::Renderer, { use std::marker::PhantomData; @@ -44,7 +43,7 @@ where for Instance where Message: Send + std::fmt::Debug + 'static, - Theme: Default + DefaultStyle, + Theme: Default + theme::Base, Renderer: program::Renderer, Update: application::Update, View: for<'a> self::View<'a, State, Message, Theme, Renderer>, @@ -201,7 +200,7 @@ impl Daemon

{ /// Sets the style logic of the [`Daemon`]. pub fn style( self, - f: impl Fn(&P::State, &P::Theme) -> Appearance, + f: impl Fn(&P::State, &P::Theme) -> theme::Style, ) -> Daemon< impl Program, > { diff --git a/src/lib.rs b/src/lib.rs index d7628ea1e4..e748690972 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -688,7 +688,7 @@ pub fn run( where State: Default + 'static, Message: std::fmt::Debug + Send + 'static, - Theme: Default + program::DefaultStyle + 'static, + Theme: Default + theme::Base + 'static, Renderer: program::Renderer + 'static, { application(title, update, view).run() diff --git a/src/program.rs b/src/program.rs index 94cb9a7d33..ace4da7406 100644 --- a/src/program.rs +++ b/src/program.rs @@ -1,11 +1,10 @@ use crate::core::text; use crate::graphics::compositor; use crate::shell; +use crate::theme; use crate::window; use crate::{Element, Executor, Result, Settings, Subscription, Task}; -pub use crate::shell::program::{Appearance, DefaultStyle}; - /// The internal definition of a [`Program`]. /// /// You should not need to implement this trait directly. Instead, use the @@ -19,7 +18,7 @@ pub trait Program: Sized { type Message: Send + std::fmt::Debug + 'static; /// The theme of the program. - type Theme: Default + DefaultStyle; + type Theme: Default + theme::Base; /// The renderer of the program. type Renderer: Renderer; @@ -51,11 +50,11 @@ pub trait Program: Sized { } fn theme(&self, _state: &Self::State, _window: window::Id) -> Self::Theme { - Self::Theme::default() + ::default() } - fn style(&self, _state: &Self::State, theme: &Self::Theme) -> Appearance { - DefaultStyle::default_style(theme) + fn style(&self, _state: &Self::State, theme: &Self::Theme) -> theme::Style { + theme::Base::base(theme) } fn scale_factor(&self, _state: &Self::State, _window: window::Id) -> f64 { @@ -153,7 +152,7 @@ pub trait Program: Sized { self.program.theme(&self.state, window) } - fn style(&self, theme: &Self::Theme) -> Appearance { + fn style(&self, theme: &Self::Theme) -> theme::Style { self.program.style(&self.state, theme) } @@ -252,7 +251,7 @@ pub fn with_title( &self, state: &Self::State, theme: &Self::Theme, - ) -> Appearance { + ) -> theme::Style { self.program.style(state, theme) } @@ -322,7 +321,7 @@ pub fn with_subscription( &self, state: &Self::State, theme: &Self::Theme, - ) -> Appearance { + ) -> theme::Style { self.program.style(state, theme) } @@ -395,7 +394,7 @@ pub fn with_theme( &self, state: &Self::State, theme: &Self::Theme, - ) -> Appearance { + ) -> theme::Style { self.program.style(state, theme) } @@ -409,7 +408,7 @@ pub fn with_theme( pub fn with_style( program: P, - f: impl Fn(&P::State, &P::Theme) -> Appearance, + f: impl Fn(&P::State, &P::Theme) -> theme::Style, ) -> impl Program { struct WithStyle { program: P, @@ -418,7 +417,7 @@ pub fn with_style( impl Program for WithStyle where - F: Fn(&P::State, &P::Theme) -> Appearance, + F: Fn(&P::State, &P::Theme) -> theme::Style, { type State = P::State; type Message = P::Message; @@ -430,7 +429,7 @@ pub fn with_style( &self, state: &Self::State, theme: &Self::Theme, - ) -> Appearance { + ) -> theme::Style { (self.style)(state, theme) } @@ -535,7 +534,7 @@ pub fn with_scale_factor( &self, state: &Self::State, theme: &Self::Theme, - ) -> Appearance { + ) -> theme::Style { self.program.style(state, theme) } @@ -609,7 +608,7 @@ pub fn with_executor( &self, state: &Self::State, theme: &Self::Theme, - ) -> Appearance { + ) -> theme::Style { self.program.style(state, theme) } diff --git a/test/Cargo.toml b/test/Cargo.toml index c09a196d9c..f6f4f45a9a 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -19,3 +19,6 @@ iced_tiny_skia.workspace = true iced_renderer.workspace = true iced_renderer.features = ["tiny-skia"] + +png.workspace = true +sha2.workspace = true diff --git a/test/src/lib.rs b/test/src/lib.rs index 211ed4a293..c90962112b 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -12,15 +12,26 @@ use iced_tiny_skia as tiny_skia; use crate::core::clipboard; use crate::core::keyboard; use crate::core::mouse; +use crate::core::theme; +use crate::core::time; use crate::core::widget; +use crate::core::window; use crate::core::{Element, Event, Font, Pixels, Rectangle, Size, SmolStr}; use crate::renderer::Renderer; use crate::runtime::user_interface; use crate::runtime::UserInterface; +use std::borrow::Cow; +use std::fs; +use std::io; +use std::path::Path; +use std::sync::Arc; + pub fn interface<'a, Message, Theme>( element: impl Into>, ) -> Interface<'a, Message, Theme, Renderer> { + let size = Size::new(512.0, 512.0); + let mut renderer = Renderer::Secondary(tiny_skia::Renderer::new( Font::default(), Pixels(16.0), @@ -28,7 +39,7 @@ pub fn interface<'a, Message, Theme>( let raw = UserInterface::build( element, - Size::new(1024.0, 1024.0), + size, user_interface::Cache::default(), &mut renderer, ); @@ -36,13 +47,24 @@ pub fn interface<'a, Message, Theme>( Interface { raw, renderer, + size, messages: Vec::new(), } } +pub fn load_font(font: impl Into>) -> Result<(), Error> { + renderer::graphics::text::font_system() + .write() + .expect("Write to font system") + .load_font(font.into()); + + Ok(()) +} + pub struct Interface<'a, Message, Theme, Renderer> { raw: UserInterface<'a, Message, Theme, Renderer>, renderer: Renderer, + size: Size, messages: Vec, } @@ -50,9 +72,9 @@ pub struct Target { bounds: Rectangle, } -impl Interface<'_, Message, Theme, Renderer> +impl Interface<'_, Message, Theme, Renderer> where - Renderer: core::Renderer, + Theme: Default + theme::Base, { pub fn find( &mut self, @@ -256,11 +278,129 @@ where ); } + pub fn snapshot(&mut self) -> Result { + let theme = Theme::default(); + let base = theme.base(); + + let _ = self.raw.update( + &[Event::Window(window::Event::RedrawRequested( + time::Instant::now(), + ))], + mouse::Cursor::Unavailable, + &mut self.renderer, + &mut clipboard::Null, + &mut self.messages, + ); + + let _ = self.raw.draw( + &mut self.renderer, + &theme, + &core::renderer::Style { + text_color: base.text_color, + }, + mouse::Cursor::Unavailable, + ); + + if let Renderer::Secondary(renderer) = &mut self.renderer { + let scale_factor = 2.0; + + let viewport = renderer::graphics::Viewport::with_physical_size( + Size::new( + (self.size.width * scale_factor).round() as u32, + (self.size.height * scale_factor).round() as u32, + ), + f64::from(scale_factor), + ); + + let rgba = tiny_skia::window::compositor::screenshot::<&str>( + renderer, + &viewport, + base.background_color, + &[], + ); + + Ok(Snapshot { + screenshot: window::Screenshot::new( + rgba, + viewport.physical_size(), + viewport.scale_factor(), + ), + }) + } else { + unreachable!() + } + } + pub fn into_messages(self) -> impl IntoIterator { self.messages } } +pub struct Snapshot { + screenshot: window::Screenshot, +} + +impl Snapshot { + pub fn matches_image(&self, path: impl AsRef) -> Result { + let path = path.as_ref().with_extension("png"); + + if path.exists() { + let file = fs::File::open(&path)?; + let decoder = png::Decoder::new(file); + + let mut reader = decoder.read_info()?; + let mut bytes = vec![0; reader.output_buffer_size()]; + let info = reader.next_frame(&mut bytes)?; + + Ok(self.screenshot.bytes == bytes[..info.buffer_size()]) + } else { + if let Some(directory) = path.parent() { + fs::create_dir_all(directory)?; + } + + let file = fs::File::create(path)?; + + let mut encoder = png::Encoder::new( + file, + self.screenshot.size.width, + self.screenshot.size.height, + ); + encoder.set_color(png::ColorType::Rgba); + + let mut writer = encoder.write_header()?; + writer.write_image_data(&self.screenshot.bytes)?; + writer.finish()?; + + Ok(true) + } + } + + pub fn matches_hash(&self, path: impl AsRef) -> Result { + use sha2::{Digest, Sha256}; + + let path = path.as_ref().with_extension("sha256"); + + let hash = { + let mut hasher = Sha256::new(); + hasher.update(&self.screenshot.bytes); + format!("{:x}", hasher.finalize()) + }; + + if path.exists() { + let saved_hash = fs::read_to_string(&path)?; + + Ok(hash == saved_hash) + } else { + if let Some(directory) = path.parent() { + fs::create_dir_all(directory)?; + } + + fs::write(path, hash)?; + Ok(true) + } + } +} + fn key_press_and_release( key: impl Into, text: Option, @@ -293,4 +433,25 @@ fn key_press_and_release( #[derive(Debug, Clone)] pub enum Error { NotFound(Selector), + IOFailed(Arc), + PngDecodingFailed(Arc), + PngEncodingFailed(Arc), +} + +impl From for Error { + fn from(error: io::Error) -> Self { + Self::IOFailed(Arc::new(error)) + } +} + +impl From for Error { + fn from(error: png::DecodingError) -> Self { + Self::PngDecodingFailed(Arc::new(error)) + } +} + +impl From for Error { + fn from(error: png::EncodingError) -> Self { + Self::PngEncodingFailed(Arc::new(error)) + } } diff --git a/winit/src/program.rs b/winit/src/program.rs index 13873edd07..26b713f392 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -8,10 +8,11 @@ use crate::conversion; use crate::core; use crate::core::mouse; use crate::core::renderer; +use crate::core::theme; use crate::core::time::Instant; use crate::core::widget::operation; use crate::core::window; -use crate::core::{Color, Element, Point, Size, Theme}; +use crate::core::{Element, Point, Size}; use crate::futures::futures::channel::mpsc; use crate::futures::futures::channel::oneshot; use crate::futures::futures::task; @@ -46,7 +47,7 @@ use std::sync::Arc; pub trait Program where Self: Sized, - Self::Theme: DefaultStyle, + Self::Theme: theme::Base, { /// The type of __messages__ your [`Program`] will produce. type Message: std::fmt::Debug + Send; @@ -106,8 +107,8 @@ where fn theme(&self, window: window::Id) -> Self::Theme; /// Returns the `Style` variation of the `Theme`. - fn style(&self, theme: &Self::Theme) -> Appearance { - theme.default_style() + fn style(&self, theme: &Self::Theme) -> theme::Style { + theme::Base::base(theme) } /// Returns the event `Subscription` for the current state of the @@ -138,37 +139,6 @@ where } } -/// The appearance of a program. -#[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 default style of a [`Program`]. -pub trait DefaultStyle { - /// Returns the default style of a [`Program`]. - fn default_style(&self) -> Appearance; -} - -impl DefaultStyle for Theme { - fn default_style(&self) -> Appearance { - default(self) - } -} - -/// The default [`Appearance`] of a [`Program`] with the built-in [`Theme`]. -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 a [`Program`] with an executor, compositor, and the provided /// settings. pub fn run( @@ -180,7 +150,7 @@ pub fn run( where P: Program + 'static, C: Compositor + 'static, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { use winit::event_loop::EventLoop; @@ -674,7 +644,7 @@ async fn run_instance( ) where P: Program + 'static, C: Compositor + 'static, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { use winit::event; use winit::event_loop::ControlFlow; @@ -1170,7 +1140,7 @@ fn build_user_interface<'a, P: Program>( id: window::Id, ) -> UserInterface<'a, P::Message, P::Theme, P::Renderer> where - P::Theme: DefaultStyle, + P::Theme: theme::Base, { debug.view_started(); let view = program.view(id); @@ -1189,7 +1159,7 @@ fn update( debug: &mut Debug, messages: &mut Vec, ) where - P::Theme: DefaultStyle, + P::Theme: theme::Base, { for message in messages.drain(..) { debug.log_message(&message); @@ -1226,7 +1196,7 @@ fn run_action( ) where P: Program, C: Compositor + 'static, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { use crate::runtime::clipboard; use crate::runtime::system; @@ -1461,7 +1431,7 @@ fn run_action( &debug.overlay(), ); - let _ = channel.send(window::Screenshot::new( + let _ = channel.send(core::window::Screenshot::new( bytes, window.state.physical_size(), window.state.viewport().scale_factor(), @@ -1536,7 +1506,7 @@ pub fn build_user_interfaces<'a, P: Program, C>( ) -> FxHashMap> where C: Compositor, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { cached_user_interfaces .drain() diff --git a/winit/src/program/state.rs b/winit/src/program/state.rs index b8a5896051..e883d04acd 100644 --- a/winit/src/program/state.rs +++ b/winit/src/program/state.rs @@ -1,17 +1,18 @@ use crate::conversion; -use crate::core::{mouse, window}; +use crate::core::{mouse, theme, window}; use crate::core::{Color, Size}; use crate::graphics::Viewport; -use crate::program::{self, Program}; -use std::fmt::{Debug, Formatter}; +use crate::program::Program; use winit::event::{Touch, WindowEvent}; use winit::window::Window; +use std::fmt::{Debug, Formatter}; + /// The state of a multi-windowed [`Program`]. pub struct State where - P::Theme: program::DefaultStyle, + P::Theme: theme::Base, { title: String, scale_factor: f64, @@ -20,12 +21,12 @@ where cursor_position: Option>, modifiers: winit::keyboard::ModifiersState, theme: P::Theme, - appearance: program::Appearance, + style: theme::Style, } impl Debug for State

where - P::Theme: program::DefaultStyle, + P::Theme: theme::Base, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("multi_window::State") @@ -34,14 +35,14 @@ where .field("viewport", &self.viewport) .field("viewport_version", &self.viewport_version) .field("cursor_position", &self.cursor_position) - .field("appearance", &self.appearance) + .field("style", &self.style) .finish() } } impl State

where - P::Theme: program::DefaultStyle, + P::Theme: theme::Base, { /// Creates a new [`State`] for the provided [`Program`]'s `window`. pub fn new( @@ -52,7 +53,7 @@ where let title = application.title(window_id); let scale_factor = application.scale_factor(window_id); let theme = application.theme(window_id); - let appearance = application.style(&theme); + let style = application.style(&theme); let viewport = { let physical_size = window.inner_size(); @@ -71,7 +72,7 @@ where cursor_position: None, modifiers: winit::keyboard::ModifiersState::default(), theme, - appearance, + style, } } @@ -127,12 +128,12 @@ where /// Returns the current background [`Color`] of the [`State`]. pub fn background_color(&self) -> Color { - self.appearance.background_color + self.style.background_color } /// Returns the current text [`Color`] of the [`State`]. pub fn text_color(&self) -> Color { - self.appearance.text_color + self.style.text_color } /// Processes the provided window event and updates the [`State`] accordingly. @@ -237,6 +238,6 @@ where // Update theme and appearance self.theme = application.theme(window_id); - self.appearance = application.style(&self.theme); + self.style = application.style(&self.theme); } } diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs index 10a973fe4e..451b2caf2c 100644 --- a/winit/src/program/window_manager.rs +++ b/winit/src/program/window_manager.rs @@ -1,9 +1,10 @@ use crate::core::mouse; +use crate::core::theme; use crate::core::time::Instant; use crate::core::window::Id; use crate::core::{Point, Size}; use crate::graphics::Compositor; -use crate::program::{DefaultStyle, Program, State}; +use crate::program::{Program, State}; use std::collections::BTreeMap; use std::sync::Arc; @@ -14,7 +15,7 @@ pub struct WindowManager where P: Program, C: Compositor, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { aliases: BTreeMap, entries: BTreeMap>, @@ -24,7 +25,7 @@ impl WindowManager where P: Program, C: Compositor, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { pub fn new() -> Self { Self { @@ -132,7 +133,7 @@ impl Default for WindowManager where P: Program, C: Compositor, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { fn default() -> Self { Self::new() @@ -144,7 +145,7 @@ pub struct Window where P: Program, C: Compositor, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { pub raw: Arc, pub state: State

, @@ -160,7 +161,7 @@ impl Window where P: Program, C: Compositor, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { pub fn position(&self) -> Option { self.raw