summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/Cargo.toml6
-rw-r--r--core/src/angle.rs33
-rw-r--r--core/src/background.rs19
-rw-r--r--core/src/border_radius.rs22
-rw-r--r--core/src/color.rs40
-rw-r--r--core/src/element.rs72
-rw-r--r--core/src/gradient.rs154
-rw-r--r--core/src/gradient/linear.rs112
-rw-r--r--core/src/lib.rs4
-rw-r--r--core/src/mouse.rs2
-rw-r--r--core/src/mouse/button.rs2
-rw-r--r--core/src/mouse/cursor.rs52
-rw-r--r--core/src/overlay.rs22
-rw-r--r--core/src/overlay/element.rs83
-rw-r--r--core/src/overlay/group.rs43
-rw-r--r--core/src/renderer.rs25
-rw-r--r--core/src/widget.rs8
-rw-r--r--core/src/widget/text.rs7
-rw-r--r--core/src/window.rs2
-rw-r--r--core/src/window/level.rs19
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,
+}