diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/Cargo.toml | 6 | ||||
-rw-r--r-- | core/src/angle.rs | 33 | ||||
-rw-r--r-- | core/src/background.rs | 19 | ||||
-rw-r--r-- | core/src/border_radius.rs | 22 | ||||
-rw-r--r-- | core/src/color.rs | 40 | ||||
-rw-r--r-- | core/src/element.rs | 72 | ||||
-rw-r--r-- | core/src/gradient.rs | 154 | ||||
-rw-r--r-- | core/src/gradient/linear.rs | 112 | ||||
-rw-r--r-- | core/src/lib.rs | 4 | ||||
-rw-r--r-- | core/src/mouse.rs | 2 | ||||
-rw-r--r-- | core/src/mouse/button.rs | 2 | ||||
-rw-r--r-- | core/src/mouse/cursor.rs | 52 | ||||
-rw-r--r-- | core/src/overlay.rs | 22 | ||||
-rw-r--r-- | core/src/overlay/element.rs | 83 | ||||
-rw-r--r-- | core/src/overlay/group.rs | 43 | ||||
-rw-r--r-- | core/src/renderer.rs | 25 | ||||
-rw-r--r-- | core/src/widget.rs | 8 | ||||
-rw-r--r-- | core/src/widget/text.rs | 7 | ||||
-rw-r--r-- | core/src/window.rs | 2 | ||||
-rw-r--r-- | core/src/window/level.rs | 19 |
20 files changed, 369 insertions, 358 deletions
diff --git a/core/Cargo.toml b/core/Cargo.toml index 92d9773f..55f2e85f 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -10,11 +10,15 @@ repository = "https://github.com/iced-rs/iced" [dependencies] bitflags = "1.2" thiserror = "1" +log = "0.4.17" twox-hash = { version = "1.5", default-features = false } [dependencies.palette] -version = "0.6" +version = "0.7" optional = true [target.'cfg(target_arch = "wasm32")'.dependencies] instant = "0.1" + +[dev-dependencies] +approx = "0.5" diff --git a/core/src/angle.rs b/core/src/angle.rs new file mode 100644 index 00000000..75a57c76 --- /dev/null +++ b/core/src/angle.rs @@ -0,0 +1,33 @@ +use crate::{Point, Rectangle, Vector}; +use std::f32::consts::PI; + +#[derive(Debug, Copy, Clone, PartialEq)] +/// Degrees +pub struct Degrees(pub f32); + +#[derive(Debug, Copy, Clone, PartialEq)] +/// Radians +pub struct Radians(pub f32); + +impl From<Degrees> for Radians { + fn from(degrees: Degrees) -> Self { + Radians(degrees.0 * PI / 180.0) + } +} + +impl Radians { + /// Calculates the line in which the [`Angle`] intercepts the `bounds`. + pub fn to_distance(&self, bounds: &Rectangle) -> (Point, Point) { + let v1 = Vector::new(f32::cos(self.0), f32::sin(self.0)); + + let distance_to_rect = f32::min( + f32::abs((bounds.y - bounds.center().y) / v1.y), + f32::abs(((bounds.x + bounds.width) - bounds.center().x) / v1.x), + ); + + let start = bounds.center() + v1 * distance_to_rect; + let end = bounds.center() - v1 * distance_to_rect; + + (start, end) + } +} diff --git a/core/src/background.rs b/core/src/background.rs index cfb95867..347c52c0 100644 --- a/core/src/background.rs +++ b/core/src/background.rs @@ -1,11 +1,14 @@ +use crate::gradient::{self, Gradient}; use crate::Color; /// The background of some element. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Background { - /// A solid color + /// A solid color. Color(Color), - // TODO: Add gradient and image variants + /// Linearly interpolate between several colors. + Gradient(Gradient), + // TODO: Add image variant } impl From<Color> for Background { @@ -14,8 +17,14 @@ impl From<Color> for Background { } } -impl From<Color> for Option<Background> { - fn from(color: Color) -> Self { - Some(Background::from(color)) +impl From<Gradient> for Background { + fn from(gradient: Gradient) -> Self { + Background::Gradient(gradient) + } +} + +impl From<gradient::Linear> for Background { + fn from(gradient: gradient::Linear) -> Self { + Background::Gradient(Gradient::Linear(gradient)) } } diff --git a/core/src/border_radius.rs b/core/src/border_radius.rs new file mode 100644 index 00000000..a444dd74 --- /dev/null +++ b/core/src/border_radius.rs @@ -0,0 +1,22 @@ +/// The border radii for the corners of a graphics primitive in the order: +/// top-left, top-right, bottom-right, bottom-left. +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct BorderRadius([f32; 4]); + +impl From<f32> for BorderRadius { + fn from(w: f32) -> Self { + Self([w; 4]) + } +} + +impl From<[f32; 4]> for BorderRadius { + fn from(radi: [f32; 4]) -> Self { + Self(radi) + } +} + +impl From<BorderRadius> for [f32; 4] { + fn from(radi: BorderRadius) -> Self { + radi.0 + } +} diff --git a/core/src/color.rs b/core/src/color.rs index fe0a1856..1392f28b 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -183,15 +183,15 @@ macro_rules! color { } #[cfg(feature = "palette")] -/// Converts from palette's `Srgba` type to a [`Color`]. +/// Converts from palette's `Rgba` type to a [`Color`]. impl From<Srgba> for Color { - fn from(srgba: Srgba) -> Self { - Color::new(srgba.red, srgba.green, srgba.blue, srgba.alpha) + fn from(rgba: Srgba) -> Self { + Color::new(rgba.red, rgba.green, rgba.blue, rgba.alpha) } } #[cfg(feature = "palette")] -/// Converts from [`Color`] to palette's `Srgba` type. +/// Converts from [`Color`] to palette's `Rgba` type. impl From<Color> for Srgba { fn from(c: Color) -> Self { Srgba::new(c.r, c.g, c.b, c.a) @@ -199,15 +199,15 @@ impl From<Color> for Srgba { } #[cfg(feature = "palette")] -/// Converts from palette's `Srgb` type to a [`Color`]. +/// Converts from palette's `Rgb` type to a [`Color`]. impl From<Srgb> for Color { - fn from(srgb: Srgb) -> Self { - Color::new(srgb.red, srgb.green, srgb.blue, 1.0) + fn from(rgb: Srgb) -> Self { + Color::new(rgb.red, rgb.green, rgb.blue, 1.0) } } #[cfg(feature = "palette")] -/// Converts from [`Color`] to palette's `Srgb` type. +/// Converts from [`Color`] to palette's `Rgb` type. impl From<Color> for Srgb { fn from(c: Color) -> Self { Srgb::new(c.r, c.g, c.b) @@ -218,12 +218,12 @@ impl From<Color> for Srgb { #[cfg(test)] mod tests { use super::*; - use palette::Blend; + use palette::blend::Blend; #[test] fn srgba_traits() { let c = Color::from_rgb(0.5, 0.4, 0.3); - // Round-trip conversion to the palette:Srgba type + // Round-trip conversion to the palette::Srgba type let s: Srgba = c.into(); let r: Color = s.into(); assert_eq!(c, r); @@ -231,6 +231,8 @@ mod tests { #[test] fn color_manipulation() { + use approx::assert_relative_eq; + let c1 = Color::from_rgb(0.5, 0.4, 0.3); let c2 = Color::from_rgb(0.2, 0.5, 0.3); @@ -238,19 +240,15 @@ mod tests { let l1 = Srgba::from(c1).into_linear(); let l2 = Srgba::from(c2).into_linear(); - // Take the lighter of each of the RGB components + // Take the lighter of each of the sRGB components let lighter = l1.lighten(l2); // Convert back to our Color - let r: Color = Srgba::from_linear(lighter).into(); - assert_eq!( - r, - Color { - r: 0.5, - g: 0.5, - b: 0.3, - a: 1.0 - } - ); + let result: Color = Srgba::from_linear(lighter).into(); + + assert_relative_eq!(result.r, 0.5); + assert_relative_eq!(result.g, 0.5); + assert_relative_eq!(result.b, 0.3); + assert_relative_eq!(result.a, 1.0); } } diff --git a/core/src/element.rs b/core/src/element.rs index 98c53737..3268f14b 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -5,9 +5,7 @@ use crate::overlay; use crate::renderer; use crate::widget; use crate::widget::tree::{self, Tree}; -use crate::{ - Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Widget, -}; +use crate::{Clipboard, Color, Layout, Length, Rectangle, Shell, Widget}; use std::any::Any; use std::borrow::Borrow; @@ -378,7 +376,7 @@ where tree: &mut Tree, event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, B>, @@ -390,7 +388,7 @@ where tree, event, layout, - cursor_position, + cursor, renderer, clipboard, &mut local_shell, @@ -408,35 +406,23 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, ) { - self.widget.draw( - tree, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ) + self.widget + .draw(tree, renderer, theme, style, layout, cursor, viewport) } fn mouse_interaction( &self, tree: &Tree, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { - self.widget.mouse_interaction( - tree, - layout, - cursor_position, - viewport, - renderer, - ) + self.widget + .mouse_interaction(tree, layout, cursor, viewport, renderer) } fn overlay<'b>( @@ -521,20 +507,14 @@ where state: &mut Tree, event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { - self.element.widget.on_event( - state, - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) + self.element + .widget + .on_event(state, event, layout, cursor, renderer, clipboard, shell) } fn draw( @@ -544,7 +524,7 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, ) { fn explain_layout<Renderer: crate::Renderer>( @@ -567,15 +547,9 @@ where } } - self.element.widget.draw( - state, - renderer, - theme, - style, - layout, - cursor_position, - viewport, - ); + self.element + .widget + .draw(state, renderer, theme, style, layout, cursor, viewport); explain_layout(renderer, self.color, layout); } @@ -584,17 +558,13 @@ where &self, state: &Tree, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { - self.element.widget.mouse_interaction( - state, - layout, - cursor_position, - viewport, - renderer, - ) + self.element + .widget + .mouse_interaction(state, layout, cursor, viewport, renderer) } fn overlay<'b>( diff --git a/core/src/gradient.rs b/core/src/gradient.rs index 61e919d6..e19622fb 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -1,27 +1,40 @@ -//! For creating a Gradient. -pub mod linear; +//! Colors that transition progressively. +use crate::{Color, Radians}; -pub use linear::Linear; +use std::cmp::Ordering; -use crate::{Color, Point, Size}; - -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] /// A fill which transitions colors progressively along a direction, either linearly, radially (TBD), /// or conically (TBD). +/// +/// For a gradient which can be used as a fill on a canvas, see [`iced_graphics::Gradient`]. pub enum Gradient { - /// A linear gradient interpolates colors along a direction from its `start` to its `end` - /// point. + /// A linear gradient interpolates colors along a direction at a specific [`Angle`]. Linear(Linear), } impl Gradient { - /// Creates a new linear [`linear::Builder`]. - pub fn linear(position: impl Into<Position>) -> linear::Builder { - linear::Builder::new(position.into()) + /// Adjust the opacity of the gradient by a multiplier applied to each color stop. + pub fn mul_alpha(mut self, alpha_multiplier: f32) -> Self { + match &mut self { + Gradient::Linear(linear) => { + for stop in linear.stops.iter_mut().flatten() { + stop.color.a *= alpha_multiplier; + } + } + } + + self } } -#[derive(Debug, Clone, Copy, PartialEq)] +impl From<Linear> for Gradient { + fn from(gradient: Linear) -> Self { + Self::Linear(gradient) + } +} + +#[derive(Debug, Default, Clone, Copy, PartialEq)] /// A point along the gradient vector where the specified [`color`] is unmixed. /// /// [`color`]: Self::color @@ -35,83 +48,58 @@ pub struct ColorStop { pub color: Color, } -#[derive(Debug)] -/// The position of the gradient within its bounds. -pub enum Position { - /// The gradient will be positioned with respect to two points. - Absolute { - /// The starting point of the gradient. - start: Point, - /// The ending point of the gradient. - end: Point, - }, - /// The gradient will be positioned relative to the provided bounds. - Relative { - /// The top left position of the bounds. - top_left: Point, - /// The width & height of the bounds. - size: Size, - /// The start [Location] of the gradient. - start: Location, - /// The end [Location] of the gradient. - end: Location, - }, +/// A linear gradient. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Linear { + /// How the [`Gradient`] is angled within its bounds. + pub angle: Radians, + /// [`ColorStop`]s along the linear gradient path. + pub stops: [Option<ColorStop>; 8], } -impl From<(Point, Point)> for Position { - fn from((start, end): (Point, Point)) -> Self { - Self::Absolute { start, end } +impl Linear { + /// Creates a new [`Linear`] gradient with the given angle in [`Radians`]. + pub fn new(angle: impl Into<Radians>) -> Self { + Self { + angle: angle.into(), + stops: [None; 8], + } } -} -#[derive(Debug, Clone, Copy)] -/// The location of a relatively-positioned gradient. -pub enum Location { - /// Top left. - TopLeft, - /// Top. - Top, - /// Top right. - TopRight, - /// Right. - Right, - /// Bottom right. - BottomRight, - /// Bottom. - Bottom, - /// Bottom left. - BottomLeft, - /// Left. - Left, -} + /// Adds a new [`ColorStop`], defined by an offset and a color, to the gradient. + /// + /// Any `offset` that is not within `0.0..=1.0` will be silently ignored. + /// + /// Any stop added after the 8th will be silently ignored. + pub fn add_stop(mut self, offset: f32, color: Color) -> Self { + if offset.is_finite() && (0.0..=1.0).contains(&offset) { + let (Ok(index) | Err(index)) = + self.stops.binary_search_by(|stop| match stop { + None => Ordering::Greater, + Some(stop) => stop.offset.partial_cmp(&offset).unwrap(), + }); -impl Location { - fn to_absolute(self, top_left: Point, size: Size) -> Point { - match self { - Location::TopLeft => top_left, - Location::Top => { - Point::new(top_left.x + size.width / 2.0, top_left.y) - } - Location::TopRight => { - Point::new(top_left.x + size.width, top_left.y) - } - Location::Right => Point::new( - top_left.x + size.width, - top_left.y + size.height / 2.0, - ), - Location::BottomRight => { - Point::new(top_left.x + size.width, top_left.y + size.height) - } - Location::Bottom => Point::new( - top_left.x + size.width / 2.0, - top_left.y + size.height, - ), - Location::BottomLeft => { - Point::new(top_left.x, top_left.y + size.height) - } - Location::Left => { - Point::new(top_left.x, top_left.y + size.height / 2.0) + if index < 8 { + self.stops[index] = Some(ColorStop { offset, color }); } + } else { + log::warn!("Gradient color stop must be within 0.0..=1.0 range."); + }; + + self + } + + /// Adds multiple [`ColorStop`]s to the gradient. + /// + /// Any stop added after the 8th will be silently ignored. + pub fn add_stops( + mut self, + stops: impl IntoIterator<Item = ColorStop>, + ) -> Self { + for stop in stops.into_iter() { + self = self.add_stop(stop.offset, stop.color) } + + self } } diff --git a/core/src/gradient/linear.rs b/core/src/gradient/linear.rs deleted file mode 100644 index c886db47..00000000 --- a/core/src/gradient/linear.rs +++ /dev/null @@ -1,112 +0,0 @@ -//! Linear gradient builder & definition. -use crate::gradient::{ColorStop, Gradient, Position}; -use crate::{Color, Point}; - -/// A linear gradient that can be used in the style of [`Fill`] or [`Stroke`]. -/// -/// [`Fill`]: crate::widget::canvas::Fill -/// [`Stroke`]: crate::widget::canvas::Stroke -#[derive(Debug, Clone, PartialEq)] -pub struct Linear { - /// The point where the linear gradient begins. - pub start: Point, - /// The point where the linear gradient ends. - pub end: Point, - /// [`ColorStop`]s along the linear gradient path. - pub color_stops: Vec<ColorStop>, -} - -/// A [`Linear`] builder. -#[derive(Debug)] -pub struct Builder { - start: Point, - end: Point, - stops: Vec<ColorStop>, - error: Option<BuilderError>, -} - -impl Builder { - /// Creates a new [`Builder`]. - pub fn new(position: Position) -> Self { - let (start, end) = match position { - Position::Absolute { start, end } => (start, end), - Position::Relative { - top_left, - size, - start, - end, - } => ( - start.to_absolute(top_left, size), - end.to_absolute(top_left, size), - ), - }; - - Self { - start, - end, - stops: vec![], - error: None, - } - } - - /// Adds a new stop, defined by an offset and a color, to the gradient. - /// - /// `offset` must be between `0.0` and `1.0` or the gradient cannot be built. - /// - /// Note: when using the [`glow`] backend, any color stop added after the 16th - /// will not be displayed. - /// - /// On the [`wgpu`] backend this limitation does not exist (technical limit is 524,288 stops). - /// - /// [`glow`]: https://docs.rs/iced_glow - /// [`wgpu`]: https://docs.rs/iced_wgpu - pub fn add_stop(mut self, offset: f32, color: Color) -> Self { - if offset.is_finite() && (0.0..=1.0).contains(&offset) { - match self.stops.binary_search_by(|stop| { - stop.offset.partial_cmp(&offset).unwrap() - }) { - Ok(_) => { - self.error = Some(BuilderError::DuplicateOffset(offset)) - } - Err(index) => { - self.stops.insert(index, ColorStop { offset, color }); - } - } - } else { - self.error = Some(BuilderError::InvalidOffset(offset)) - }; - - self - } - - /// Builds the linear [`Gradient`] of this [`Builder`]. - /// - /// Returns `BuilderError` if gradient in invalid. - pub fn build(self) -> Result<Gradient, BuilderError> { - if self.stops.is_empty() { - Err(BuilderError::MissingColorStop) - } else if let Some(error) = self.error { - Err(error) - } else { - Ok(Gradient::Linear(Linear { - start: self.start, - end: self.end, - color_stops: self.stops, - })) - } - } -} - -/// An error that happened when building a [`Linear`] gradient. -#[derive(Debug, thiserror::Error)] -pub enum BuilderError { - #[error("Gradients must contain at least one color stop.")] - /// Gradients must contain at least one color stop. - MissingColorStop, - #[error("Offset {0} must be a unique, finite number.")] - /// Offsets in a gradient must all be unique & finite. - DuplicateOffset(f32), - #[error("Offset {0} must be between 0.0..=1.0.")] - /// Offsets in a gradient must be between 0.0..=1.0. - InvalidOffset(f32), -} diff --git a/core/src/lib.rs b/core/src/lib.rs index 89dfb828..76d775e7 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -42,7 +42,9 @@ pub mod touch; pub mod widget; pub mod window; +mod angle; mod background; +mod border_radius; mod color; mod content_fit; mod element; @@ -57,7 +59,9 @@ mod size; mod vector; pub use alignment::Alignment; +pub use angle::{Degrees, Radians}; pub use background::Background; +pub use border_radius::BorderRadius; pub use clipboard::Clipboard; pub use color::Color; pub use content_fit::ContentFit; diff --git a/core/src/mouse.rs b/core/src/mouse.rs index 0c405ce6..d13a60fb 100644 --- a/core/src/mouse.rs +++ b/core/src/mouse.rs @@ -2,10 +2,12 @@ pub mod click; mod button; +mod cursor; mod event; mod interaction; pub use button::Button; pub use click::Click; +pub use cursor::Cursor; pub use event::{Event, ScrollDelta}; pub use interaction::Interaction; diff --git a/core/src/mouse/button.rs b/core/src/mouse/button.rs index aeb8a55d..3eec7f42 100644 --- a/core/src/mouse/button.rs +++ b/core/src/mouse/button.rs @@ -11,5 +11,5 @@ pub enum Button { Middle, /// Some other button. - Other(u8), + Other(u16), } diff --git a/core/src/mouse/cursor.rs b/core/src/mouse/cursor.rs new file mode 100644 index 00000000..203526e9 --- /dev/null +++ b/core/src/mouse/cursor.rs @@ -0,0 +1,52 @@ +use crate::{Point, Rectangle, Vector}; + +/// The mouse cursor state. +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub enum Cursor { + /// The cursor has a defined position. + Available(Point), + + /// The cursor is currently unavailable (i.e. out of bounds or busy). + #[default] + Unavailable, +} + +impl Cursor { + /// Returns the absolute position of the [`Cursor`], if available. + pub fn position(self) -> Option<Point> { + match self { + Cursor::Available(position) => Some(position), + Cursor::Unavailable => None, + } + } + + /// Returns the absolute position of the [`Cursor`], if available and inside + /// the given bounds. + /// + /// If the [`Cursor`] is not over the provided bounds, this method will + /// return `None`. + pub fn position_over(self, bounds: Rectangle) -> Option<Point> { + self.position().filter(|p| bounds.contains(*p)) + } + + /// Returns the relative position of the [`Cursor`] inside the given bounds, + /// if available. + /// + /// If the [`Cursor`] is not over the provided bounds, this method will + /// return `None`. + pub fn position_in(self, bounds: Rectangle) -> Option<Point> { + self.position_over(bounds) + .map(|p| p - Vector::new(bounds.x, bounds.y)) + } + + /// Returns the relative position of the [`Cursor`] from the given origin, + /// if available. + pub fn position_from(self, origin: Point) -> Option<Point> { + self.position().map(|p| p - Vector::new(origin.x, origin.y)) + } + + /// Returns true if the [`Cursor`] is over the given `bounds`. + pub fn is_over(self, bounds: Rectangle) -> bool { + self.position_over(bounds).is_some() + } +} diff --git a/core/src/overlay.rs b/core/src/overlay.rs index b9f3c735..2e05db93 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -38,7 +38,7 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, ); /// Applies a [`widget::Operation`] to the [`Overlay`]. @@ -66,7 +66,7 @@ where &mut self, _event: Event, _layout: Layout<'_>, - _cursor_position: Point, + _cursor: mouse::Cursor, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, _shell: &mut Shell<'_, Message>, @@ -80,7 +80,7 @@ where fn mouse_interaction( &self, _layout: Layout<'_>, - _cursor_position: Point, + _cursor: mouse::Cursor, _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { @@ -91,9 +91,23 @@ where /// /// By default, it returns true if the bounds of the `layout` contain /// the `cursor_position`. - fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { + fn is_over( + &self, + layout: Layout<'_>, + _renderer: &Renderer, + cursor_position: Point, + ) -> bool { layout.bounds().contains(cursor_position) } + + /// Returns the nested overlay of the [`Overlay`], if there is any. + fn overlay<'a>( + &'a mut self, + _layout: Layout<'_>, + _renderer: &Renderer, + ) -> Option<Element<'a, Message, Renderer>> { + None + } } /// Returns a [`Group`] of overlay [`Element`] children. diff --git a/core/src/overlay/element.rs b/core/src/overlay/element.rs index 237d25d1..c2134343 100644 --- a/core/src/overlay/element.rs +++ b/core/src/overlay/element.rs @@ -68,35 +68,25 @@ where &mut self, event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { - self.overlay.on_event( - event, - layout, - cursor_position, - renderer, - clipboard, - shell, - ) + self.overlay + .on_event(event, layout, cursor, renderer, clipboard, shell) } /// Returns the current [`mouse::Interaction`] of the [`Element`]. pub fn mouse_interaction( &self, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { - self.overlay.mouse_interaction( - layout, - cursor_position, - viewport, - renderer, - ) + self.overlay + .mouse_interaction(layout, cursor, viewport, renderer) } /// Draws the [`Element`] and its children using the given [`Layout`]. @@ -106,10 +96,9 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, ) { - self.overlay - .draw(renderer, theme, style, layout, cursor_position) + self.overlay.draw(renderer, theme, style, layout, cursor) } /// Applies a [`widget::Operation`] to the [`Element`]. @@ -123,8 +112,22 @@ where } /// Returns true if the cursor is over the [`Element`]. - pub fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { - self.overlay.is_over(layout, cursor_position) + pub fn is_over( + &self, + layout: Layout<'_>, + renderer: &Renderer, + cursor_position: Point, + ) -> bool { + self.overlay.is_over(layout, renderer, cursor_position) + } + + /// Returns the nested overlay of the [`Element`], if there is any. + pub fn overlay<'b>( + &'b mut self, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option<Element<'b, Message, Renderer>> { + self.overlay.overlay(layout, renderer) } } @@ -215,7 +218,7 @@ where &mut self, event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, B>, @@ -226,7 +229,7 @@ where let event_status = self.content.on_event( event, layout, - cursor_position, + cursor, renderer, clipboard, &mut local_shell, @@ -240,16 +243,12 @@ where fn mouse_interaction( &self, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { - self.content.mouse_interaction( - layout, - cursor_position, - viewport, - renderer, - ) + self.content + .mouse_interaction(layout, cursor, viewport, renderer) } fn draw( @@ -258,13 +257,27 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, ) { - self.content - .draw(renderer, theme, style, layout, cursor_position) + self.content.draw(renderer, theme, style, layout, cursor) } - fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { - self.content.is_over(layout, cursor_position) + fn is_over( + &self, + layout: Layout<'_>, + renderer: &Renderer, + cursor_position: Point, + ) -> bool { + self.content.is_over(layout, renderer, cursor_position) + } + + fn overlay<'b>( + &'b mut self, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option<Element<'b, B, Renderer>> { + self.content + .overlay(layout, renderer) + .map(|overlay| overlay.map(self.mapper)) } } diff --git a/core/src/overlay/group.rs b/core/src/overlay/group.rs index 0c48df34..deffaad0 100644 --- a/core/src/overlay/group.rs +++ b/core/src/overlay/group.rs @@ -81,7 +81,7 @@ where &mut self, event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, @@ -93,7 +93,7 @@ where child.on_event( event.clone(), layout, - cursor_position, + cursor, renderer, clipboard, shell, @@ -108,17 +108,17 @@ where theme: &<Renderer as crate::Renderer>::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, ) { for (child, layout) in self.children.iter().zip(layout.children()) { - child.draw(renderer, theme, style, layout, cursor_position); + child.draw(renderer, theme, style, layout, cursor); } } fn mouse_interaction( &self, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { @@ -126,12 +126,7 @@ where .iter() .zip(layout.children()) .map(|(child, layout)| { - child.mouse_interaction( - layout, - cursor_position, - viewport, - renderer, - ) + child.mouse_interaction(layout, cursor, viewport, renderer) }) .max() .unwrap_or_default() @@ -152,11 +147,33 @@ where }); } - fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { + fn is_over( + &self, + layout: Layout<'_>, + renderer: &Renderer, + cursor_position: Point, + ) -> bool { self.children .iter() .zip(layout.children()) - .any(|(child, layout)| child.is_over(layout, cursor_position)) + .any(|(child, layout)| { + child.is_over(layout, renderer, cursor_position) + }) + } + + fn overlay<'b>( + &'b mut self, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option<overlay::Element<'b, Message, Renderer>> { + let children = self + .children + .iter_mut() + .zip(layout.children()) + .filter_map(|(child, layout)| child.overlay(layout, renderer)) + .collect::<Vec<_>>(); + + (!children.is_empty()).then(|| Group::with_children(children).overlay()) } } diff --git a/core/src/renderer.rs b/core/src/renderer.rs index d6247e39..7c73d2e4 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -6,7 +6,7 @@ mod null; pub use null::Null; use crate::layout; -use crate::{Background, Color, Element, Rectangle, Vector}; +use crate::{Background, BorderRadius, Color, Element, Rectangle, Vector}; /// A component that can be used by widgets to draw themselves on a screen. pub trait Renderer: Sized { @@ -60,29 +60,6 @@ pub struct Quad { pub border_color: Color, } -/// The border radi for the corners of a graphics primitive in the order: -/// top-left, top-right, bottom-right, bottom-left. -#[derive(Debug, Clone, Copy, PartialEq, Default)] -pub struct BorderRadius([f32; 4]); - -impl From<f32> for BorderRadius { - fn from(w: f32) -> Self { - Self([w; 4]) - } -} - -impl From<[f32; 4]> for BorderRadius { - fn from(radi: [f32; 4]) -> Self { - Self(radi) - } -} - -impl From<BorderRadius> for [f32; 4] { - fn from(radi: BorderRadius) -> Self { - radi.0 - } -} - /// The styling attributes of a [`Renderer`]. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Style { diff --git a/core/src/widget.rs b/core/src/widget.rs index 769f8659..79d86444 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -15,7 +15,7 @@ use crate::layout::{self, Layout}; use crate::mouse; use crate::overlay; use crate::renderer; -use crate::{Clipboard, Length, Point, Rectangle, Shell}; +use crate::{Clipboard, Length, Rectangle, Shell}; /// A component that displays information and allows interaction. /// @@ -67,7 +67,7 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, ); @@ -111,7 +111,7 @@ where _state: &mut Tree, _event: Event, _layout: Layout<'_>, - _cursor_position: Point, + _cursor: mouse::Cursor, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, _shell: &mut Shell<'_, Message>, @@ -126,7 +126,7 @@ where &self, _state: &Tree, _layout: Layout<'_>, - _cursor_position: Point, + _cursor: mouse::Cursor, _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 90af88b7..e934a2f5 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -1,12 +1,11 @@ //! Write some text for your users to read. use crate::alignment; use crate::layout; +use crate::mouse; use crate::renderer; use crate::text; use crate::widget::Tree; -use crate::{ - Color, Element, Layout, Length, Pixels, Point, Rectangle, Size, Widget, -}; +use crate::{Color, Element, Layout, Length, Pixels, Rectangle, Size, Widget}; use std::borrow::Cow; @@ -163,7 +162,7 @@ where theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - _cursor_position: Point, + _cursor_position: mouse::Cursor, _viewport: &Rectangle, ) { draw( diff --git a/core/src/window.rs b/core/src/window.rs index 81bd7e3d..a6dbdfb4 100644 --- a/core/src/window.rs +++ b/core/src/window.rs @@ -2,12 +2,14 @@ pub mod icon; mod event; +mod level; mod mode; mod redraw_request; mod user_attention; pub use event::Event; pub use icon::Icon; +pub use level::Level; pub use mode::Mode; pub use redraw_request::RedrawRequest; pub use user_attention::UserAttention; diff --git a/core/src/window/level.rs b/core/src/window/level.rs new file mode 100644 index 00000000..3878ecac --- /dev/null +++ b/core/src/window/level.rs @@ -0,0 +1,19 @@ +/// A window level groups windows with respect to their z-position. +/// +/// The relative ordering between windows in different window levels is fixed. +/// The z-order of a window within the same window level may change dynamically +/// on user interaction. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum Level { + /// The default behavior. + #[default] + Normal, + + /// The window will always be below normal windows. + /// + /// This is useful for a widget-based app. + AlwaysOnBottom, + + /// The window will always be on top of normal windows. + AlwaysOnTop, +} |