From 999ad2d288a9354f045bb2e1b838014b3d302779 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 23 Mar 2024 19:23:08 +0100 Subject: Try catalog theming approach with `Button` --- widget/src/helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 4863e550..e60096d1 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -117,7 +117,7 @@ pub fn button<'a, Message, Theme, Renderer>( content: impl Into>, ) -> Button<'a, Message, Theme, Renderer> where - Theme: button::DefaultStyle + 'a, + Theme: button::Catalog + 'a, Renderer: core::Renderer, { Button::new(content) -- cgit From e657dc2ecd2ffa72c0abd87f9a59dcc1acbc7083 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 24 Mar 2024 02:08:20 +0100 Subject: Fine-tune `Catalog` approach for `button`, `checkbox`, and `svg` --- widget/src/helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index e60096d1..4c4d1012 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -161,7 +161,7 @@ pub fn checkbox<'a, Message, Theme, Renderer>( is_checked: bool, ) -> Checkbox<'a, Message, Theme, Renderer> where - Theme: checkbox::DefaultStyle + 'a, + Theme: checkbox::Catalog + 'a, Renderer: core::text::Renderer, { Checkbox::new(label, is_checked) @@ -367,7 +367,7 @@ pub fn svg<'a, Theme>( handle: impl Into, ) -> crate::Svg<'a, Theme> where - Theme: crate::svg::DefaultStyle + 'a, + Theme: crate::svg::Catalog, { crate::Svg::new(handle) } -- cgit From f0ae9a0c38c2532220a7460916604914db94c078 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 24 Mar 2024 05:03:09 +0100 Subject: Use `Catalog` approach for all widgets --- widget/src/helpers.rs | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 4c4d1012..77b30882 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -7,6 +7,7 @@ use crate::core; use crate::core::widget::operation; use crate::core::{Element, Length, Pixels}; use crate::keyed; +use crate::overlay; use crate::pick_list::{self, PickList}; use crate::progress_bar::{self, ProgressBar}; use crate::radio::{self, Radio}; @@ -58,7 +59,7 @@ pub fn container<'a, Message, Theme, Renderer>( content: impl Into>, ) -> Container<'a, Message, Theme, Renderer> where - Theme: container::DefaultStyle + 'a, + Theme: container::Catalog + 'a, Renderer: core::Renderer, { Container::new(content) @@ -104,7 +105,7 @@ pub fn scrollable<'a, Message, Theme, Renderer>( content: impl Into>, ) -> Scrollable<'a, Message, Theme, Renderer> where - Theme: scrollable::DefaultStyle + 'a, + Theme: scrollable::Catalog + 'a, Renderer: core::Renderer, { Scrollable::new(content) @@ -134,7 +135,7 @@ pub fn tooltip<'a, Message, Theme, Renderer>( position: tooltip::Position, ) -> crate::Tooltip<'a, Message, Theme, Renderer> where - Theme: container::DefaultStyle + 'a, + Theme: container::Catalog + 'a, Renderer: core::text::Renderer, { Tooltip::new(content, tooltip, position) @@ -147,7 +148,7 @@ pub fn text<'a, Theme, Renderer>( text: impl ToString, ) -> Text<'a, Theme, Renderer> where - Theme: text::DefaultStyle + 'a, + Theme: text::Catalog + 'a, Renderer: core::text::Renderer, { Text::new(text.to_string()) @@ -178,7 +179,7 @@ pub fn radio<'a, Message, Theme, Renderer, V>( ) -> Radio<'a, Message, Theme, Renderer> where Message: Clone, - Theme: radio::DefaultStyle + 'a, + Theme: radio::Catalog + 'a, Renderer: core::text::Renderer, V: Copy + Eq, { @@ -194,7 +195,7 @@ pub fn toggler<'a, Message, Theme, Renderer>( f: impl Fn(bool) -> Message + 'a, ) -> Toggler<'a, Message, Theme, Renderer> where - Theme: toggler::DefaultStyle + 'a, + Theme: toggler::Catalog + 'a, Renderer: core::text::Renderer, { Toggler::new(label, is_checked, f) @@ -209,7 +210,7 @@ pub fn text_input<'a, Message, Theme, Renderer>( ) -> TextInput<'a, Message, Theme, Renderer> where Message: Clone, - Theme: text_input::DefaultStyle + 'a, + Theme: text_input::Catalog + 'a, Renderer: core::text::Renderer, { TextInput::new(placeholder, value) @@ -223,7 +224,7 @@ pub fn text_editor<'a, Message, Theme, Renderer>( ) -> TextEditor<'a, core::text::highlighter::PlainText, Message, Theme, Renderer> where Message: Clone, - Theme: text_editor::DefaultStyle + 'a, + Theme: text_editor::Catalog + 'a, Renderer: core::text::Renderer, { TextEditor::new(content) @@ -240,7 +241,7 @@ pub fn slider<'a, T, Message, Theme>( where T: Copy + From + std::cmp::PartialOrd, Message: Clone, - Theme: slider::DefaultStyle + 'a, + Theme: slider::Catalog + 'a, { Slider::new(range, value, on_change) } @@ -256,7 +257,7 @@ pub fn vertical_slider<'a, T, Message, Theme>( where T: Copy + From + std::cmp::PartialOrd, Message: Clone, - Theme: vertical_slider::DefaultStyle + 'a, + Theme: vertical_slider::Catalog + 'a, { VerticalSlider::new(range, value, on_change) } @@ -274,7 +275,7 @@ where L: Borrow<[T]> + 'a, V: Borrow + 'a, Message: Clone, - Theme: pick_list::DefaultStyle, + Theme: pick_list::Catalog + overlay::menu::Catalog, Renderer: core::text::Renderer, { PickList::new(options, selected, on_selected) @@ -291,7 +292,7 @@ pub fn combo_box<'a, T, Message, Theme, Renderer>( ) -> ComboBox<'a, T, Message, Theme, Renderer> where T: std::fmt::Display + Clone, - Theme: combo_box::DefaultStyle + 'a, + Theme: combo_box::Catalog + 'a, Renderer: core::text::Renderer, { ComboBox::new(state, placeholder, selection, on_selected) @@ -318,7 +319,7 @@ pub fn vertical_space() -> Space { /// [`Rule`]: crate::Rule pub fn horizontal_rule<'a, Theme>(height: impl Into) -> Rule<'a, Theme> where - Theme: rule::DefaultStyle + 'a, + Theme: rule::Catalog + 'a, { Rule::horizontal(height) } @@ -328,7 +329,7 @@ where /// [`Rule`]: crate::Rule pub fn vertical_rule<'a, Theme>(width: impl Into) -> Rule<'a, Theme> where - Theme: rule::DefaultStyle + 'a, + Theme: rule::Catalog + 'a, { Rule::vertical(width) } @@ -345,7 +346,7 @@ pub fn progress_bar<'a, Theme>( value: f32, ) -> ProgressBar<'a, Theme> where - Theme: progress_bar::DefaultStyle + 'a, + Theme: progress_bar::Catalog + 'a, { ProgressBar::new(range, value) } @@ -395,7 +396,7 @@ pub fn qr_code<'a, Theme>( data: &'a crate::qr_code::Data, ) -> crate::QRCode<'a, Theme> where - Theme: crate::qr_code::DefaultStyle + 'a, + Theme: crate::qr_code::Catalog + 'a, { crate::QRCode::new(data) } -- cgit From 1d83e59e8ab51d403baee0daa878644c6a37be3f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 1 Apr 2024 21:36:08 +0200 Subject: Specialize `widget::text` helper with custom `IntoContent` trait --- widget/src/helpers.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 77b30882..deff0261 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -145,13 +145,13 @@ where /// /// [`Text`]: core::widget::Text pub fn text<'a, Theme, Renderer>( - text: impl ToString, + text: impl text::IntoContent<'a>, ) -> Text<'a, Theme, Renderer> where Theme: text::Catalog + 'a, Renderer: core::text::Renderer, { - Text::new(text.to_string()) + Text::new(text) } /// Creates a new [`Checkbox`]. -- cgit From b8d5df28172a9f50c8b48123df05a336801f2bdc Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 1 Apr 2024 21:36:52 +0200 Subject: Reintroduce old `text` helper as `value` helper --- widget/src/helpers.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index deff0261..5bfb590f 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -154,6 +154,19 @@ where Text::new(text) } +/// Creates a new [`Text`] widget that displays the provided value. +/// +/// [`Text`]: core::widget::Text +pub fn value<'a, Theme, Renderer>( + value: impl ToString, +) -> Text<'a, Theme, Renderer> +where + Theme: text::Catalog + 'a, + Renderer: core::text::Renderer, +{ + Text::new(value.to_string()) +} + /// Creates a new [`Checkbox`]. /// /// [`Checkbox`]: crate::Checkbox -- cgit From 34f799aa3dbb58c49708c1f1d9ee436922db636d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 1 Apr 2024 21:47:55 +0200 Subject: Rename `text::IntoContent` to `IntoFragment` --- widget/src/helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 5bfb590f..61789c19 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -145,7 +145,7 @@ where /// /// [`Text`]: core::widget::Text pub fn text<'a, Theme, Renderer>( - text: impl text::IntoContent<'a>, + text: impl text::IntoFragment<'a>, ) -> Text<'a, Theme, Renderer> where Theme: text::Catalog + 'a, -- cgit From 0c74d2645649a88799a894ed684a728d135043fa Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 25 Apr 2024 01:39:34 +0200 Subject: Implement `Stack` widget It can be used to stack elements on top of each other! --- widget/src/helpers.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 61789c19..2afed3e6 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -21,7 +21,7 @@ use crate::text_input::{self, TextInput}; use crate::toggler::{self, Toggler}; use crate::tooltip::{self, Tooltip}; use crate::vertical_slider::{self, VerticalSlider}; -use crate::{Column, MouseArea, Row, Space, Themer}; +use crate::{Column, MouseArea, Row, Space, Stack, Themer}; use std::borrow::Borrow; use std::ops::RangeInclusive; @@ -52,6 +52,19 @@ macro_rules! row { ); } +/// Creates a [`Stack`] with the given children. +/// +/// [`Stack`]: crate::Stack +#[macro_export] +macro_rules! stack { + () => ( + $crate::Stack::new() + ); + ($($x:expr),+ $(,)?) => ( + $crate::Stack::with_children([$($crate::core::Element::from($x)),+]) + ); +} + /// Creates a new [`Container`] with the provided content. /// /// [`Container`]: crate::Container @@ -98,6 +111,18 @@ where Row::with_children(children) } +/// Creates a new [`Stack`] with the given children. +/// +/// [`Stack`]: crate::Stack +pub fn stack<'a, Message, Theme, Renderer>( + children: impl IntoIterator>, +) -> Stack<'a, Message, Theme, Renderer> +where + Renderer: core::Renderer, +{ + Stack::with_children(children) +} + /// Creates a new [`Scrollable`] with the provided content. /// /// [`Scrollable`]: crate::Scrollable -- cgit From 4cd45643d7d2aa83212162f17a9b28ddae4a9340 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 25 Apr 2024 06:05:00 +0200 Subject: Introduce `opaque` widget helper --- widget/src/helpers.rs | 169 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 2afed3e6..fd345251 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -5,7 +5,7 @@ use crate::combo_box::{self, ComboBox}; use crate::container::{self, Container}; use crate::core; use crate::core::widget::operation; -use crate::core::{Element, Length, Pixels}; +use crate::core::{Element, Length, Pixels, Widget}; use crate::keyed; use crate::overlay; use crate::pick_list::{self, PickList}; @@ -123,6 +123,173 @@ where Stack::with_children(children) } +/// Wraps the given widget and captures any mouse button presses inside the bounds of +/// the widget—therefore making it _opaque_. +/// +/// This helper is meant to be used to mark elements in a [`Stack`] to avoid mouse +/// events from passing through layers. +/// +/// [`Stack`]: crate::Stack +pub fn opaque<'a, Message, Theme, Renderer>( + content: impl Into>, +) -> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: 'a, + Renderer: core::Renderer + 'a, +{ + use crate::core::event::{self, Event}; + use crate::core::layout::{self, Layout}; + use crate::core::mouse; + use crate::core::renderer; + use crate::core::widget::tree::{self, Tree}; + use crate::core::{Rectangle, Shell, Size}; + + struct Opaque<'a, Message, Theme, Renderer> { + content: Element<'a, Message, Theme, Renderer>, + } + + impl<'a, Message, Theme, Renderer> Widget + for Opaque<'a, Message, Theme, Renderer> + where + Renderer: core::Renderer, + { + fn tag(&self) -> tree::Tag { + self.content.as_widget().tag() + } + + fn state(&self) -> tree::State { + self.content.as_widget().state() + } + + fn children(&self) -> Vec { + self.content.as_widget().children() + } + + fn diff(&self, tree: &mut Tree) { + self.content.as_widget().diff(tree); + } + + fn size(&self) -> Size { + self.content.as_widget().size() + } + + fn size_hint(&self) -> Size { + self.content.as_widget().size_hint() + } + + fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + self.content.as_widget().layout(tree, renderer, limits) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + self.content + .as_widget() + .draw(tree, renderer, theme, style, layout, cursor, viewport); + } + + fn operate( + &self, + state: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn operation::Operation, + ) { + self.content + .as_widget() + .operate(state, layout, renderer, operation); + } + + fn on_event( + &mut self, + state: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn core::Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + let is_mouse_press = matches!( + event, + core::Event::Mouse(mouse::Event::ButtonPressed(_)) + ); + + if let core::event::Status::Captured = + self.content.as_widget_mut().on_event( + state, event, layout, cursor, renderer, clipboard, shell, + viewport, + ) + { + return event::Status::Captured; + } + + if is_mouse_press && cursor.is_over(layout.bounds()) { + event::Status::Captured + } else { + event::Status::Ignored + } + } + + fn mouse_interaction( + &self, + state: &core::widget::Tree, + layout: core::Layout<'_>, + cursor: core::mouse::Cursor, + viewport: &core::Rectangle, + renderer: &Renderer, + ) -> core::mouse::Interaction { + let interaction = self + .content + .as_widget() + .mouse_interaction(state, layout, cursor, viewport, renderer); + + if interaction == mouse::Interaction::None + && cursor.is_over(layout.bounds()) + { + mouse::Interaction::Idle + } else { + interaction + } + } + + fn overlay<'b>( + &'b mut self, + state: &'b mut core::widget::Tree, + layout: core::Layout<'_>, + renderer: &Renderer, + translation: core::Vector, + ) -> Option> + { + self.content.as_widget_mut().overlay( + state, + layout, + renderer, + translation, + ) + } + } + + Element::new(Opaque { + content: content.into(), + }) +} + /// Creates a new [`Scrollable`] with the provided content. /// /// [`Scrollable`]: crate::Scrollable -- cgit From 23ef6547ad0f0cc2944664ace4be7cdacc0a23cf Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 27 Apr 2024 06:06:13 +0200 Subject: Introduce `hover` widget --- widget/src/helpers.rs | 230 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 229 insertions(+), 1 deletion(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index fd345251..c86223bc 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -124,7 +124,7 @@ where } /// Wraps the given widget and captures any mouse button presses inside the bounds of -/// the widget—therefore making it _opaque_. +/// the widget—effectively making it _opaque_. /// /// This helper is meant to be used to mark elements in a [`Stack`] to avoid mouse /// events from passing through layers. @@ -290,6 +290,234 @@ where }) } +/// Displays a widget on top of another one, only when the base widget is hovered. +/// +/// This works analogously to a [`stack`], but it will only display the layer on top +/// when the cursor is over the base. It can be useful for removing visual clutter. +pub fn hover<'a, Message, Theme, Renderer>( + base: impl Into>, + top: impl Into>, +) -> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: 'a, + Renderer: core::Renderer + 'a, +{ + use crate::core::event::{self, Event}; + use crate::core::layout::{self, Layout}; + use crate::core::mouse; + use crate::core::renderer; + use crate::core::widget::tree::{self, Tree}; + use crate::core::{Rectangle, Shell, Size}; + + struct Hover<'a, Message, Theme, Renderer> { + base: Element<'a, Message, Theme, Renderer>, + top: Element<'a, Message, Theme, Renderer>, + is_overlay_active: bool, + } + + impl<'a, Message, Theme, Renderer> Widget + for Hover<'a, Message, Theme, Renderer> + where + Renderer: core::Renderer, + { + fn tag(&self) -> tree::Tag { + struct Tag; + tree::Tag::of::() + } + + fn children(&self) -> Vec { + vec![Tree::new(&self.base), Tree::new(&self.top)] + } + + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&[&self.base, &self.top]); + } + + fn size(&self) -> Size { + self.base.as_widget().size() + } + + fn size_hint(&self) -> Size { + self.base.as_widget().size_hint() + } + + fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let base = self.base.as_widget().layout( + &mut tree.children[0], + renderer, + limits, + ); + + let top = self.top.as_widget().layout( + &mut tree.children[1], + renderer, + &layout::Limits::new(Size::ZERO, base.size()), + ); + + layout::Node::with_children(base.size(), vec![base, top]) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + let mut children = layout.children().zip(&tree.children); + + let (base_layout, base_tree) = children.next().unwrap(); + + self.base.as_widget().draw( + base_tree, + renderer, + theme, + style, + base_layout, + cursor, + viewport, + ); + + if cursor.is_over(layout.bounds()) || self.is_overlay_active { + let (top_layout, top_tree) = children.next().unwrap(); + + renderer.with_layer(layout.bounds(), |renderer| { + self.top.as_widget().draw( + top_tree, renderer, theme, style, top_layout, cursor, + viewport, + ); + }); + } + } + + fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn operation::Operation, + ) { + let children = [&self.base, &self.top] + .into_iter() + .zip(layout.children().zip(&mut tree.children)); + + for (child, (layout, tree)) in children { + child.as_widget().operate(tree, layout, renderer, operation); + } + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn core::Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + let mut children = layout.children().zip(&mut tree.children); + let (base_layout, base_tree) = children.next().unwrap(); + + let top_status = if cursor.is_over(layout.bounds()) { + let (top_layout, top_tree) = children.next().unwrap(); + + self.top.as_widget_mut().on_event( + top_tree, + event.clone(), + top_layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ) + } else { + event::Status::Ignored + }; + + if top_status == event::Status::Captured { + return top_status; + } + + self.base.as_widget_mut().on_event( + base_tree, + event.clone(), + base_layout, + cursor, + renderer, + clipboard, + shell, + viewport, + ) + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + [&self.base, &self.top] + .into_iter() + .rev() + .zip(layout.children().rev().zip(tree.children.iter().rev())) + .map(|(child, (layout, tree))| { + child.as_widget().mouse_interaction( + tree, layout, cursor, viewport, renderer, + ) + }) + .find(|&interaction| interaction != mouse::Interaction::None) + .unwrap_or_default() + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut core::widget::Tree, + layout: core::Layout<'_>, + renderer: &Renderer, + translation: core::Vector, + ) -> Option> + { + let overlay = [&mut self.base, &mut self.top] + .into_iter() + .rev() + .zip( + layout.children().rev().zip(tree.children.iter_mut().rev()), + ) + .find_map(|(child, (layout, tree))| { + child.as_widget_mut().overlay( + tree, + layout, + renderer, + translation, + ) + }); + + self.is_overlay_active = overlay.is_some(); + overlay + } + } + + Element::new(Hover { + base: base.into(), + top: top.into(), + is_overlay_active: false, + }) +} + /// Creates a new [`Scrollable`] with the provided content. /// /// [`Scrollable`]: crate::Scrollable -- cgit From 40dff6b23d4666d69e27addcfb90d9557f59ed6c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 27 Apr 2024 06:15:58 +0200 Subject: Fix `overlay` behavior in `hover` widget --- widget/src/helpers.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index c86223bc..0b5adde7 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -313,7 +313,7 @@ where struct Hover<'a, Message, Theme, Renderer> { base: Element<'a, Message, Theme, Renderer>, top: Element<'a, Message, Theme, Renderer>, - is_overlay_active: bool, + is_top_overlay_active: bool, } impl<'a, Message, Theme, Renderer> Widget @@ -387,7 +387,7 @@ where viewport, ); - if cursor.is_over(layout.bounds()) || self.is_overlay_active { + if cursor.is_over(layout.bounds()) || self.is_top_overlay_active { let (top_layout, top_tree) = children.next().unwrap(); renderer.with_layer(layout.bounds(), |renderer| { @@ -491,13 +491,10 @@ where translation: core::Vector, ) -> Option> { - let overlay = [&mut self.base, &mut self.top] + let mut overlays = [&mut self.base, &mut self.top] .into_iter() - .rev() - .zip( - layout.children().rev().zip(tree.children.iter_mut().rev()), - ) - .find_map(|(child, (layout, tree))| { + .zip(layout.children().zip(tree.children.iter_mut())) + .map(|(child, (layout, tree))| { child.as_widget_mut().overlay( tree, layout, @@ -506,15 +503,21 @@ where ) }); - self.is_overlay_active = overlay.is_some(); - overlay + if let Some(base_overlay) = overlays.next()? { + return Some(base_overlay); + } + + let top_overlay = overlays.next()?; + self.is_top_overlay_active = top_overlay.is_some(); + + top_overlay } } Element::new(Hover { base: base.into(), top: top.into(), - is_overlay_active: false, + is_top_overlay_active: false, }) } -- cgit From 95ac45e33d8490f0cc5c3cdea80be78f338d44e5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 27 Apr 2024 06:19:25 +0200 Subject: Fix ambiguous link in documentation of `hover` helper --- widget/src/helpers.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 0b5adde7..5f27b680 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -294,6 +294,8 @@ where /// /// This works analogously to a [`stack`], but it will only display the layer on top /// when the cursor is over the base. It can be useful for removing visual clutter. +/// +/// [`stack`]: stack() pub fn hover<'a, Message, Theme, Renderer>( base: impl Into>, top: impl Into>, -- cgit From 9c0f2dc9a5ea172afd27b13d55dead40098eb7e3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 27 Apr 2024 11:21:18 +0200 Subject: Fix top layer clipping in `hover` widget --- widget/src/helpers.rs | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 5f27b680..3940e389 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -375,29 +375,32 @@ where cursor: mouse::Cursor, viewport: &Rectangle, ) { - let mut children = layout.children().zip(&tree.children); + if let Some(bounds) = layout.bounds().intersection(viewport) { + let mut children = layout.children().zip(&tree.children); - let (base_layout, base_tree) = children.next().unwrap(); - - self.base.as_widget().draw( - base_tree, - renderer, - theme, - style, - base_layout, - cursor, - viewport, - ); - - if cursor.is_over(layout.bounds()) || self.is_top_overlay_active { - let (top_layout, top_tree) = children.next().unwrap(); + let (base_layout, base_tree) = children.next().unwrap(); - renderer.with_layer(layout.bounds(), |renderer| { - self.top.as_widget().draw( - top_tree, renderer, theme, style, top_layout, cursor, - viewport, - ); - }); + self.base.as_widget().draw( + base_tree, + renderer, + theme, + style, + base_layout, + cursor, + viewport, + ); + + if cursor.is_over(layout.bounds()) || self.is_top_overlay_active + { + let (top_layout, top_tree) = children.next().unwrap(); + + renderer.with_layer(bounds, |renderer| { + self.top.as_widget().draw( + top_tree, renderer, theme, style, top_layout, + cursor, viewport, + ); + }); + } } } -- cgit From 05c90775816a97e44d499aaff5e9de57b6144e8b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 27 Apr 2024 11:28:43 +0200 Subject: Propagate mouse movement and button releases unconditionally in `hover` --- widget/src/helpers.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 3940e389..48c5dde4 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -434,7 +434,14 @@ where let mut children = layout.children().zip(&mut tree.children); let (base_layout, base_tree) = children.next().unwrap(); - let top_status = if cursor.is_over(layout.bounds()) { + let top_status = if matches!( + event, + Event::Mouse( + mouse::Event::CursorMoved { .. } + | mouse::Event::ButtonReleased(_) + ) + ) || cursor.is_over(layout.bounds()) + { let (top_layout, top_tree) = children.next().unwrap(); self.top.as_widget_mut().on_event( -- cgit From 15057a05c118dafcb8cf90d4119e66caaa6026c5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 3 May 2024 09:11:46 +0200 Subject: Introduce `center` widget helper ... and also make `center_x` and `center_y` set `width` and `height` to `Length::Fill`, respectively. This targets the most common use case when centering things and removes a bunch of boilerplate as a result. --- widget/src/helpers.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 48c5dde4..fd8614f5 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -78,6 +78,27 @@ where Container::new(content) } +/// Creates a new [`Container`] that fills all the available space +/// and centers its contents inside. +/// +/// This is equivalent to: +/// ```rust,no_run +/// # use iced_widget::Container; +/// # fn container(x: A) -> Container<'static, ()> { unreachable!() } +/// let centered = container("Centered!").center(); +/// ``` +/// +/// [`Container`]: crate::Container +pub fn center<'a, Message, Theme, Renderer>( + content: impl Into>, +) -> Container<'a, Message, Theme, Renderer> +where + Theme: container::Catalog + 'a, + Renderer: core::Renderer, +{ + container(content).fill().center() +} + /// Creates a new [`Column`] with the given children. pub fn column<'a, Message, Theme, Renderer>( children: impl IntoIterator>, -- cgit From 05f69f495e9b52e9a7d7bada420558d4c4f6730c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 13 May 2024 17:56:02 +0200 Subject: Ask for explicit `Length` in `center_*` methods --- widget/src/helpers.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'widget/src/helpers.rs') diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index fd8614f5..a1ecd9d1 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -83,9 +83,10 @@ where /// /// This is equivalent to: /// ```rust,no_run +/// # use iced_widget::core::Length; /// # use iced_widget::Container; /// # fn container(x: A) -> Container<'static, ()> { unreachable!() } -/// let centered = container("Centered!").center(); +/// let centered = container("Centered!").center(Length::Fill); /// ``` /// /// [`Container`]: crate::Container @@ -96,7 +97,7 @@ where Theme: container::Catalog + 'a, Renderer: core::Renderer, { - container(content).fill().center() + container(content).center(Length::Fill) } /// Creates a new [`Column`] with the given children. -- cgit