From 387fc0be26ccd1adc66c1dc80420f9b08d0c023a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 7 Jan 2020 02:25:57 +0100 Subject: [PATCH] Implement styling for `Radio` --- examples/styling.rs | 50 +++++++++++++++++++++++------ native/src/renderer/null.rs | 3 ++ native/src/widget/radio.rs | 33 +++++++++++-------- src/native.rs | 6 ++-- style/src/lib.rs | 1 + style/src/radio.rs | 53 +++++++++++++++++++++++++++++++ wgpu/src/renderer/widget/radio.rs | 26 ++++++++------- wgpu/src/widget.rs | 1 + wgpu/src/widget/radio.rs | 10 ++++++ 9 files changed, 145 insertions(+), 38 deletions(-) create mode 100644 style/src/radio.rs create mode 100644 wgpu/src/widget/radio.rs diff --git a/examples/styling.rs b/examples/styling.rs index 426e0638..b5600e85 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -51,12 +51,15 @@ impl Sandbox for Styling { let choose_theme = style::Theme::ALL.iter().fold( Column::new().spacing(10).push(Text::new("Choose a theme:")), |column, theme| { - column.push(Radio::new( - *theme, - &format!("{:?}", theme), - Some(self.theme), - Message::ThemeChanged, - )) + column.push( + Radio::new( + *theme, + &format!("{:?}", theme), + Some(self.theme), + Message::ThemeChanged, + ) + .style(self.theme), + ) }, ); @@ -110,7 +113,7 @@ impl Sandbox for Styling { mod style { use iced::{ - button, container, progress_bar, scrollable, slider, text_input, + button, container, progress_bar, radio, scrollable, slider, text_input, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -138,6 +141,15 @@ mod style { } } + impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Radio.into(), + } + } + } + impl From for Box { fn from(theme: Theme) -> Self { match theme { @@ -213,8 +225,8 @@ mod style { mod dark { use iced::{ - button, container, progress_bar, scrollable, slider, text_input, - Background, Color, + button, container, progress_bar, radio, scrollable, slider, + text_input, Background, Color, }; const SURFACE: Color = Color::from_rgb( @@ -255,6 +267,26 @@ mod style { } } + pub struct Radio; + + impl radio::StyleSheet for Radio { + fn active(&self) -> radio::Style { + radio::Style { + background: Background::Color(SURFACE), + dot_color: Color::WHITE, + border_width: 0, + border_color: Color::TRANSPARENT, + } + } + + fn hovered(&self) -> radio::Style { + radio::Style { + background: Background::Color(Color { a: 0.5, ..SURFACE }), + ..self.active() + } + } + } + pub struct TextInput; impl text_input::StyleSheet for TextInput { diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index b721e29e..4fb4f2d5 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -152,6 +152,8 @@ impl button::Renderer for Null { } impl radio::Renderer for Null { + type Style = (); + fn default_size(&self) -> u32 { 20 } @@ -162,6 +164,7 @@ impl radio::Renderer for Null { _is_selected: bool, _is_mouse_over: bool, _label: Self::Output, + _style: &Self::Style, ) { } } diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 6ac00770..faf2736c 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -1,7 +1,7 @@ //! Create choices using radio buttons. use crate::{ input::{mouse, ButtonState}, - layout, row, text, Align, Clipboard, Color, Element, Event, Font, Hasher, + layout, row, text, Align, Clipboard, Element, Event, Font, Hasher, HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text, VerticalAlignment, Widget, }; @@ -12,7 +12,8 @@ use std::hash::Hash; /// /// # Example /// ``` -/// # use iced_native::Radio; +/// # type Radio = +/// # iced_native::Radio; /// # /// #[derive(Debug, Clone, Copy, PartialEq, Eq)] /// pub enum Choice { @@ -34,14 +35,14 @@ use std::hash::Hash; /// /// ![Radio buttons drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/radio.png?raw=true) #[allow(missing_debug_implementations)] -pub struct Radio { +pub struct Radio { is_selected: bool, on_click: Message, label: String, - label_color: Option, + style: Renderer::Style, } -impl Radio { +impl Radio { /// Creates a new [`Radio`] button. /// /// It expects: @@ -61,20 +62,20 @@ impl Radio { is_selected: Some(value) == selected, on_click: f(value), label: String::from(label), - label_color: None, + style: Renderer::Style::default(), } } - /// Sets the `Color` of the label of the [`Radio`]. + /// Sets the style of the [`Radio`] button. /// /// [`Radio`]: struct.Radio.html - pub fn label_color>(mut self, color: C) -> Self { - self.label_color = Some(color.into()); + pub fn style(mut self, style: impl Into) -> Self { + self.style = style.into(); self } } -impl Widget for Radio +impl Widget for Radio where Renderer: self::Renderer + text::Renderer + row::Renderer, Message: Clone, @@ -149,7 +150,7 @@ where &self.label, text::Renderer::default_size(renderer), Font::Default, - self.label_color, + None, HorizontalAlignment::Left, VerticalAlignment::Center, ); @@ -162,6 +163,7 @@ where self.is_selected, is_mouse_over, label, + &self.style, ) } @@ -178,6 +180,8 @@ where /// [`Radio`]: struct.Radio.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + type Style: Default; + /// Returns the default size of a [`Radio`] button. /// /// [`Radio`]: struct.Radio.html @@ -198,16 +202,17 @@ pub trait Renderer: crate::Renderer { is_selected: bool, is_mouse_over: bool, label: Self::Output, + style: &Self::Style, ) -> Self::Output; } -impl<'a, Message, Renderer> From> +impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: self::Renderer + row::Renderer + text::Renderer, + Renderer: 'static + self::Renderer + row::Renderer + text::Renderer, Message: 'static + Clone, { - fn from(radio: Radio) -> Element<'a, Message, Renderer> { + fn from(radio: Radio) -> Element<'a, Message, Renderer> { Element::new(radio) } } diff --git a/src/native.rs b/src/native.rs index 5a4db8f6..2f2182ff 100644 --- a/src/native.rs +++ b/src/native.rs @@ -34,13 +34,13 @@ pub mod widget { pub use iced_winit::svg::{Handle, Svg}; } - pub use iced_winit::{Checkbox, Radio, Text}; + pub use iced_winit::{Checkbox, Text}; #[doc(no_inline)] pub use { button::Button, container::Container, image::Image, - progress_bar::ProgressBar, scrollable::Scrollable, slider::Slider, - svg::Svg, text_input::TextInput, + progress_bar::ProgressBar, radio::Radio, scrollable::Scrollable, + slider::Slider, svg::Svg, text_input::TextInput, }; /// A container that distributes its contents vertically. diff --git a/style/src/lib.rs b/style/src/lib.rs index 3f3dc0b0..991e0644 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -1,6 +1,7 @@ pub mod button; pub mod container; pub mod progress_bar; +pub mod radio; pub mod scrollable; pub mod slider; pub mod text_input; diff --git a/style/src/radio.rs b/style/src/radio.rs new file mode 100644 index 00000000..1f0689b9 --- /dev/null +++ b/style/src/radio.rs @@ -0,0 +1,53 @@ +//! Create choices using radio buttons. +use iced_core::{Background, Color}; + +/// The appearance of a radio button. +#[derive(Debug)] +pub struct Style { + pub background: Background, + pub dot_color: Color, + pub border_width: u16, + pub border_color: Color, +} + +/// A set of rules that dictate the style of a radio button. +pub trait StyleSheet { + fn active(&self) -> Style; + + fn hovered(&self) -> Style; +} + +struct Default; + +impl StyleSheet for Default { + fn active(&self) -> Style { + Style { + background: Background::Color(Color::from_rgb(0.95, 0.95, 0.95)), + dot_color: Color::from_rgb(0.3, 0.3, 0.3), + border_width: 1, + border_color: Color::from_rgb(0.6, 0.6, 0.6), + } + } + + fn hovered(&self) -> Style { + Style { + background: Background::Color(Color::from_rgb(0.90, 0.90, 0.90)), + ..self.active() + } + } +} + +impl std::default::Default for Box { + fn default() -> Self { + Box::new(Default) + } +} + +impl From for Box +where + T: 'static + StyleSheet, +{ + fn from(style: T) -> Self { + Box::new(style) + } +} diff --git a/wgpu/src/renderer/widget/radio.rs b/wgpu/src/renderer/widget/radio.rs index aa1dbadc..564f066b 100644 --- a/wgpu/src/renderer/widget/radio.rs +++ b/wgpu/src/renderer/widget/radio.rs @@ -1,10 +1,12 @@ -use crate::{Primitive, Renderer}; +use crate::{radio::StyleSheet, Primitive, Renderer}; use iced_native::{radio, Background, Color, MouseCursor, Rectangle}; const SIZE: f32 = 28.0; const DOT_SIZE: f32 = SIZE / 2.0; impl radio::Renderer for Renderer { + type Style = Box; + fn default_size(&self) -> u32 { SIZE as u32 } @@ -15,20 +17,20 @@ impl radio::Renderer for Renderer { is_selected: bool, is_mouse_over: bool, (label, _): Self::Output, + style_sheet: &Self::Style, ) -> Self::Output { + let style = if is_mouse_over { + style_sheet.hovered() + } else { + style_sheet.active() + }; + let radio = Primitive::Quad { bounds, - background: Background::Color( - if is_mouse_over { - [0.90, 0.90, 0.90] - } else { - [0.95, 0.95, 0.95] - } - .into(), - ), + background: style.background, border_radius: (SIZE / 2.0) as u16, - border_width: 1, - border_color: Color::from_rgb(0.6, 0.6, 0.6), + border_width: style.border_width, + border_color: style.border_color, }; ( @@ -41,7 +43,7 @@ impl radio::Renderer for Renderer { width: bounds.width - DOT_SIZE, height: bounds.height - DOT_SIZE, }, - background: Background::Color([0.3, 0.3, 0.3].into()), + background: Background::Color(style.dot_color), border_radius: (DOT_SIZE / 2.0) as u16, border_width: 0, border_color: Color::TRANSPARENT, diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs index 3f3dc0b0..991e0644 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -1,6 +1,7 @@ pub mod button; pub mod container; pub mod progress_bar; +pub mod radio; pub mod scrollable; pub mod slider; pub mod text_input; diff --git a/wgpu/src/widget/radio.rs b/wgpu/src/widget/radio.rs new file mode 100644 index 00000000..6e5cf042 --- /dev/null +++ b/wgpu/src/widget/radio.rs @@ -0,0 +1,10 @@ +//! Create choices using radio buttons. +use crate::Renderer; + +pub use iced_style::radio::{Style, StyleSheet}; + +/// A circular button representing a choice. +/// +/// This is an alias of an `iced_native` radio button with an +/// `iced_wgpu::Renderer`. +pub type Radio = iced_native::Radio;