summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-12-14 03:49:24 +0100
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-12-14 03:49:24 +0100
commit2cf4abf25bb5702635c19a22353399db8cef7be3 (patch)
tree3859d0fede59e92864c66c2ace89bc1d401769d6
parent6572909ab5b004176f6d261b67b4caa99f1f54bb (diff)
downloadiced-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.rs2
-rw-r--r--core/src/renderer.rs19
-rw-r--r--core/src/settings.rs (renamed from src/settings.rs)9
-rw-r--r--examples/todos/snapshots/creates_a_new_task.sha2562
-rw-r--r--examples/todos/src/main.rs19
-rw-r--r--graphics/src/text.rs3
-rw-r--r--renderer/src/lib.rs28
-rw-r--r--src/lib.rs6
-rw-r--r--test/Cargo.toml3
-rw-r--r--test/src/lib.rs142
-rw-r--r--tiny_skia/src/lib.rs25
-rw-r--r--winit/src/settings.rs11
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")))]
diff --git a/src/lib.rs b/src/lib.rs
index d13ee7d0..427e789c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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,
+ }
+ }
+}