From ed30b487d649ffa0967ab8bfd66f4820ee2150fd 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:54:54 +0100 Subject: [PATCH] Implement styling for `Checkbox` --- examples/styling.rs | 79 ++++++++++++++++++++++++---- native/src/renderer/null.rs | 3 ++ native/src/widget/checkbox.rs | 45 +++++++++------- src/native.rs | 4 +- style/src/checkbox.rs | 55 +++++++++++++++++++ style/src/lib.rs | 1 + wgpu/src/renderer/widget/checkbox.rs | 31 +++++------ wgpu/src/widget.rs | 1 + wgpu/src/widget/checkbox.rs | 9 ++++ 9 files changed, 181 insertions(+), 47 deletions(-) create mode 100644 style/src/checkbox.rs create mode 100644 wgpu/src/widget/checkbox.rs diff --git a/examples/styling.rs b/examples/styling.rs index b5600e85..97095cff 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -1,7 +1,7 @@ use iced::{ - button, scrollable, slider, text_input, Button, Column, Container, Element, - Length, ProgressBar, Radio, Row, Sandbox, Scrollable, Settings, Slider, - Text, TextInput, + button, scrollable, slider, text_input, Button, Checkbox, Column, + Container, Element, Length, ProgressBar, Radio, Row, Sandbox, Scrollable, + Settings, Slider, Text, TextInput, }; pub fn main() { @@ -17,6 +17,7 @@ struct Styling { button: button::State, slider: slider::State, slider_value: f32, + debug: bool, } #[derive(Debug, Clone)] @@ -25,6 +26,7 @@ enum Message { InputChanged(String), ButtonPressed, SliderChanged(f32), + DebugToggled(bool), } impl Sandbox for Styling { @@ -44,6 +46,7 @@ impl Sandbox for Styling { Message::InputChanged(value) => self.input_value = value, Message::ButtonPressed => (), Message::SliderChanged(value) => self.slider_value = value, + Message::DebugToggled(debug) => self.debug = debug, } } @@ -89,18 +92,34 @@ impl Sandbox for Styling { let progress_bar = ProgressBar::new(0.0..=100.0, self.slider_value).style(self.theme); - let content = Column::new() + let checkbox = Checkbox::new( + self.debug, + "Enable layout debugger", + Message::DebugToggled, + ) + .style(self.theme); + + let content: Element<_> = Column::new() .spacing(20) .padding(20) .max_width(600) .push(choose_theme) .push(Row::new().spacing(10).push(text_input).push(button)) .push(slider) - .push(progress_bar); + .push(progress_bar) + .push(checkbox) + .into(); - let scrollable = Scrollable::new(&mut self.scroll) - .style(self.theme) - .push(Container::new(content).width(Length::Fill).center_x()); + let scrollable = + Scrollable::new(&mut self.scroll).style(self.theme).push( + Container::new(if self.debug { + content.explain(self.theme.debug_color()) + } else { + content + }) + .width(Length::Fill) + .center_x(), + ); Container::new(scrollable) .width(Length::Fill) @@ -113,7 +132,8 @@ impl Sandbox for Styling { mod style { use iced::{ - button, container, progress_bar, radio, scrollable, slider, text_input, + button, checkbox, container, progress_bar, radio, scrollable, slider, + text_input, Color, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -124,6 +144,13 @@ mod style { impl Theme { pub const ALL: [Theme; 2] = [Theme::Light, Theme::Dark]; + + pub fn debug_color(&self) -> Color { + match self { + Theme::Light => Color::BLACK, + Theme::Dark => Color::WHITE, + } + } } impl Default for Theme { @@ -195,6 +222,15 @@ mod style { } } + impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Checkbox.into(), + } + } + } + mod light { use iced::{button, Background, Color, Vector}; @@ -225,8 +261,8 @@ mod style { mod dark { use iced::{ - button, container, progress_bar, radio, scrollable, slider, - text_input, Background, Color, + button, checkbox, container, progress_bar, radio, scrollable, + slider, text_input, Background, Color, }; const SURFACE: Color = Color::from_rgb( @@ -432,5 +468,26 @@ mod style { } } } + + pub struct Checkbox; + + impl checkbox::StyleSheet for Checkbox { + fn active(&self) -> checkbox::Style { + checkbox::Style { + background: Background::Color(SURFACE), + checkmark_color: Color::WHITE, + border_radius: 2, + border_width: 0, + border_color: Color::TRANSPARENT, + } + } + + fn hovered(&self) -> checkbox::Style { + checkbox::Style { + background: Background::Color(Color { a: 0.5, ..SURFACE }), + ..self.active() + } + } + } } } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 4fb4f2d5..bd5bba23 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -170,6 +170,8 @@ impl radio::Renderer for Null { } impl checkbox::Renderer for Null { + type Style = (); + fn default_size(&self) -> u32 { 20 } @@ -180,6 +182,7 @@ impl checkbox::Renderer for Null { _is_checked: bool, _is_mouse_over: bool, _label: Self::Output, + _style: &Self::Style, ) { } } diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 87a7f629..13a59410 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -3,7 +3,7 @@ use std::hash::Hash; 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, }; @@ -13,7 +13,7 @@ use crate::{ /// # Example /// /// ``` -/// # use iced_native::Checkbox; +/// # type Checkbox = iced_native::Checkbox; /// # /// pub enum Message { /// CheckboxToggled(bool), @@ -26,15 +26,15 @@ use crate::{ /// /// ![Checkbox drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true) #[allow(missing_debug_implementations)] -pub struct Checkbox { +pub struct Checkbox { is_checked: bool, on_toggle: Box Message>, label: String, - label_color: Option, width: Length, + style: Renderer::Style, } -impl Checkbox { +impl Checkbox { /// Creates a new [`Checkbox`]. /// /// It expects: @@ -53,19 +53,11 @@ impl Checkbox { is_checked, on_toggle: Box::new(f), label: String::from(label), - label_color: None, width: Length::Fill, + style: Renderer::Style::default(), } } - /// Sets the color of the label of the [`Checkbox`]. - /// - /// [`Checkbox`]: struct.Checkbox.html - pub fn label_color>(mut self, color: C) -> Self { - self.label_color = Some(color.into()); - self - } - /// Sets the width of the [`Checkbox`]. /// /// [`Checkbox`]: struct.Checkbox.html @@ -73,9 +65,18 @@ impl Checkbox { self.width = width; self } + + /// Sets the style of the [`Checkbox`]. + /// + /// [`Checkbox`]: struct.Checkbox.html + pub fn style(mut self, style: impl Into) -> Self { + self.style = style.into(); + self + } } -impl Widget for Checkbox +impl Widget + for Checkbox where Renderer: self::Renderer + text::Renderer + row::Renderer, { @@ -152,7 +153,7 @@ where &self.label, text::Renderer::default_size(renderer), Font::Default, - self.label_color, + None, HorizontalAlignment::Left, VerticalAlignment::Center, ); @@ -165,6 +166,7 @@ where self.is_checked, is_mouse_over, label, + &self.style, ) } @@ -181,6 +183,8 @@ where /// [`Checkbox`]: struct.Checkbox.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + type Style: Default; + /// Returns the default size of a [`Checkbox`]. /// /// [`Checkbox`]: struct.Checkbox.html @@ -201,16 +205,19 @@ pub trait Renderer: crate::Renderer { is_checked: 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 + text::Renderer + row::Renderer, + Renderer: 'static + self::Renderer + text::Renderer + row::Renderer, Message: 'static, { - fn from(checkbox: Checkbox) -> Element<'a, Message, Renderer> { + fn from( + checkbox: Checkbox, + ) -> Element<'a, Message, Renderer> { Element::new(checkbox) } } diff --git a/src/native.rs b/src/native.rs index 2f2182ff..35441a3e 100644 --- a/src/native.rs +++ b/src/native.rs @@ -34,11 +34,11 @@ pub mod widget { pub use iced_winit::svg::{Handle, Svg}; } - pub use iced_winit::{Checkbox, Text}; + pub use iced_winit::Text; #[doc(no_inline)] pub use { - button::Button, container::Container, image::Image, + button::Button, checkbox::Checkbox, container::Container, image::Image, progress_bar::ProgressBar, radio::Radio, scrollable::Scrollable, slider::Slider, svg::Svg, text_input::TextInput, }; diff --git a/style/src/checkbox.rs b/style/src/checkbox.rs new file mode 100644 index 00000000..e84dfd18 --- /dev/null +++ b/style/src/checkbox.rs @@ -0,0 +1,55 @@ +//! Show toggle controls using checkboxes. +use iced_core::{Background, Color}; + +/// The appearance of a checkbox. +#[derive(Debug)] +pub struct Style { + pub background: Background, + pub checkmark_color: Color, + pub border_radius: u16, + pub border_width: u16, + pub border_color: Color, +} + +/// A set of rules that dictate the style of a checkbox. +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)), + checkmark_color: Color::from_rgb(0.3, 0.3, 0.3), + border_radius: 5, + 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/style/src/lib.rs b/style/src/lib.rs index 991e0644..e0f56594 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -1,4 +1,5 @@ pub mod button; +pub mod checkbox; pub mod container; pub mod progress_bar; pub mod radio; diff --git a/wgpu/src/renderer/widget/checkbox.rs b/wgpu/src/renderer/widget/checkbox.rs index 1ed27ff7..cd90be5e 100644 --- a/wgpu/src/renderer/widget/checkbox.rs +++ b/wgpu/src/renderer/widget/checkbox.rs @@ -1,12 +1,13 @@ -use crate::{Primitive, Renderer}; +use crate::{checkbox::StyleSheet, Primitive, Renderer}; use iced_native::{ - checkbox, Background, Color, HorizontalAlignment, MouseCursor, Rectangle, - VerticalAlignment, + checkbox, HorizontalAlignment, MouseCursor, Rectangle, VerticalAlignment, }; const SIZE: f32 = 28.0; impl checkbox::Renderer for Renderer { + type Style = Box; + fn default_size(&self) -> u32 { SIZE as u32 } @@ -17,20 +18,20 @@ impl checkbox::Renderer for Renderer { is_checked: 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 checkbox = Primitive::Quad { bounds, - background: Background::Color( - if is_mouse_over { - [0.90, 0.90, 0.90] - } else { - [0.95, 0.95, 0.95] - } - .into(), - ), - border_radius: 5, - border_width: 1, - border_color: Color::from_rgb(0.6, 0.6, 0.6), + background: style.background, + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, }; ( @@ -41,7 +42,7 @@ impl checkbox::Renderer for Renderer { font: crate::text::BUILTIN_ICONS, size: bounds.height * 0.7, bounds: bounds, - color: [0.3, 0.3, 0.3].into(), + color: style.checkmark_color, horizontal_alignment: HorizontalAlignment::Center, vertical_alignment: VerticalAlignment::Center, }; diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs index 991e0644..e0f56594 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -1,4 +1,5 @@ pub mod button; +pub mod checkbox; pub mod container; pub mod progress_bar; pub mod radio; diff --git a/wgpu/src/widget/checkbox.rs b/wgpu/src/widget/checkbox.rs new file mode 100644 index 00000000..da0d7a84 --- /dev/null +++ b/wgpu/src/widget/checkbox.rs @@ -0,0 +1,9 @@ +//! Show toggle controls using checkboxes. +use crate::Renderer; + +pub use iced_style::checkbox::{Style, StyleSheet}; + +/// A box that can be checked. +/// +/// This is an alias of an `iced_native` checkbox with an `iced_wgpu::Renderer`. +pub type Checkbox = iced_native::Checkbox;