From 8d68c8584ea99d3974571cd92edcb31999ebb8fa Mon Sep 17 00:00:00 2001 From: Billy Messenger Date: Thu, 13 Aug 2020 12:54:34 -0500 Subject: [PATCH] widget Rule added --- examples/styling/src/main.rs | 35 ++++++++-- glow/src/widget.rs | 3 + glow/src/widget/rule.rs | 10 +++ graphics/src/widget.rs | 3 + graphics/src/widget/rule.rs | 89 +++++++++++++++++++++++++ native/src/widget.rs | 3 + native/src/widget/rule.rs | 125 +++++++++++++++++++++++++++++++++++ src/widget.rs | 6 +- style/src/lib.rs | 1 + style/src/rule.rs | 51 ++++++++++++++ wgpu/src/widget.rs | 3 + wgpu/src/widget/rule.rs | 10 +++ 12 files changed, 331 insertions(+), 8 deletions(-) create mode 100644 glow/src/widget/rule.rs create mode 100644 graphics/src/widget/rule.rs create mode 100644 native/src/widget/rule.rs create mode 100644 style/src/rule.rs create mode 100644 wgpu/src/widget/rule.rs diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 63ab9d62..c969526c 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -1,7 +1,7 @@ use iced::{ button, scrollable, slider, text_input, Align, Button, Checkbox, Column, - Container, Element, Length, ProgressBar, Radio, Row, Sandbox, Scrollable, - Settings, Slider, Space, Text, TextInput, + Container, Element, Length, ProgressBar, Radio, Row, Rule, Sandbox, + Scrollable, Settings, Slider, Space, Text, TextInput, }; pub fn main() { @@ -113,14 +113,17 @@ impl Sandbox for Styling { .padding(20) .max_width(600) .push(choose_theme) + .push(Rule::horizontal(38).style(self.theme)) .push(Row::new().spacing(10).push(text_input).push(button)) .push(slider) .push(progress_bar) .push( Row::new() .spacing(10) + .height(Length::Units(100)) .align_items(Align::Center) .push(scrollable) + .push(Rule::vertical(38).style(self.theme)) .push(checkbox), ); @@ -136,8 +139,8 @@ impl Sandbox for Styling { mod style { use iced::{ - button, checkbox, container, progress_bar, radio, scrollable, slider, - text_input, + button, checkbox, container, progress_bar, radio, rule, scrollable, + slider, text_input, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -228,6 +231,15 @@ mod style { } } + impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Rule.into(), + } + } + } + mod light { use iced::{button, Background, Color, Vector}; @@ -258,7 +270,7 @@ mod style { mod dark { use iced::{ - button, checkbox, container, progress_bar, radio, scrollable, + button, checkbox, container, progress_bar, radio, rule, scrollable, slider, text_input, Background, Color, }; @@ -516,5 +528,18 @@ mod style { } } } + + pub struct Rule; + + impl rule::StyleSheet for Rule { + fn style(&self) -> rule::Style { + rule::Style { + color: SURFACE, + width: 2, + radius: 1, + fill_percent: 90, + } + } + } } } diff --git a/glow/src/widget.rs b/glow/src/widget.rs index 4e2fedc5..0e33909d 100644 --- a/glow/src/widget.rs +++ b/glow/src/widget.rs @@ -16,6 +16,7 @@ pub mod pane_grid; pub mod pick_list; pub mod progress_bar; pub mod radio; +pub mod rule; pub mod scrollable; pub mod slider; pub mod text_input; @@ -35,6 +36,8 @@ pub use progress_bar::ProgressBar; #[doc(no_inline)] pub use radio::Radio; #[doc(no_inline)] +pub use rule::Rule; +#[doc(no_inline)] pub use scrollable::Scrollable; #[doc(no_inline)] pub use slider::Slider; diff --git a/glow/src/widget/rule.rs b/glow/src/widget/rule.rs new file mode 100644 index 00000000..16eaa267 --- /dev/null +++ b/glow/src/widget/rule.rs @@ -0,0 +1,10 @@ +//! Display a horizontal or vertical rule for dividing content. + +use crate::Renderer; + +pub use iced_graphics::rule::{Style, StyleSheet}; + +/// Display a horizontal or vertical rule for dividing content. +/// +/// This is an alias of an `iced_native` rule with an `iced_glow::Renderer`. +pub type Rule = iced_native::Rule; diff --git a/graphics/src/widget.rs b/graphics/src/widget.rs index 94a65011..f87b558a 100644 --- a/graphics/src/widget.rs +++ b/graphics/src/widget.rs @@ -15,6 +15,7 @@ pub mod pane_grid; pub mod pick_list; pub mod progress_bar; pub mod radio; +pub mod rule; pub mod scrollable; pub mod slider; pub mod svg; @@ -40,6 +41,8 @@ pub use progress_bar::ProgressBar; #[doc(no_inline)] pub use radio::Radio; #[doc(no_inline)] +pub use rule::Rule; +#[doc(no_inline)] pub use scrollable::Scrollable; #[doc(no_inline)] pub use slider::Slider; diff --git a/graphics/src/widget/rule.rs b/graphics/src/widget/rule.rs new file mode 100644 index 00000000..5ff5197d --- /dev/null +++ b/graphics/src/widget/rule.rs @@ -0,0 +1,89 @@ +//! Display a horizontal or vertical rule for dividing content. + +use crate::{Backend, Primitive, Renderer}; +use iced_native::mouse; +use iced_native::rule; +use iced_native::{Background, Color, Rectangle}; + +pub use iced_style::rule::{Style, StyleSheet}; + +/// Display a horizontal or vertical rule for dividing content. +/// +/// This is an alias of an `iced_native` rule with an `iced_graphics::Renderer`. +pub type Rule = iced_native::Rule>; + +impl rule::Renderer for Renderer +where + B: Backend, +{ + type Style = Box; + + fn draw( + &mut self, + bounds: Rectangle, + style_sheet: &Self::Style, + is_horizontal: bool, + ) -> Self::Output { + let style = style_sheet.style(); + + let line = if is_horizontal { + let line_y = (bounds.y + (bounds.height / 2.0) + - (style.width as f32 / 2.0)) + .round(); + + let (line_x, line_width) = if style.fill_percent >= 100 { + (bounds.x, bounds.width) + } else { + let percent_width = + (bounds.width * style.fill_percent as f32 / 100.0).round(); + ( + bounds.x + ((bounds.width - percent_width) / 2.0).round(), + percent_width, + ) + }; + + Primitive::Quad { + bounds: Rectangle { + x: line_x, + y: line_y, + width: line_width, + height: style.width as f32, + }, + background: Background::Color(style.color), + border_radius: style.radius, + border_width: 0, + border_color: Color::TRANSPARENT, + } + } else { + let line_x = (bounds.x + (bounds.width / 2.0) + - (style.width as f32 / 2.0)) + .round(); + + let (line_y, line_height) = if style.fill_percent >= 100 { + (bounds.y, bounds.height) + } else { + let percent_height = + (bounds.height * style.fill_percent as f32 / 100.0).round(); + ( + bounds.y + ((bounds.height - percent_height) / 2.0).round(), + percent_height, + ) + }; + + Primitive::Quad { + bounds: Rectangle { + x: line_x, + y: line_y, + width: style.width as f32, + height: line_height, + }, + background: Background::Color(style.color), + border_radius: style.radius, + border_width: 0, + border_color: Color::TRANSPARENT, + } + }; + + (line, mouse::Interaction::default()) + } +} diff --git a/native/src/widget.rs b/native/src/widget.rs index 8539e519..a10281df 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -30,6 +30,7 @@ pub mod pick_list; pub mod progress_bar; pub mod radio; pub mod row; +pub mod rule; pub mod scrollable; pub mod slider; pub mod space; @@ -58,6 +59,8 @@ pub use radio::Radio; #[doc(no_inline)] pub use row::Row; #[doc(no_inline)] +pub use rule::Rule; +#[doc(no_inline)] pub use scrollable::Scrollable; #[doc(no_inline)] pub use slider::Slider; diff --git a/native/src/widget/rule.rs b/native/src/widget/rule.rs new file mode 100644 index 00000000..25cec53b --- /dev/null +++ b/native/src/widget/rule.rs @@ -0,0 +1,125 @@ +//! Display a horizontal or vertical rule for dividing content. + +use std::hash::Hash; + +use crate::{ + layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, +}; + +/// Display a horizontal or vertical rule for dividing content. +#[derive(Debug, Copy, Clone)] +pub struct Rule { + width: Length, + height: Length, + style: Renderer::Style, + is_horizontal: bool, +} + +impl Rule { + /// Creates a horizontal [`Rule`] for dividing content by the given vertical spacing. + /// + /// [`Rule`]: struct.Rule.html + pub fn horizontal(spacing: u16) -> Self { + Rule { + width: Length::Fill, + height: Length::from(Length::Units(spacing)), + style: Renderer::Style::default(), + is_horizontal: true, + } + } + + /// Creates a vertical [`Rule`] for dividing content by the given horizontal spacing. + /// + /// [`Rule`]: struct.Rule.html + pub fn vertical(spacing: u16) -> Self { + Rule { + width: Length::from(Length::Units(spacing)), + height: Length::Fill, + style: Renderer::Style::default(), + is_horizontal: false, + } + } + + /// Sets the style of the [`Rule`]. + /// + /// [`Rule`]: struct.Rule.html + pub fn style(mut self, style: impl Into) -> Self { + self.style = style.into(); + self + } +} + +impl Widget for Rule +where + Renderer: self::Renderer, +{ + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + self.height + } + + fn layout( + &self, + _renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits.width(self.width).height(self.height); + + layout::Node::new(limits.resolve(Size::ZERO)) + } + + fn draw( + &self, + renderer: &mut Renderer, + _defaults: &Renderer::Defaults, + layout: Layout<'_>, + _cursor_position: Point, + ) -> Renderer::Output { + renderer.draw(layout.bounds(), &self.style, self.is_horizontal) + } + + fn hash_layout(&self, state: &mut Hasher) { + struct Marker; + std::any::TypeId::of::().hash(state); + + self.width.hash(state); + self.height.hash(state); + } +} + +/// The renderer of a [`Rule`]. +/// +/// [`Rule`]: struct.Rule.html +pub trait Renderer: crate::Renderer { + /// The style supported by this renderer. + type Style: Default; + + /// Draws a [`Rule`]. + /// + /// It receives: + /// * the bounds of the [`Rule`] + /// * the style of the [`Rule`] + /// * whether the [`Rule`] is horizontal (true) or vertical (false) + /// + /// [`Rule`]: struct.Rule.html + fn draw( + &mut self, + bounds: Rectangle, + style: &Self::Style, + is_horizontal: bool, + ) -> Self::Output; +} + +impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> +where + Renderer: 'a + self::Renderer, + Message: 'a, +{ + fn from(rule: Rule) -> Element<'a, Message, Renderer> { + Element::new(rule) + } +} diff --git a/src/widget.rs b/src/widget.rs index b26f14d4..e8fff9cc 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -20,7 +20,7 @@ mod platform { pub use crate::renderer::widget::{ button, checkbox, container, pane_grid, pick_list, progress_bar, radio, - scrollable, slider, text_input, Column, Row, Space, Text, + rule, scrollable, slider, text_input, Column, Row, Space, Text, }; #[cfg(any(feature = "canvas", feature = "glow_canvas"))] @@ -46,8 +46,8 @@ mod platform { pub use { button::Button, checkbox::Checkbox, container::Container, image::Image, pane_grid::PaneGrid, pick_list::PickList, progress_bar::ProgressBar, - radio::Radio, scrollable::Scrollable, slider::Slider, svg::Svg, - text_input::TextInput, + radio::Radio, rule::Rule, scrollable::Scrollable, slider::Slider, + svg::Svg, text_input::TextInput, }; #[cfg(any(feature = "canvas", feature = "glow_canvas"))] diff --git a/style/src/lib.rs b/style/src/lib.rs index 8e402bb1..3d23d990 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -11,6 +11,7 @@ pub mod menu; pub mod pick_list; pub mod progress_bar; pub mod radio; +pub mod rule; pub mod scrollable; pub mod slider; pub mod text_input; diff --git a/style/src/rule.rs b/style/src/rule.rs new file mode 100644 index 00000000..dbd72d41 --- /dev/null +++ b/style/src/rule.rs @@ -0,0 +1,51 @@ +//! Display a horizontal or vertical rule for dividing content. + +use iced_core::Color; + +/// The appearance of a rule. +#[derive(Debug, Clone, Copy)] +pub struct Style { + /// The color of the rule. + pub color: Color, + /// The width (thickness) of the rule line. + pub width: u16, + /// The radius of the rectangle corners. + pub radius: u16, + /// The percent from [0, 100] of the filled space the rule + /// will be drawn. + pub fill_percent: u16, +} + +/// A set of rules that dictate the style of a rule. +pub trait StyleSheet { + /// Produces the style of a rule. + fn style(&self) -> Style; +} + +struct Default; + +impl StyleSheet for Default { + fn style(&self) -> Style { + Style { + color: [0.6, 0.6, 0.6, 0.49].into(), + width: 1, + radius: 0, + fill_percent: 90, + } + } +} + +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/widget.rs b/wgpu/src/widget.rs index ced64332..1dae26f5 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -16,6 +16,7 @@ pub mod pane_grid; pub mod pick_list; pub mod progress_bar; pub mod radio; +pub mod rule; pub mod scrollable; pub mod slider; pub mod text_input; @@ -35,6 +36,8 @@ pub use progress_bar::ProgressBar; #[doc(no_inline)] pub use radio::Radio; #[doc(no_inline)] +pub use rule::Rule; +#[doc(no_inline)] pub use scrollable::Scrollable; #[doc(no_inline)] pub use slider::Slider; diff --git a/wgpu/src/widget/rule.rs b/wgpu/src/widget/rule.rs new file mode 100644 index 00000000..630a6f33 --- /dev/null +++ b/wgpu/src/widget/rule.rs @@ -0,0 +1,10 @@ +//! Display a horizontal or vertical rule for dividing content. + +use crate::Renderer; + +pub use iced_graphics::rule::{Style, StyleSheet}; + +/// Display a horizontal or vertical rule for dividing content. +/// +/// This is an alias of an `iced_native` rule with an `iced_wgpu::Renderer`. +pub type Rule = iced_native::Rule;