From 188db4da48954b95a3fe79bcd22689ffc3a661e0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 Mar 2024 05:52:48 +0100 Subject: Draft support for dynamic custom renderer injection --- core/src/image.rs | 4 ++-- core/src/renderer.rs | 2 +- core/src/svg.rs | 9 +++++++-- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'core') diff --git a/core/src/image.rs b/core/src/image.rs index e5fdcd83..32b95f03 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -186,11 +186,11 @@ pub trait Renderer: crate::Renderer { type Handle: Clone + Hash; /// Returns the dimensions of an image for the given [`Handle`]. - fn dimensions(&self, handle: &Self::Handle) -> Size; + fn measure_image(&self, handle: &Self::Handle) -> Size; /// Draws an image with the given [`Handle`] and inside the provided /// `bounds`. - fn draw( + fn draw_image( &mut self, handle: Self::Handle, filter_method: FilterMethod, diff --git a/core/src/renderer.rs b/core/src/renderer.rs index 1139b41c..47b09d32 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -10,7 +10,7 @@ use crate::{ }; /// A component that can be used by widgets to draw themselves on a screen. -pub trait Renderer: Sized { +pub trait Renderer { /// Draws the primitives recorded in the given closure in a new layer. /// /// The layer will clip its contents to the provided `bounds`. diff --git a/core/src/svg.rs b/core/src/svg.rs index d63e3c95..ab207cca 100644 --- a/core/src/svg.rs +++ b/core/src/svg.rs @@ -91,8 +91,13 @@ impl std::fmt::Debug for Data { /// [renderer]: crate::renderer pub trait Renderer: crate::Renderer { /// Returns the default dimensions of an SVG for the given [`Handle`]. - fn dimensions(&self, handle: &Handle) -> Size; + fn measure_svg(&self, handle: &Handle) -> Size; /// Draws an SVG with the given [`Handle`], an optional [`Color`] filter, and inside the provided `bounds`. - fn draw(&mut self, handle: Handle, color: Option, bounds: Rectangle); + fn draw_svg( + &mut self, + handle: Handle, + color: Option, + bounds: Rectangle, + ); } -- cgit From 3645d34d6a1ba1247238e830e9eefd52d9e5b986 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 21 Mar 2024 22:27:17 +0100 Subject: Implement composable, type-safe renderer fallback --- core/src/renderer.rs | 28 ++++++++++++++++++++++++++-- core/src/renderer/null.rs | 13 ++++++------- 2 files changed, 32 insertions(+), 9 deletions(-) (limited to 'core') diff --git a/core/src/renderer.rs b/core/src/renderer.rs index 47b09d32..406b33f3 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -11,17 +11,41 @@ use crate::{ /// A component that can be used by widgets to draw themselves on a screen. pub trait Renderer { + /// Starts recording a new layer. + fn start_layer(&mut self); + + /// Ends recording a new layer. + /// + /// The new layer will clip its contents to the provided `bounds`. + fn end_layer(&mut self, bounds: Rectangle); + /// Draws the primitives recorded in the given closure in a new layer. /// /// The layer will clip its contents to the provided `bounds`. - fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)); + fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { + self.start_layer(); + f(self); + self.end_layer(bounds); + } + + /// Starts recording with a new [`Transformation`]. + fn start_transformation(&mut self); + + /// Ends recording a new layer. + /// + /// The new layer will clip its contents to the provided `bounds`. + fn end_transformation(&mut self, transformation: Transformation); /// Applies a [`Transformation`] to the primitives recorded in the given closure. fn with_transformation( &mut self, transformation: Transformation, f: impl FnOnce(&mut Self), - ); + ) { + self.start_transformation(); + f(self); + self.end_transformation(transformation); + } /// Applies a translation to the primitives recorded in the given closure. fn with_translation( diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 83688ff7..0d7b7c14 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -21,14 +21,13 @@ impl Null { } impl Renderer for Null { - fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {} + fn start_layer(&mut self) {} - fn with_transformation( - &mut self, - _transformation: Transformation, - _f: impl FnOnce(&mut Self), - ) { - } + fn end_layer(&mut self, _bounds: Rectangle) {} + + fn start_transformation(&mut self) {} + + fn end_transformation(&mut self, _transformation: Transformation) {} fn clear(&mut self) {} -- cgit From 1f13a91361258a1607c71f4840a26a6437f88612 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Mar 2024 05:27:31 +0100 Subject: Make `iced_tiny_skia` optional with a `tiny-skia` feature --- core/src/element.rs | 4 ++-- core/src/renderer.rs | 4 ---- core/src/renderer/null.rs | 34 +++++++++++++++++++--------------- core/src/size.rs | 2 +- 4 files changed, 22 insertions(+), 22 deletions(-) (limited to 'core') diff --git a/core/src/element.rs b/core/src/element.rs index 989eaa3b..7d918a2e 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -95,7 +95,7 @@ impl<'a, Message, Theme, Renderer> Element<'a, Message, Theme, Renderer> { /// /// ```no_run /// # mod iced { - /// # pub type Element<'a, Message> = iced_core::Element<'a, Message, iced_core::Theme, iced_core::renderer::Null>; + /// # pub type Element<'a, Message> = iced_core::Element<'a, Message, iced_core::Theme, ()>; /// # /// # pub mod widget { /// # pub fn row<'a, Message>(iter: impl IntoIterator>) -> super::Element<'a, Message> { @@ -109,7 +109,7 @@ impl<'a, Message, Theme, Renderer> Element<'a, Message, Theme, Renderer> { /// # pub enum Message {} /// # pub struct Counter; /// # - /// # pub type Element<'a, Message> = iced_core::Element<'a, Message, iced_core::Theme, iced_core::renderer::Null>; + /// # pub type Element<'a, Message> = iced_core::Element<'a, Message, iced_core::Theme, ()>; /// # /// # impl Counter { /// # pub fn view(&self) -> Element { diff --git a/core/src/renderer.rs b/core/src/renderer.rs index 406b33f3..dfedcd45 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -1,10 +1,6 @@ //! Write your own renderer. -#[cfg(debug_assertions)] mod null; -#[cfg(debug_assertions)] -pub use null::Null; - use crate::{ Background, Border, Color, Rectangle, Shadow, Size, Transformation, Vector, }; diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 0d7b7c14..af7dc15f 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -1,4 +1,5 @@ use crate::alignment; +use crate::image; use crate::renderer::{self, Renderer}; use crate::text::{self, Text}; use crate::{ @@ -7,20 +8,7 @@ use crate::{ use std::borrow::Cow; -/// A renderer that does nothing. -/// -/// It can be useful if you are writing tests! -#[derive(Debug, Clone, Copy, Default)] -pub struct Null; - -impl Null { - /// Creates a new [`Null`] renderer. - pub fn new() -> Null { - Null - } -} - -impl Renderer for Null { +impl Renderer for () { fn start_layer(&mut self) {} fn end_layer(&mut self, _bounds: Rectangle) {} @@ -39,7 +27,7 @@ impl Renderer for Null { } } -impl text::Renderer for Null { +impl text::Renderer for () { type Font = Font; type Paragraph = (); type Editor = (); @@ -173,3 +161,19 @@ impl text::Editor for () { ) { } } + +impl image::Renderer for () { + type Handle = (); + + fn measure_image(&self, _handle: &Self::Handle) -> Size { + Size::default() + } + + fn draw_image( + &mut self, + _handle: Self::Handle, + _filter_method: image::FilterMethod, + _bounds: Rectangle, + ) { + } +} diff --git a/core/src/size.rs b/core/src/size.rs index 267fc90e..55db759d 100644 --- a/core/src/size.rs +++ b/core/src/size.rs @@ -1,7 +1,7 @@ use crate::Vector; /// An amount of space in 2 dimensions. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub struct Size { /// The width. pub width: T, -- cgit From 4f2f40c68b4647f281d34034beb159a41422aa06 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 22 Mar 2024 05:41:15 +0100 Subject: Fix standalone compilation of `iced_widget` crate --- core/src/renderer.rs | 1 + core/src/renderer/null.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'core') diff --git a/core/src/renderer.rs b/core/src/renderer.rs index dfedcd45..6712314e 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -1,4 +1,5 @@ //! Write your own renderer. +#[cfg(debug_assertions)] mod null; use crate::{ diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index af7dc15f..c26ce1a5 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -1,6 +1,7 @@ use crate::alignment; use crate::image; use crate::renderer::{self, Renderer}; +use crate::svg; use crate::text::{self, Text}; use crate::{ Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, @@ -177,3 +178,17 @@ impl image::Renderer for () { ) { } } + +impl svg::Renderer for () { + fn measure_svg(&self, _handle: &svg::Handle) -> Size { + Size::default() + } + + fn draw_svg( + &mut self, + _handle: svg::Handle, + _color: Option, + _bounds: Rectangle, + ) { + } +} -- cgit From 999ad2d288a9354f045bb2e1b838014b3d302779 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 23 Mar 2024 19:23:08 +0100 Subject: Try catalog theming approach with `Button` --- core/src/closure.rs | 33 +++++++++++++++++++++++++++++++++ core/src/lib.rs | 1 + 2 files changed, 34 insertions(+) create mode 100644 core/src/closure.rs (limited to 'core') diff --git a/core/src/closure.rs b/core/src/closure.rs new file mode 100644 index 00000000..dc7b4fee --- /dev/null +++ b/core/src/closure.rs @@ -0,0 +1,33 @@ +//! Box closures with ease. +//! +//! These are just a bunch of types that wrap boxed closures with +//! blanket [`From`] implementations for easy conversions. +//! +//! Mainly, it allows functions to take `Into` where `T` may end +//! up being a boxed closure. + +/// A boxed closure that takes `A` by reference and produces `O`. +#[allow(missing_debug_implementations)] +pub struct Unary<'a, A, O>(pub Box O + 'a>); + +impl<'a, A, O, T> From for Unary<'a, A, O> +where + T: Fn(&A) -> O + 'a, +{ + fn from(f: T) -> Self { + Self(Box::new(f)) + } +} + +/// A boxed closure that takes `A` by reference and `B` by value and produces `O`. +#[allow(missing_debug_implementations)] +pub struct Binary<'a, A, B, O>(pub Box O + 'a>); + +impl<'a, A, B, O, T> From for Binary<'a, A, B, O> +where + T: Fn(&A, B) -> O + 'a, +{ + fn from(f: T) -> Self { + Self(Box::new(f)) + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index d076413e..36b47d80 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -19,6 +19,7 @@ pub mod alignment; pub mod border; pub mod clipboard; +pub mod closure; pub mod event; pub mod font; pub mod gradient; -- cgit From e657dc2ecd2ffa72c0abd87f9a59dcc1acbc7083 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 24 Mar 2024 02:08:20 +0100 Subject: Fine-tune `Catalog` approach for `button`, `checkbox`, and `svg` --- core/src/closure.rs | 33 --------------------------------- core/src/lib.rs | 1 - 2 files changed, 34 deletions(-) delete mode 100644 core/src/closure.rs (limited to 'core') diff --git a/core/src/closure.rs b/core/src/closure.rs deleted file mode 100644 index dc7b4fee..00000000 --- a/core/src/closure.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Box closures with ease. -//! -//! These are just a bunch of types that wrap boxed closures with -//! blanket [`From`] implementations for easy conversions. -//! -//! Mainly, it allows functions to take `Into` where `T` may end -//! up being a boxed closure. - -/// A boxed closure that takes `A` by reference and produces `O`. -#[allow(missing_debug_implementations)] -pub struct Unary<'a, A, O>(pub Box O + 'a>); - -impl<'a, A, O, T> From for Unary<'a, A, O> -where - T: Fn(&A) -> O + 'a, -{ - fn from(f: T) -> Self { - Self(Box::new(f)) - } -} - -/// A boxed closure that takes `A` by reference and `B` by value and produces `O`. -#[allow(missing_debug_implementations)] -pub struct Binary<'a, A, B, O>(pub Box O + 'a>); - -impl<'a, A, B, O, T> From for Binary<'a, A, B, O> -where - T: Fn(&A, B) -> O + 'a, -{ - fn from(f: T) -> Self { - Self(Box::new(f)) - } -} diff --git a/core/src/lib.rs b/core/src/lib.rs index 36b47d80..d076413e 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -19,7 +19,6 @@ pub mod alignment; pub mod border; pub mod clipboard; -pub mod closure; pub mod event; pub mod font; pub mod gradient; -- cgit From f0ae9a0c38c2532220a7460916604914db94c078 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 24 Mar 2024 05:03:09 +0100 Subject: Use `Catalog` approach for all widgets --- core/src/widget/text.rs | 117 +++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 46 deletions(-) (limited to 'core') diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 66e2d066..12f6956a 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -18,6 +18,7 @@ pub use text::{LineHeight, Shaping}; #[allow(missing_debug_implementations)] pub struct Text<'a, Theme, Renderer> where + Theme: Catalog, Renderer: text::Renderer, { content: Cow<'a, str>, @@ -29,18 +30,16 @@ where vertical_alignment: alignment::Vertical, font: Option, shaping: Shaping, - style: Style<'a, Theme>, + class: Theme::Class<'a>, } impl<'a, Theme, Renderer> Text<'a, Theme, Renderer> where + Theme: Catalog, Renderer: text::Renderer, { /// Create a new fragment of [`Text`] with the given contents. - pub fn new(content: impl Into>) -> Self - where - Theme: DefaultStyle + 'a, - { + pub fn new(content: impl Into>) -> Self { Text { content: content.into(), size: None, @@ -51,7 +50,7 @@ where horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, shaping: Shaping::Basic, - style: Box::new(Theme::default_style), + class: Theme::default(), } } @@ -75,25 +74,6 @@ where self } - /// Sets the style of the [`Text`]. - pub fn style(mut self, style: impl Fn(&Theme) -> Appearance + 'a) -> Self { - self.style = Box::new(style); - self - } - - /// Sets the [`Color`] of the [`Text`]. - pub fn color(self, color: impl Into) -> Self { - self.color_maybe(Some(color)) - } - - /// Sets the [`Color`] of the [`Text`], if `Some`. - pub fn color_maybe(mut self, color: Option>) -> Self { - let color = color.map(Into::into); - - self.style = Box::new(move |_theme| Appearance { color }); - self - } - /// Sets the width of the [`Text`] boundaries. pub fn width(mut self, width: impl Into) -> Self { self.width = width.into(); @@ -129,6 +109,42 @@ where self.shaping = shaping; self } + + /// Sets the style of the [`Text`]. + #[must_use] + pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self + where + Theme::Class<'a>: From>, + { + self.class = (Box::new(style) as StyleFn<'a, Theme>).into(); + self + } + + /// Sets the [`Color`] of the [`Text`]. + pub fn color(self, color: impl Into) -> Self + where + Theme::Class<'a>: From>, + { + self.color_maybe(Some(color)) + } + + /// Sets the [`Color`] of the [`Text`], if `Some`. + pub fn color_maybe(self, color: Option>) -> Self + where + Theme::Class<'a>: From>, + { + let color = color.map(Into::into); + + self.style(move |_theme| Style { color }) + } + + /// Sets the style class of the [`Text`]. + #[cfg(feature = "advanced")] + #[must_use] + pub fn class(mut self, class: impl Into>) -> Self { + self.class = class.into(); + self + } } /// The internal state of a [`Text`] widget. @@ -138,6 +154,7 @@ pub struct State(P); impl<'a, Message, Theme, Renderer> Widget for Text<'a, Theme, Renderer> where + Theme: Catalog, Renderer: text::Renderer, { fn tag(&self) -> tree::Tag { @@ -182,15 +199,15 @@ where tree: &Tree, renderer: &mut Renderer, theme: &Theme, - style: &renderer::Style, + defaults: &renderer::Style, layout: Layout<'_>, _cursor_position: mouse::Cursor, viewport: &Rectangle, ) { let state = tree.state.downcast_ref::>(); - let appearance = (self.style)(theme); + let style = theme.style(&self.class); - draw(renderer, style, layout, state, appearance, viewport); + draw(renderer, defaults, layout, state, style, viewport); } } @@ -250,7 +267,7 @@ pub fn draw( style: &renderer::Style, layout: Layout<'_>, state: &State, - appearance: Appearance, + appearance: Style, viewport: &Rectangle, ) where Renderer: text::Renderer, @@ -281,7 +298,7 @@ pub fn draw( impl<'a, Message, Theme, Renderer> From> for Element<'a, Message, Theme, Renderer> where - Theme: 'a, + Theme: Catalog + 'a, Renderer: text::Renderer + 'a, { fn from( @@ -293,7 +310,7 @@ where impl<'a, Theme, Renderer> From<&'a str> for Text<'a, Theme, Renderer> where - Theme: DefaultStyle + 'a, + Theme: Catalog + 'a, Renderer: text::Renderer, { fn from(content: &'a str) -> Self { @@ -304,7 +321,7 @@ where impl<'a, Message, Theme, Renderer> From<&'a str> for Element<'a, Message, Theme, Renderer> where - Theme: DefaultStyle + 'a, + Theme: Catalog + 'a, Renderer: text::Renderer + 'a, { fn from(content: &'a str) -> Self { @@ -314,30 +331,38 @@ where /// The appearance of some text. #[derive(Debug, Clone, Copy, Default)] -pub struct Appearance { +pub struct Style { /// The [`Color`] of the text. /// /// The default, `None`, means using the inherited color. pub color: Option, } -/// The style of some [`Text`]. -pub type Style<'a, Theme> = Box Appearance + 'a>; +/// The theme catalog of a [`Text`]. +pub trait Catalog: Sized { + /// The item class of this [`Catalog`]. + type Class<'a>; -/// The default style of some [`Text`]. -pub trait DefaultStyle { - /// Returns the default style of some [`Text`]. - fn default_style(&self) -> Appearance; + /// The default class produced by this [`Catalog`]. + fn default<'a>() -> Self::Class<'a>; + + /// The [`Style`] of a class with the given status. + fn style(&self, item: &Self::Class<'_>) -> Style; } -impl DefaultStyle for Theme { - fn default_style(&self) -> Appearance { - Appearance::default() +/// A styling function for a [`Text`]. +/// +/// This is just a boxed closure: `Fn(&Theme, Status) -> Style`. +pub type StyleFn<'a, Theme> = Box Style + 'a>; + +impl Catalog for Theme { + type Class<'a> = StyleFn<'a, Self>; + + fn default<'a>() -> Self::Class<'a> { + Box::new(|_theme| Style::default()) } -} -impl DefaultStyle for Color { - fn default_style(&self) -> Appearance { - Appearance { color: Some(*self) } + fn style(&self, class: &Self::Class<'_>) -> Style { + class(self) } } -- cgit From a8ceb9469bc54fdffb2a03d6e479c0cf074fe9f5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 26 Mar 2024 04:21:02 +0100 Subject: Propagate `advanced` feature to `iced_core` --- core/Cargo.toml | 1 + 1 file changed, 1 insertion(+) (limited to 'core') diff --git a/core/Cargo.toml b/core/Cargo.toml index 29a95ad7..32d233ee 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -12,6 +12,7 @@ keywords.workspace = true [features] auto-detect-theme = ["dep:dark-light"] +advanced = [] [dependencies] bitflags.workspace = true -- cgit