diff options
| author | 2024-05-23 13:29:45 +0200 | |
|---|---|---|
| committer | 2024-05-23 13:29:45 +0200 | |
| commit | d8ba6b0673a33724a177f3a1ba59705527280142 (patch) | |
| tree | 89482c8d1e3a03e00b3a8151abbb81e30ae5898c /core/src | |
| parent | 72ed8bcc8def9956e25f3720a3095fc96bb2eef0 (diff) | |
| parent | 468794d918eb06c1dbebb33c32b10017ad335f05 (diff) | |
| download | iced-d8ba6b0673a33724a177f3a1ba59705527280142.tar.gz iced-d8ba6b0673a33724a177f3a1ba59705527280142.tar.bz2 iced-d8ba6b0673a33724a177f3a1ba59705527280142.zip | |
Merge branch 'master' into feat/text-macro
Diffstat (limited to 'core/src')
| -rw-r--r-- | core/src/angle.rs | 75 | ||||
| -rw-r--r-- | core/src/content_fit.rs | 17 | ||||
| -rw-r--r-- | core/src/element.rs | 4 | ||||
| -rw-r--r-- | core/src/hasher.rs | 2 | ||||
| -rw-r--r-- | core/src/image.rs | 216 | ||||
| -rw-r--r-- | core/src/layout.rs | 2 | ||||
| -rw-r--r-- | core/src/lib.rs | 11 | ||||
| -rw-r--r-- | core/src/mouse/interaction.rs | 1 | ||||
| -rw-r--r-- | core/src/overlay.rs | 2 | ||||
| -rw-r--r-- | core/src/rectangle.rs | 79 | ||||
| -rw-r--r-- | core/src/renderer.rs | 33 | ||||
| -rw-r--r-- | core/src/renderer/null.rs | 75 | ||||
| -rw-r--r-- | core/src/rotation.rs | 72 | ||||
| -rw-r--r-- | core/src/size.rs | 45 | ||||
| -rw-r--r-- | core/src/svg.rs | 16 | ||||
| -rw-r--r-- | core/src/text.rs | 10 | ||||
| -rw-r--r-- | core/src/text/paragraph.rs | 6 | ||||
| -rw-r--r-- | core/src/theme.rs | 25 | ||||
| -rw-r--r-- | core/src/theme/palette.rs | 18 | ||||
| -rw-r--r-- | core/src/transformation.rs | 6 | ||||
| -rw-r--r-- | core/src/vector.rs | 3 | ||||
| -rw-r--r-- | core/src/widget.rs | 2 | ||||
| -rw-r--r-- | core/src/widget/text.rs | 199 | ||||
| -rw-r--r-- | core/src/window/position.rs | 8 | ||||
| -rw-r--r-- | core/src/window/redraw_request.rs | 2 | ||||
| -rw-r--r-- | core/src/window/settings/wasm.rs | 12 | 
26 files changed, 682 insertions, 259 deletions
| diff --git a/core/src/angle.rs b/core/src/angle.rs index dc3c0e93..9c8a9b24 100644 --- a/core/src/angle.rs +++ b/core/src/angle.rs @@ -1,12 +1,17 @@  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)]  pub struct Degrees(pub f32); +impl Degrees { +    /// The range of degrees of a circle. +    pub const RANGE: RangeInclusive<Self> = Self(0.0)..=Self(360.0); +} +  impl PartialEq<f32> for Degrees {      fn eq(&self, other: &f32) -> bool {          self.0.eq(other) @@ -19,6 +24,52 @@ impl PartialOrd<f32> for Degrees {      }  } +impl From<f32> for Degrees { +    fn from(degrees: f32) -> Self { +        Self(degrees) +    } +} + +impl From<u8> for Degrees { +    fn from(degrees: u8) -> Self { +        Self(f32::from(degrees)) +    } +} + +impl From<Degrees> for f32 { +    fn from(degrees: Degrees) -> Self { +        degrees.0 +    } +} + +impl From<Degrees> for f64 { +    fn from(degrees: Degrees) -> Self { +        Self::from(degrees.0) +    } +} + +impl Mul<f32> 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<Self> { +        Some(Self(n as f32)) +    } + +    fn from_u64(n: u64) -> Option<Self> { +        Some(Self(n as f32)) +    } + +    fn from_f64(n: f64) -> Option<Self> { +        Some(Self(n as f32)) +    } +} +  /// Radians  #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]  pub struct Radians(pub f32); @@ -65,6 +116,12 @@ impl From<u8> for Radians {      }  } +impl From<Radians> for f32 { +    fn from(radians: Radians) -> Self { +        radians.0 +    } +} +  impl From<Radians> for f64 {      fn from(radians: Radians) -> Self {          Self::from(radians.0) @@ -107,6 +164,14 @@ impl Add for Radians {      }  } +impl Add<Degrees> 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; @@ -153,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<f32> for Radians {      fn eq(&self, other: &f32) -> bool {          self.0.eq(other) diff --git a/core/src/content_fit.rs b/core/src/content_fit.rs index 6bbedc7a..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 @@ -11,7 +13,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 +25,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. @@ -117,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/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<Item = super::Element<'a, Message>>) -> 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<Message> { 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]) { diff --git a/core/src/image.rs b/core/src/image.rs index e5fdcd83..82ecdd0f 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -1,15 +1,45 @@  //! Load and draw raster graphics. -use crate::{Hasher, Rectangle, Size}; +pub use bytes::Bytes; -use std::hash::{Hash, Hasher as _}; -use std::path::PathBuf; -use std::sync::Arc; +use crate::{Radians, Rectangle, Size}; + +use rustc_hash::FxHasher; +use std::hash::{Hash, Hasher}; +use std::path::{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 { +    /// 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), + +    /// 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), + +    /// A handle pointing to decoded image pixels in RGBA format. +    /// +    /// Use [`from_rgba`] to create this variant. +    /// +    /// [`from_rgba`]: Self::from_rgba +    Rgba { +        /// The id of this handle. +        id: Id, +        /// The width of the image. +        width: u32, +        /// The height of the image. +        height: u32, +        /// The pixels. +        pixels: Bytes, +    },  }  impl Handle { @@ -17,56 +47,48 @@ impl Handle {      ///      /// Makes an educated guess about the image format by examining the data in the file.      pub fn from_path<T: Into<PathBuf>>(path: T) -> Handle { -        Self::from_data(Data::Path(path.into())) -    } +        let path = path.into(); -    /// Creates an image [`Handle`] containing the image pixels directly. This -    /// function expects the input data to be provided as a `Vec<u8>` of RGBA -    /// pixels. -    /// -    /// This is useful if you have already decoded your image. -    pub fn from_pixels( -        width: u32, -        height: u32, -        pixels: impl AsRef<[u8]> + Send + Sync + 'static, -    ) -> Handle { -        Self::from_data(Data::Rgba { -            width, -            height, -            pixels: Bytes::new(pixels), -        }) +        Self::Path(Id::path(&path), path)      } -    /// Creates an image [`Handle`] containing the image data directly. +    /// 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_memory( -        bytes: impl AsRef<[u8]> + Send + Sync + 'static, -    ) -> Handle { -        Self::from_data(Data::Bytes(Bytes::new(bytes))) +    pub fn from_bytes(bytes: impl Into<Bytes>) -> Handle { +        Self::Bytes(Id::unique(), bytes.into())      } -    fn from_data(data: Data) -> Handle { -        let mut hasher = Hasher::default(); -        data.hash(&mut hasher); - -        Handle { -            id: hasher.finish(), -            data, +    /// 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_rgba( +        width: u32, +        height: u32, +        pixels: impl Into<Bytes>, +    ) -> Handle { +        Self::Rgba { +            id: Id::unique(), +            width, +            height, +            pixels: pixels.into(),          }      }      /// Returns the unique identifier of the [`Handle`]. -    pub fn id(&self) -> u64 { -        self.id -    } - -    /// Returns a reference to the image [`Data`]. -    pub fn data(&self) -> &Data { -        &self.data +    pub fn id(&self) -> Id { +        match self { +            Handle::Path(id, _) +            | Handle::Bytes(id, _) +            | Handle::Rgba { id, .. } => *id, +        }      }  } @@ -79,90 +101,46 @@ where      }  } -impl Hash for Handle { -    fn hash<H: std::hash::Hasher>(&self, state: &mut H) { -        self.id.hash(state); -    } -} - -/// A wrapper around raw image data. -/// -/// It behaves like a `&[u8]`. -#[derive(Clone)] -pub struct Bytes(Arc<dyn AsRef<[u8]> + 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 { +impl std::fmt::Debug for Handle {      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -        self.0.as_ref().as_ref().fmt(f) +        match self { +            Self::Path(_, path) => write!(f, "Path({path:?})"), +            Self::Bytes(_, _) => write!(f, "Bytes(...)"), +            Self::Rgba { width, height, .. } => { +                write!(f, "Pixels({width} * {height})") +            } +        }      }  } -impl std::hash::Hash for Bytes { -    fn hash<H: std::hash::Hasher>(&self, state: &mut H) { -        self.0.as_ref().as_ref().hash(state); -    } -} +/// The unique identifier of some [`Handle`] data. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Id(_Id); -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 -    } +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum _Id { +    Unique(u64), +    Hash(u64),  } -impl Eq for Bytes {} - -impl AsRef<[u8]> for Bytes { -    fn as_ref(&self) -> &[u8] { -        self.0.as_ref().as_ref() -    } -} +impl Id { +    fn unique() -> Self { +        use std::sync::atomic::{self, AtomicU64}; -impl std::ops::Deref for Bytes { -    type Target = [u8]; +        static NEXT_ID: AtomicU64 = AtomicU64::new(0); -    fn deref(&self) -> &[u8] { -        self.0.as_ref().as_ref() +        Self(_Id::Unique(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)))      } -} -/// The data of a raster image. -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum Data { -    /// File data -    Path(PathBuf), +    fn path(path: impl AsRef<Path>) -> Self { +        let hash = { +            let mut hasher = FxHasher::default(); +            path.as_ref().hash(&mut hasher); -    /// 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, -    }, -} +            hasher.finish() +        }; -impl std::fmt::Debug for Data { -    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, .. } => { -                write!(f, "Pixels({width} * {height})") -            } -        } +        Self(_Id::Hash(hash))      }  } @@ -183,17 +161,19 @@ 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 dimensions(&self, handle: &Self::Handle) -> Size<u32>; +    fn measure_image(&self, handle: &Self::Handle) -> Size<u32>;      /// 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,          bounds: Rectangle, +        rotation: Radians, +        opacity: f32,      );  } 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<Item = Layout<'a>> { +    pub fn children(self) -> impl DoubleEndedIterator<Item = Layout<'a>> {          self.node.children().iter().map(move |node| {              Layout::with_offset(                  Vector::new(self.position.x, self.position.y), diff --git a/core/src/lib.rs b/core/src/lib.rs index d076413e..32156441 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; @@ -41,12 +34,12 @@ mod background;  mod color;  mod content_fit;  mod element; -mod hasher;  mod length;  mod padding;  mod pixels;  mod point;  mod rectangle; +mod rotation;  mod shadow;  mod shell;  mod size; @@ -64,7 +57,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; @@ -73,6 +65,7 @@ pub use pixels::Pixels;  pub use point::Point;  pub use rectangle::Rectangle;  pub use renderer::Renderer; +pub use rotation::Rotation;  pub use shadow::Shadow;  pub use shell::Shell;  pub use size::Size; 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/rectangle.rs b/core/src/rectangle.rs index c1c2eeac..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<T = f32> {      /// X coordinate of the top-left corner. @@ -16,24 +16,32 @@ pub struct Rectangle<T = f32> {      pub height: T,  } -impl Rectangle<f32> { -    /// 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<T> Rectangle<T> +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<T>) -> 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<f32> { +    /// 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 const 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,          } @@ -139,13 +147,20 @@ impl Rectangle<f32> {      }      /// Snaps the [`Rectangle`] to __unsigned__ integer coordinates. -    pub fn snap(self) -> Rectangle<u32> { -        Rectangle { +    pub fn snap(self) -> Option<Rectangle<u32>> { +        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. @@ -157,6 +172,18 @@ impl Rectangle<f32> {              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<f32> for Rectangle<f32> { @@ -212,3 +239,19 @@ where          }      }  } + +impl<T> std::ops::Mul<Vector<T>> for Rectangle<T> +where +    T: std::ops::Mul<Output = T> + Copy, +{ +    type Output = Rectangle<T>; + +    fn mul(self, scale: Vector<T>) -> 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.rs b/core/src/renderer.rs index 1139b41c..a2785ae8 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -2,26 +2,47 @@  #[cfg(debug_assertions)]  mod null; -#[cfg(debug_assertions)] -pub use null::Null; -  use crate::{      Background, Border, Color, Rectangle, Shadow, Size, Transformation, Vector,  };  /// A component that can be used by widgets to draw themselves on a screen. -pub trait Renderer: Sized { +pub trait Renderer { +    /// Starts recording a new layer. +    fn start_layer(&mut self, bounds: Rectangle); + +    /// Ends recording a new layer. +    /// +    /// The new layer will clip its contents to the provided `bounds`. +    fn end_layer(&mut self); +      /// 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(bounds); +        f(self); +        self.end_layer(); +    } + +    /// Starts recording with a new [`Transformation`]. +    fn start_transformation(&mut self, transformation: Transformation); + +    /// Ends recording a new layer. +    /// +    /// The new layer will clip its contents to the provided `bounds`. +    fn end_transformation(&mut self);      /// 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(transformation); +        f(self); +        self.end_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..e8709dbc 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -1,34 +1,21 @@  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, +    Background, Color, Font, Pixels, Point, Radians, Rectangle, Size, +    Transformation,  }; -use std::borrow::Cow; +impl Renderer for () { +    fn start_layer(&mut self, _bounds: Rectangle) {} -/// A renderer that does nothing. -/// -/// It can be useful if you are writing tests! -#[derive(Debug, Clone, Copy, Default)] -pub struct Null; +    fn end_layer(&mut self) {} -impl Null { -    /// Creates a new [`Null`] renderer. -    pub fn new() -> Null { -        Null -    } -} - -impl Renderer for Null { -    fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {} +    fn start_transformation(&mut self, _transformation: Transformation) {} -    fn with_transformation( -        &mut self, -        _transformation: Transformation, -        _f: impl FnOnce(&mut Self), -    ) { -    } +    fn end_transformation(&mut self) {}      fn clear(&mut self) {} @@ -40,7 +27,7 @@ impl Renderer for Null {      }  } -impl text::Renderer for Null { +impl text::Renderer for () {      type Font = Font;      type Paragraph = ();      type Editor = (); @@ -57,8 +44,6 @@ impl text::Renderer for Null {          Pixels(16.0)      } -    fn load_font(&mut self, _font: Cow<'static, [u8]>) {} -      fn fill_paragraph(          &mut self,          _paragraph: &Self::Paragraph, @@ -79,7 +64,7 @@ impl text::Renderer for Null {      fn fill_text(          &mut self, -        _paragraph: Text<'_, Self::Font>, +        _paragraph: Text,          _position: Point,          _color: Color,          _clip_bounds: Rectangle, @@ -90,11 +75,11 @@ impl text::Renderer for Null {  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      } @@ -174,3 +159,37 @@ impl text::Editor for () {      ) {      }  } + +impl image::Renderer for () { +    type Handle = (); + +    fn measure_image(&self, _handle: &Self::Handle) -> Size<u32> { +        Size::default() +    } + +    fn draw_image( +        &mut self, +        _handle: Self::Handle, +        _filter_method: image::FilterMethod, +        _bounds: Rectangle, +        _rotation: Radians, +        _opacity: f32, +    ) { +    } +} + +impl svg::Renderer for () { +    fn measure_svg(&self, _handle: &svg::Handle) -> Size<u32> { +        Size::default() +    } + +    fn draw_svg( +        &mut self, +        _handle: svg::Handle, +        _color: Option<Color>, +        _bounds: Rectangle, +        _rotation: Radians, +        _opacity: f32, +    ) { +    } +} diff --git a/core/src/rotation.rs b/core/src/rotation.rs new file mode 100644 index 00000000..afa8d79e --- /dev/null +++ b/core/src/rotation.rs @@ -0,0 +1,72 @@ +//! Control the rotation of some content (like an image) within a space. +use crate::{Degrees, 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, 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. +    /// +    /// 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. +    Solid(Radians), +} + +impl Rotation { +    /// Returns the angle of the [`Rotation`] in [`Radians`]. +    pub fn radians(self) -> Radians { +        match self { +            Rotation::Floating(radians) | Rotation::Solid(radians) => radians, +        } +    } + +    /// 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()) +    } + +    /// 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) => size.rotate(rotation), +        } +    } +} + +impl Default for Rotation { +    fn default() -> Self { +        Self::Floating(Radians(0.0)) +    } +} + +impl From<Radians> for Rotation { +    fn from(radians: Radians) -> Self { +        Self::Floating(radians) +    } +} + +impl From<f32> for Rotation { +    fn from(radians: f32) -> Self { +        Self::Floating(Radians(radians)) +    } +} diff --git a/core/src/size.rs b/core/src/size.rs index 267fc90e..d7459355 100644 --- a/core/src/size.rs +++ b/core/src/size.rs @@ -1,7 +1,7 @@ -use crate::Vector; +use crate::{Radians, 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<T = f32> {      /// The width.      pub width: T, @@ -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<T> From<[T; 2]> for Size<T> { @@ -99,3 +112,31 @@ where          }      }  } + +impl<T> std::ops::Mul<T> for Size<T> +where +    T: std::ops::Mul<Output = T> + Copy, +{ +    type Output = Size<T>; + +    fn mul(self, rhs: T) -> Self::Output { +        Size { +            width: self.width * rhs, +            height: self.height * rhs, +        } +    } +} + +impl<T> std::ops::Mul<Vector<T>> for Size<T> +where +    T: std::ops::Mul<Output = T> + Copy, +{ +    type Output = Size<T>; + +    fn mul(self, scale: Vector<T>) -> 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 d63e3c95..946b8156 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, Radians, 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 { @@ -91,8 +92,15 @@ 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<u32>; +    fn measure_svg(&self, handle: &Handle) -> Size<u32>;      /// Draws an SVG with the given [`Handle`], an optional [`Color`] filter, and inside the provided `bounds`. -    fn draw(&mut self, handle: Handle, color: Option<Color>, bounds: Rectangle); +    fn draw_svg( +        &mut self, +        handle: Handle, +        color: Option<Color>, +        bounds: Rectangle, +        rotation: Radians, +        opacity: f32, +    );  } diff --git a/core/src/text.rs b/core/src/text.rs index edef79c2..b30feae0 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -11,14 +11,13 @@ 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.  #[derive(Debug, Clone, Copy)] -pub struct Text<'a, Font> { +pub struct Text<Content = String, Font = crate::Font> {      /// The content of the paragraph. -    pub content: &'a str, +    pub content: Content,      /// The bounds of the paragraph.      pub bounds: Size, @@ -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( @@ -219,7 +215,7 @@ pub trait Renderer: crate::Renderer {      /// [`Color`].      fn fill_text(          &mut self, -        text: Text<'_, Self::Font>, +        text: Text<String, Self::Font>,          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<Point>;      /// 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 => { 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<Theme> = +                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 { diff --git a/core/src/theme/palette.rs b/core/src/theme/palette.rs index ca91c248..e0ff397a 100644 --- a/core/src/theme/palette.rs +++ b/core/src/theme/palette.rs @@ -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 is_dark(background) { -        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,6 +636,13 @@ fn is_readable(a: Color, b: Color) -> bool {      a_srgb.has_enhanced_contrast_text(b_srgb)  } +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_hsl(color: Color) -> Hsl {      Hsl::from_color(Rgb::from(color))  } 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; 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<T> Vector<T> {  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<T> std::ops::Add for Vector<T> 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. diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 66e2d066..f1f0b345 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -18,9 +18,10 @@ 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>, +    fragment: Fragment<'a>,      size: Option<Pixels>,      line_height: LineHeight,      width: Length, @@ -29,20 +30,18 @@ where      vertical_alignment: alignment::Vertical,      font: Option<Renderer::Font>,      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<Cow<'a, str>>) -> Self -    where -        Theme: DefaultStyle + 'a, -    { +    pub fn new(fragment: impl IntoFragment<'a>) -> Self {          Text { -            content: content.into(), +            fragment: fragment.into_fragment(),              size: None,              line_height: LineHeight::default(),              font: 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<Color>) -> Self { -        self.color_maybe(Some(color)) -    } - -    /// Sets the [`Color`] of the [`Text`], if `Some`. -    pub fn color_maybe(mut self, color: Option<impl Into<Color>>) -> 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<Length>) -> 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<StyleFn<'a, Theme>>, +    { +        self.class = (Box::new(style) as StyleFn<'a, Theme>).into(); +        self +    } + +    /// Sets the [`Color`] of the [`Text`]. +    pub fn color(self, color: impl Into<Color>) -> Self +    where +        Theme::Class<'a>: From<StyleFn<'a, Theme>>, +    { +        self.color_maybe(Some(color)) +    } + +    /// Sets the [`Color`] of the [`Text`], if `Some`. +    pub fn color_maybe(self, color: Option<impl Into<Color>>) -> Self +    where +        Theme::Class<'a>: From<StyleFn<'a, Theme>>, +    { +        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<Theme::Class<'a>>) -> Self { +        self.class = class.into(); +        self +    }  }  /// The internal state of a [`Text`] widget. @@ -138,6 +154,7 @@ pub struct State<P: Paragraph>(P);  impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>      for Text<'a, Theme, Renderer>  where +    Theme: Catalog,      Renderer: text::Renderer,  {      fn tag(&self) -> tree::Tag { @@ -167,7 +184,7 @@ where              limits,              self.width,              self.height, -            &self.content, +            &self.fragment,              self.line_height,              self.size,              self.font, @@ -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::<State<Renderer::Paragraph>>(); -        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<Renderer>(      style: &renderer::Style,      layout: Layout<'_>,      state: &State<Renderer::Paragraph>, -    appearance: Appearance, +    appearance: Style,      viewport: &Rectangle,  ) where      Renderer: text::Renderer, @@ -281,7 +298,7 @@ pub fn draw<Renderer>(  impl<'a, Message, Theme, Renderer> From<Text<'a, Theme, Renderer>>      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,116 @@ 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<Color>,  } -/// The style of some [`Text`]. -pub type Style<'a, Theme> = Box<dyn Fn(&Theme) -> Appearance + 'a>; +/// The theme catalog of a [`Text`]. +pub trait Catalog: Sized { +    /// The item class of this [`Catalog`]. +    type Class<'a>; + +    /// The default class produced by this [`Catalog`]. +    fn default<'a>() -> Self::Class<'a>; -/// The default style of some [`Text`]. -pub trait DefaultStyle { -    /// Returns the default style of some [`Text`]. -    fn default_style(&self) -> Appearance; +    /// 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<dyn Fn(&Theme) -> Style + 'a>; + +impl Catalog for Theme { +    type Class<'a> = StyleFn<'a, Self>; + +    fn default<'a>() -> Self::Class<'a> { +        Box::new(|_theme| Style::default()) +    } + +    fn style(&self, class: &Self::Class<'_>) -> Style { +        class(self) +    } +} + +/// A fragment of [`Text`]. +/// +/// This is just an alias to a string that may be either +/// borrowed or owned. +pub type Fragment<'a> = Cow<'a, str>; + +/// 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> IntoFragment<'a> for Fragment<'a> { +    fn into_fragment(self) -> Fragment<'a> { +        self      }  } -impl DefaultStyle for Color { -    fn default_style(&self) -> Appearance { -        Appearance { color: Some(*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) +    } +} + +impl<'a> IntoFragment<'a> for &'a String { +    fn into_fragment(self) -> Fragment<'a> { +        Fragment::Borrowed(self.as_str()) +    } +} + +impl<'a> IntoFragment<'a> for String { +    fn into_fragment(self) -> Fragment<'a> { +        Fragment::Owned(self) +    } +} + +macro_rules! into_fragment { +    ($type:ty) => { +        impl<'a> IntoFragment<'a> for $type { +            fn into_fragment(self) -> Fragment<'a> { +                Fragment::Owned(self.to_string()) +            } +        } + +        impl<'a> IntoFragment<'a> for &$type { +            fn into_fragment(self) -> Fragment<'a> { +                Fragment::Owned(self.to_string()) +            } +        } +    }; +} + +into_fragment!(char); +into_fragment!(bool); + +into_fragment!(u8); +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); 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 { 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() { 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<String>,  } + +impl Default for PlatformSpecific { +    fn default() -> Self { +        Self { +            target: Some(String::from("iced")), +        } +    } +} | 
