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