summaryrefslogtreecommitdiffstats
path: root/native
diff options
context:
space:
mode:
Diffstat (limited to 'native')
-rw-r--r--native/Cargo.toml4
-rw-r--r--native/src/element.rs20
-rw-r--r--native/src/layout/limits.rs22
-rw-r--r--native/src/lib.rs4
-rw-r--r--native/src/overlay.rs9
-rw-r--r--native/src/overlay/element.rs10
-rw-r--r--native/src/overlay/menu.rs4
-rw-r--r--native/src/renderer.rs25
-rw-r--r--native/src/subscription.rs2
-rw-r--r--native/src/svg.rs6
-rw-r--r--native/src/user_interface.rs41
-rw-r--r--native/src/widget.rs16
-rw-r--r--native/src/widget/button.rs10
-rw-r--r--native/src/widget/checkbox.rs2
-rw-r--r--native/src/widget/column.rs9
-rw-r--r--native/src/widget/container.rs8
-rw-r--r--native/src/widget/helpers.rs27
-rw-r--r--native/src/widget/image/viewer.rs13
-rw-r--r--native/src/widget/pane_grid.rs15
-rw-r--r--native/src/widget/pane_grid/content.rs11
-rw-r--r--native/src/widget/pane_grid/title_bar.rs11
-rw-r--r--native/src/widget/pick_list.rs4
-rw-r--r--native/src/widget/progress_bar.rs6
-rw-r--r--native/src/widget/radio.rs4
-rw-r--r--native/src/widget/row.rs9
-rw-r--r--native/src/widget/rule.rs2
-rw-r--r--native/src/widget/scrollable.rs20
-rw-r--r--native/src/widget/slider.rs6
-rw-r--r--native/src/widget/svg.rs54
-rw-r--r--native/src/widget/text_input.rs19
-rw-r--r--native/src/widget/toggler.rs26
-rw-r--r--native/src/widget/tooltip.rs4
-rw-r--r--native/src/widget/vertical_slider.rs470
-rw-r--r--native/src/window.rs2
-rw-r--r--native/src/window/action.rs41
-rw-r--r--native/src/window/user_attention.rs21
36 files changed, 800 insertions, 157 deletions
diff --git a/native/Cargo.toml b/native/Cargo.toml
index 558909be..bbf92951 100644
--- a/native/Cargo.toml
+++ b/native/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "iced_native"
-version = "0.6.1"
+version = "0.7.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
description = "A renderer-agnostic library for native GUIs"
@@ -25,5 +25,5 @@ path = "../futures"
features = ["thread-pool"]
[dependencies.iced_style]
-version = "0.5"
+version = "0.5.1"
path = "../style"
diff --git a/native/src/element.rs b/native/src/element.rs
index f941a490..2409b1c9 100644
--- a/native/src/element.rs
+++ b/native/src/element.rs
@@ -290,6 +290,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<B>,
) {
struct MapOperation<'a, B> {
@@ -334,8 +335,12 @@ where
}
}
- self.widget
- .operate(tree, layout, &mut MapOperation { operation });
+ self.widget.operate(
+ tree,
+ layout,
+ renderer,
+ &mut MapOperation { operation },
+ );
}
fn on_event(
@@ -405,7 +410,7 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@@ -473,9 +478,12 @@ where
&self,
state: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
- self.element.widget.operate(state, layout, operation)
+ self.element
+ .widget
+ .operate(state, layout, renderer, operation)
}
fn on_event(
@@ -519,7 +527,7 @@ where
bounds: layout.bounds(),
border_color: color,
border_width: 1.0,
- border_radius: 0.0,
+ border_radius: 0.0.into(),
},
Color::TRANSPARENT,
);
@@ -560,7 +568,7 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
state: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
diff --git a/native/src/layout/limits.rs b/native/src/layout/limits.rs
index 6d5f6563..33a452d0 100644
--- a/native/src/layout/limits.rs
+++ b/native/src/layout/limits.rs
@@ -51,7 +51,7 @@ impl Limits {
}
Length::Units(units) => {
let new_width =
- (units as f32).min(self.max.width).max(self.min.width);
+ (units as f32).clamp(self.min.width, self.max.width);
self.min.width = new_width;
self.max.width = new_width;
@@ -73,7 +73,7 @@ impl Limits {
}
Length::Units(units) => {
let new_height =
- (units as f32).min(self.max.height).max(self.min.height);
+ (units as f32).clamp(self.min.height, self.max.height);
self.min.height = new_height;
self.max.height = new_height;
@@ -86,16 +86,14 @@ impl Limits {
/// Applies a minimum width constraint to the current [`Limits`].
pub fn min_width(mut self, min_width: u32) -> Limits {
- self.min.width =
- self.min.width.max(min_width as f32).min(self.max.width);
+ self.min.width = self.min.width.clamp(min_width as f32, self.max.width);
self
}
/// Applies a maximum width constraint to the current [`Limits`].
pub fn max_width(mut self, max_width: u32) -> Limits {
- self.max.width =
- self.max.width.min(max_width as f32).max(self.min.width);
+ self.max.width = self.max.width.clamp(self.min.width, max_width as f32);
self
}
@@ -103,7 +101,7 @@ impl Limits {
/// Applies a minimum height constraint to the current [`Limits`].
pub fn min_height(mut self, min_height: u32) -> Limits {
self.min.height =
- self.min.height.max(min_height as f32).min(self.max.height);
+ self.min.height.clamp(min_height as f32, self.max.height);
self
}
@@ -111,7 +109,7 @@ impl Limits {
/// Applies a maximum height constraint to the current [`Limits`].
pub fn max_height(mut self, max_height: u32) -> Limits {
self.max.height =
- self.max.height.min(max_height as f32).max(self.min.height);
+ self.max.height.clamp(self.min.height, max_height as f32);
self
}
@@ -157,14 +155,10 @@ impl Limits {
/// intrinsic size of some content.
pub fn resolve(&self, intrinsic_size: Size) -> Size {
Size::new(
- intrinsic_size
- .width
- .min(self.max.width)
- .max(self.fill.width),
+ intrinsic_size.width.clamp(self.fill.width, self.max.width),
intrinsic_size
.height
- .min(self.max.height)
- .max(self.fill.height),
+ .clamp(self.fill.height, self.max.height),
)
}
}
diff --git a/native/src/lib.rs b/native/src/lib.rs
index 62b8f372..ce7c010d 100644
--- a/native/src/lib.rs
+++ b/native/src/lib.rs
@@ -23,8 +23,8 @@
//! - Build a new renderer, see the [renderer] module.
//! - Build a custom widget, start at the [`Widget`] trait.
//!
-//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.5/core
-//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.5/winit
+//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.6/core
+//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.6/winit
//! [`druid`]: https://github.com/xi-editor/druid
//! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle
//! [renderer]: crate::renderer
diff --git a/native/src/overlay.rs b/native/src/overlay.rs
index 8c01581f..22f8b6ec 100644
--- a/native/src/overlay.rs
+++ b/native/src/overlay.rs
@@ -44,8 +44,9 @@ where
/// Applies a [`widget::Operation`] to the [`Overlay`].
fn operate(
- &self,
+ &mut self,
_layout: Layout<'_>,
+ _renderer: &Renderer,
_operation: &mut dyn widget::Operation<Message>,
) {
}
@@ -93,7 +94,7 @@ where
/// This method will generally only be used by advanced users that are
/// implementing the [`Widget`](crate::Widget) trait.
pub fn from_children<'a, Message, Renderer>(
- children: &'a [crate::Element<'_, Message, Renderer>],
+ children: &'a mut [crate::Element<'_, Message, Renderer>],
tree: &'a mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@@ -102,11 +103,11 @@ where
Renderer: crate::Renderer,
{
children
- .iter()
+ .iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.filter_map(|((child, state), layout)| {
- child.as_widget().overlay(state, layout, renderer)
+ child.as_widget_mut().overlay(state, layout, renderer)
})
.next()
}
diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs
index 09eee86d..498e9ae3 100644
--- a/native/src/overlay/element.rs
+++ b/native/src/overlay/element.rs
@@ -106,11 +106,12 @@ where
/// Applies a [`widget::Operation`] to the [`Element`].
pub fn operate(
- &self,
+ &mut self,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
- self.overlay.operate(layout, operation);
+ self.overlay.operate(layout, renderer, operation);
}
}
@@ -142,8 +143,9 @@ where
}
fn operate(
- &self,
+ &mut self,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<B>,
) {
struct MapOperation<'a, B> {
@@ -189,7 +191,7 @@ where
}
self.content
- .operate(layout, &mut MapOperation { operation });
+ .operate(layout, renderer, &mut MapOperation { operation });
}
fn on_event(
diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs
index 2e4f70b5..099b1a97 100644
--- a/native/src/overlay/menu.rs
+++ b/native/src/overlay/menu.rs
@@ -287,7 +287,7 @@ where
},
border_color: appearance.border_color,
border_width: appearance.border_width,
- border_radius: appearance.border_radius,
+ border_radius: appearance.border_radius.into(),
},
appearance.background,
);
@@ -479,7 +479,7 @@ where
bounds,
border_color: Color::TRANSPARENT,
border_width: 0.0,
- border_radius: appearance.border_radius,
+ border_radius: appearance.border_radius.into(),
},
appearance.selected_background,
);
diff --git a/native/src/renderer.rs b/native/src/renderer.rs
index ef64ac36..5e776be6 100644
--- a/native/src/renderer.rs
+++ b/native/src/renderer.rs
@@ -50,7 +50,7 @@ pub struct Quad {
pub bounds: Rectangle,
/// The border radius of the [`Quad`].
- pub border_radius: f32,
+ pub border_radius: BorderRadius,
/// The border width of the [`Quad`].
pub border_width: f32,
@@ -59,6 +59,29 @@ 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/native/src/subscription.rs b/native/src/subscription.rs
index d24801d4..c60b1281 100644
--- a/native/src/subscription.rs
+++ b/native/src/subscription.rs
@@ -155,7 +155,7 @@ where
/// Check out the [`websocket`] example, which showcases this pattern to maintain a WebSocket
/// connection open.
///
-/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.5/examples/websocket
+/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.6/examples/websocket
pub fn unfold<I, T, Fut, Message>(
id: I,
initial: T,
diff --git a/native/src/svg.rs b/native/src/svg.rs
index a8e481d2..2168e409 100644
--- a/native/src/svg.rs
+++ b/native/src/svg.rs
@@ -1,5 +1,5 @@
//! Load and draw vector graphics.
-use crate::{Hasher, Rectangle, Size};
+use crate::{Color, Hasher, Rectangle, Size};
use std::borrow::Cow;
use std::hash::{Hash, Hasher as _};
@@ -84,6 +84,6 @@ pub trait Renderer: crate::Renderer {
/// Returns the default dimensions of an SVG for the given [`Handle`].
fn dimensions(&self, handle: &Handle) -> Size<u32>;
- /// Draws an SVG with the given [`Handle`] and inside the provided `bounds`.
- fn draw(&mut self, handle: Handle, bounds: Rectangle);
+ /// 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);
}
diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs
index 7c82878c..2b43829d 100644
--- a/native/src/user_interface.rs
+++ b/native/src/user_interface.rs
@@ -18,8 +18,8 @@ use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
/// The [`integration_opengl`] & [`integration_wgpu`] examples use a
/// [`UserInterface`] to integrate Iced in an existing graphical application.
///
-/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.5/examples/integration_opengl
-/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.5/examples/integration_wgpu
+/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.6/examples/integration_opengl
+/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/examples/integration_wgpu
#[allow(missing_debug_implementations)]
pub struct UserInterface<'a, Message, Renderer> {
root: Element<'a, Message, Renderer>,
@@ -190,7 +190,7 @@ where
let mut state = State::Updated;
let mut manual_overlay =
- ManuallyDrop::new(self.root.as_widget().overlay(
+ ManuallyDrop::new(self.root.as_widget_mut().overlay(
&mut self.state,
Layout::new(&self.base),
renderer,
@@ -226,7 +226,7 @@ where
);
manual_overlay =
- ManuallyDrop::new(self.root.as_widget().overlay(
+ ManuallyDrop::new(self.root.as_widget_mut().overlay(
&mut self.state,
Layout::new(&self.base),
renderer,
@@ -395,11 +395,11 @@ where
let viewport = Rectangle::with_size(self.bounds);
- let base_cursor = if let Some(overlay) = self.root.as_widget().overlay(
- &mut self.state,
- Layout::new(&self.base),
- renderer,
- ) {
+ let base_cursor = if let Some(overlay) = self
+ .root
+ .as_widget_mut()
+ .overlay(&mut self.state, Layout::new(&self.base), renderer)
+ {
let overlay_layout = self
.overlay
.take()
@@ -452,7 +452,7 @@ where
overlay
.as_ref()
.and_then(|layout| {
- root.as_widget()
+ root.as_widget_mut()
.overlay(&mut self.state, Layout::new(base), renderer)
.map(|overlay| {
let overlay_interaction = overlay.mouse_interaction(
@@ -493,17 +493,24 @@ where
self.root.as_widget().operate(
&mut self.state,
Layout::new(&self.base),
+ renderer,
operation,
);
- if let Some(layout) = self.overlay.as_ref() {
- if let Some(overlay) = self.root.as_widget().overlay(
- &mut self.state,
- Layout::new(&self.base),
- renderer,
- ) {
- overlay.operate(Layout::new(layout), operation);
+ if let Some(mut overlay) = self.root.as_widget_mut().overlay(
+ &mut self.state,
+ Layout::new(&self.base),
+ renderer,
+ ) {
+ if self.overlay.is_none() {
+ self.overlay = Some(overlay.layout(renderer, self.bounds));
}
+
+ overlay.operate(
+ Layout::new(self.overlay.as_ref().unwrap()),
+ renderer,
+ operation,
+ );
}
}
diff --git a/native/src/widget.rs b/native/src/widget.rs
index 526c7d00..f714e28a 100644
--- a/native/src/widget.rs
+++ b/native/src/widget.rs
@@ -33,6 +33,7 @@ pub mod text_input;
pub mod toggler;
pub mod tooltip;
pub mod tree;
+pub mod vertical_slider;
mod action;
mod id;
@@ -79,6 +80,8 @@ pub use toggler::Toggler;
pub use tooltip::Tooltip;
#[doc(no_inline)]
pub use tree::Tree;
+#[doc(no_inline)]
+pub use vertical_slider::VerticalSlider;
pub use action::Action;
pub use id::Id;
@@ -107,12 +110,12 @@ use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell};
/// - [`geometry`], a custom widget showcasing how to draw geometry with the
/// `Mesh2D` primitive in [`iced_wgpu`].
///
-/// [examples]: https://github.com/iced-rs/iced/tree/0.5/examples
-/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.5/examples/bezier_tool
-/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.5/examples/custom_widget
-/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.5/examples/geometry
+/// [examples]: https://github.com/iced-rs/iced/tree/0.6/examples
+/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.6/examples/bezier_tool
+/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.6/examples/custom_widget
+/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.6/examples/geometry
/// [`lyon`]: https://github.com/nical/lyon
-/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.5/wgpu
+/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.6/wgpu
pub trait Widget<Message, Renderer>
where
Renderer: crate::Renderer,
@@ -172,6 +175,7 @@ where
&self,
_state: &mut Tree,
_layout: Layout<'_>,
+ _renderer: &Renderer,
_operation: &mut dyn Operation<Message>,
) {
}
@@ -208,7 +212,7 @@ where
/// Returns the overlay of the [`Widget`], if there is any.
fn overlay<'a>(
- &'a self,
+ &'a mut self,
_state: &'a mut Tree,
_layout: Layout<'_>,
_renderer: &Renderer,
diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs
index fa5da24b..b4276317 100644
--- a/native/src/widget/button.rs
+++ b/native/src/widget/button.rs
@@ -169,12 +169,14 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
operation.container(None, &mut |operation| {
self.content.as_widget().operate(
&mut tree.children[0],
layout.children().next().unwrap(),
+ renderer,
operation,
);
});
@@ -260,12 +262,12 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- self.content.as_widget().overlay(
+ self.content.as_widget_mut().overlay(
&mut tree.children[0],
layout.children().next().unwrap(),
renderer,
@@ -393,7 +395,7 @@ where
y: bounds.y + styling.shadow_offset.y,
..bounds
},
- border_radius: styling.border_radius,
+ border_radius: styling.border_radius.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
@@ -404,7 +406,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: styling.border_radius,
+ border_radius: styling.border_radius.into(),
border_width: styling.border_width,
border_color: styling.border_color,
},
diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs
index 77d639a9..bec5c448 100644
--- a/native/src/widget/checkbox.rs
+++ b/native/src/widget/checkbox.rs
@@ -236,7 +236,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: custom_style.border_radius,
+ border_radius: custom_style.border_radius.into(),
border_width: custom_style.border_width,
border_color: custom_style.border_color,
},
diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs
index a8b0f183..f2ef132a 100644
--- a/native/src/widget/column.rs
+++ b/native/src/widget/column.rs
@@ -147,6 +147,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
operation.container(None, &mut |operation| {
@@ -155,7 +156,9 @@ where
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), layout)| {
- child.as_widget().operate(state, layout, operation);
+ child
+ .as_widget()
+ .operate(state, layout, renderer, operation);
})
});
}
@@ -242,12 +245,12 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- overlay::from_children(&self.children, tree, layout, renderer)
+ overlay::from_children(&mut self.children, tree, layout, renderer)
}
}
diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs
index 9d3e4d9b..cdf1c859 100644
--- a/native/src/widget/container.rs
+++ b/native/src/widget/container.rs
@@ -169,12 +169,14 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
operation.container(None, &mut |operation| {
self.content.as_widget().operate(
&mut tree.children[0],
layout.children().next().unwrap(),
+ renderer,
operation,
);
});
@@ -248,12 +250,12 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- self.content.as_widget().overlay(
+ self.content.as_widget_mut().overlay(
&mut tree.children[0],
layout.children().next().unwrap(),
renderer,
@@ -321,7 +323,7 @@ pub fn draw_background<Renderer>(
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: appearance.border_radius,
+ border_radius: appearance.border_radius.into(),
border_width: appearance.border_width,
border_color: appearance.border_color,
},
diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs
index 3bce9e60..5b241f83 100644
--- a/native/src/widget/helpers.rs
+++ b/native/src/widget/helpers.rs
@@ -162,7 +162,7 @@ where
Renderer: crate::text::Renderer,
Renderer::Theme: widget::toggler::StyleSheet,
{
- widget::Toggler::new(is_checked, label, f)
+ widget::Toggler::new(label, is_checked, f)
}
/// Creates a new [`TextInput`].
@@ -198,6 +198,23 @@ where
widget::Slider::new(range, value, on_change)
}
+/// Creates a new [`VerticalSlider`].
+///
+/// [`VerticalSlider`]: widget::VerticalSlider
+pub fn vertical_slider<'a, T, Message, Renderer>(
+ range: std::ops::RangeInclusive<T>,
+ value: T,
+ on_change: impl Fn(T) -> Message + 'a,
+) -> widget::VerticalSlider<'a, T, Message, Renderer>
+where
+ T: Copy + From<u8> + std::cmp::PartialOrd,
+ Message: Clone,
+ Renderer: crate::Renderer,
+ Renderer::Theme: widget::slider::StyleSheet,
+{
+ widget::VerticalSlider::new(range, value, on_change)
+}
+
/// Creates a new [`PickList`].
///
/// [`PickList`]: widget::PickList
@@ -285,6 +302,12 @@ where
///
/// [`Svg`]: widget::Svg
/// [`Handle`]: widget::svg::Handle
-pub fn svg(handle: impl Into<widget::svg::Handle>) -> widget::Svg {
+pub fn svg<Renderer>(
+ handle: impl Into<widget::svg::Handle>,
+) -> widget::Svg<Renderer>
+where
+ Renderer: crate::svg::Renderer,
+ Renderer::Theme: widget::svg::StyleSheet,
+{
widget::Svg::new(handle)
}
diff --git a/native/src/widget/image/viewer.rs b/native/src/widget/image/viewer.rs
index 9c83287e..fdbd3216 100644
--- a/native/src/widget/image/viewer.rs
+++ b/native/src/widget/image/viewer.rs
@@ -170,8 +170,7 @@ where
} else {
state.scale / (1.0 + self.scale_step)
})
- .max(self.min_scale)
- .min(self.max_scale);
+ .clamp(self.min_scale, self.max_scale);
let image_size = image_size(
renderer,
@@ -251,16 +250,14 @@ where
let x = if bounds.width < image_size.width {
(state.starting_offset.x - delta.x)
- .min(hidden_width)
- .max(-hidden_width)
+ .clamp(-hidden_width, hidden_width)
} else {
0.0
};
let y = if bounds.height < image_size.height {
(state.starting_offset.y - delta.y)
- .min(hidden_height)
- .max(-hidden_height)
+ .clamp(-hidden_height, hidden_height)
} else {
0.0
};
@@ -374,8 +371,8 @@ impl State {
(image_size.height - bounds.height / 2.0).max(0.0).round();
Vector::new(
- self.current_offset.x.min(hidden_width).max(-hidden_width),
- self.current_offset.y.min(hidden_height).max(-hidden_height),
+ self.current_offset.x.clamp(-hidden_width, hidden_width),
+ self.current_offset.y.clamp(-hidden_height, hidden_height),
)
}
diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs
index 8f9065b0..f8dbab74 100644
--- a/native/src/widget/pane_grid.rs
+++ b/native/src/widget/pane_grid.rs
@@ -6,7 +6,7 @@
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
//! drag and drop, and hotkey support.
//!
-//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.5/examples/pane_grid
+//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.6/examples/pane_grid
mod axis;
mod configuration;
mod content;
@@ -294,6 +294,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
operation.container(None, &mut |operation| {
@@ -302,7 +303,7 @@ where
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|(((_pane, content), state), layout)| {
- content.operate(state, layout, operation);
+ content.operate(state, layout, renderer, operation);
})
});
}
@@ -444,13 +445,13 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'_, Message, Renderer>> {
self.contents
- .iter()
+ .iter_mut()
.zip(&mut tree.children)
.zip(layout.children())
.filter_map(|(((_, pane), tree), layout)| {
@@ -630,13 +631,13 @@ pub fn update<'a, Message, T: Draggable>(
let position =
cursor_position.y - bounds.y - rectangle.y;
- (position / rectangle.height).max(0.1).min(0.9)
+ (position / rectangle.height).clamp(0.1, 0.9)
}
Axis::Vertical => {
let position =
cursor_position.x - bounds.x - rectangle.x;
- (position / rectangle.width).max(0.1).min(0.9)
+ (position / rectangle.width).clamp(0.1, 0.9)
}
};
@@ -877,7 +878,7 @@ pub fn draw<Renderer, T>(
height: split_region.height,
},
},
- border_radius: 0.0,
+ border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs
index 5e843cff..c9b0df07 100644
--- a/native/src/widget/pane_grid/content.rs
+++ b/native/src/widget/pane_grid/content.rs
@@ -187,6 +187,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
let body_layout = if let Some(title_bar) = &self.title_bar {
@@ -195,6 +196,7 @@ where
title_bar.operate(
&mut tree.children[1],
children.next().unwrap(),
+ renderer,
operation,
);
@@ -206,6 +208,7 @@ where
self.body.as_widget().operate(
&mut tree.children[0],
body_layout,
+ renderer,
operation,
);
}
@@ -305,12 +308,12 @@ where
}
pub(crate) fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- if let Some(title_bar) = self.title_bar.as_ref() {
+ if let Some(title_bar) = self.title_bar.as_mut() {
let mut children = layout.children();
let title_bar_layout = children.next()?;
@@ -321,14 +324,14 @@ where
match title_bar.overlay(title_bar_state, title_bar_layout, renderer)
{
Some(overlay) => Some(overlay),
- None => self.body.as_widget().overlay(
+ None => self.body.as_widget_mut().overlay(
body_state,
children.next()?,
renderer,
),
}
} else {
- self.body.as_widget().overlay(
+ self.body.as_widget_mut().overlay(
&mut tree.children[0],
layout,
renderer,
diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs
index 115f6270..ea0969aa 100644
--- a/native/src/widget/pane_grid/title_bar.rs
+++ b/native/src/widget/pane_grid/title_bar.rs
@@ -261,6 +261,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn widget::Operation<Message>,
) {
let mut children = layout.children();
@@ -282,6 +283,7 @@ where
controls.as_widget().operate(
&mut tree.children[1],
controls_layout,
+ renderer,
operation,
)
};
@@ -290,6 +292,7 @@ where
self.content.as_widget().operate(
&mut tree.children[0],
title_layout,
+ renderer,
operation,
)
}
@@ -395,7 +398,7 @@ where
}
pub(crate) fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@@ -415,13 +418,13 @@ where
let controls_state = states.next().unwrap();
content
- .as_widget()
+ .as_widget_mut()
.overlay(title_state, title_layout, renderer)
.or_else(move || {
- controls.as_ref().and_then(|controls| {
+ controls.as_mut().and_then(|controls| {
let controls_layout = children.next()?;
- controls.as_widget().overlay(
+ controls.as_widget_mut().overlay(
controls_state,
controls_layout,
renderer,
diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs
index 43ae7ebb..c2853314 100644
--- a/native/src/widget/pick_list.rs
+++ b/native/src/widget/pick_list.rs
@@ -282,7 +282,7 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
_renderer: &Renderer,
@@ -600,7 +600,7 @@ pub fn draw<T, Renderer>(
bounds,
border_color: style.border_color,
border_width: style.border_width,
- border_radius: style.border_radius,
+ border_radius: style.border_radius.into(),
},
style.background,
);
diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs
index b053d959..7d5d5be5 100644
--- a/native/src/widget/progress_bar.rs
+++ b/native/src/widget/progress_bar.rs
@@ -47,7 +47,7 @@ where
/// * the current value of the [`ProgressBar`]
pub fn new(range: RangeInclusive<f32>, value: f32) -> Self {
ProgressBar {
- value: value.max(*range.start()).min(*range.end()),
+ value: value.clamp(*range.start(), *range.end()),
range,
width: Length::Fill,
height: None,
@@ -129,7 +129,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds: Rectangle { ..bounds },
- border_radius: style.border_radius,
+ border_radius: style.border_radius.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
@@ -143,7 +143,7 @@ where
width: active_progress_width,
..bounds
},
- border_radius: style.border_radius,
+ border_radius: style.border_radius.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs
index 743689c7..b95ccc5b 100644
--- a/native/src/widget/radio.rs
+++ b/native/src/widget/radio.rs
@@ -245,7 +245,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: size / 2.0,
+ border_radius: (size / 2.0).into(),
border_width: custom_style.border_width,
border_color: custom_style.border_color,
},
@@ -261,7 +261,7 @@ where
width: bounds.width - dot_size,
height: bounds.height - dot_size,
},
- border_radius: dot_size / 2.0,
+ border_radius: (dot_size / 2.0).into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs
index eda7c2d3..108e98e4 100644
--- a/native/src/widget/row.rs
+++ b/native/src/widget/row.rs
@@ -134,6 +134,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
operation.container(None, &mut |operation| {
@@ -142,7 +143,9 @@ where
.zip(&mut tree.children)
.zip(layout.children())
.for_each(|((child, state), layout)| {
- child.as_widget().operate(state, layout, operation);
+ child
+ .as_widget()
+ .operate(state, layout, renderer, operation);
})
});
}
@@ -229,12 +232,12 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- overlay::from_children(&self.children, tree, layout, renderer)
+ overlay::from_children(&mut self.children, tree, layout, renderer)
}
}
diff --git a/native/src/widget/rule.rs b/native/src/widget/rule.rs
index e44d8d99..2dc7b6f0 100644
--- a/native/src/widget/rule.rs
+++ b/native/src/widget/rule.rs
@@ -123,7 +123,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: style.radius,
+ border_radius: style.radius.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs
index 32ec6eb3..20780f89 100644
--- a/native/src/widget/scrollable.rs
+++ b/native/src/widget/scrollable.rs
@@ -164,6 +164,7 @@ where
&self,
tree: &mut Tree,
layout: Layout<'_>,
+ renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
let state = tree.state.downcast_mut::<State>();
@@ -174,6 +175,7 @@ where
self.content.as_widget().operate(
&mut tree.children[0],
layout.children().next().unwrap(),
+ renderer,
operation,
);
});
@@ -276,13 +278,13 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
self.content
- .as_widget()
+ .as_widget_mut()
.overlay(
&mut tree.children[0],
layout.children().next().unwrap(),
@@ -704,7 +706,7 @@ pub fn draw<Renderer>(
renderer.fill_quad(
renderer::Quad {
bounds: scrollbar.bounds,
- border_radius: style.border_radius,
+ border_radius: style.border_radius.into(),
border_width: style.border_width,
border_color: style.border_color,
},
@@ -714,14 +716,13 @@ pub fn draw<Renderer>(
);
}
- if is_mouse_over
- || state.is_scroller_grabbed()
- || is_scrollbar_visible
+ if (is_mouse_over || state.is_scroller_grabbed())
+ && is_scrollbar_visible
{
renderer.fill_quad(
renderer::Quad {
bounds: scrollbar.scroller.bounds,
- border_radius: style.scroller.border_radius,
+ border_radius: style.scroller.border_radius.into(),
border_width: style.scroller.border_width,
border_color: style.scroller.border_color,
},
@@ -882,8 +883,7 @@ impl State {
self.offset = Offset::Absolute(
(self.offset.absolute(bounds, content_bounds) - delta_y)
- .max(0.0)
- .min((content_bounds.height - bounds.height) as f32),
+ .clamp(0.0, content_bounds.height - bounds.height),
);
}
@@ -906,7 +906,7 @@ impl State {
/// `0` represents scrollbar at the top, while `1` represents scrollbar at
/// the bottom.
pub fn snap_to(&mut self, percentage: f32) {
- self.offset = Offset::Relative(percentage.max(0.0).min(1.0));
+ self.offset = Offset::Relative(percentage.clamp(0.0, 1.0));
}
/// Unsnaps the current scroll position, if snapped, given the bounds of the
diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs
index fd9160a4..87030a4d 100644
--- a/native/src/widget/slider.rs
+++ b/native/src/widget/slider.rs
@@ -380,7 +380,7 @@ pub fn draw<T, R>(
width: bounds.width,
height: 2.0,
},
- border_radius: 0.0,
+ border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
@@ -395,7 +395,7 @@ pub fn draw<T, R>(
width: bounds.width,
height: 2.0,
},
- border_radius: 0.0,
+ border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
@@ -435,7 +435,7 @@ pub fn draw<T, R>(
width: handle_width,
height: handle_height,
},
- border_radius: handle_border_radius,
+ border_radius: handle_border_radius.into(),
border_width: style.handle.border_width,
border_color: style.handle.border_color,
},
diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs
index 1015ed0a..f83f5acf 100644
--- a/native/src/widget/svg.rs
+++ b/native/src/widget/svg.rs
@@ -9,6 +9,7 @@ use crate::{
use std::path::PathBuf;
+pub use iced_style::svg::{Appearance, StyleSheet};
pub use svg::Handle;
/// A vector graphics image.
@@ -17,15 +18,24 @@ pub use svg::Handle;
///
/// [`Svg`] images can have a considerable rendering cost when resized,
/// specially when they are complex.
-#[derive(Debug, Clone)]
-pub struct Svg {
+#[allow(missing_debug_implementations)]
+pub struct Svg<Renderer>
+where
+ Renderer: svg::Renderer,
+ Renderer::Theme: StyleSheet,
+{
handle: Handle,
width: Length,
height: Length,
content_fit: ContentFit,
+ style: <Renderer::Theme as StyleSheet>::Style,
}
-impl Svg {
+impl<Renderer> Svg<Renderer>
+where
+ Renderer: svg::Renderer,
+ Renderer::Theme: StyleSheet,
+{
/// Creates a new [`Svg`] from the given [`Handle`].
pub fn new(handle: impl Into<Handle>) -> Self {
Svg {
@@ -33,22 +43,26 @@ impl Svg {
width: Length::Fill,
height: Length::Shrink,
content_fit: ContentFit::Contain,
+ style: Default::default(),
}
}
/// Creates a new [`Svg`] that will display the contents of the file at the
/// provided path.
+ #[must_use]
pub fn from_path(path: impl Into<PathBuf>) -> Self {
Self::new(Handle::from_path(path))
}
/// Sets the width of the [`Svg`].
+ #[must_use]
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
/// Sets the height of the [`Svg`].
+ #[must_use]
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
@@ -57,17 +71,29 @@ impl Svg {
/// Sets the [`ContentFit`] of the [`Svg`].
///
/// Defaults to [`ContentFit::Contain`]
+ #[must_use]
pub fn content_fit(self, content_fit: ContentFit) -> Self {
Self {
content_fit,
..self
}
}
+
+ /// Sets the style variant of this [`Svg`].
+ #[must_use]
+ pub fn style(
+ mut self,
+ style: <Renderer::Theme as StyleSheet>::Style,
+ ) -> Self {
+ self.style = style;
+ self
+ }
}
-impl<Message, Renderer> Widget<Message, Renderer> for Svg
+impl<Message, Renderer> Widget<Message, Renderer> for Svg<Renderer>
where
Renderer: svg::Renderer,
+ Renderer::Theme: iced_style::svg::StyleSheet,
{
fn width(&self) -> Length {
self.width
@@ -114,7 +140,7 @@ where
&self,
_state: &Tree,
renderer: &mut Renderer,
- _theme: &Renderer::Theme,
+ theme: &Renderer::Theme,
_style: &renderer::Style,
layout: Layout<'_>,
_cursor_position: Point,
@@ -138,7 +164,13 @@ where
..bounds
};
- renderer.draw(self.handle.clone(), drawing_bounds + offset)
+ let appearance = theme.appearance(&self.style);
+
+ renderer.draw(
+ self.handle.clone(),
+ appearance.color,
+ drawing_bounds + offset,
+ );
};
if adjusted_fit.width > bounds.width
@@ -146,16 +178,18 @@ where
{
renderer.with_layer(bounds, render);
} else {
- render(renderer)
+ render(renderer);
}
}
}
-impl<'a, Message, Renderer> From<Svg> for Element<'a, Message, Renderer>
+impl<'a, Message, Renderer> From<Svg<Renderer>>
+ for Element<'a, Message, Renderer>
where
- Renderer: svg::Renderer,
+ Renderer: svg::Renderer + 'a,
+ Renderer::Theme: iced_style::svg::StyleSheet,
{
- fn from(icon: Svg) -> Element<'a, Message, Renderer> {
+ fn from(icon: Svg<Renderer>) -> Element<'a, Message, Renderer> {
Element::new(icon)
}
}
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index 14e7e1b7..8b4514e3 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -228,6 +228,7 @@ where
&self,
tree: &mut Tree,
_layout: Layout<'_>,
+ _renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
let state = tree.state.downcast_mut::<State>();
@@ -453,9 +454,17 @@ where
)
} else {
None
- };
+ }
+ .unwrap_or(0);
- state.cursor.move_to(position.unwrap_or(0));
+ if state.keyboard_modifiers.shift() {
+ state.cursor.select_range(
+ state.cursor.start(value),
+ position,
+ );
+ } else {
+ state.cursor.move_to(position);
+ }
state.is_dragging = true;
}
click::Kind::Double => {
@@ -801,7 +810,7 @@ pub fn draw<Renderer>(
renderer.fill_quad(
renderer::Quad {
bounds,
- border_radius: appearance.border_radius,
+ border_radius: appearance.border_radius.into(),
border_width: appearance.border_width,
border_color: appearance.border_color,
},
@@ -833,7 +842,7 @@ pub fn draw<Renderer>(
width: 1.0,
height: text_bounds.height,
},
- border_radius: 0.0,
+ border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
@@ -877,7 +886,7 @@ pub fn draw<Renderer>(
width,
height: text_bounds.height,
},
- border_radius: 0.0,
+ border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs
index 99a56ea8..f0a944a3 100644
--- a/native/src/widget/toggler.rs
+++ b/native/src/widget/toggler.rs
@@ -24,9 +24,9 @@ pub use iced_style::toggler::{Appearance, StyleSheet};
/// TogglerToggled(bool),
/// }
///
-/// let is_active = true;
+/// let is_toggled = true;
///
-/// Toggler::new(is_active, String::from("Toggle me!"), |b| Message::TogglerToggled(b));
+/// Toggler::new(String::from("Toggle me!"), is_toggled, |b| Message::TogglerToggled(b));
/// ```
#[allow(missing_debug_implementations)]
pub struct Toggler<'a, Message, Renderer>
@@ -34,7 +34,7 @@ where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
{
- is_active: bool,
+ is_toggled: bool,
on_toggle: Box<dyn Fn(bool) -> Message + 'a>,
label: Option<String>,
width: Length,
@@ -63,15 +63,15 @@ where
/// will receive the new state of the [`Toggler`] and must produce a
/// `Message`.
pub fn new<F>(
- is_active: bool,
label: impl Into<Option<String>>,
+ is_toggled: bool,
f: F,
) -> Self
where
F: 'a + Fn(bool) -> Message,
{
Toggler {
- is_active,
+ is_toggled,
on_toggle: Box::new(f),
label: label.into(),
width: Length::Fill,
@@ -193,7 +193,7 @@ where
let mouse_over = layout.bounds().contains(cursor_position);
if mouse_over {
- shell.publish((self.on_toggle)(!self.is_active));
+ shell.publish((self.on_toggle)(!self.is_toggled));
event::Status::Captured
} else {
@@ -260,13 +260,13 @@ where
let is_mouse_over = bounds.contains(cursor_position);
let style = if is_mouse_over {
- theme.hovered(&self.style, self.is_active)
+ theme.hovered(&self.style, self.is_toggled)
} else {
- theme.active(&self.style, self.is_active)
+ theme.active(&self.style, self.is_toggled)
};
- let border_radius = bounds.height as f32 / BORDER_RADIUS_RATIO;
- let space = SPACE_RATIO * bounds.height as f32;
+ let border_radius = bounds.height / BORDER_RADIUS_RATIO;
+ let space = SPACE_RATIO * bounds.height;
let toggler_background_bounds = Rectangle {
x: bounds.x + space,
@@ -278,7 +278,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds: toggler_background_bounds,
- border_radius,
+ border_radius: border_radius.into(),
border_width: 1.0,
border_color: style
.background_border
@@ -289,7 +289,7 @@ where
let toggler_foreground_bounds = Rectangle {
x: bounds.x
- + if self.is_active {
+ + if self.is_toggled {
bounds.width - 2.0 * space - (bounds.height - (4.0 * space))
} else {
2.0 * space
@@ -302,7 +302,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds: toggler_foreground_bounds,
- border_radius,
+ border_radius: border_radius.into(),
border_width: 1.0,
border_color: style
.foreground_border
diff --git a/native/src/widget/tooltip.rs b/native/src/widget/tooltip.rs
index 9347a886..084dc269 100644
--- a/native/src/widget/tooltip.rs
+++ b/native/src/widget/tooltip.rs
@@ -221,12 +221,12 @@ where
}
fn overlay<'b>(
- &'b self,
+ &'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
- self.content.as_widget().overlay(
+ self.content.as_widget_mut().overlay(
&mut tree.children[0],
layout,
renderer,
diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs
new file mode 100644
index 00000000..28e8405c
--- /dev/null
+++ b/native/src/widget/vertical_slider.rs
@@ -0,0 +1,470 @@
+//! Display an interactive selector of a single value from a range of values.
+//!
+//! A [`VerticalSlider`] has some local [`State`].
+use std::ops::RangeInclusive;
+
+pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet};
+
+use crate::event::{self, Event};
+use crate::widget::tree::{self, Tree};
+use crate::{
+ layout, mouse, renderer, touch, Background, Clipboard, Color, Element,
+ Layout, Length, Point, Rectangle, Shell, Size, Widget,
+};
+
+/// An vertical bar and a handle that selects a single value from a range of
+/// values.
+///
+/// A [`VerticalSlider`] will try to fill the vertical space of its container.
+///
+/// The [`VerticalSlider`] range of numeric values is generic and its step size defaults
+/// to 1 unit.
+///
+/// # Example
+/// ```
+/// # use iced_native::widget::vertical_slider;
+/// # use iced_native::renderer::Null;
+/// #
+/// # type VerticalSlider<'a, T, Message> = vertical_slider::VerticalSlider<'a, T, Message, Null>;
+/// #
+/// #[derive(Clone)]
+/// pub enum Message {
+/// SliderChanged(f32),
+/// }
+///
+/// let value = 50.0;
+///
+/// VerticalSlider::new(0.0..=100.0, value, Message::SliderChanged);
+/// ```
+#[allow(missing_debug_implementations)]
+pub struct VerticalSlider<'a, T, Message, Renderer>
+where
+ Renderer: crate::Renderer,
+ Renderer::Theme: StyleSheet,
+{
+ range: RangeInclusive<T>,
+ step: T,
+ value: T,
+ on_change: Box<dyn Fn(T) -> Message + 'a>,
+ on_release: Option<Message>,
+ width: u16,
+ height: Length,
+ style: <Renderer::Theme as StyleSheet>::Style,
+}
+
+impl<'a, T, Message, Renderer> VerticalSlider<'a, T, Message, Renderer>
+where
+ T: Copy + From<u8> + std::cmp::PartialOrd,
+ Message: Clone,
+ Renderer: crate::Renderer,
+ Renderer::Theme: StyleSheet,
+{
+ /// The default width of a [`VerticalSlider`].
+ pub const DEFAULT_WIDTH: u16 = 22;
+
+ /// Creates a new [`VerticalSlider`].
+ ///
+ /// It expects:
+ /// * an inclusive range of possible values
+ /// * the current value of the [`VerticalSlider`]
+ /// * a function that will be called when the [`VerticalSlider`] is dragged.
+ /// It receives the new value of the [`VerticalSlider`] and must produce a
+ /// `Message`.
+ pub fn new<F>(range: RangeInclusive<T>, value: T, on_change: F) -> Self
+ where
+ F: 'a + Fn(T) -> Message,
+ {
+ let value = if value >= *range.start() {
+ value
+ } else {
+ *range.start()
+ };
+
+ let value = if value <= *range.end() {
+ value
+ } else {
+ *range.end()
+ };
+
+ VerticalSlider {
+ value,
+ range,
+ step: T::from(1),
+ on_change: Box::new(on_change),
+ on_release: None,
+ width: Self::DEFAULT_WIDTH,
+ height: Length::Fill,
+ style: Default::default(),
+ }
+ }
+
+ /// Sets the release message of the [`VerticalSlider`].
+ /// This is called when the mouse is released from the slider.
+ ///
+ /// Typically, the user's interaction with the slider is finished when this message is produced.
+ /// This is useful if you need to spawn a long-running task from the slider's result, where
+ /// the default on_change message could create too many events.
+ pub fn on_release(mut self, on_release: Message) -> Self {
+ self.on_release = Some(on_release);
+ self
+ }
+
+ /// Sets the width of the [`VerticalSlider`].
+ pub fn width(mut self, width: u16) -> Self {
+ self.width = width;
+ self
+ }
+
+ /// Sets the height of the [`VerticalSlider`].
+ pub fn height(mut self, height: Length) -> Self {
+ self.height = height;
+ self
+ }
+
+ /// Sets the style of the [`VerticalSlider`].
+ pub fn style(
+ mut self,
+ style: impl Into<<Renderer::Theme as StyleSheet>::Style>,
+ ) -> Self {
+ self.style = style.into();
+ self
+ }
+
+ /// Sets the step size of the [`VerticalSlider`].
+ pub fn step(mut self, step: T) -> Self {
+ self.step = step;
+ self
+ }
+}
+
+impl<'a, T, Message, Renderer> Widget<Message, Renderer>
+ for VerticalSlider<'a, T, Message, Renderer>
+where
+ T: Copy + Into<f64> + num_traits::FromPrimitive,
+ Message: Clone,
+ Renderer: crate::Renderer,
+ Renderer::Theme: StyleSheet,
+{
+ fn tag(&self) -> tree::Tag {
+ tree::Tag::of::<State>()
+ }
+
+ fn state(&self) -> tree::State {
+ tree::State::new(State::new())
+ }
+
+ fn width(&self) -> Length {
+ Length::Shrink
+ }
+
+ fn height(&self) -> Length {
+ self.height
+ }
+
+ fn layout(
+ &self,
+ _renderer: &Renderer,
+ limits: &layout::Limits,
+ ) -> layout::Node {
+ let limits =
+ limits.width(Length::Units(self.width)).height(self.height);
+
+ let size = limits.resolve(Size::ZERO);
+
+ layout::Node::new(size)
+ }
+
+ fn on_event(
+ &mut self,
+ tree: &mut Tree,
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ _renderer: &Renderer,
+ _clipboard: &mut dyn Clipboard,
+ shell: &mut Shell<'_, Message>,
+ ) -> event::Status {
+ update(
+ event,
+ layout,
+ cursor_position,
+ shell,
+ tree.state.downcast_mut::<State>(),
+ &mut self.value,
+ &self.range,
+ self.step,
+ self.on_change.as_ref(),
+ &self.on_release,
+ )
+ }
+
+ fn draw(
+ &self,
+ tree: &Tree,
+ renderer: &mut Renderer,
+ theme: &Renderer::Theme,
+ _style: &renderer::Style,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ _viewport: &Rectangle,
+ ) {
+ draw(
+ renderer,
+ layout,
+ cursor_position,
+ tree.state.downcast_ref::<State>(),
+ self.value,
+ &self.range,
+ theme,
+ &self.style,
+ )
+ }
+
+ fn mouse_interaction(
+ &self,
+ tree: &Tree,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ _viewport: &Rectangle,
+ _renderer: &Renderer,
+ ) -> mouse::Interaction {
+ mouse_interaction(
+ layout,
+ cursor_position,
+ tree.state.downcast_ref::<State>(),
+ )
+ }
+}
+
+impl<'a, T, Message, Renderer> From<VerticalSlider<'a, T, Message, Renderer>>
+ for Element<'a, Message, Renderer>
+where
+ T: 'a + Copy + Into<f64> + num_traits::FromPrimitive,
+ Message: 'a + Clone,
+ Renderer: 'a + crate::Renderer,
+ Renderer::Theme: StyleSheet,
+{
+ fn from(
+ slider: VerticalSlider<'a, T, Message, Renderer>,
+ ) -> Element<'a, Message, Renderer> {
+ Element::new(slider)
+ }
+}
+
+/// Processes an [`Event`] and updates the [`State`] of a [`VerticalSlider`]
+/// accordingly.
+pub fn update<Message, T>(
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ shell: &mut Shell<'_, Message>,
+ state: &mut State,
+ value: &mut T,
+ range: &RangeInclusive<T>,
+ step: T,
+ on_change: &dyn Fn(T) -> Message,
+ on_release: &Option<Message>,
+) -> event::Status
+where
+ T: Copy + Into<f64> + num_traits::FromPrimitive,
+ Message: Clone,
+{
+ let is_dragging = state.is_dragging;
+
+ let mut change = || {
+ let bounds = layout.bounds();
+ let new_value = if cursor_position.y >= bounds.y + bounds.height {
+ *range.start()
+ } else if cursor_position.y <= bounds.y {
+ *range.end()
+ } else {
+ let step = step.into();
+ let start = (*range.start()).into();
+ let end = (*range.end()).into();
+
+ let percent = 1.0
+ - f64::from(cursor_position.y - bounds.y)
+ / f64::from(bounds.height);
+
+ let steps = (percent * (end - start) / step).round();
+ let value = steps * step + start;
+
+ if let Some(value) = T::from_f64(value) {
+ value
+ } else {
+ return;
+ }
+ };
+
+ if ((*value).into() - new_value.into()).abs() > f64::EPSILON {
+ shell.publish((on_change)(new_value));
+
+ *value = new_value;
+ }
+ };
+
+ match event {
+ Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
+ | Event::Touch(touch::Event::FingerPressed { .. }) => {
+ if layout.bounds().contains(cursor_position) {
+ change();
+ state.is_dragging = true;
+
+ return event::Status::Captured;
+ }
+ }
+ Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
+ | Event::Touch(touch::Event::FingerLifted { .. })
+ | Event::Touch(touch::Event::FingerLost { .. }) => {
+ if is_dragging {
+ if let Some(on_release) = on_release.clone() {
+ shell.publish(on_release);
+ }
+ state.is_dragging = false;
+
+ return event::Status::Captured;
+ }
+ }
+ Event::Mouse(mouse::Event::CursorMoved { .. })
+ | Event::Touch(touch::Event::FingerMoved { .. }) => {
+ if is_dragging {
+ change();
+
+ return event::Status::Captured;
+ }
+ }
+ _ => {}
+ }
+
+ event::Status::Ignored
+}
+
+/// Draws a [`VerticalSlider`].
+pub fn draw<T, R>(
+ renderer: &mut R,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ state: &State,
+ value: T,
+ range: &RangeInclusive<T>,
+ style_sheet: &dyn StyleSheet<Style = <R::Theme as StyleSheet>::Style>,
+ style: &<R::Theme as StyleSheet>::Style,
+) where
+ T: Into<f64> + Copy,
+ R: crate::Renderer,
+ R::Theme: StyleSheet,
+{
+ let bounds = layout.bounds();
+ let is_mouse_over = bounds.contains(cursor_position);
+
+ let style = if state.is_dragging {
+ style_sheet.dragging(style)
+ } else if is_mouse_over {
+ style_sheet.hovered(style)
+ } else {
+ style_sheet.active(style)
+ };
+
+ let rail_x = bounds.x + (bounds.width / 2.0).round();
+
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: Rectangle {
+ x: rail_x - 1.0,
+ y: bounds.y,
+ width: 2.0,
+ height: bounds.height,
+ },
+ border_radius: 0.0.into(),
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ },
+ style.rail_colors.0,
+ );
+
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: Rectangle {
+ x: rail_x + 1.0,
+ y: bounds.y,
+ width: 2.0,
+ height: bounds.height,
+ },
+ border_radius: 0.0.into(),
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ },
+ Background::Color(style.rail_colors.1),
+ );
+
+ let (handle_width, handle_height, handle_border_radius) = match style
+ .handle
+ .shape
+ {
+ HandleShape::Circle { radius } => (radius * 2.0, radius * 2.0, radius),
+ HandleShape::Rectangle {
+ width,
+ border_radius,
+ } => (f32::from(width), bounds.width, border_radius),
+ };
+
+ let value = value.into() as f32;
+ let (range_start, range_end) = {
+ let (start, end) = range.clone().into_inner();
+
+ (start.into() as f32, end.into() as f32)
+ };
+
+ let handle_offset = if range_start >= range_end {
+ 0.0
+ } else {
+ bounds.height * (value - range_end) / (range_start - range_end)
+ - handle_width / 2.0
+ };
+
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: Rectangle {
+ x: rail_x - (handle_height / 2.0),
+ y: bounds.y + handle_offset.round(),
+ width: handle_height,
+ height: handle_width,
+ },
+ border_radius: handle_border_radius.into(),
+ border_width: style.handle.border_width,
+ border_color: style.handle.border_color,
+ },
+ style.handle.color,
+ );
+}
+
+/// Computes the current [`mouse::Interaction`] of a [`VerticalSlider`].
+pub fn mouse_interaction(
+ layout: Layout<'_>,
+ cursor_position: Point,
+ state: &State,
+) -> mouse::Interaction {
+ let bounds = layout.bounds();
+ let is_mouse_over = bounds.contains(cursor_position);
+
+ if state.is_dragging {
+ mouse::Interaction::Grabbing
+ } else if is_mouse_over {
+ mouse::Interaction::Grab
+ } else {
+ mouse::Interaction::default()
+ }
+}
+
+/// The local state of a [`VerticalSlider`].
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub struct State {
+ is_dragging: bool,
+}
+
+impl State {
+ /// Creates a new [`State`].
+ pub fn new() -> State {
+ State::default()
+ }
+}
diff --git a/native/src/window.rs b/native/src/window.rs
index f910b8f2..1b97e655 100644
--- a/native/src/window.rs
+++ b/native/src/window.rs
@@ -2,7 +2,9 @@
mod action;
mod event;
mod mode;
+mod user_attention;
pub use action::Action;
pub use event::Event;
pub use mode::Mode;
+pub use user_attention::UserAttention;
diff --git a/native/src/window/action.rs b/native/src/window/action.rs
index 009dcc27..f0fe845d 100644
--- a/native/src/window/action.rs
+++ b/native/src/window/action.rs
@@ -1,10 +1,12 @@
-use crate::window::Mode;
+use crate::window::{Mode, UserAttention};
use iced_futures::MaybeSend;
use std::fmt;
/// An operation to be performed on some window.
pub enum Action<T> {
+ /// Closes the current window and exits the application.
+ Close,
/// Moves the window with the left mouse button until the button is
/// released.
///
@@ -33,10 +35,29 @@ pub enum Action<T> {
},
/// Set the [`Mode`] of the window.
SetMode(Mode),
- /// Sets the window to maximized or back
- ToggleMaximize,
/// Fetch the current [`Mode`] of the window.
FetchMode(Box<dyn FnOnce(Mode) -> T + 'static>),
+ /// Sets the window to maximized or back
+ ToggleMaximize,
+ /// Toggles whether window has decorations
+ /// ## Platform-specific
+ /// - **X11:** Not implemented.
+ /// - **Web:** Unsupported.
+ ToggleDecorations,
+ /// Requests user attention to the window, this has no effect if the application
+ /// is already focused. How requesting for user attention manifests is platform dependent,
+ /// see [`UserAttentionType`] for details.
+ ///
+ /// Providing `None` will unset the request for user attention. Unsetting the request for
+ /// user attention might not be done automatically by the WM when the window receives input.
+ ///
+ /// ## Platform-specific
+ ///
+ /// - **iOS / Android / Web:** Unsupported.
+ /// - **macOS:** `None` has no effect.
+ /// - **X11:** Requests for user attention must be manually cleared.
+ /// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect.
+ RequestUserAttention(Option<UserAttention>),
}
impl<T> Action<T> {
@@ -49,14 +70,19 @@ impl<T> Action<T> {
T: 'static,
{
match self {
+ Self::Close => Action::Close,
Self::Drag => Action::Drag,
Self::Resize { width, height } => Action::Resize { width, height },
Self::Maximize(bool) => Action::Maximize(bool),
Self::Minimize(bool) => Action::Minimize(bool),
Self::Move { x, y } => Action::Move { x, y },
Self::SetMode(mode) => Action::SetMode(mode),
- Self::ToggleMaximize => Action::ToggleMaximize,
Self::FetchMode(o) => Action::FetchMode(Box::new(move |s| f(o(s)))),
+ Self::ToggleMaximize => Action::ToggleMaximize,
+ Self::ToggleDecorations => Action::ToggleDecorations,
+ Self::RequestUserAttention(attention_type) => {
+ Action::RequestUserAttention(attention_type)
+ }
}
}
}
@@ -64,6 +90,7 @@ impl<T> Action<T> {
impl<T> fmt::Debug for Action<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
+ Self::Close => write!(f, "Action::Close"),
Self::Drag => write!(f, "Action::Drag"),
Self::Resize { width, height } => write!(
f,
@@ -76,8 +103,12 @@ impl<T> fmt::Debug for Action<T> {
write!(f, "Action::Move {{ x: {}, y: {} }}", x, y)
}
Self::SetMode(mode) => write!(f, "Action::SetMode({:?})", mode),
- Self::ToggleMaximize => write!(f, "Action::ToggleMaximize"),
Self::FetchMode(_) => write!(f, "Action::FetchMode"),
+ Self::ToggleMaximize => write!(f, "Action::ToggleMaximize"),
+ Self::ToggleDecorations => write!(f, "Action::ToggleDecorations"),
+ Self::RequestUserAttention(_) => {
+ write!(f, "Action::RequestUserAttention")
+ }
}
}
}
diff --git a/native/src/window/user_attention.rs b/native/src/window/user_attention.rs
new file mode 100644
index 00000000..b03dfeef
--- /dev/null
+++ b/native/src/window/user_attention.rs
@@ -0,0 +1,21 @@
+/// The type of user attention to request.
+///
+/// ## Platform-specific
+///
+/// - **X11:** Sets the WM's `XUrgencyHint`. No distinction between [`Critical`] and [`Informational`].
+///
+/// [`Critical`]: Self::Critical
+/// [`Informational`]: Self::Informational
+#[derive(Debug, Clone, Copy)]
+pub enum UserAttention {
+ /// ## Platform-specific
+ ///
+ /// - **macOS:** Bounces the dock icon until the application is in focus.
+ /// - **Windows:** Flashes both the window and the taskbar button until the application is in focus.
+ Critical,
+ /// ## Platform-specific
+ ///
+ /// - **macOS:** Bounces the dock icon once.
+ /// - **Windows:** Flashes the taskbar button until the application is in focus.
+ Informational,
+}