diff options
author | 2024-12-14 03:49:24 +0100 | |
---|---|---|
committer | 2024-12-14 03:49:24 +0100 | |
commit | 2cf4abf25bb5702635c19a22353399db8cef7be3 (patch) | |
tree | 3859d0fede59e92864c66c2ace89bc1d401769d6 | |
parent | 6572909ab5b004176f6d261b67b4caa99f1f54bb (diff) | |
download | iced-2cf4abf25bb5702635c19a22353399db8cef7be3.tar.gz iced-2cf4abf25bb5702635c19a22353399db8cef7be3.tar.bz2 iced-2cf4abf25bb5702635c19a22353399db8cef7be3.zip |
Support custom renderers in `iced_test` through `renderer::Headless` trait
-rw-r--r-- | core/src/lib.rs | 2 | ||||
-rw-r--r-- | core/src/renderer.rs | 19 | ||||
-rw-r--r-- | core/src/settings.rs (renamed from src/settings.rs) | 9 | ||||
-rw-r--r-- | examples/todos/snapshots/creates_a_new_task.sha256 | 2 | ||||
-rw-r--r-- | examples/todos/src/main.rs | 19 | ||||
-rw-r--r-- | graphics/src/text.rs | 3 | ||||
-rw-r--r-- | renderer/src/lib.rs | 28 | ||||
-rw-r--r-- | src/lib.rs | 6 | ||||
-rw-r--r-- | test/Cargo.toml | 3 | ||||
-rw-r--r-- | test/src/lib.rs | 142 | ||||
-rw-r--r-- | tiny_skia/src/lib.rs | 25 | ||||
-rw-r--r-- | winit/src/settings.rs | 11 |
12 files changed, 190 insertions, 79 deletions
diff --git a/core/src/lib.rs b/core/src/lib.rs index df599f45..645f7a90 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -40,6 +40,7 @@ mod pixels; mod point; mod rectangle; mod rotation; +mod settings; mod shadow; mod shell; mod size; @@ -67,6 +68,7 @@ pub use point::Point; pub use rectangle::Rectangle; pub use renderer::Renderer; pub use rotation::Rotation; +pub use settings::Settings; pub use shadow::Shadow; pub use shell::Shell; pub use size::Size; diff --git a/core/src/renderer.rs b/core/src/renderer.rs index 6684517f..68e070e8 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -3,7 +3,8 @@ mod null; use crate::{ - Background, Border, Color, Rectangle, Shadow, Size, Transformation, Vector, + Background, Border, Color, Font, Pixels, Rectangle, Shadow, Size, + Transformation, Vector, }; /// A component that can be used by widgets to draw themselves on a screen. @@ -100,3 +101,19 @@ impl Default for Style { } } } + +/// A headless renderer is a renderer that can render offscreen without +/// a window nor a compositor. +pub trait Headless { + /// Creates a new [`Headless`] renderer; + fn new(default_font: Font, default_text_size: Pixels) -> Self; + + /// Draws offscreen into a screenshot, returning a collection of + /// bytes representing the rendered pixels in RGBA order. + fn screenshot( + &mut self, + size: Size<u32>, + scale_factor: f32, + background_color: Color, + ) -> Vec<u8>; +} diff --git a/src/settings.rs b/core/src/settings.rs index ebac7a86..36bbb699 100644 --- a/src/settings.rs +++ b/core/src/settings.rs @@ -48,12 +48,3 @@ impl Default for Settings { } } } - -impl From<Settings> for iced_winit::Settings { - fn from(settings: Settings) -> iced_winit::Settings { - iced_winit::Settings { - id: settings.id, - fonts: settings.fonts, - } - } -} diff --git a/examples/todos/snapshots/creates_a_new_task.sha256 b/examples/todos/snapshots/creates_a_new_task.sha256 index 75d5f2ce..15efcd77 100644 --- a/examples/todos/snapshots/creates_a_new_task.sha256 +++ b/examples/todos/snapshots/creates_a_new_task.sha256 @@ -1 +1 @@ -b41c73d214894bf5f94f787e5f265cff6500822b2d4a29a4ac0c847a71db7123
\ No newline at end of file +a7c2ac4b57f84416812e2134e48fe34db55a757d9176beedf5854a2f69532e32
\ No newline at end of file diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 1d4aa8a4..fe23cbc9 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -590,16 +590,25 @@ impl SavedState { mod tests { use super::*; - use iced_test::{interface, load_font, selector, Error}; + use iced::Settings; + use iced_test::{selector, Error, Simulator}; + + fn simulator(todos: &Todos) -> Simulator<Message> { + Simulator::with_settings( + Settings { + fonts: vec![Todos::ICON_FONT.into()], + ..Settings::default() + }, + todos.view(), + ) + } #[test] fn it_creates_a_new_task() -> Result<(), Error> { - load_font(Todos::ICON_FONT)?; - let (mut todos, _command) = Todos::new(); let _command = todos.update(Message::Loaded(Err(LoadError::File))); - let mut ui = interface(todos.view()); + let mut ui = simulator(&todos); let _input = ui.click("new-task")?; ui.typewrite("Create the universe"); @@ -609,7 +618,7 @@ mod tests { let _command = todos.update(message); } - let mut ui = interface(todos.view()); + let mut ui = simulator(&todos); let _ = ui.find(selector::text("Create the universe"))?; let snapshot = ui.snapshot()?; diff --git a/graphics/src/text.rs b/graphics/src/text.rs index e6f06b61..f954e318 100644 --- a/graphics/src/text.rs +++ b/graphics/src/text.rs @@ -146,7 +146,8 @@ impl Text { /// The regular variant of the [Fira Sans] font. /// -/// It is loaded as part of the default fonts in Wasm builds. +/// It is loaded as part of the default fonts when the `fira-sans` +/// feature is enabled. /// /// [Fira Sans]: https://mozilla.github.io/Fira/ #[cfg(feature = "fira-sans")] diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs index 220542e1..ee20a458 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -23,6 +23,9 @@ pub type Compositor = renderer::Compositor; #[cfg(all(feature = "wgpu", feature = "tiny-skia"))] mod renderer { + use crate::core::renderer; + use crate::core::{Color, Font, Pixels, Size}; + pub type Renderer = crate::fallback::Renderer< iced_wgpu::Renderer, iced_tiny_skia::Renderer, @@ -32,6 +35,31 @@ mod renderer { iced_wgpu::window::Compositor, iced_tiny_skia::window::Compositor, >; + + impl renderer::Headless for Renderer { + fn new(default_font: Font, default_text_size: Pixels) -> Self { + Self::Secondary(iced_tiny_skia::Renderer::new( + default_font, + default_text_size, + )) + } + + fn screenshot( + &mut self, + size: Size<u32>, + scale_factor: f32, + background_color: Color, + ) -> Vec<u8> { + match self { + crate::fallback::Renderer::Primary(_) => unreachable!( + "iced_wgpu does not support headless mode yet!" + ), + crate::fallback::Renderer::Secondary(renderer) => { + renderer.screenshot(size, scale_factor, background_color) + } + } + } + } } #[cfg(all(feature = "wgpu", not(feature = "tiny-skia")))] @@ -491,7 +491,6 @@ mod program; pub mod application; pub mod daemon; -pub mod settings; pub mod time; pub mod window; @@ -506,8 +505,8 @@ pub use crate::core::padding; pub use crate::core::theme; pub use crate::core::{ Alignment, Background, Border, Color, ContentFit, Degrees, Gradient, - Length, Padding, Pixels, Point, Radians, Rectangle, Rotation, Shadow, Size, - Theme, Transformation, Vector, + Length, Padding, Pixels, Point, Radians, Rectangle, Rotation, Settings, + Shadow, Size, Theme, Transformation, Vector, }; pub use crate::runtime::exit; pub use iced_futures::Subscription; @@ -626,7 +625,6 @@ pub use executor::Executor; pub use font::Font; pub use program::Program; pub use renderer::Renderer; -pub use settings::Settings; pub use task::Task; #[doc(inline)] diff --git a/test/Cargo.toml b/test/Cargo.toml index c63a9e14..ff6cb38a 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -15,10 +15,9 @@ workspace = true [dependencies] iced_runtime.workspace = true -iced_tiny_skia.workspace = true iced_renderer.workspace = true -iced_renderer.features = ["tiny-skia", "fira-sans"] +iced_renderer.features = ["fira-sans"] png.workspace = true sha2.workspace = true diff --git a/test/src/lib.rs b/test/src/lib.rs index 232b447e..6c1d6bdc 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -7,7 +7,6 @@ pub use selector::Selector; use iced_renderer as renderer; use iced_runtime as runtime; use iced_runtime::core; -use iced_tiny_skia as tiny_skia; use crate::core::clipboard; use crate::core::keyboard; @@ -16,8 +15,7 @@ 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::core::{Element, Event, Font, Rectangle, Settings, Size, SmolStr}; use crate::runtime::user_interface; use crate::runtime::UserInterface; @@ -27,32 +25,17 @@ use std::io; use std::path::Path; use std::sync::Arc; -pub fn interface<'a, Message, Theme>( +pub fn simulator<'a, Message, Theme, Renderer>( element: impl Into<Element<'a, Message, Theme, Renderer>>, -) -> Interface<'a, Message, Theme, Renderer> { - let size = Size::new(512.0, 512.0); - - let mut renderer = Renderer::Secondary(tiny_skia::Renderer::new( - Font::with_name("Fira Sans"), - Pixels(16.0), - )); - - let raw = UserInterface::build( - element, - size, - user_interface::Cache::default(), - &mut renderer, - ); - - Interface { - raw, - renderer, - size, - messages: Vec::new(), - } +) -> Simulator<'a, Message, Theme, Renderer> +where + Theme: Default + theme::Base, + Renderer: core::Renderer + core::renderer::Headless, +{ + Simulator::new(element) } -pub fn load_font(font: impl Into<Cow<'static, [u8]>>) -> Result<(), Error> { +fn load_font(font: impl Into<Cow<'static, [u8]>>) -> Result<(), Error> { renderer::graphics::text::font_system() .write() .expect("Write to font system") @@ -61,21 +44,78 @@ pub fn load_font(font: impl Into<Cow<'static, [u8]>>) -> Result<(), Error> { Ok(()) } -pub struct Interface<'a, Message, Theme, Renderer> { +pub struct Simulator< + 'a, + Message, + Theme = core::Theme, + Renderer = renderer::Renderer, +> { raw: UserInterface<'a, Message, Theme, Renderer>, renderer: Renderer, - size: Size, + window_size: Size, messages: Vec<Message>, } pub struct Target { - bounds: Rectangle, + pub bounds: Rectangle, } -impl<Message, Theme> Interface<'_, Message, Theme, Renderer> +impl<'a, Message, Theme, Renderer> Simulator<'a, Message, Theme, Renderer> where Theme: Default + theme::Base, + Renderer: core::Renderer + core::renderer::Headless, { + pub fn new( + element: impl Into<Element<'a, Message, Theme, Renderer>>, + ) -> Self { + Self::with_settings(Settings::default(), element) + } + + pub fn with_settings( + settings: Settings, + element: impl Into<Element<'a, Message, Theme, Renderer>>, + ) -> Self { + Self::with_settings_and_size( + settings, + window::Settings::default().size, + element, + ) + } + + pub fn with_settings_and_size( + settings: Settings, + window_size: impl Into<Size>, + element: impl Into<Element<'a, Message, Theme, Renderer>>, + ) -> Self { + let window_size = window_size.into(); + + let default_font = match settings.default_font { + Font::DEFAULT => Font::with_name("Fira Sans"), + _ => settings.default_font, + }; + + for font in settings.fonts { + load_font(font).expect("Font must be valid"); + } + + let mut renderer = + Renderer::new(default_font, settings.default_text_size); + + let raw = UserInterface::build( + element, + window_size, + user_interface::Cache::default(), + &mut renderer, + ); + + Simulator { + raw, + renderer, + window_size, + messages: Vec::new(), + } + } + pub fn find( &mut self, selector: impl Into<Selector>, @@ -301,34 +341,26 @@ where mouse::Cursor::Unavailable, ); - if let Renderer::Secondary(renderer) = &mut self.renderer { - let scale_factor = 2.0; + 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 physical_size = Size::new( + (self.window_size.width * scale_factor).round() as u32, + (self.window_size.height * scale_factor).round() as u32, + ); - let rgba = tiny_skia::window::compositor::screenshot::<&str>( - renderer, - &viewport, - base.background_color, - &[], - ); + let rgba = self.renderer.screenshot( + physical_size, + scale_factor, + base.background_color, + ); - Ok(Snapshot { - screenshot: window::Screenshot::new( - rgba, - viewport.physical_size(), - viewport.scale_factor(), - ), - }) - } else { - unreachable!() - } + Ok(Snapshot { + screenshot: window::Screenshot::new( + rgba, + physical_size, + f64::from(scale_factor), + ), + }) } pub fn into_messages(self) -> impl IntoIterator<Item = Message> { diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index 758921d4..a42f1de4 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -29,7 +29,7 @@ pub use geometry::Geometry; use crate::core::renderer; use crate::core::{ - Background, Color, Font, Pixels, Point, Rectangle, Transformation, + Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, }; use crate::engine::Engine; use crate::graphics::compositor; @@ -405,3 +405,26 @@ impl core::svg::Renderer for Renderer { impl compositor::Default for Renderer { type Compositor = window::Compositor; } + +impl renderer::Headless for Renderer { + fn new(default_font: Font, default_text_size: Pixels) -> Self { + Self::new(default_font, default_text_size) + } + + fn screenshot( + &mut self, + size: Size<u32>, + scale_factor: f32, + background_color: Color, + ) -> Vec<u8> { + let viewport = + Viewport::with_physical_size(size, f64::from(scale_factor)); + + window::compositor::screenshot::<&str>( + self, + &viewport, + background_color, + &[], + ) + } +} diff --git a/winit/src/settings.rs b/winit/src/settings.rs index 78368a04..e2bf8abf 100644 --- a/winit/src/settings.rs +++ b/winit/src/settings.rs @@ -1,4 +1,6 @@ //! Configure your application. +use crate::core; + use std::borrow::Cow; /// The settings of an application. @@ -13,3 +15,12 @@ pub struct Settings { /// The fonts to load on boot. pub fonts: Vec<Cow<'static, [u8]>>, } + +impl From<core::Settings> for Settings { + fn from(settings: core::Settings) -> Self { + Self { + id: settings.id, + fonts: settings.fonts, + } + } +} |