From 52a185fbab728b85cf414d4997567f52ebc66205 Mon Sep 17 00:00:00 2001 From: Kaiden42 Date: Sat, 19 Sep 2020 18:44:27 +0200 Subject: Implement `Toggler` widget for iced_native --- native/src/renderer/null.rs | 18 ++- native/src/widget.rs | 3 + native/src/widget/toggler.rs | 262 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 native/src/widget/toggler.rs (limited to 'native/src') diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 28746585..89bb9433 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -1,6 +1,6 @@ use crate::{ button, checkbox, column, container, pane_grid, progress_bar, radio, row, - scrollable, slider, text, text_input, Color, Element, Font, + scrollable, slider, text, text_input, toggler, Color, Element, Font, HorizontalAlignment, Layout, Padding, Point, Rectangle, Renderer, Size, VerticalAlignment, }; @@ -288,3 +288,19 @@ impl pane_grid::Renderer for Null { ) { } } + +impl toggler::Renderer for Null { + type Style = (); + + const DEFAULT_SIZE: u16 = 20; + + fn draw( + &mut self, + _bounds: Rectangle, + _is_checked: bool, + _is_mouse_over: bool, + _label: Self::Output, + _style: &Self::Style, + ) { + } +} diff --git a/native/src/widget.rs b/native/src/widget.rs index 791c53a3..759fe71a 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -36,6 +36,7 @@ pub mod space; pub mod svg; pub mod text; pub mod text_input; +pub mod toggler; pub mod tooltip; #[doc(no_inline)] @@ -73,6 +74,8 @@ pub use text::Text; #[doc(no_inline)] pub use text_input::TextInput; #[doc(no_inline)] +pub use toggler::Toggler; +#[doc(no_inline)] pub use tooltip::Tooltip; use crate::event::{self, Event}; diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs new file mode 100644 index 00000000..250abac4 --- /dev/null +++ b/native/src/widget/toggler.rs @@ -0,0 +1,262 @@ +//! Show toggle controls using togglers. +use std::hash::Hash; + +use crate::{ + event, layout, mouse, row, text, Align, Clipboard, Element, Event, Hasher, + HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text, + VerticalAlignment, Widget, +}; + +/// A toggler widget +/// +/// # Example +/// +/// ``` +/// # type Toggler = iced_native::Toggler; +/// # +/// pub enum Message { +/// TogglerToggled(bool), +/// } +/// +/// let is_active = true; +/// +/// Toggler::new(is_active, "Toggle me!", |b| Message::TogglerToggled(b)) +/// ``` +/// +#[allow(missing_debug_implementations)] +pub struct Toggler { + is_active: bool, + on_toggle: Box Message>, + label: String, + width: Length, + size: u16, + text_size: Option, + font: Renderer::Font, + style: Renderer::Style, +} + +impl + Toggler +{ + /// Creates a new [`Toggler`]. + /// + /// It expects: + /// * a boolean describing whether the [`Toggler`] is checked or not + /// * the label of the [`Toggler`] + /// * a function that will be called when the [`Toggler`] is toggled. It + /// will receive the new state of the [`Toggler`] and must produce a + /// `Message`. + /// + /// [`Toggler`]: struct.Toggler.html + pub fn new(is_active: bool, label: impl Into, f: F) -> Self + where + F: 'static + Fn(bool) -> Message, + { + Toggler { + is_active, + on_toggle: Box::new(f), + label: label.into(), + width: Length::Fill, + size: ::DEFAULT_SIZE, + text_size: None, + font: Renderer::Font::default(), + style: Renderer::Style::default(), + } + } + + /// Sets the size of the [`Toggler`]. + /// + /// [`Toggler`]: struct.Toggler.html + pub fn size(mut self, size: u16) -> Self { + self.size = size; + self + } + + /// Sets the width of the [`Toggler`]. + /// + /// [`Toggler`]: struct.Toggler.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } + + /// Sets the text size o the [`Toggler`]. + /// + /// [`Toggler`]: struct.Toggler.html + pub fn text_size(mut self, text_size: u16) -> Self { + self.text_size = Some(text_size); + self + } + + /// Sets the [`Font`] of the text of the [`Toggler`] + /// + /// [`Toggler`]: struct.Toggler.html + /// [`Font`]: ../../struct.Font.html + pub fn font(mut self, font: Renderer::Font) -> Self { + self.font = font; + self + } + + /// Sets the style of the [`Toggler`]. + /// + /// [`Toggler`]: struct.Toggler.html + pub fn style(mut self, style: impl Into) -> Self { + self.style = style.into(); + self + } +} + +impl Widget for Toggler +where + Renderer: self::Renderer + text::Renderer + row::Renderer, +{ + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + Length::Shrink + } + + fn layout( + &self, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + Row::<(), Renderer>::new() + .width(self.width) + .align_items(Align::Center) + .push( + Text::new(&self.label) + .font(self.font) + .width(self.width) + .size(self.text_size.unwrap_or(renderer.default_size())), + ) + .push( + Row::new() + .width(Length::Units(2 * self.size)) + .height(Length::Units(self.size)), + ) + .layout(renderer, limits) + } + + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + _renderer: &Renderer, + _clipboard: &mut dyn Clipboard, + messages: &mut Vec, + ) -> event::Status { + match event { + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { + let mouse_over = layout.bounds().contains(cursor_position); + + if mouse_over { + messages.push((self.on_toggle)(!self.is_active)); + + event::Status::Captured + } else { + event::Status::Ignored + } + } + _ => event::Status::Ignored, + } + } + + fn draw( + &self, + renderer: &mut Renderer, + defaults: &Renderer::Defaults, + layout: Layout<'_>, + cursor_position: Point, + _viewport: &Rectangle, + ) -> Renderer::Output { + let bounds = layout.bounds(); + let mut children = layout.children(); + + let label_layout = children.next().unwrap(); + let toggler_layout = children.next().unwrap(); + let toggler_bounds = toggler_layout.bounds(); + + let label = text::Renderer::draw( + renderer, + defaults, + label_layout.bounds(), + &self.label, + self.text_size.unwrap_or(renderer.default_size()), + self.font, + None, + HorizontalAlignment::Left, + VerticalAlignment::Center, + ); + + let is_mouse_over = bounds.contains(cursor_position); + + self::Renderer::draw( + renderer, + toggler_bounds, + self.is_active, + is_mouse_over, + label, + &self.style, + ) + } + + fn hash_layout(&self, state: &mut Hasher) { + struct Marker; + std::any::TypeId::of::().hash(state); + + self.label.hash(state) + } +} + +/// The renderer of a [`Toggler`]. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`Toggler`] in your user interface. +/// +/// [`Toggler`]: struct.Toggler.html +/// [renderer]: ../../renderer/index.html +pub trait Renderer: crate::Renderer { + /// The style supported by this renderer. + type Style: Default; + + /// The default size of a [`Toggler`]. + /// + /// [`Toggler`]: struct.Toggler.html + const DEFAULT_SIZE: u16; + + /// Draws a [`Toggler`]. + /// + /// It receives: + /// * the bounds of the [`Toggler`] + /// * whether the [`Toggler`] is activated or not + /// * whether the mouse is over the [`Toggler`] or not + /// * the drawn label of the [`Toggler`] + /// * the style of the [`Toggler`] + /// + /// [`Toggler`]: struct.Toggler.html + fn draw( + &mut self, + bounds: Rectangle, + is_active: bool, + is_mouse_over: bool, + label: Self::Output, + style: &Self::Style, + ) -> Self::Output; +} + +impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> +where + Renderer: 'a + self::Renderer + text::Renderer + row::Renderer, + Message: 'a, +{ + fn from( + toggler: Toggler, + ) -> Element<'a, Message, Renderer> { + Element::new(toggler) + } +} -- cgit From 7370dfac6e798667771d768dbc5c3ed04930364c Mon Sep 17 00:00:00 2001 From: Kaiden42 Date: Sat, 19 Sep 2020 19:57:56 +0200 Subject: fix missing semicolon in doc test --- native/src/widget/toggler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'native/src') diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index 250abac4..1acdf6ec 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -20,7 +20,7 @@ use crate::{ /// /// let is_active = true; /// -/// Toggler::new(is_active, "Toggle me!", |b| Message::TogglerToggled(b)) +/// Toggler::new(is_active, "Toggle me!", |b| Message::TogglerToggled(b)); /// ``` /// #[allow(missing_debug_implementations)] -- cgit From aa18a6e0d5550a83510aaf38a2b01d4a5fa56ccd Mon Sep 17 00:00:00 2001 From: Kaiden42 Date: Thu, 24 Sep 2020 15:49:48 +0200 Subject: Add alignment of `Toggler` label. --- native/src/widget/toggler.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'native/src') diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index 1acdf6ec..63058d06 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -31,6 +31,8 @@ pub struct Toggler { width: Length, size: u16, text_size: Option, + text_align: Option, + spacing: u16, font: Renderer::Font, style: Renderer::Style, } @@ -59,6 +61,8 @@ impl width: Length::Fill, size: ::DEFAULT_SIZE, text_size: None, + text_align: None, + spacing: 0, font: Renderer::Font::default(), style: Renderer::Style::default(), } @@ -88,6 +92,22 @@ impl self } + /// Sets the alignment of the text of the [`Toggler`] + /// + /// [`Toggler`]: struct.Toggler.html + pub fn text_align(mut self, align: HorizontalAlignment) -> Self { + self.text_align = Some(align); + self + } + + /// Sets the spacing between the [`Toggler`] and the text. + /// + /// [`Toggler`]: struct.Toggler.html + pub fn spacing(mut self, spacing: u16) -> Self { + self.spacing = spacing; + self + } + /// Sets the [`Font`] of the text of the [`Toggler`] /// /// [`Toggler`]: struct.Toggler.html @@ -125,9 +145,13 @@ where ) -> layout::Node { Row::<(), Renderer>::new() .width(self.width) + .spacing(self.spacing) .align_items(Align::Center) .push( Text::new(&self.label) + .horizontal_alignment( + self.text_align.unwrap_or(HorizontalAlignment::Left), + ) .font(self.font) .width(self.width) .size(self.text_size.unwrap_or(renderer.default_size())), @@ -188,7 +212,7 @@ where self.text_size.unwrap_or(renderer.default_size()), self.font, None, - HorizontalAlignment::Left, + self.text_align.unwrap_or(HorizontalAlignment::Left), VerticalAlignment::Center, ); -- cgit From 7a626f3b7b6871052e5fade697e120cfb7d726d7 Mon Sep 17 00:00:00 2001 From: Kaiden42 Date: Thu, 24 Sep 2020 16:31:39 +0200 Subject: Change label of `Toggler` to optional --- native/src/renderer/null.rs | 2 +- native/src/widget/toggler.rs | 74 +++++++++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 30 deletions(-) (limited to 'native/src') diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 89bb9433..bb57c163 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -299,7 +299,7 @@ impl toggler::Renderer for Null { _bounds: Rectangle, _is_checked: bool, _is_mouse_over: bool, - _label: Self::Output, + _label: Option, _style: &Self::Style, ) { } diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index 63058d06..36e7d110 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -20,14 +20,14 @@ use crate::{ /// /// let is_active = true; /// -/// Toggler::new(is_active, "Toggle me!", |b| Message::TogglerToggled(b)); +/// Toggler::new(is_active, String::from("Toggle me!"), |b| Message::TogglerToggled(b)); /// ``` /// #[allow(missing_debug_implementations)] pub struct Toggler { is_active: bool, on_toggle: Box Message>, - label: String, + label: Option, width: Length, size: u16, text_size: Option, @@ -44,13 +44,17 @@ impl /// /// It expects: /// * a boolean describing whether the [`Toggler`] is checked or not - /// * the label of the [`Toggler`] + /// * An optional label for the [`Toggler`] /// * a function that will be called when the [`Toggler`] is toggled. It /// will receive the new state of the [`Toggler`] and must produce a /// `Message`. /// /// [`Toggler`]: struct.Toggler.html - pub fn new(is_active: bool, label: impl Into, f: F) -> Self + pub fn new( + is_active: bool, + label: impl Into>, + f: F, + ) -> Self where F: 'static + Fn(bool) -> Message, { @@ -143,25 +147,30 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - Row::<(), Renderer>::new() + let mut row = Row::<(), Renderer>::new() .width(self.width) .spacing(self.spacing) - .align_items(Align::Center) - .push( - Text::new(&self.label) + .align_items(Align::Center); + + if let Some(label) = &self.label { + row = row.push( + Text::new(label) .horizontal_alignment( self.text_align.unwrap_or(HorizontalAlignment::Left), ) .font(self.font) .width(self.width) .size(self.text_size.unwrap_or(renderer.default_size())), - ) - .push( - Row::new() - .width(Length::Units(2 * self.size)) - .height(Length::Units(self.size)), - ) - .layout(renderer, limits) + ); + } + + row = row.push( + Row::new() + .width(Length::Units(2 * self.size)) + .height(Length::Units(self.size)), + ); + + row.layout(renderer, limits) } fn on_event( @@ -200,22 +209,29 @@ where let bounds = layout.bounds(); let mut children = layout.children(); - let label_layout = children.next().unwrap(); + let label = match &self.label { + Some(label) => { + let label_layout = children.next().unwrap(); + + Some(text::Renderer::draw( + renderer, + defaults, + label_layout.bounds(), + &label, + self.text_size.unwrap_or(renderer.default_size()), + self.font, + None, + self.text_align.unwrap_or(HorizontalAlignment::Left), + VerticalAlignment::Center, + )) + } + + None => None, + }; + let toggler_layout = children.next().unwrap(); let toggler_bounds = toggler_layout.bounds(); - let label = text::Renderer::draw( - renderer, - defaults, - label_layout.bounds(), - &self.label, - self.text_size.unwrap_or(renderer.default_size()), - self.font, - None, - self.text_align.unwrap_or(HorizontalAlignment::Left), - VerticalAlignment::Center, - ); - let is_mouse_over = bounds.contains(cursor_position); self::Renderer::draw( @@ -267,7 +283,7 @@ pub trait Renderer: crate::Renderer { bounds: Rectangle, is_active: bool, is_mouse_over: bool, - label: Self::Output, + label: Option, style: &Self::Style, ) -> Self::Output; } -- cgit From c0cfd9d5cf373d4d7f89cf14c15f36e9995c1dbf Mon Sep 17 00:00:00 2001 From: Kaiden42 Date: Fri, 25 Sep 2020 16:38:30 +0200 Subject: Update documentation of `Toggler` --- native/src/widget/toggler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'native/src') diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index 36e7d110..8dbd94a1 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -96,7 +96,7 @@ impl self } - /// Sets the alignment of the text of the [`Toggler`] + /// Sets the horizontal alignment of the text of the [`Toggler`] /// /// [`Toggler`]: struct.Toggler.html pub fn text_align(mut self, align: HorizontalAlignment) -> Self { -- cgit From a32ce271bd38a6d405859210fa3fbd5a14146ce8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Date: Thu, 3 Jun 2021 20:27:32 +0700 Subject: Rename `text_align` to `text_alignment` in `Toggler` --- native/src/widget/toggler.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'native/src') diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index 8dbd94a1..d565bda1 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -31,7 +31,7 @@ pub struct Toggler { width: Length, size: u16, text_size: Option, - text_align: Option, + text_alignment: HorizontalAlignment, spacing: u16, font: Renderer::Font, style: Renderer::Style, @@ -65,7 +65,7 @@ impl width: Length::Fill, size: ::DEFAULT_SIZE, text_size: None, - text_align: None, + text_alignment: HorizontalAlignment::Left, spacing: 0, font: Renderer::Font::default(), style: Renderer::Style::default(), @@ -99,8 +99,8 @@ impl /// Sets the horizontal alignment of the text of the [`Toggler`] /// /// [`Toggler`]: struct.Toggler.html - pub fn text_align(mut self, align: HorizontalAlignment) -> Self { - self.text_align = Some(align); + pub fn text_alignment(mut self, alignment: HorizontalAlignment) -> Self { + self.text_alignment = alignment; self } @@ -155,9 +155,7 @@ where if let Some(label) = &self.label { row = row.push( Text::new(label) - .horizontal_alignment( - self.text_align.unwrap_or(HorizontalAlignment::Left), - ) + .horizontal_alignment(self.text_alignment) .font(self.font) .width(self.width) .size(self.text_size.unwrap_or(renderer.default_size())), @@ -221,7 +219,7 @@ where self.text_size.unwrap_or(renderer.default_size()), self.font, None, - self.text_align.unwrap_or(HorizontalAlignment::Left), + self.text_alignment, VerticalAlignment::Center, )) } -- cgit From ef5f46bcddffb191bde5dd2df131bdd1197a1e69 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Date: Thu, 3 Jun 2021 20:28:36 +0700 Subject: Use intra-doc links in `Toggler` docs --- native/src/widget/toggler.rs | 23 ----------------------- 1 file changed, 23 deletions(-) (limited to 'native/src') diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index d565bda1..4035276c 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -22,7 +22,6 @@ use crate::{ /// /// Toggler::new(is_active, String::from("Toggle me!"), |b| Message::TogglerToggled(b)); /// ``` -/// #[allow(missing_debug_implementations)] pub struct Toggler { is_active: bool, @@ -48,8 +47,6 @@ impl /// * a function that will be called when the [`Toggler`] is toggled. It /// will receive the new state of the [`Toggler`] and must produce a /// `Message`. - /// - /// [`Toggler`]: struct.Toggler.html pub fn new( is_active: bool, label: impl Into>, @@ -73,57 +70,42 @@ impl } /// Sets the size of the [`Toggler`]. - /// - /// [`Toggler`]: struct.Toggler.html pub fn size(mut self, size: u16) -> Self { self.size = size; self } /// Sets the width of the [`Toggler`]. - /// - /// [`Toggler`]: struct.Toggler.html pub fn width(mut self, width: Length) -> Self { self.width = width; self } /// Sets the text size o the [`Toggler`]. - /// - /// [`Toggler`]: struct.Toggler.html pub fn text_size(mut self, text_size: u16) -> Self { self.text_size = Some(text_size); self } /// Sets the horizontal alignment of the text of the [`Toggler`] - /// - /// [`Toggler`]: struct.Toggler.html pub fn text_alignment(mut self, alignment: HorizontalAlignment) -> Self { self.text_alignment = alignment; self } /// Sets the spacing between the [`Toggler`] and the text. - /// - /// [`Toggler`]: struct.Toggler.html pub fn spacing(mut self, spacing: u16) -> Self { self.spacing = spacing; self } /// Sets the [`Font`] of the text of the [`Toggler`] - /// - /// [`Toggler`]: struct.Toggler.html - /// [`Font`]: ../../struct.Font.html pub fn font(mut self, font: Renderer::Font) -> Self { self.font = font; self } /// Sets the style of the [`Toggler`]. - /// - /// [`Toggler`]: struct.Toggler.html pub fn style(mut self, style: impl Into) -> Self { self.style = style.into(); self @@ -255,15 +237,12 @@ where /// Your [renderer] will need to implement this trait before being /// able to use a [`Toggler`] in your user interface. /// -/// [`Toggler`]: struct.Toggler.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { /// The style supported by this renderer. type Style: Default; /// The default size of a [`Toggler`]. - /// - /// [`Toggler`]: struct.Toggler.html const DEFAULT_SIZE: u16; /// Draws a [`Toggler`]. @@ -274,8 +253,6 @@ pub trait Renderer: crate::Renderer { /// * whether the mouse is over the [`Toggler`] or not /// * the drawn label of the [`Toggler`] /// * the style of the [`Toggler`] - /// - /// [`Toggler`]: struct.Toggler.html fn draw( &mut self, bounds: Rectangle, -- cgit