From 8ce16aba6204cb5c02a709cdf79c309f7b7e0196 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 Mar 2024 07:13:34 +0100 Subject: Fix redundant import in `window::redraw_request` --- core/src/window/redraw_request.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/window/redraw_request.rs b/core/src/window/redraw_request.rs index 8a59e83c..b0c000d6 100644 --- a/core/src/window/redraw_request.rs +++ b/core/src/window/redraw_request.rs @@ -13,7 +13,7 @@ pub enum RedrawRequest { #[cfg(test)] mod tests { use super::*; - use crate::time::{Duration, Instant}; + use crate::time::Duration; #[test] fn ordering() { -- cgit From bf93c82fac4519e5320f9caee8bde2fdc1c6e41a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 Mar 2024 05:41:56 +0100 Subject: Try to find an `#iced` element by default on Wasm --- core/src/window/settings/wasm.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/window/settings/wasm.rs b/core/src/window/settings/wasm.rs index 8e0f1bbc..30e60b6a 100644 --- a/core/src/window/settings/wasm.rs +++ b/core/src/window/settings/wasm.rs @@ -1,11 +1,21 @@ //! Platform specific settings for WebAssembly. /// The platform specific window settings of an application. -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct PlatformSpecific { /// The identifier of a DOM element that will be replaced with the /// application. /// /// If set to `None`, the application will be appended to the HTML body. + /// + /// By default, it is set to `"iced"`. pub target: Option, } + +impl Default for PlatformSpecific { + fn default() -> Self { + Self { + target: Some(String::from("iced")), + } + } +} -- cgit From 9db6ac8f202ebdc1453edee01da0b30aee0949d8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 Mar 2024 23:58:17 +0100 Subject: Introduce `auto-detect-theme` feature --- core/Cargo.toml | 6 ++++++ core/src/theme.rs | 25 +++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/Cargo.toml b/core/Cargo.toml index c273fcb4..29a95ad7 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -10,6 +10,9 @@ homepage.workspace = true categories.workspace = true keywords.workspace = true +[features] +auto-detect-theme = ["dep:dark-light"] + [dependencies] bitflags.workspace = true glam.workspace = true @@ -22,6 +25,9 @@ thiserror.workspace = true web-time.workspace = true xxhash-rust.workspace = true +dark-light.workspace = true +dark-light.optional = true + [target.'cfg(windows)'.dependencies] raw-window-handle.workspace = true diff --git a/core/src/theme.rs b/core/src/theme.rs index 948aaf83..6b2c04da 100644 --- a/core/src/theme.rs +++ b/core/src/theme.rs @@ -7,10 +7,9 @@ use std::fmt; use std::sync::Arc; /// A built-in theme. -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, PartialEq)] pub enum Theme { /// The built-in light variant. - #[default] Light, /// The built-in dark variant. Dark, @@ -161,6 +160,28 @@ impl Theme { } } +impl Default for Theme { + fn default() -> Self { + #[cfg(feature = "auto-detect-theme")] + { + use once_cell::sync::Lazy; + + static DEFAULT: Lazy = + Lazy::new(|| match dark_light::detect() { + dark_light::Mode::Dark => Theme::Dark, + dark_light::Mode::Light | dark_light::Mode::Default => { + Theme::Light + } + }); + + DEFAULT.clone() + } + + #[cfg(not(feature = "auto-detect-theme"))] + Theme::Light + } +} + impl fmt::Display for Theme { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { -- cgit 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 From faa53647cc83577e1ecb81a450c948b3fa4203e0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 30 Mar 2024 15:57:12 +0100 Subject: Replace `xxhash-rust` with `rustc-hash` --- core/Cargo.toml | 2 +- core/src/hasher.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/Cargo.toml b/core/Cargo.toml index 32d233ee..d3529d98 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,10 +21,10 @@ log.workspace = true num-traits.workspace = true once_cell.workspace = true palette.workspace = true +rustc-hash.workspace = true smol_str.workspace = true thiserror.workspace = true web-time.workspace = true -xxhash-rust.workspace = true dark-light.workspace = true dark-light.optional = true diff --git a/core/src/hasher.rs b/core/src/hasher.rs index a13d78af..13180e41 100644 --- a/core/src/hasher.rs +++ b/core/src/hasher.rs @@ -1,7 +1,7 @@ /// The hasher used to compare layouts. #[allow(missing_debug_implementations)] // Doesn't really make sense to have debug on the hasher state anyways. #[derive(Default)] -pub struct Hasher(xxhash_rust::xxh3::Xxh3); +pub struct Hasher(rustc_hash::FxHasher); impl core::hash::Hasher for Hasher { fn write(&mut self, bytes: &[u8]) { -- cgit From 6216c513d5e5853bf1d43342094e91a74981f4f2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 1 Apr 2024 11:30:01 +0200 Subject: Use generic `Content` in `Text` to avoid reallocation in `fill_text` --- core/src/renderer/null.rs | 6 +++--- core/src/text.rs | 6 +++--- core/src/text/paragraph.rs | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'core') diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index c26ce1a5..1caf71b3 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -67,7 +67,7 @@ impl text::Renderer for () { fn fill_text( &mut self, - _paragraph: Text<'_, Self::Font>, + _paragraph: Text, _position: Point, _color: Color, _clip_bounds: Rectangle, @@ -78,11 +78,11 @@ impl text::Renderer for () { impl text::Paragraph for () { type Font = Font; - fn with_text(_text: Text<'_, Self::Font>) -> Self {} + fn with_text(_text: Text<&str>) -> Self {} fn resize(&mut self, _new_bounds: Size) {} - fn compare(&self, _text: Text<'_, Self::Font>) -> text::Difference { + fn compare(&self, _text: Text<&str>) -> text::Difference { text::Difference::None } diff --git a/core/src/text.rs b/core/src/text.rs index edef79c2..3f1d2c77 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -16,9 +16,9 @@ use std::hash::{Hash, Hasher}; /// A paragraph. #[derive(Debug, Clone, Copy)] -pub struct Text<'a, Font> { +pub struct Text { /// The content of the paragraph. - pub content: &'a str, + pub content: Content, /// The bounds of the paragraph. pub bounds: Size, @@ -219,7 +219,7 @@ pub trait Renderer: crate::Renderer { /// [`Color`]. fn fill_text( &mut self, - text: Text<'_, Self::Font>, + text: Text, position: Point, color: Color, clip_bounds: Rectangle, diff --git a/core/src/text/paragraph.rs b/core/src/text/paragraph.rs index de1fb74d..8ff04015 100644 --- a/core/src/text/paragraph.rs +++ b/core/src/text/paragraph.rs @@ -8,14 +8,14 @@ pub trait Paragraph: Sized + Default { type Font: Copy + PartialEq; /// Creates a new [`Paragraph`] laid out with the given [`Text`]. - fn with_text(text: Text<'_, Self::Font>) -> Self; + fn with_text(text: Text<&str, Self::Font>) -> Self; /// Lays out the [`Paragraph`] with some new boundaries. fn resize(&mut self, new_bounds: Size); /// Compares the [`Paragraph`] with some desired [`Text`] and returns the /// [`Difference`]. - fn compare(&self, text: Text<'_, Self::Font>) -> Difference; + fn compare(&self, text: Text<&str, Self::Font>) -> Difference; /// Returns the horizontal alignment of the [`Paragraph`]. fn horizontal_alignment(&self) -> alignment::Horizontal; @@ -35,7 +35,7 @@ pub trait Paragraph: Sized + Default { fn grapheme_position(&self, line: usize, index: usize) -> Option; /// Updates the [`Paragraph`] to match the given [`Text`], if needed. - fn update(&mut self, text: Text<'_, Self::Font>) { + fn update(&mut self, text: Text<&str, Self::Font>) { match self.compare(text) { Difference::None => {} Difference::Bounds => { -- cgit From f5bcfec8211c04c4b05f63d01d52d3e5d2cc123e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 1 Apr 2024 11:59:46 +0200 Subject: Use `rustc-hash` for most of our `HashMap` and `HashSet` instances --- core/src/image.rs | 5 +++-- core/src/lib.rs | 2 -- core/src/svg.rs | 5 +++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'core') diff --git a/core/src/image.rs b/core/src/image.rs index 32b95f03..dc74e5c1 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -1,6 +1,7 @@ //! Load and draw raster graphics. -use crate::{Hasher, Rectangle, Size}; +use crate::{Rectangle, Size}; +use rustc_hash::FxHasher; use std::hash::{Hash, Hasher as _}; use std::path::PathBuf; use std::sync::Arc; @@ -50,7 +51,7 @@ impl Handle { } fn from_data(data: Data) -> Handle { - let mut hasher = Hasher::default(); + let mut hasher = FxHasher::default(); data.hash(&mut hasher); Handle { diff --git a/core/src/lib.rs b/core/src/lib.rs index d076413e..832b2d2d 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -41,7 +41,6 @@ mod background; mod color; mod content_fit; mod element; -mod hasher; mod length; mod padding; mod pixels; @@ -64,7 +63,6 @@ pub use element::Element; pub use event::Event; pub use font::Font; pub use gradient::Gradient; -pub use hasher::Hasher; pub use layout::Layout; pub use length::Length; pub use overlay::Overlay; diff --git a/core/src/svg.rs b/core/src/svg.rs index ab207cca..0106e0c2 100644 --- a/core/src/svg.rs +++ b/core/src/svg.rs @@ -1,6 +1,7 @@ //! Load and draw vector graphics. -use crate::{Color, Hasher, Rectangle, Size}; +use crate::{Color, Rectangle, Size}; +use rustc_hash::FxHasher; use std::borrow::Cow; use std::hash::{Hash, Hasher as _}; use std::path::PathBuf; @@ -30,7 +31,7 @@ impl Handle { } fn from_data(data: Data) -> Handle { - let mut hasher = Hasher::default(); + let mut hasher = FxHasher::default(); data.hash(&mut hasher); Handle { -- cgit From 1d83e59e8ab51d403baee0daa878644c6a37be3f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 1 Apr 2024 21:36:08 +0200 Subject: Specialize `widget::text` helper with custom `IntoContent` trait --- core/src/widget/text.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 12f6956a..e05eb0d9 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -21,7 +21,7 @@ where Theme: Catalog, Renderer: text::Renderer, { - content: Cow<'a, str>, + content: Content<'a>, size: Option, line_height: LineHeight, width: Length, @@ -39,9 +39,9 @@ where Renderer: text::Renderer, { /// Create a new fragment of [`Text`] with the given contents. - pub fn new(content: impl Into>) -> Self { + pub fn new(content: impl IntoContent<'a>) -> Self { Text { - content: content.into(), + content: content.into_content(), size: None, line_height: LineHeight::default(), font: None, @@ -366,3 +366,67 @@ impl Catalog for Theme { class(self) } } + +/// The content of a [`Text`] widget. +/// +/// This is just an alias to a string that may be either +/// borrowed or owned. +pub type Content<'a> = Cow<'a, str>; + +/// A trait for converting a value to some text [`Content`]. +pub trait IntoContent<'a> { + /// Converts the value to some text [`Content`]. + fn into_content(self) -> Content<'a>; +} + +impl<'a> IntoContent<'a> for &'a str { + fn into_content(self) -> Content<'a> { + Content::Borrowed(self) + } +} + +impl<'a> IntoContent<'a> for &'a String { + fn into_content(self) -> Content<'a> { + Content::Borrowed(self.as_str()) + } +} + +impl<'a> IntoContent<'a> for String { + fn into_content(self) -> Content<'a> { + Content::Owned(self) + } +} + +macro_rules! into_content { + ($type:ty) => { + impl<'a> IntoContent<'a> for $type { + fn into_content(self) -> Content<'a> { + Content::Owned(self.to_string()) + } + } + + impl<'a> IntoContent<'a> for &$type { + fn into_content(self) -> Content<'a> { + Content::Owned(self.to_string()) + } + } + }; +} + +into_content!(char); +into_content!(bool); + +into_content!(u8); +into_content!(u16); +into_content!(u32); +into_content!(u64); +into_content!(u128); + +into_content!(i8); +into_content!(i16); +into_content!(i32); +into_content!(i64); +into_content!(i128); + +into_content!(f32); +into_content!(f64); -- cgit From 34f799aa3dbb58c49708c1f1d9ee436922db636d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 1 Apr 2024 21:47:55 +0200 Subject: Rename `text::IntoContent` to `IntoFragment` --- core/src/widget/text.rs | 80 ++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) (limited to 'core') diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index e05eb0d9..123c6a69 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -21,7 +21,7 @@ where Theme: Catalog, Renderer: text::Renderer, { - content: Content<'a>, + fragment: Fragment<'a>, size: Option, line_height: LineHeight, width: Length, @@ -39,9 +39,9 @@ where Renderer: text::Renderer, { /// Create a new fragment of [`Text`] with the given contents. - pub fn new(content: impl IntoContent<'a>) -> Self { + pub fn new(fragment: impl IntoFragment<'a>) -> Self { Text { - content: content.into_content(), + fragment: fragment.into_fragment(), size: None, line_height: LineHeight::default(), font: None, @@ -184,7 +184,7 @@ where limits, self.width, self.height, - &self.content, + &self.fragment, self.line_height, self.size, self.font, @@ -367,66 +367,66 @@ impl Catalog for Theme { } } -/// The content of a [`Text`] widget. +/// A fragment of [`Text`]. /// /// This is just an alias to a string that may be either /// borrowed or owned. -pub type Content<'a> = Cow<'a, str>; +pub type Fragment<'a> = Cow<'a, str>; -/// A trait for converting a value to some text [`Content`]. -pub trait IntoContent<'a> { - /// Converts the value to some text [`Content`]. - fn into_content(self) -> Content<'a>; +/// A trait for converting a value to some text [`Fragment`]. +pub trait IntoFragment<'a> { + /// Converts the value to some text [`Fragment`]. + fn into_fragment(self) -> Fragment<'a>; } -impl<'a> IntoContent<'a> for &'a str { - fn into_content(self) -> Content<'a> { - Content::Borrowed(self) +impl<'a> IntoFragment<'a> for &'a str { + fn into_fragment(self) -> Fragment<'a> { + Fragment::Borrowed(self) } } -impl<'a> IntoContent<'a> for &'a String { - fn into_content(self) -> Content<'a> { - Content::Borrowed(self.as_str()) +impl<'a> IntoFragment<'a> for &'a String { + fn into_fragment(self) -> Fragment<'a> { + Fragment::Borrowed(self.as_str()) } } -impl<'a> IntoContent<'a> for String { - fn into_content(self) -> Content<'a> { - Content::Owned(self) +impl<'a> IntoFragment<'a> for String { + fn into_fragment(self) -> Fragment<'a> { + Fragment::Owned(self) } } -macro_rules! into_content { +macro_rules! into_fragment { ($type:ty) => { - impl<'a> IntoContent<'a> for $type { - fn into_content(self) -> Content<'a> { - Content::Owned(self.to_string()) + impl<'a> IntoFragment<'a> for $type { + fn into_fragment(self) -> Fragment<'a> { + Fragment::Owned(self.to_string()) } } - impl<'a> IntoContent<'a> for &$type { - fn into_content(self) -> Content<'a> { - Content::Owned(self.to_string()) + impl<'a> IntoFragment<'a> for &$type { + fn into_fragment(self) -> Fragment<'a> { + Fragment::Owned(self.to_string()) } } }; } -into_content!(char); -into_content!(bool); +into_fragment!(char); +into_fragment!(bool); -into_content!(u8); -into_content!(u16); -into_content!(u32); -into_content!(u64); -into_content!(u128); +into_fragment!(u8); +into_fragment!(u16); +into_fragment!(u32); +into_fragment!(u64); +into_fragment!(u128); -into_content!(i8); -into_content!(i16); -into_content!(i32); -into_content!(i64); -into_content!(i128); +into_fragment!(i8); +into_fragment!(i16); +into_fragment!(i32); +into_fragment!(i64); +into_fragment!(i128); -into_content!(f32); -into_content!(f64); +into_fragment!(f32); +into_fragment!(f64); -- cgit From dee43d5f66c97dddffebbc02c4a87674fb2c13b9 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 2 Apr 2024 09:24:22 +0200 Subject: Implement `IntoFragment` for `usize` and `isize` --- core/src/widget/text.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'core') diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 123c6a69..53591e41 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -421,12 +421,14 @@ into_fragment!(u16); into_fragment!(u32); into_fragment!(u64); into_fragment!(u128); +into_fragment!(usize); into_fragment!(i8); into_fragment!(i16); into_fragment!(i32); into_fragment!(i64); into_fragment!(i128); +into_fragment!(isize); into_fragment!(f32); into_fragment!(f64); -- cgit From 99a904112ca111f2ab0e60e30b6c369741b1653b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 2 Apr 2024 10:08:10 +0200 Subject: Implement `IntoFragment` for `Fragment` --- core/src/widget/text.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'core') diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 53591e41..f1f0b345 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -379,6 +379,18 @@ pub trait IntoFragment<'a> { fn into_fragment(self) -> Fragment<'a>; } +impl<'a> IntoFragment<'a> for Fragment<'a> { + fn into_fragment(self) -> Fragment<'a> { + self + } +} + +impl<'a, 'b> IntoFragment<'a> for &'a Fragment<'b> { + fn into_fragment(self) -> Fragment<'a> { + Fragment::Borrowed(self) + } +} + impl<'a> IntoFragment<'a> for &'a str { fn into_fragment(self) -> Fragment<'a> { Fragment::Borrowed(self) -- cgit From b05e61f5c8ae61c9f3c7cc08cded53901ebbccfd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 3 Apr 2024 21:07:54 +0200 Subject: Redesign `iced_wgpu` layering architecture --- core/src/rectangle.rs | 27 ++++++++++++++++----------- core/src/renderer.rs | 8 ++++---- core/src/renderer/null.rs | 4 ++-- core/src/size.rs | 14 ++++++++++++++ core/src/transformation.rs | 6 ++++++ 5 files changed, 42 insertions(+), 17 deletions(-) (limited to 'core') diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index c1c2eeac..45acd5ac 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -16,24 +16,29 @@ pub struct Rectangle { pub height: T, } -impl Rectangle { - /// Creates a new [`Rectangle`] with its top-left corner in the given - /// [`Point`] and with the provided [`Size`]. - pub fn new(top_left: Point, size: Size) -> Self { +impl Rectangle +where + T: Default, +{ + /// Creates a new [`Rectangle`] with its top-left corner at the origin + /// and with the provided [`Size`]. + pub fn with_size(size: Size) -> Self { Self { - x: top_left.x, - y: top_left.y, + x: T::default(), + y: T::default(), width: size.width, height: size.height, } } +} - /// Creates a new [`Rectangle`] with its top-left corner at the origin - /// and with the provided [`Size`]. - pub fn with_size(size: Size) -> Self { +impl Rectangle { + /// Creates a new [`Rectangle`] with its top-left corner in the given + /// [`Point`] and with the provided [`Size`]. + pub fn new(top_left: Point, size: Size) -> Self { Self { - x: 0.0, - y: 0.0, + x: top_left.x, + y: top_left.y, width: size.width, height: size.height, } diff --git a/core/src/renderer.rs b/core/src/renderer.rs index 6712314e..f5ef8f68 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -9,7 +9,7 @@ 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); + fn start_layer(&mut self, bounds: Rectangle); /// Ends recording a new layer. /// @@ -20,13 +20,13 @@ pub trait Renderer { /// /// The layer will clip its contents to the provided `bounds`. fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { - self.start_layer(); + self.start_layer(bounds); f(self); self.end_layer(bounds); } /// Starts recording with a new [`Transformation`]. - fn start_transformation(&mut self); + fn start_transformation(&mut self, transformation: Transformation); /// Ends recording a new layer. /// @@ -39,7 +39,7 @@ pub trait Renderer { transformation: Transformation, f: impl FnOnce(&mut Self), ) { - self.start_transformation(); + self.start_transformation(transformation); f(self); self.end_transformation(transformation); } diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 1caf71b3..f36d19aa 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -10,11 +10,11 @@ use crate::{ use std::borrow::Cow; impl Renderer for () { - fn start_layer(&mut self) {} + fn start_layer(&mut self, _bounds: Rectangle) {} fn end_layer(&mut self, _bounds: Rectangle) {} - fn start_transformation(&mut self) {} + fn start_transformation(&mut self, _transformation: Transformation) {} fn end_transformation(&mut self, _transformation: Transformation) {} diff --git a/core/src/size.rs b/core/src/size.rs index 55db759d..c2b5671a 100644 --- a/core/src/size.rs +++ b/core/src/size.rs @@ -99,3 +99,17 @@ where } } } + +impl std::ops::Mul for Size +where + T: std::ops::Mul + Copy, +{ + type Output = Size; + + fn mul(self, rhs: T) -> Self::Output { + Size { + width: self.width * rhs, + height: self.height * rhs, + } + } +} diff --git a/core/src/transformation.rs b/core/src/transformation.rs index b2c488b0..74183147 100644 --- a/core/src/transformation.rs +++ b/core/src/transformation.rs @@ -42,6 +42,12 @@ impl Transformation { } } +impl Default for Transformation { + fn default() -> Self { + Transformation::IDENTITY + } +} + impl Mul for Transformation { type Output = Self; -- cgit From 6d3e1d835e1688fbc58622a03a784ed25ed3f0e1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 5 Apr 2024 23:59:21 +0200 Subject: Decouple caching from layering and simplify everything --- core/src/rectangle.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index 45acd5ac..446d3769 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -33,9 +33,12 @@ where } impl Rectangle { + /// A rectangle starting at [`Point::ORIGIN`] with infinite width and height. + pub const INFINITE: Self = Self::new(Point::ORIGIN, Size::INFINITY); + /// Creates a new [`Rectangle`] with its top-left corner in the given /// [`Point`] and with the provided [`Size`]. - pub fn new(top_left: Point, size: Size) -> Self { + pub const fn new(top_left: Point, size: Size) -> Self { Self { x: top_left.x, y: top_left.y, -- cgit From c45c79b5d6e25f73dc76a816c08a7041ad971c66 Mon Sep 17 00:00:00 2001 From: David Huculak Date: Sun, 7 Apr 2024 02:20:44 -0400 Subject: add stronger guarantee of readability/contrast for palette background/text color pairs --- core/src/theme/palette.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/src/theme/palette.rs b/core/src/theme/palette.rs index ca91c248..91543567 100644 --- a/core/src/theme/palette.rs +++ b/core/src/theme/palette.rs @@ -612,11 +612,19 @@ fn mix(a: Color, b: Color, factor: f32) -> Color { fn readable(background: Color, text: Color) -> Color { if is_readable(background, text) { - text - } else if is_dark(background) { + return text; + } + + let fallback = if is_dark(background) { Color::WHITE } else { Color::BLACK + }; + + if is_readable(background, fallback) { + fallback + } else { + fallback.inverse() } } -- cgit From 5cd98f069dea8720bca7748d6c12fa410cbe79b5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 7 Apr 2024 12:42:12 +0200 Subject: Use built-in `[lints]` table in `Cargo.toml` --- core/Cargo.toml | 3 +++ core/src/lib.rs | 7 ------- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'core') diff --git a/core/Cargo.toml b/core/Cargo.toml index d3529d98..7bd37021 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -10,6 +10,9 @@ homepage.workspace = true categories.workspace = true keywords.workspace = true +[lints] +workspace = true + [features] auto-detect-theme = ["dep:dark-light"] advanced = [] diff --git a/core/src/lib.rs b/core/src/lib.rs index 832b2d2d..feda4fb4 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -9,13 +9,6 @@ #![doc( html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" )] -#![forbid(unsafe_code, rust_2018_idioms)] -#![deny( - missing_debug_implementations, - missing_docs, - unused_results, - rustdoc::broken_intra_doc_links -)] pub mod alignment; pub mod border; pub mod clipboard; -- cgit From efa75607baa073aba333ab0bfdee1e490a016097 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 7 Apr 2024 18:52:07 +0200 Subject: Revert "Merge pull request #2376 from Davidster/fix_palette_readable_color_contrast" This reverts commit 63042354fc51884098f88e240f73e689295df31c, reversing changes made to 31d1d5fecbef50fa319cabd5d4194f1e4aaefa21. --- core/src/theme/palette.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'core') diff --git a/core/src/theme/palette.rs b/core/src/theme/palette.rs index 91543567..ca91c248 100644 --- a/core/src/theme/palette.rs +++ b/core/src/theme/palette.rs @@ -612,19 +612,11 @@ fn mix(a: Color, b: Color, factor: f32) -> Color { fn readable(background: Color, text: Color) -> Color { if is_readable(background, text) { - return text; - } - - let fallback = if is_dark(background) { + text + } else if is_dark(background) { Color::WHITE } else { Color::BLACK - }; - - if is_readable(background, fallback) { - fallback - } else { - fallback.inverse() } } -- cgit From ee86aea7f298c0bdc72733b47c40270ff38c2ba6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 7 Apr 2024 19:32:49 +0200 Subject: Use `Lch` to choose white text when not readable in `theme::palette` --- core/src/theme/palette.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/src/theme/palette.rs b/core/src/theme/palette.rs index ca91c248..aca72eb0 100644 --- a/core/src/theme/palette.rs +++ b/core/src/theme/palette.rs @@ -4,7 +4,7 @@ use crate::{color, Color}; use once_cell::sync::Lazy; use palette::color_difference::Wcag21RelativeContrast; use palette::rgb::Rgb; -use palette::{FromColor, Hsl, Mix}; +use palette::{FromColor, Hsl, Lch, Mix}; /// A color palette. #[derive(Debug, Clone, Copy, PartialEq)] @@ -613,7 +613,7 @@ fn mix(a: Color, b: Color, factor: f32) -> Color { fn readable(background: Color, text: Color) -> Color { if is_readable(background, text) { text - } else if is_dark(background) { + } else if to_lch(background).l < 70.0 { Color::WHITE } else { Color::BLACK @@ -635,6 +635,10 @@ fn to_hsl(color: Color) -> Hsl { Hsl::from_color(Rgb::from(color)) } +fn to_lch(color: Color) -> Lch { + Lch::from_color(Rgb::from(color)) +} + fn from_hsl(hsl: Hsl) -> Color { Rgb::from_color(hsl).into() } -- cgit From 72b975ec82660b39f27b6cb015b763caf20e6483 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 7 Apr 2024 19:37:35 +0200 Subject: Pick best contrast between black/white in `theme::palette` --- core/src/theme/palette.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'core') diff --git a/core/src/theme/palette.rs b/core/src/theme/palette.rs index aca72eb0..e0ff397a 100644 --- a/core/src/theme/palette.rs +++ b/core/src/theme/palette.rs @@ -4,7 +4,7 @@ use crate::{color, Color}; use once_cell::sync::Lazy; use palette::color_difference::Wcag21RelativeContrast; use palette::rgb::Rgb; -use palette::{FromColor, Hsl, Lch, Mix}; +use palette::{FromColor, Hsl, Mix}; /// A color palette. #[derive(Debug, Clone, Copy, PartialEq)] @@ -613,10 +613,15 @@ fn mix(a: Color, b: Color, factor: f32) -> Color { fn readable(background: Color, text: Color) -> Color { if is_readable(background, text) { text - } else if to_lch(background).l < 70.0 { - Color::WHITE } else { - Color::BLACK + let white_contrast = relative_contrast(background, Color::WHITE); + let black_contrast = relative_contrast(background, Color::BLACK); + + if white_contrast >= black_contrast { + Color::WHITE + } else { + Color::BLACK + } } } @@ -631,12 +636,15 @@ fn is_readable(a: Color, b: Color) -> bool { a_srgb.has_enhanced_contrast_text(b_srgb) } -fn to_hsl(color: Color) -> Hsl { - Hsl::from_color(Rgb::from(color)) +fn relative_contrast(a: Color, b: Color) -> f32 { + let a_srgb = Rgb::from(a); + let b_srgb = Rgb::from(b); + + a_srgb.relative_contrast(b_srgb) } -fn to_lch(color: Color) -> Lch { - Lch::from_color(Rgb::from(color)) +fn to_hsl(color: Color) -> Hsl { + Hsl::from_color(Rgb::from(color)) } fn from_hsl(hsl: Hsl) -> Color { -- cgit From d922b478156488a7bc03c6e791e05c040d702634 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 8 Apr 2024 15:04:35 +0200 Subject: Reintroduce support for custom primitives in `iced_wgpu` --- core/src/rectangle.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'core') diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index 446d3769..2ab50137 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -147,13 +147,20 @@ impl Rectangle { } /// Snaps the [`Rectangle`] to __unsigned__ integer coordinates. - pub fn snap(self) -> Rectangle { - Rectangle { + pub fn snap(self) -> Option> { + let width = self.width as u32; + let height = self.height as u32; + + if width < 1 || height < 1 { + return None; + } + + Some(Rectangle { x: self.x as u32, y: self.y as u32, - width: self.width as u32, - height: self.height as u32, - } + width, + height, + }) } /// Expands the [`Rectangle`] a given amount. -- cgit From 6ad5bb3597f640ac329801adf735d633bf0a512f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 9 Apr 2024 22:25:16 +0200 Subject: Port `iced_tiny_skia` to new layering architecture --- core/src/renderer.rs | 8 ++++---- core/src/renderer/null.rs | 8 ++------ core/src/text.rs | 4 ---- 3 files changed, 6 insertions(+), 14 deletions(-) (limited to 'core') diff --git a/core/src/renderer.rs b/core/src/renderer.rs index f5ef8f68..a2785ae8 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -14,7 +14,7 @@ pub trait Renderer { /// Ends recording a new layer. /// /// The new layer will clip its contents to the provided `bounds`. - fn end_layer(&mut self, bounds: Rectangle); + fn end_layer(&mut self); /// Draws the primitives recorded in the given closure in a new layer. /// @@ -22,7 +22,7 @@ pub trait Renderer { fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { self.start_layer(bounds); f(self); - self.end_layer(bounds); + self.end_layer(); } /// Starts recording with a new [`Transformation`]. @@ -31,7 +31,7 @@ pub trait Renderer { /// Ends recording a new layer. /// /// The new layer will clip its contents to the provided `bounds`. - fn end_transformation(&mut self, transformation: Transformation); + fn end_transformation(&mut self); /// Applies a [`Transformation`] to the primitives recorded in the given closure. fn with_transformation( @@ -41,7 +41,7 @@ pub trait Renderer { ) { self.start_transformation(transformation); f(self); - self.end_transformation(transformation); + self.end_transformation(); } /// Applies a translation to the primitives recorded in the given closure. diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index f36d19aa..fe38ec87 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -7,16 +7,14 @@ use crate::{ Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, }; -use std::borrow::Cow; - impl Renderer for () { fn start_layer(&mut self, _bounds: Rectangle) {} - fn end_layer(&mut self, _bounds: Rectangle) {} + fn end_layer(&mut self) {} fn start_transformation(&mut self, _transformation: Transformation) {} - fn end_transformation(&mut self, _transformation: Transformation) {} + fn end_transformation(&mut self) {} fn clear(&mut self) {} @@ -45,8 +43,6 @@ impl text::Renderer for () { Pixels(16.0) } - fn load_font(&mut self, _font: Cow<'static, [u8]>) {} - fn fill_paragraph( &mut self, _paragraph: &Self::Paragraph, diff --git a/core/src/text.rs b/core/src/text.rs index 3f1d2c77..b30feae0 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -11,7 +11,6 @@ pub use paragraph::Paragraph; use crate::alignment; use crate::{Color, Pixels, Point, Rectangle, Size}; -use std::borrow::Cow; use std::hash::{Hash, Hasher}; /// A paragraph. @@ -192,9 +191,6 @@ pub trait Renderer: crate::Renderer { /// Returns the default size of [`Text`]. fn default_size(&self) -> Pixels; - /// Loads a [`Self::Font`] from its bytes. - fn load_font(&mut self, font: Cow<'static, [u8]>); - /// Draws the given [`Paragraph`] at the given position and with the given /// [`Color`]. fn fill_paragraph( -- cgit From 0c74d2645649a88799a894ed684a728d135043fa Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 25 Apr 2024 01:39:34 +0200 Subject: Implement `Stack` widget It can be used to stack elements on top of each other! --- core/src/layout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/layout.rs b/core/src/layout.rs index 95720aba..98d05602 100644 --- a/core/src/layout.rs +++ b/core/src/layout.rs @@ -54,7 +54,7 @@ impl<'a> Layout<'a> { } /// Returns an iterator over the [`Layout`] of the children of a [`Node`]. - pub fn children(self) -> impl Iterator> { + pub fn children(self) -> impl DoubleEndedIterator> { self.node.children().iter().map(move |node| { Layout::with_offset( Vector::new(self.position.x, self.position.y), -- cgit From 4cd45643d7d2aa83212162f17a9b28ddae4a9340 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 25 Apr 2024 06:05:00 +0200 Subject: Introduce `opaque` widget helper --- core/src/mouse/interaction.rs | 1 + core/src/overlay.rs | 2 +- core/src/widget.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/src/mouse/interaction.rs b/core/src/mouse/interaction.rs index 6ad66229..065eb8e7 100644 --- a/core/src/mouse/interaction.rs +++ b/core/src/mouse/interaction.rs @@ -3,6 +3,7 @@ #[allow(missing_docs)] pub enum Interaction { #[default] + None, Idle, Pointer, Grab, diff --git a/core/src/overlay.rs b/core/src/overlay.rs index 03076a30..3a57fe16 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -79,7 +79,7 @@ where _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { - mouse::Interaction::Idle + mouse::Interaction::None } /// Returns true if the cursor is over the [`Overlay`]. diff --git a/core/src/widget.rs b/core/src/widget.rs index 58a9f19b..b02e3a4f 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -137,7 +137,7 @@ where _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { - mouse::Interaction::Idle + mouse::Interaction::None } /// Returns the overlay of the [`Widget`], if there is any. -- cgit From 8d27af24a76d9792e22b3380e11b846fd5533805 Mon Sep 17 00:00:00 2001 From: Bajix Date: Wed, 27 Mar 2024 12:54:01 -0700 Subject: Utilize bytes::Bytes for images --- core/Cargo.toml | 1 + core/src/image.rs | 62 ++++++------------------------------------------------- 2 files changed, 7 insertions(+), 56 deletions(-) (limited to 'core') diff --git a/core/Cargo.toml b/core/Cargo.toml index 7bd37021..3c557bca 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -19,6 +19,7 @@ advanced = [] [dependencies] bitflags.workspace = true +bytes.workspace = true glam.workspace = true log.workspace = true num-traits.workspace = true diff --git a/core/src/image.rs b/core/src/image.rs index dc74e5c1..7316ede7 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -1,10 +1,11 @@ //! Load and draw raster graphics. +pub use bytes::Bytes; + use crate::{Rectangle, Size}; use rustc_hash::FxHasher; use std::hash::{Hash, Hasher as _}; use std::path::PathBuf; -use std::sync::Arc; /// A handle of some image data. #[derive(Debug, Clone, PartialEq, Eq)] @@ -29,12 +30,12 @@ impl Handle { pub fn from_pixels( width: u32, height: u32, - pixels: impl AsRef<[u8]> + Send + Sync + 'static, + pixels: impl Into, ) -> Handle { Self::from_data(Data::Rgba { width, height, - pixels: Bytes::new(pixels), + pixels: pixels.into(), }) } @@ -44,10 +45,8 @@ impl Handle { /// /// This is useful if you already have your image loaded in-memory, maybe /// because you downloaded or generated it procedurally. - pub fn from_memory( - bytes: impl AsRef<[u8]> + Send + Sync + 'static, - ) -> Handle { - Self::from_data(Data::Bytes(Bytes::new(bytes))) + pub fn from_memory(bytes: impl Into) -> Handle { + Self::from_data(Data::Bytes(bytes.into())) } fn from_data(data: Data) -> Handle { @@ -86,55 +85,6 @@ impl Hash for Handle { } } -/// A wrapper around raw image data. -/// -/// It behaves like a `&[u8]`. -#[derive(Clone)] -pub struct Bytes(Arc + Send + Sync + 'static>); - -impl Bytes { - /// Creates new [`Bytes`] around `data`. - pub fn new(data: impl AsRef<[u8]> + Send + Sync + 'static) -> Self { - Self(Arc::new(data)) - } -} - -impl std::fmt::Debug for Bytes { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.as_ref().as_ref().fmt(f) - } -} - -impl std::hash::Hash for Bytes { - fn hash(&self, state: &mut H) { - self.0.as_ref().as_ref().hash(state); - } -} - -impl PartialEq for Bytes { - fn eq(&self, other: &Self) -> bool { - let a = self.as_ref(); - let b = other.as_ref(); - core::ptr::eq(a, b) || a == b - } -} - -impl Eq for Bytes {} - -impl AsRef<[u8]> for Bytes { - fn as_ref(&self) -> &[u8] { - self.0.as_ref().as_ref() - } -} - -impl std::ops::Deref for Bytes { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - self.0.as_ref().as_ref() - } -} - /// The data of a raster image. #[derive(Clone, PartialEq, Eq, Hash)] pub enum Data { -- cgit From 45254ab88c6ca76759523069c2fb8734de626f02 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 May 2024 00:55:49 +0200 Subject: Use `Bytes` as the `Container` of `ImageBuffer` Since we don't need to mutate images once loaded, we avoid unnecessary extra allocations. --- core/src/image.rs | 91 +++++++++++++++++++++++-------------------------------- 1 file changed, 38 insertions(+), 53 deletions(-) (limited to 'core') diff --git a/core/src/image.rs b/core/src/image.rs index 7316ede7..5b31fbcf 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -4,14 +4,27 @@ pub use bytes::Bytes; use crate::{Rectangle, Size}; use rustc_hash::FxHasher; -use std::hash::{Hash, Hasher as _}; +use std::hash::{Hash, Hasher}; use std::path::PathBuf; /// A handle of some image data. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Handle { - id: u64, - data: Data, +#[derive(Clone, PartialEq, Eq)] +pub enum Handle { + /// File data + Path(PathBuf), + + /// In-memory data + Bytes(Bytes), + + /// Decoded image pixels in RGBA format. + Rgba { + /// The width of the image. + width: u32, + /// The height of the image. + height: u32, + /// The pixels. + pixels: Bytes, + }, } impl Handle { @@ -19,7 +32,7 @@ impl Handle { /// /// Makes an educated guess about the image format by examining the data in the file. pub fn from_path>(path: T) -> Handle { - Self::from_data(Data::Path(path.into())) + Self::Path(path.into()) } /// Creates an image [`Handle`] containing the image pixels directly. This @@ -32,11 +45,11 @@ impl Handle { height: u32, pixels: impl Into, ) -> Handle { - Self::from_data(Data::Rgba { + Self::Rgba { width, height, pixels: pixels.into(), - }) + } } /// Creates an image [`Handle`] containing the image data directly. @@ -46,27 +59,25 @@ impl Handle { /// This is useful if you already have your image loaded in-memory, maybe /// because you downloaded or generated it procedurally. pub fn from_memory(bytes: impl Into) -> Handle { - Self::from_data(Data::Bytes(bytes.into())) - } - - fn from_data(data: Data) -> Handle { - let mut hasher = FxHasher::default(); - data.hash(&mut hasher); - - Handle { - id: hasher.finish(), - data, - } + Self::Bytes(bytes.into()) } /// Returns the unique identifier of the [`Handle`]. pub fn id(&self) -> u64 { - self.id + let mut hasher = FxHasher::default(); + self.hash(&mut hasher); + + hasher.finish() } +} - /// Returns a reference to the image [`Data`]. - pub fn data(&self) -> &Data { - &self.data +impl Hash for Handle { + fn hash(&self, state: &mut H) { + match self { + Self::Path(path) => path.hash(state), + Self::Bytes(bytes) => bytes.as_ptr().hash(state), + Self::Rgba { pixels, .. } => pixels.as_ptr().hash(state), + } } } @@ -79,38 +90,12 @@ where } } -impl Hash for Handle { - fn hash(&self, state: &mut H) { - self.id.hash(state); - } -} - -/// The data of a raster image. -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum Data { - /// File data - Path(PathBuf), - - /// In-memory data - Bytes(Bytes), - - /// Decoded image pixels in RGBA format. - Rgba { - /// The width of the image. - width: u32, - /// The height of the image. - height: u32, - /// The pixels. - pixels: Bytes, - }, -} - -impl std::fmt::Debug for Data { +impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Data::Path(path) => write!(f, "Path({path:?})"), - Data::Bytes(_) => write!(f, "Bytes(...)"), - Data::Rgba { width, height, .. } => { + Self::Path(path) => write!(f, "Path({path:?})"), + Self::Bytes(_) => write!(f, "Bytes(...)"), + Self::Rgba { width, height, .. } => { write!(f, "Pixels({width} * {height})") } } -- cgit From b52c7bb610f593fffc624d461dca17ac50c81626 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 May 2024 01:39:43 +0200 Subject: Use an opaque `Id` type for `image::Handle` Hashing pointers is a terrible idea. --- core/src/image.rs | 68 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 21 deletions(-) (limited to 'core') diff --git a/core/src/image.rs b/core/src/image.rs index 5b31fbcf..a0e40787 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -5,19 +5,21 @@ use crate::{Rectangle, Size}; use rustc_hash::FxHasher; use std::hash::{Hash, Hasher}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; /// A handle of some image data. #[derive(Clone, PartialEq, Eq)] pub enum Handle { /// File data - Path(PathBuf), + Path(Id, PathBuf), /// In-memory data - Bytes(Bytes), + Bytes(Id, Bytes), /// Decoded image pixels in RGBA format. Rgba { + /// The id of this handle. + id: Id, /// The width of the image. width: u32, /// The height of the image. @@ -32,7 +34,9 @@ impl Handle { /// /// Makes an educated guess about the image format by examining the data in the file. pub fn from_path>(path: T) -> Handle { - Self::Path(path.into()) + let path = path.into(); + + Self::Path(Id::path(&path), path) } /// Creates an image [`Handle`] containing the image pixels directly. This @@ -46,6 +50,7 @@ impl Handle { pixels: impl Into, ) -> Handle { Self::Rgba { + id: Id::unique(), width, height, pixels: pixels.into(), @@ -59,24 +64,15 @@ impl Handle { /// This is useful if you already have your image loaded in-memory, maybe /// because you downloaded or generated it procedurally. pub fn from_memory(bytes: impl Into) -> Handle { - Self::Bytes(bytes.into()) + Self::Bytes(Id::unique(), bytes.into()) } /// Returns the unique identifier of the [`Handle`]. - pub fn id(&self) -> u64 { - let mut hasher = FxHasher::default(); - self.hash(&mut hasher); - - hasher.finish() - } -} - -impl Hash for Handle { - fn hash(&self, state: &mut H) { + pub fn id(&self) -> Id { match self { - Self::Path(path) => path.hash(state), - Self::Bytes(bytes) => bytes.as_ptr().hash(state), - Self::Rgba { pixels, .. } => pixels.as_ptr().hash(state), + Handle::Path(id, _) + | Handle::Bytes(id, _) + | Handle::Rgba { id, .. } => *id, } } } @@ -93,8 +89,8 @@ where impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Path(path) => write!(f, "Path({path:?})"), - Self::Bytes(_) => write!(f, "Bytes(...)"), + Self::Path(_, path) => write!(f, "Path({path:?})"), + Self::Bytes(_, _) => write!(f, "Bytes(...)"), Self::Rgba { width, height, .. } => { write!(f, "Pixels({width} * {height})") } @@ -102,6 +98,36 @@ impl std::fmt::Debug for Handle { } } +/// The unique identifier of some [`Handle`] data. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Id { + /// A unique identifier. + Unique(u64), + /// A hash identifier. + Hash(u64), +} + +impl Id { + fn unique() -> Self { + use std::sync::atomic::{self, AtomicU64}; + + static NEXT_ID: AtomicU64 = AtomicU64::new(0); + + Self::Unique(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)) + } + + fn path(path: impl AsRef) -> Self { + let hash = { + let mut hasher = FxHasher::default(); + path.as_ref().hash(&mut hasher); + + hasher.finish() + }; + + Self::Hash(hash) + } +} + /// Image filtering strategy. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub enum FilterMethod { @@ -119,7 +145,7 @@ pub trait Renderer: crate::Renderer { /// The image Handle to be displayed. Iced exposes its own default implementation of a [`Handle`] /// /// [`Handle`]: Self::Handle - type Handle: Clone + Hash; + type Handle: Clone; /// Returns the dimensions of an image for the given [`Handle`]. fn measure_image(&self, handle: &Self::Handle) -> Size; -- cgit From 58ea914ad21ea9c5ae7b7b1c167ed084c9ddb07a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 May 2024 01:52:49 +0200 Subject: Make `image::Id` actually opaque --- core/src/image.rs | 60 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 22 deletions(-) (limited to 'core') diff --git a/core/src/image.rs b/core/src/image.rs index a0e40787..c44ccc30 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -10,13 +10,26 @@ use std::path::{Path, PathBuf}; /// A handle of some image data. #[derive(Clone, PartialEq, Eq)] pub enum Handle { - /// File data + /// A file handle. The image data will be read + /// from the file path. + /// + /// Use [`from_path`] to create this variant. + /// + /// [`from_path`]: Self::from_path Path(Id, PathBuf), - /// In-memory data + /// A handle pointing to some encoded image bytes in-memory. + /// + /// Use [`from_bytes`] to create this variant. + /// + /// [`from_bytes`]: Self::from_bytes Bytes(Id, Bytes), - /// Decoded image pixels in RGBA format. + /// A handle pointing to decoded image pixels in RGBA format. + /// + /// Use [`from_rgba`] to create this variant. + /// + /// [`from_rgba`]: Self::from_bytes Rgba { /// The id of this handle. id: Id, @@ -39,12 +52,24 @@ impl Handle { Self::Path(Id::path(&path), path) } - /// Creates an image [`Handle`] containing the image pixels directly. This - /// function expects the input data to be provided as a `Vec` of RGBA - /// pixels. + /// Creates an image [`Handle`] containing the encoded image data directly. + /// + /// Makes an educated guess about the image format by examining the given data. + /// + /// This is useful if you already have your image loaded in-memory, maybe + /// because you downloaded or generated it procedurally. + pub fn from_bytes(bytes: impl Into) -> Handle { + Self::Bytes(Id::unique(), bytes.into()) + } + + /// Creates an image [`Handle`] containing the decoded image pixels directly. + /// + /// This function expects the pixel data to be provided as a collection of [`Bytes`] + /// of RGBA pixels. Therefore, the length of the pixel data should always be + /// `width * height * 4`. /// /// This is useful if you have already decoded your image. - pub fn from_pixels( + pub fn from_rgba( width: u32, height: u32, pixels: impl Into, @@ -57,16 +82,6 @@ impl Handle { } } - /// Creates an image [`Handle`] containing the image data directly. - /// - /// Makes an educated guess about the image format by examining the given data. - /// - /// This is useful if you already have your image loaded in-memory, maybe - /// because you downloaded or generated it procedurally. - pub fn from_memory(bytes: impl Into) -> Handle { - Self::Bytes(Id::unique(), bytes.into()) - } - /// Returns the unique identifier of the [`Handle`]. pub fn id(&self) -> Id { match self { @@ -100,10 +115,11 @@ impl std::fmt::Debug for Handle { /// The unique identifier of some [`Handle`] data. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Id { - /// A unique identifier. +pub struct Id(_Id); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum _Id { Unique(u64), - /// A hash identifier. Hash(u64), } @@ -113,7 +129,7 @@ impl Id { static NEXT_ID: AtomicU64 = AtomicU64::new(0); - Self::Unique(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)) + Self(_Id::Unique(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed))) } fn path(path: impl AsRef) -> Self { @@ -124,7 +140,7 @@ impl Id { hasher.finish() }; - Self::Hash(hash) + Self(_Id::Hash(hash)) } } -- cgit From 01b014c19fa2a3c200fb2077e31822f525f729cf Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 May 2024 01:53:25 +0200 Subject: Fix documentation link in `image::Handle` --- core/src/image.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/image.rs b/core/src/image.rs index c44ccc30..c38239bc 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -29,7 +29,7 @@ pub enum Handle { /// /// Use [`from_rgba`] to create this variant. /// - /// [`from_rgba`]: Self::from_bytes + /// [`from_rgba`]: Self::from_rgba Rgba { /// The id of this handle. id: Id, -- cgit From 09a6bcfffc24f5abdc8709403bab7ae1e01563f1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 13:15:17 +0200 Subject: Add `Image` rotation support Co-authored-by: DKolter <68352124+DKolter@users.noreply.github.com> --- core/src/image.rs | 2 ++ core/src/lib.rs | 2 ++ core/src/renderer/null.rs | 4 ++++ core/src/rotation.rs | 37 +++++++++++++++++++++++++++++++++++++ core/src/svg.rs | 2 ++ 5 files changed, 47 insertions(+) create mode 100644 core/src/rotation.rs (limited to 'core') diff --git a/core/src/image.rs b/core/src/image.rs index c38239bc..5d1ab441 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -173,5 +173,7 @@ pub trait Renderer: crate::Renderer { handle: Self::Handle, filter_method: FilterMethod, bounds: Rectangle, + rotation: f32, + scale: Size, ); } diff --git a/core/src/lib.rs b/core/src/lib.rs index feda4fb4..da3ddcac 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -39,6 +39,7 @@ mod padding; mod pixels; mod point; mod rectangle; +mod rotation; mod shadow; mod shell; mod size; @@ -64,6 +65,7 @@ pub use pixels::Pixels; pub use point::Point; pub use rectangle::Rectangle; pub use renderer::Renderer; +pub use rotation::RotationLayout; pub use shadow::Shadow; pub use shell::Shell; pub use size::Size; diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index fe38ec87..d2dcfe4d 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -171,6 +171,8 @@ impl image::Renderer for () { _handle: Self::Handle, _filter_method: image::FilterMethod, _bounds: Rectangle, + _rotation: f32, + _scale: Size, ) { } } @@ -185,6 +187,8 @@ impl svg::Renderer for () { _handle: svg::Handle, _color: Option, _bounds: Rectangle, + _rotation: f32, + _scale: Size, ) { } } diff --git a/core/src/rotation.rs b/core/src/rotation.rs new file mode 100644 index 00000000..821aa494 --- /dev/null +++ b/core/src/rotation.rs @@ -0,0 +1,37 @@ +//! Control the rotation of some content (like an image) with the `RotationLayout` within a +//! space. +use crate::Size; + +/// The strategy used to rotate the content. +/// +/// This is used to control the behavior of the layout when the content is rotated +/// by a certain angle. +#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)] +pub enum RotationLayout { + /// The layout is kept exactly as it was before the rotation. + /// + /// This is especially useful when used for animations, as it will avoid the + /// layout being shifted or resized when smoothly i.e. an icon. + Keep, + /// The layout is adjusted to fit the rotated content. + /// + /// This allows you to rotate an image and have the layout adjust to fit the new + /// size of the image. + Change, +} + +impl RotationLayout { + /// Applies the rotation to the layout while respecting the [`RotationLayout`] strategy. + /// The rotation is given in radians. + pub fn apply_to_size(&self, size: Size, rotation: f32) -> Size { + match self { + Self::Keep => size, + Self::Change => Size { + width: (size.width * rotation.cos()).abs() + + (size.height * rotation.sin()).abs(), + height: (size.width * rotation.sin()).abs() + + (size.height * rotation.cos()).abs(), + }, + } + } +} diff --git a/core/src/svg.rs b/core/src/svg.rs index 0106e0c2..74dd7f4a 100644 --- a/core/src/svg.rs +++ b/core/src/svg.rs @@ -100,5 +100,7 @@ pub trait Renderer: crate::Renderer { handle: Handle, color: Option, bounds: Rectangle, + rotation: f32, + scale: Size, ); } -- cgit From a57313b23ecb9843856ca0ea08635b6121fcb2cb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 15:21:22 +0200 Subject: Simplify image rotation API and its internals --- core/src/angle.rs | 6 ++++ core/src/content_fit.rs | 3 +- core/src/image.rs | 5 ++-- core/src/lib.rs | 2 +- core/src/rectangle.rs | 16 +++++++++++ core/src/renderer/null.rs | 9 +++--- core/src/rotation.rs | 71 ++++++++++++++++++++++++++++++++++------------- core/src/size.rs | 14 ++++++++++ core/src/svg.rs | 5 ++-- core/src/vector.rs | 3 ++ 10 files changed, 101 insertions(+), 33 deletions(-) (limited to 'core') diff --git a/core/src/angle.rs b/core/src/angle.rs index dc3c0e93..69630717 100644 --- a/core/src/angle.rs +++ b/core/src/angle.rs @@ -65,6 +65,12 @@ impl From for Radians { } } +impl From for f32 { + fn from(radians: Radians) -> Self { + radians.0 + } +} + impl From for f64 { fn from(radians: Radians) -> Self { Self::from(radians.0) diff --git a/core/src/content_fit.rs b/core/src/content_fit.rs index 6bbedc7a..56d2ffa6 100644 --- a/core/src/content_fit.rs +++ b/core/src/content_fit.rs @@ -11,7 +11,7 @@ use crate::Size; /// in CSS, see [Mozilla's docs][1], or run the `tour` example /// /// [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit -#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, Default)] pub enum ContentFit { /// Scale as big as it can be without needing to crop or hide parts. /// @@ -23,6 +23,7 @@ pub enum ContentFit { /// This is a great fit for when you need to display an image without losing /// any part of it, particularly when the image itself is the focus of the /// screen. + #[default] Contain, /// Scale the image to cover all of the bounding box, cropping if needed. diff --git a/core/src/image.rs b/core/src/image.rs index 5d1ab441..91a7fd36 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -1,7 +1,7 @@ //! Load and draw raster graphics. pub use bytes::Bytes; -use crate::{Rectangle, Size}; +use crate::{Radians, Rectangle, Size}; use rustc_hash::FxHasher; use std::hash::{Hash, Hasher}; @@ -173,7 +173,6 @@ pub trait Renderer: crate::Renderer { handle: Self::Handle, filter_method: FilterMethod, bounds: Rectangle, - rotation: f32, - scale: Size, + rotation: Radians, ); } diff --git a/core/src/lib.rs b/core/src/lib.rs index da3ddcac..32156441 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -65,7 +65,7 @@ pub use pixels::Pixels; pub use point::Point; pub use rectangle::Rectangle; pub use renderer::Renderer; -pub use rotation::RotationLayout; +pub use rotation::Rotation; pub use shadow::Shadow; pub use shell::Shell; pub use size::Size; diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index 2ab50137..fb66131a 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -227,3 +227,19 @@ where } } } + +impl std::ops::Mul> for Rectangle +where + T: std::ops::Mul + Copy, +{ + type Output = Rectangle; + + fn mul(self, scale: Vector) -> Self { + Rectangle { + x: self.x * scale.x, + y: self.y * scale.y, + width: self.width * scale.x, + height: self.height * scale.y, + } + } +} diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index d2dcfe4d..91519b40 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -4,7 +4,8 @@ use crate::renderer::{self, Renderer}; use crate::svg; use crate::text::{self, Text}; use crate::{ - Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, + Background, Color, Font, Pixels, Point, Radians, Rectangle, Size, + Transformation, }; impl Renderer for () { @@ -171,8 +172,7 @@ impl image::Renderer for () { _handle: Self::Handle, _filter_method: image::FilterMethod, _bounds: Rectangle, - _rotation: f32, - _scale: Size, + _rotation: Radians, ) { } } @@ -187,8 +187,7 @@ impl svg::Renderer for () { _handle: svg::Handle, _color: Option, _bounds: Rectangle, - _rotation: f32, - _scale: Size, + _rotation: Radians, ) { } } diff --git a/core/src/rotation.rs b/core/src/rotation.rs index 821aa494..ebb85f3c 100644 --- a/core/src/rotation.rs +++ b/core/src/rotation.rs @@ -1,37 +1,68 @@ -//! Control the rotation of some content (like an image) with the `RotationLayout` within a -//! space. -use crate::Size; +//! Control the rotation of some content (like an image) within a space. +use crate::{Radians, Size}; /// The strategy used to rotate the content. /// /// This is used to control the behavior of the layout when the content is rotated /// by a certain angle. -#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)] -pub enum RotationLayout { - /// The layout is kept exactly as it was before the rotation. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Rotation { + /// The element will float while rotating. The layout will be kept exactly as it was + /// before the rotation. /// /// This is especially useful when used for animations, as it will avoid the /// layout being shifted or resized when smoothly i.e. an icon. - Keep, - /// The layout is adjusted to fit the rotated content. + /// + /// This is the default. + Floating(Radians), + /// The element will be solid while rotating. The layout will be adjusted to fit + /// the rotated content. /// /// This allows you to rotate an image and have the layout adjust to fit the new /// size of the image. - Change, + Solid(Radians), } -impl RotationLayout { - /// Applies the rotation to the layout while respecting the [`RotationLayout`] strategy. - /// The rotation is given in radians. - pub fn apply_to_size(&self, size: Size, rotation: f32) -> Size { +impl Rotation { + /// Returns the angle of the [`Rotation`] in [`Radians`]. + pub fn radians(self) -> Radians { + match self { + Rotation::Floating(radians) | Rotation::Solid(radians) => radians, + } + } + + /// Rotates the given [`Size`]. + pub fn apply(self, size: Size) -> Size { match self { - Self::Keep => size, - Self::Change => Size { - width: (size.width * rotation.cos()).abs() - + (size.height * rotation.sin()).abs(), - height: (size.width * rotation.sin()).abs() - + (size.height * rotation.cos()).abs(), - }, + Self::Floating(_) => size, + Self::Solid(rotation) => { + let radians = f32::from(rotation); + + Size { + width: (size.width * radians.cos()).abs() + + (size.height * radians.sin()).abs(), + height: (size.width * radians.sin()).abs() + + (size.height * radians.cos()).abs(), + } + } } } } + +impl Default for Rotation { + fn default() -> Self { + Self::Floating(Radians(0.0)) + } +} + +impl From for Rotation { + fn from(radians: Radians) -> Self { + Self::Floating(radians) + } +} + +impl From for Rotation { + fn from(radians: f32) -> Self { + Self::Floating(Radians(radians)) + } +} diff --git a/core/src/size.rs b/core/src/size.rs index c2b5671a..66be2d85 100644 --- a/core/src/size.rs +++ b/core/src/size.rs @@ -113,3 +113,17 @@ where } } } + +impl std::ops::Mul> for Size +where + T: std::ops::Mul + Copy, +{ + type Output = Size; + + fn mul(self, scale: Vector) -> Self::Output { + Size { + width: self.width * scale.x, + height: self.height * scale.y, + } + } +} diff --git a/core/src/svg.rs b/core/src/svg.rs index 74dd7f4a..01f102e3 100644 --- a/core/src/svg.rs +++ b/core/src/svg.rs @@ -1,5 +1,5 @@ //! Load and draw vector graphics. -use crate::{Color, Rectangle, Size}; +use crate::{Color, Radians, Rectangle, Size}; use rustc_hash::FxHasher; use std::borrow::Cow; @@ -100,7 +100,6 @@ pub trait Renderer: crate::Renderer { handle: Handle, color: Option, bounds: Rectangle, - rotation: f32, - scale: Size, + rotation: Radians, ); } diff --git a/core/src/vector.rs b/core/src/vector.rs index 1380c3b3..049e648f 100644 --- a/core/src/vector.rs +++ b/core/src/vector.rs @@ -18,6 +18,9 @@ impl Vector { impl Vector { /// The zero [`Vector`]. pub const ZERO: Self = Self::new(0.0, 0.0); + + /// The unit [`Vector`]. + pub const UNIT: Self = Self::new(0.0, 0.0); } impl std::ops::Add for Vector -- cgit From efc55b655bfce98fc32e698cf3c2007e27be941a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 17:14:20 +0200 Subject: Create `ferris` example to showcase `ContentFit` and `Rotation` --- core/src/angle.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ core/src/content_fit.rs | 14 ++++++++++++++ core/src/rotation.rs | 7 ++++++- 3 files changed, 63 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/angle.rs b/core/src/angle.rs index 69630717..8322273c 100644 --- a/core/src/angle.rs +++ b/core/src/angle.rs @@ -7,6 +7,11 @@ use std::ops::{Add, AddAssign, Div, Mul, RangeInclusive, Sub, SubAssign}; #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub struct Degrees(pub f32); +impl Degrees { + /// The range of degrees of a circle. + pub const RANGE: RangeInclusive = Self(0.0)..=Self(360.0); +} + impl PartialEq for Degrees { fn eq(&self, other: &f32) -> bool { self.0.eq(other) @@ -19,6 +24,44 @@ impl PartialOrd for Degrees { } } +impl From for Degrees { + fn from(degrees: f32) -> Self { + Self(degrees) + } +} + +impl From for Degrees { + fn from(degrees: u8) -> Self { + Self(f32::from(degrees)) + } +} + +impl From for f32 { + fn from(degrees: Degrees) -> Self { + degrees.0 + } +} + +impl From for f64 { + fn from(degrees: Degrees) -> Self { + Self::from(degrees.0) + } +} + +impl num_traits::FromPrimitive for Degrees { + fn from_i64(n: i64) -> Option { + Some(Self(n as f32)) + } + + fn from_u64(n: u64) -> Option { + Some(Self(n as f32)) + } + + fn from_f64(n: f64) -> Option { + Some(Self(n as f32)) + } +} + /// Radians #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] pub struct Radians(pub f32); diff --git a/core/src/content_fit.rs b/core/src/content_fit.rs index 56d2ffa6..19642716 100644 --- a/core/src/content_fit.rs +++ b/core/src/content_fit.rs @@ -1,6 +1,8 @@ //! Control the fit of some content (like an image) within a space. use crate::Size; +use std::fmt; + /// The strategy used to fit the contents of a widget to its bounding box. /// /// Each variant of this enum is a strategy that can be applied for resolving @@ -118,3 +120,15 @@ impl ContentFit { } } } + +impl fmt::Display for ContentFit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + ContentFit::Contain => "Contain", + ContentFit::Cover => "Cover", + ContentFit::Fill => "Fill", + ContentFit::None => "None", + ContentFit::ScaleDown => "Scale Down", + }) + } +} diff --git a/core/src/rotation.rs b/core/src/rotation.rs index ebb85f3c..f36ef089 100644 --- a/core/src/rotation.rs +++ b/core/src/rotation.rs @@ -1,5 +1,5 @@ //! Control the rotation of some content (like an image) within a space. -use crate::{Radians, Size}; +use crate::{Degrees, Radians, Size}; /// The strategy used to rotate the content. /// @@ -31,6 +31,11 @@ impl Rotation { } } + /// Returns the angle of the [`Rotation`] in [`Degrees`]. + pub fn degrees(self) -> Degrees { + Degrees(self.radians().0.to_degrees()) + } + /// Rotates the given [`Size`]. pub fn apply(self, size: Size) -> Size { match self { -- cgit From eac5bcb64f17dfbb52b64ea4f95693462986bb69 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 3 May 2024 07:04:57 +0200 Subject: Fix `Image::bounds` when rotation present in `iced_graphics` --- core/src/rectangle.rs | 16 ++++++++++++++-- core/src/rotation.rs | 14 +++----------- core/src/size.rs | 15 ++++++++++++++- 3 files changed, 31 insertions(+), 14 deletions(-) (limited to 'core') diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index fb66131a..1556e072 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -1,6 +1,6 @@ -use crate::{Point, Size, Vector}; +use crate::{Point, Radians, Size, Vector}; -/// A rectangle. +/// An axis-aligned rectangle. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct Rectangle { /// X coordinate of the top-left corner. @@ -172,6 +172,18 @@ impl Rectangle { height: self.height + amount * 2.0, } } + + /// Rotates the [`Rectangle`] and returns the smallest [`Rectangle`] + /// containing it. + pub fn rotate(self, rotation: Radians) -> Self { + let size = self.size().rotate(rotation); + let position = Point::new( + self.center_x() - size.width / 2.0, + self.center_y() - size.height / 2.0, + ); + + Self::new(position, size) + } } impl std::ops::Mul for Rectangle { diff --git a/core/src/rotation.rs b/core/src/rotation.rs index f36ef089..00a8c302 100644 --- a/core/src/rotation.rs +++ b/core/src/rotation.rs @@ -36,20 +36,12 @@ impl Rotation { Degrees(self.radians().0.to_degrees()) } - /// Rotates the given [`Size`]. + /// Applies the [`Rotation`] to the given [`Size`], returning + /// the minimum [`Size`] containing the rotated one. pub fn apply(self, size: Size) -> Size { match self { Self::Floating(_) => size, - Self::Solid(rotation) => { - let radians = f32::from(rotation); - - Size { - width: (size.width * radians.cos()).abs() - + (size.height * radians.sin()).abs(), - height: (size.width * radians.sin()).abs() - + (size.height * radians.cos()).abs(), - } - } + Self::Solid(rotation) => size.rotate(rotation), } } } diff --git a/core/src/size.rs b/core/src/size.rs index 66be2d85..d7459355 100644 --- a/core/src/size.rs +++ b/core/src/size.rs @@ -1,4 +1,4 @@ -use crate::Vector; +use crate::{Radians, Vector}; /// An amount of space in 2 dimensions. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] @@ -51,6 +51,19 @@ impl Size { height: self.height + other.height, } } + + /// Rotates the given [`Size`] and returns the minimum [`Size`] + /// containing it. + pub fn rotate(self, rotation: Radians) -> Size { + let radians = f32::from(rotation); + + Size { + width: (self.width * radians.cos()).abs() + + (self.height * radians.sin()).abs(), + height: (self.width * radians.sin()).abs() + + (self.height * radians.cos()).abs(), + } + } } impl From<[T; 2]> for Size { -- cgit From 4010e3983d40e24a5d7b590d8fec9801651199ce Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 3 May 2024 07:23:55 +0200 Subject: Add `spin` mode to `ferris` example :crab: --- core/src/angle.rs | 26 +++++++++++++++++++++++++- core/src/rotation.rs | 7 +++++++ 2 files changed, 32 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/angle.rs b/core/src/angle.rs index 8322273c..9c8a9b24 100644 --- a/core/src/angle.rs +++ b/core/src/angle.rs @@ -1,7 +1,7 @@ use crate::{Point, Rectangle, Vector}; use std::f32::consts::{FRAC_PI_2, PI}; -use std::ops::{Add, AddAssign, Div, Mul, RangeInclusive, Sub, SubAssign}; +use std::ops::{Add, AddAssign, Div, Mul, RangeInclusive, Rem, Sub, SubAssign}; /// Degrees #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] @@ -48,6 +48,14 @@ impl From for f64 { } } +impl Mul for Degrees { + type Output = Degrees; + + fn mul(self, rhs: f32) -> Self::Output { + Self(self.0 * rhs) + } +} + impl num_traits::FromPrimitive for Degrees { fn from_i64(n: i64) -> Option { Some(Self(n as f32)) @@ -156,6 +164,14 @@ impl Add for Radians { } } +impl Add for Radians { + type Output = Self; + + fn add(self, rhs: Degrees) -> Self::Output { + Self(self.0 + rhs.0.to_radians()) + } +} + impl AddAssign for Radians { fn add_assign(&mut self, rhs: Radians) { self.0 = self.0 + rhs.0; @@ -202,6 +218,14 @@ impl Div for Radians { } } +impl Rem for Radians { + type Output = Self; + + fn rem(self, rhs: Self) -> Self::Output { + Self(self.0 % rhs.0) + } +} + impl PartialEq for Radians { fn eq(&self, other: &f32) -> bool { self.0.eq(other) diff --git a/core/src/rotation.rs b/core/src/rotation.rs index 00a8c302..afa8d79e 100644 --- a/core/src/rotation.rs +++ b/core/src/rotation.rs @@ -31,6 +31,13 @@ impl Rotation { } } + /// Returns a mutable reference to the angle of the [`Rotation`] in [`Radians`]. + pub fn radians_mut(&mut self) -> &mut Radians { + match self { + Rotation::Floating(radians) | Rotation::Solid(radians) => radians, + } + } + /// Returns the angle of the [`Rotation`] in [`Degrees`]. pub fn degrees(self) -> Degrees { Degrees(self.radians().0.to_degrees()) -- cgit From fa9e1d96ea1924b51749b775ea0e67e69bc8a305 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 3 May 2024 13:25:58 +0200 Subject: Introduce dynamic `opacity` support for `Image` and `Svg` --- core/src/image.rs | 1 + core/src/renderer/null.rs | 2 ++ core/src/svg.rs | 1 + 3 files changed, 4 insertions(+) (limited to 'core') diff --git a/core/src/image.rs b/core/src/image.rs index 91a7fd36..82ecdd0f 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -174,5 +174,6 @@ pub trait Renderer: crate::Renderer { filter_method: FilterMethod, bounds: Rectangle, rotation: Radians, + opacity: f32, ); } diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 91519b40..e8709dbc 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -173,6 +173,7 @@ impl image::Renderer for () { _filter_method: image::FilterMethod, _bounds: Rectangle, _rotation: Radians, + _opacity: f32, ) { } } @@ -188,6 +189,7 @@ impl svg::Renderer for () { _color: Option, _bounds: Rectangle, _rotation: Radians, + _opacity: f32, ) { } } diff --git a/core/src/svg.rs b/core/src/svg.rs index 01f102e3..946b8156 100644 --- a/core/src/svg.rs +++ b/core/src/svg.rs @@ -101,5 +101,6 @@ pub trait Renderer: crate::Renderer { color: Option, bounds: Rectangle, rotation: Radians, + opacity: f32, ); } -- cgit From b19e95fa1844ca726aa5761f2d331fd780854bc6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 10 May 2024 22:16:35 +0200 Subject: Add `SpecificWith` variant to `window::Position` --- core/src/window/position.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/src/window/position.rs b/core/src/window/position.rs index 73391e75..1c8e86b6 100644 --- a/core/src/window/position.rs +++ b/core/src/window/position.rs @@ -1,4 +1,4 @@ -use crate::Point; +use crate::{Point, Size}; /// The position of a window in a given screen. #[derive(Debug, Clone, Copy, PartialEq)] @@ -15,6 +15,12 @@ pub enum Position { /// at (0, 0) you would have to set the position to /// `(PADDING_X, PADDING_Y)`. Specific(Point), + /// Like [`Specific`], but the window is positioned with the specific coordinates returned by the function. + /// + /// The function receives the window size and the monitor's resolution as input. + /// + /// [`Specific`]: Self::Specific + SpecificWith(fn(Size, Size) -> Point), } impl Default for Position { -- cgit