summaryrefslogtreecommitdiffstats
path: root/native/src/widget
diff options
context:
space:
mode:
Diffstat (limited to 'native/src/widget')
-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
21 files changed, 640 insertions, 90 deletions
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()
+ }
+}