summaryrefslogtreecommitdiffstats
path: root/widget
diff options
context:
space:
mode:
Diffstat (limited to 'widget')
-rw-r--r--widget/Cargo.toml1
-rw-r--r--widget/src/button.rs15
-rw-r--r--widget/src/checkbox.rs10
-rw-r--r--widget/src/container.rs2
-rw-r--r--widget/src/helpers.rs168
-rw-r--r--widget/src/markdown.rs11
-rw-r--r--widget/src/progress_bar.rs94
-rw-r--r--widget/src/scrollable.rs50
-rw-r--r--widget/src/text_editor.rs4
-rw-r--r--widget/src/text_input.rs15
-rw-r--r--widget/src/vertical_slider.rs39
11 files changed, 351 insertions, 58 deletions
diff --git a/widget/Cargo.toml b/widget/Cargo.toml
index 98a81145..e19cad08 100644
--- a/widget/Cargo.toml
+++ b/widget/Cargo.toml
@@ -33,7 +33,6 @@ iced_renderer.workspace = true
iced_runtime.workspace = true
num-traits.workspace = true
-once_cell.workspace = true
rustc-hash.workspace = true
thiserror.workspace = true
unicode-segmentation.workspace = true
diff --git a/widget/src/button.rs b/widget/src/button.rs
index a2f9945c..11839d5e 100644
--- a/widget/src/button.rs
+++ b/widget/src/button.rs
@@ -633,6 +633,21 @@ pub fn success(theme: &Theme, status: Status) -> Style {
}
}
+/// A warning button; denoting a risky action.
+pub fn warning(theme: &Theme, status: Status) -> Style {
+ let palette = theme.extended_palette();
+ let base = styled(palette.warning.base);
+
+ match status {
+ Status::Active | Status::Pressed => base,
+ Status::Hovered => Style {
+ background: Some(Background::Color(palette.warning.strong.color)),
+ ..base
+ },
+ Status::Disabled => disabled(base),
+ }
+}
+
/// A danger button; denoting a destructive action.
pub fn danger(theme: &Theme, status: Status) -> Style {
let palette = theme.extended_palette();
diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs
index 3686d34c..663bfad1 100644
--- a/widget/src/checkbox.rs
+++ b/widget/src/checkbox.rs
@@ -445,6 +445,16 @@ where
);
}
}
+
+ fn operate(
+ &self,
+ _state: &mut Tree,
+ layout: Layout<'_>,
+ _renderer: &Renderer,
+ operation: &mut dyn widget::Operation,
+ ) {
+ operation.text(None, layout.bounds(), &self.label);
+ }
}
impl<'a, Message, Theme, Renderer> From<Checkbox<'a, Message, Theme, Renderer>>
diff --git a/widget/src/container.rs b/widget/src/container.rs
index d9740f72..a411a7d2 100644
--- a/widget/src/container.rs
+++ b/widget/src/container.rs
@@ -493,11 +493,11 @@ pub fn visible_bounds(id: Id) -> Task<Option<Rectangle>> {
impl Operation<Option<Rectangle>> for VisibleBounds {
fn scrollable(
&mut self,
- _state: &mut dyn widget::operation::Scrollable,
_id: Option<&widget::Id>,
bounds: Rectangle,
_content_bounds: Rectangle,
translation: Vector,
+ _state: &mut dyn widget::operation::Scrollable,
) {
match self.scrollables.last() {
Some((last_translation, last_viewport, _depth)) => {
diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs
index cdfd2daf..b1e02943 100644
--- a/widget/src/helpers.rs
+++ b/widget/src/helpers.rs
@@ -232,10 +232,10 @@ where
///
/// This is equivalent to:
/// ```rust,no_run
-/// # use iced_widget::core::Length;
+/// # use iced_widget::core::Length::Fill;
/// # use iced_widget::Container;
/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
-/// let centered = container("Centered!").center(Length::Fill);
+/// let center = container("Center!").center(Fill);
/// ```
///
/// [`Container`]: crate::Container
@@ -249,6 +249,166 @@ where
container(content).center(Length::Fill)
}
+/// Creates a new [`Container`] that fills all the available space
+/// horizontally and centers its contents inside.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let center_x = container("Horizontal Center!").center_x(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn center_x<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content).center_x(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// vertically and centers its contents inside.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let center_y = container("Vertical Center!").center_y(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn center_y<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content).center_y(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// horizontally and right-aligns its contents inside.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let right = container("Right!").align_right(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn right<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content).align_right(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// and aligns its contents inside to the right center.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let right_center = container("Bottom Center!").align_right(Fill).center_y(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn right_center<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content)
+ .align_right(Length::Fill)
+ .center_y(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// vertically and bottom-aligns its contents inside.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let bottom = container("Bottom!").align_bottom(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn bottom<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content).align_bottom(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// and aligns its contents inside to the bottom center.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let bottom_center = container("Bottom Center!").center_x(Fill).align_bottom(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn bottom_center<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content)
+ .center_x(Length::Fill)
+ .align_bottom(Length::Fill)
+}
+
+/// Creates a new [`Container`] that fills all the available space
+/// and aligns its contents inside to the bottom right corner.
+///
+/// This is equivalent to:
+/// ```rust,no_run
+/// # use iced_widget::core::Length::Fill;
+/// # use iced_widget::Container;
+/// # fn container<A>(x: A) -> Container<'static, ()> { unreachable!() }
+/// let bottom_right = container("Bottom!").align_right(Fill).align_bottom(Fill);
+/// ```
+///
+/// [`Container`]: crate::Container
+pub fn bottom_right<'a, Message, Theme, Renderer>(
+ content: impl Into<Element<'a, Message, Theme, Renderer>>,
+) -> Container<'a, Message, Theme, Renderer>
+where
+ Theme: container::Catalog + 'a,
+ Renderer: core::Renderer,
+{
+ container(content)
+ .align_right(Length::Fill)
+ .align_bottom(Length::Fill)
+}
+
/// Creates a new [`Pin`] widget with the given content.
///
/// A [`Pin`] widget positions its contents at some fixed coordinates inside of its boundaries.
@@ -1708,9 +1868,9 @@ where
{
use crate::core::{Alignment, Font};
use crate::svg;
- use once_cell::sync::Lazy;
+ use std::sync::LazyLock;
- static LOGO: Lazy<svg::Handle> = Lazy::new(|| {
+ static LOGO: LazyLock<svg::Handle> = LazyLock::new(|| {
svg::Handle::from_memory(include_bytes!("../assets/iced-logo.svg"))
});
diff --git a/widget/src/markdown.rs b/widget/src/markdown.rs
index d6bebb9b..c0648e9e 100644
--- a/widget/src/markdown.rs
+++ b/widget/src/markdown.rs
@@ -305,12 +305,21 @@ pub fn parse(markdown: &str) -> impl Iterator<Item = Item> + '_ {
None
}
pulldown_cmark::Tag::List(first_item) if !metadata && !table => {
+ let prev = if spans.is_empty() {
+ None
+ } else {
+ produce(
+ &mut lists,
+ Item::Paragraph(Text::new(spans.drain(..).collect())),
+ )
+ };
+
lists.push(List {
start: first_item,
items: Vec::new(),
});
- None
+ prev
}
pulldown_cmark::Tag::Item => {
lists
diff --git a/widget/src/progress_bar.rs b/widget/src/progress_bar.rs
index 8f85dcf3..554e8a44 100644
--- a/widget/src/progress_bar.rs
+++ b/widget/src/progress_bar.rs
@@ -59,8 +59,9 @@ where
{
range: RangeInclusive<f32>,
value: f32,
- width: Length,
- height: Option<Length>,
+ length: Length,
+ girth: Length,
+ is_vertical: bool,
class: Theme::Class<'a>,
}
@@ -68,8 +69,8 @@ impl<'a, Theme> ProgressBar<'a, Theme>
where
Theme: Catalog,
{
- /// The default height of a [`ProgressBar`].
- pub const DEFAULT_HEIGHT: f32 = 30.0;
+ /// The default girth of a [`ProgressBar`].
+ pub const DEFAULT_GIRTH: f32 = 30.0;
/// Creates a new [`ProgressBar`].
///
@@ -80,21 +81,30 @@ where
ProgressBar {
value: value.clamp(*range.start(), *range.end()),
range,
- width: Length::Fill,
- height: None,
+ length: Length::Fill,
+ girth: Length::from(Self::DEFAULT_GIRTH),
+ is_vertical: false,
class: Theme::default(),
}
}
/// Sets the width of the [`ProgressBar`].
- pub fn width(mut self, width: impl Into<Length>) -> Self {
- self.width = width.into();
+ pub fn length(mut self, length: impl Into<Length>) -> Self {
+ self.length = length.into();
self
}
/// Sets the height of the [`ProgressBar`].
- pub fn height(mut self, height: impl Into<Length>) -> Self {
- self.height = Some(height.into());
+ pub fn girth(mut self, girth: impl Into<Length>) -> Self {
+ self.girth = girth.into();
+ self
+ }
+
+ /// Turns the [`ProgressBar`] into a vertical [`ProgressBar`].
+ ///
+ /// By default, a [`ProgressBar`] is horizontal.
+ pub fn vertical(mut self) -> Self {
+ self.is_vertical = true;
self
}
@@ -115,6 +125,22 @@ where
self.class = class.into();
self
}
+
+ fn width(&self) -> Length {
+ if self.is_vertical {
+ self.girth
+ } else {
+ self.length
+ }
+ }
+
+ fn height(&self) -> Length {
+ if self.is_vertical {
+ self.length
+ } else {
+ self.girth
+ }
+ }
}
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
@@ -125,8 +151,8 @@ where
{
fn size(&self) -> Size<Length> {
Size {
- width: self.width,
- height: self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)),
+ width: self.width(),
+ height: self.height(),
}
}
@@ -136,11 +162,7 @@ where
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- layout::atomic(
- limits,
- self.width,
- self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)),
- )
+ layout::atomic(limits, self.width(), self.height())
}
fn draw(
@@ -156,11 +178,16 @@ where
let bounds = layout.bounds();
let (range_start, range_end) = self.range.clone().into_inner();
- let active_progress_width = if range_start >= range_end {
+ let length = if self.is_vertical {
+ bounds.height
+ } else {
+ bounds.width
+ };
+
+ let active_progress_length = if range_start >= range_end {
0.0
} else {
- bounds.width * (self.value - range_start)
- / (range_end - range_start)
+ length * (self.value - range_start) / (range_end - range_start)
};
let style = theme.style(&self.class);
@@ -174,13 +201,23 @@ where
style.background,
);
- if active_progress_width > 0.0 {
+ if active_progress_length > 0.0 {
+ let bounds = if self.is_vertical {
+ Rectangle {
+ y: bounds.y + bounds.height - active_progress_length,
+ height: active_progress_length,
+ ..bounds
+ }
+ } else {
+ Rectangle {
+ width: active_progress_length,
+ ..bounds
+ }
+ };
+
renderer.fill_quad(
renderer::Quad {
- bounds: Rectangle {
- width: active_progress_width,
- ..bounds
- },
+ bounds,
border: Border {
color: Color::TRANSPARENT,
..style.border
@@ -274,6 +311,13 @@ pub fn success(theme: &Theme) -> Style {
styled(palette.background.strong.color, palette.success.base.color)
}
+/// The warning style of a [`ProgressBar`].
+pub fn warning(theme: &Theme) -> Style {
+ let palette = theme.extended_palette();
+
+ styled(palette.background.strong.color, palette.warning.base.color)
+}
+
/// The danger style of a [`ProgressBar`].
pub fn danger(theme: &Theme) -> Style {
let palette = theme.extended_palette();
diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs
index 41bb15f9..b08d5d09 100644
--- a/widget/src/scrollable.rs
+++ b/widget/src/scrollable.rs
@@ -487,11 +487,11 @@ where
state.translation(self.direction, bounds, content_bounds);
operation.scrollable(
- state,
self.id.as_ref().map(|id| &id.0),
bounds,
content_bounds,
translation,
+ state,
);
operation.container(
@@ -906,14 +906,21 @@ where
is_vertical_scrollbar_dragged: state
.y_scroller_grabbed_at
.is_some(),
+ is_horizontal_scrollbar_disabled: scrollbars.is_x_disabled(),
+ is_vertical_scrollbar_disabled: scrollbars.is_y_disabled(),
}
} else if cursor_over_scrollable.is_some() {
Status::Hovered {
is_horizontal_scrollbar_hovered: mouse_over_x_scrollbar,
is_vertical_scrollbar_hovered: mouse_over_y_scrollbar,
+ is_horizontal_scrollbar_disabled: scrollbars.is_x_disabled(),
+ is_vertical_scrollbar_disabled: scrollbars.is_y_disabled(),
}
} else {
- Status::Active
+ Status::Active {
+ is_horizontal_scrollbar_disabled: scrollbars.is_x_disabled(),
+ is_vertical_scrollbar_disabled: scrollbars.is_y_disabled(),
+ }
};
if let Event::Window(window::Event::RedrawRequested(_now)) = event {
@@ -968,8 +975,13 @@ where
_ => mouse::Cursor::Unavailable,
};
- let style = theme
- .style(&self.class, self.last_status.unwrap_or(Status::Active));
+ let style = theme.style(
+ &self.class,
+ self.last_status.unwrap_or(Status::Active {
+ is_horizontal_scrollbar_disabled: false,
+ is_vertical_scrollbar_disabled: false,
+ }),
+ );
container::draw_background(renderer, &style.container, layout.bounds());
@@ -1661,6 +1673,7 @@ impl Scrollbars {
bounds: scrollbar_bounds,
scroller,
alignment: vertical.alignment,
+ disabled: content_bounds.height <= bounds.height,
})
} else {
None
@@ -1730,6 +1743,7 @@ impl Scrollbars {
bounds: scrollbar_bounds,
scroller,
alignment: horizontal.alignment,
+ disabled: content_bounds.width <= bounds.width,
})
} else {
None
@@ -1758,6 +1772,14 @@ impl Scrollbars {
}
}
+ fn is_y_disabled(&self) -> bool {
+ self.y.map(|y| y.disabled).unwrap_or(false)
+ }
+
+ fn is_x_disabled(&self) -> bool {
+ self.x.map(|x| x.disabled).unwrap_or(false)
+ }
+
fn grab_y_scroller(&self, cursor_position: Point) -> Option<f32> {
let scrollbar = self.y?;
let scroller = scrollbar.scroller?;
@@ -1804,6 +1826,7 @@ pub(super) mod internals {
pub bounds: Rectangle,
pub scroller: Option<Scroller>,
pub alignment: Anchor,
+ pub disabled: bool,
}
impl Scrollbar {
@@ -1867,13 +1890,22 @@ pub(super) mod internals {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Status {
/// The [`Scrollable`] can be interacted with.
- Active,
+ Active {
+ /// Whether or not the horizontal scrollbar is disabled meaning the content isn't overflowing.
+ is_horizontal_scrollbar_disabled: bool,
+ /// Whether or not the vertical scrollbar is disabled meaning the content isn't overflowing.
+ is_vertical_scrollbar_disabled: bool,
+ },
/// The [`Scrollable`] is being hovered.
Hovered {
/// Indicates if the horizontal scrollbar is being hovered.
is_horizontal_scrollbar_hovered: bool,
/// Indicates if the vertical scrollbar is being hovered.
is_vertical_scrollbar_hovered: bool,
+ /// Whether or not the horizontal scrollbar is disabled meaning the content isn't overflowing.
+ is_horizontal_scrollbar_disabled: bool,
+ /// Whether or not the vertical scrollbar is disabled meaning the content isn't overflowing.
+ is_vertical_scrollbar_disabled: bool,
},
/// The [`Scrollable`] is being dragged.
Dragged {
@@ -1881,6 +1913,10 @@ pub enum Status {
is_horizontal_scrollbar_dragged: bool,
/// Indicates if the vertical scrollbar is being dragged.
is_vertical_scrollbar_dragged: bool,
+ /// Whether or not the horizontal scrollbar is disabled meaning the content isn't overflowing.
+ is_horizontal_scrollbar_disabled: bool,
+ /// Whether or not the vertical scrollbar is disabled meaning the content isn't overflowing.
+ is_vertical_scrollbar_disabled: bool,
},
}
@@ -1958,7 +1994,7 @@ pub fn default(theme: &Theme, status: Status) -> Style {
};
match status {
- Status::Active => Style {
+ Status::Active { .. } => Style {
container: container::Style::default(),
vertical_rail: scrollbar,
horizontal_rail: scrollbar,
@@ -1967,6 +2003,7 @@ pub fn default(theme: &Theme, status: Status) -> Style {
Status::Hovered {
is_horizontal_scrollbar_hovered,
is_vertical_scrollbar_hovered,
+ ..
} => {
let hovered_scrollbar = Rail {
scroller: Scroller {
@@ -1994,6 +2031,7 @@ pub fn default(theme: &Theme, status: Status) -> Style {
Status::Dragged {
is_horizontal_scrollbar_dragged,
is_vertical_scrollbar_dragged,
+ ..
} => {
let dragged_scrollbar = Rail {
scroller: Scroller {
diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs
index ffd06b77..ad852ce9 100644
--- a/widget/src/text_editor.rs
+++ b/widget/src/text_editor.rs
@@ -971,13 +971,13 @@ where
fn operate(
&self,
tree: &mut widget::Tree,
- _layout: Layout<'_>,
+ layout: Layout<'_>,
_renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
let state = tree.state.downcast_mut::<State<Highlighter>>();
- operation.focusable(state, None);
+ operation.focusable(None, layout.bounds(), state);
}
}
diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs
index c3f0b25a..57ebe46a 100644
--- a/widget/src/text_input.rs
+++ b/widget/src/text_input.rs
@@ -617,14 +617,23 @@ where
fn operate(
&self,
tree: &mut Tree,
- _layout: Layout<'_>,
+ layout: Layout<'_>,
_renderer: &Renderer,
operation: &mut dyn Operation,
) {
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
- operation.focusable(state, self.id.as_ref().map(|id| &id.0));
- operation.text_input(state, self.id.as_ref().map(|id| &id.0));
+ operation.focusable(
+ self.id.as_ref().map(|id| &id.0),
+ layout.bounds(),
+ state,
+ );
+
+ operation.text_input(
+ self.id.as_ref().map(|id| &id.0),
+ layout.bounds(),
+ state,
+ );
}
fn update(
diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs
index c1af142b..2ed9419a 100644
--- a/widget/src/vertical_slider.rs
+++ b/widget/src/vertical_slider.rs
@@ -42,6 +42,7 @@ use crate::core::mouse;
use crate::core::renderer;
use crate::core::touch;
use crate::core::widget::tree::{self, Tree};
+use crate::core::window;
use crate::core::{
self, Clipboard, Element, Event, Length, Pixels, Point, Rectangle, Shell,
Size, Widget,
@@ -98,6 +99,7 @@ where
width: f32,
height: Length,
class: Theme::Class<'a>,
+ status: Option<Status>,
}
impl<'a, T, Message, Theme> VerticalSlider<'a, T, Message, Theme>
@@ -144,6 +146,7 @@ where
width: Self::DEFAULT_WIDTH,
height: Length::Fill,
class: Theme::default(),
+ status: None,
}
}
@@ -390,7 +393,9 @@ where
shell.capture_event();
}
}
- Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => {
+ Event::Keyboard(keyboard::Event::KeyPressed {
+ ref key, ..
+ }) => {
if cursor.is_over(layout.bounds()) {
match key {
Key::Named(key::Named::ArrowUp) => {
@@ -410,32 +415,36 @@ where
}
_ => {}
}
+
+ let current_status = if state.is_dragging {
+ Status::Dragged
+ } else if cursor.is_over(layout.bounds()) {
+ Status::Hovered
+ } else {
+ Status::Active
+ };
+
+ if let Event::Window(window::Event::RedrawRequested(_now)) = event {
+ self.status = Some(current_status);
+ } else if self.status.is_some_and(|status| status != current_status) {
+ shell.request_redraw();
+ }
}
fn draw(
&self,
- tree: &Tree,
+ _tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
_style: &renderer::Style,
layout: Layout<'_>,
- cursor: mouse::Cursor,
+ _cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
- let state = tree.state.downcast_ref::<State>();
let bounds = layout.bounds();
- let is_mouse_over = cursor.is_over(bounds);
- let style = theme.style(
- &self.class,
- if state.is_dragging {
- Status::Dragged
- } else if is_mouse_over {
- Status::Hovered
- } else {
- Status::Active
- },
- );
+ let style =
+ theme.style(&self.class, self.status.unwrap_or(Status::Active));
let (handle_width, handle_height, handle_border_radius) =
match style.handle.shape {