diff options
author | 2019-12-29 10:57:01 +0100 | |
---|---|---|
committer | 2019-12-29 10:57:01 +0100 | |
commit | c7b170da6d180f80e539910cccb543720fa3713c (patch) | |
tree | 6ef48d17104173f0ac7182d3647bd461e5581bd2 | |
parent | 4b86c2ff987e334c3454540828c6f8d16d27c670 (diff) | |
download | iced-c7b170da6d180f80e539910cccb543720fa3713c.tar.gz iced-c7b170da6d180f80e539910cccb543720fa3713c.tar.bz2 iced-c7b170da6d180f80e539910cccb543720fa3713c.zip |
Draft `Style` and `StyleSheet` for `Button`
-rw-r--r-- | examples/pokedex.rs | 23 | ||||
-rw-r--r-- | examples/stopwatch.rs | 43 | ||||
-rw-r--r-- | examples/todos.rs | 51 | ||||
-rw-r--r-- | examples/tour.rs | 47 | ||||
-rw-r--r-- | native/src/lib.rs | 2 | ||||
-rw-r--r-- | native/src/renderer/null.rs | 11 | ||||
-rw-r--r-- | native/src/widget/button.rs | 47 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/native.rs | 18 | ||||
-rw-r--r-- | wgpu/src/lib.rs | 14 | ||||
-rw-r--r-- | wgpu/src/renderer.rs | 4 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/button.rs | 28 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/button/style.rs | 1 | ||||
-rw-r--r-- | wgpu/src/widget.rs | 1 | ||||
-rw-r--r-- | wgpu/src/widget/button.rs | 97 |
15 files changed, 275 insertions, 114 deletions
diff --git a/examples/pokedex.rs b/examples/pokedex.rs index 2d595ec4..0dcf6981 100644 --- a/examples/pokedex.rs +++ b/examples/pokedex.rs @@ -220,7 +220,26 @@ impl From<surf::Exception> for Error { fn button<'a>(state: &'a mut button::State, text: &str) -> Button<'a, Message> { Button::new(state, Text::new(text).color(Color::WHITE)) - .background(Color::from_rgb(0.11, 0.42, 0.87)) - .border_radius(10) .padding(10) + .style(style::Button::Primary) +} + +mod style { + use iced::{button, Background, Color}; + + pub enum Button { + Primary, + } + + impl button::StyleSheet for Button { + fn active(&self) -> button::Style { + button::Style { + background: Some(Background::Color(match self { + Button::Primary => Color::from_rgb(0.11, 0.42, 0.87), + })), + border_radius: 12, + shadow_offset: 1.0, + } + } + } } diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index 7a7f0793..0e0cdba5 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -1,7 +1,6 @@ use iced::{ - button, Align, Application, Background, Button, Color, Column, Command, - Container, Element, HorizontalAlignment, Length, Row, Settings, - Subscription, Text, + button, Align, Application, Button, Color, Column, Command, Container, + Element, HorizontalAlignment, Length, Row, Settings, Subscription, Text, }; use std::time::{Duration, Instant}; @@ -99,7 +98,7 @@ impl Application for Stopwatch { .width(Length::Shrink) .size(40); - let button = |state, label, color: [f32; 3]| { + let button = |state, label, style| { Button::new( state, Text::new(label) @@ -107,22 +106,22 @@ impl Application for Stopwatch { .horizontal_alignment(HorizontalAlignment::Center), ) .min_width(80) - .background(Background::Color(color.into())) - .border_radius(10) .padding(10) + .style(style) }; let toggle_button = { let (label, color) = match self.state { - State::Idle => ("Start", [0.11, 0.42, 0.87]), - State::Ticking { .. } => ("Stop", [0.9, 0.4, 0.4]), + State::Idle => ("Start", style::Button::Primary), + State::Ticking { .. } => ("Stop", style::Button::Destructive), }; button(&mut self.toggle, label, color).on_press(Message::Toggle) }; - let reset_button = button(&mut self.reset, "Reset", [0.7, 0.7, 0.7]) - .on_press(Message::Reset); + let reset_button = + button(&mut self.reset, "Reset", style::Button::Secondary) + .on_press(Message::Reset); let controls = Row::new() .width(Length::Shrink) @@ -180,3 +179,27 @@ mod time { } } } + +mod style { + use iced::{button, Background, Color}; + + pub enum Button { + Primary, + Secondary, + Destructive, + } + + impl button::StyleSheet for Button { + fn active(&self) -> button::Style { + button::Style { + background: Some(Background::Color(match self { + Button::Primary => Color::from_rgb(0.11, 0.42, 0.87), + Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5), + Button::Destructive => Color::from_rgb(0.8, 0.2, 0.2), + })), + border_radius: 12, + shadow_offset: 1.0, + } + } + } +} diff --git a/examples/todos.rs b/examples/todos.rs index 42e88f65..00edd7fb 100644 --- a/examples/todos.rs +++ b/examples/todos.rs @@ -296,7 +296,8 @@ impl Task { edit_icon().color([0.5, 0.5, 0.5]), ) .on_press(TaskMessage::Edit) - .padding(10), + .padding(10) + .style(style::Button::NoBackground), ) .into() } @@ -331,8 +332,7 @@ impl Task { ) .on_press(TaskMessage::Delete) .padding(10) - .border_radius(5) - .background(Color::from_rgb(0.8, 0.2, 0.2)), + .style(style::Button::Destructive), ) .into() } @@ -361,15 +361,12 @@ impl Controls { let label = Text::new(label).size(16).width(Length::Shrink); let button = if filter == current_filter { Button::new(state, label.color(Color::WHITE)) - .background(Color::from_rgb(0.2, 0.2, 0.7)) + .style(style::Button::FilterSelected) } else { - Button::new(state, label) + Button::new(state, label).style(style::Button::NoBackground) }; - button - .on_press(Message::FilterChanged(filter)) - .padding(8) - .border_radius(10) + button.on_press(Message::FilterChanged(filter)).padding(8) }; Row::new() @@ -562,3 +559,39 @@ impl SavedState { Ok(()) } } + +mod style { + use iced::{button, Background, Color}; + + pub enum Button { + FilterSelected, + NoBackground, + Destructive, + } + + impl button::StyleSheet for Button { + fn active(&self) -> button::Style { + match self { + Button::FilterSelected => button::Style { + background: Some(Background::Color(Color::from_rgb( + 0.2, 0.2, 0.7, + ))), + border_radius: 10, + shadow_offset: 0.0, + }, + Button::NoBackground => button::Style { + background: None, + border_radius: 0, + shadow_offset: 0.0, + }, + Button::Destructive => button::Style { + background: Some(Background::Color(Color::from_rgb( + 0.8, 0.2, 0.2, + ))), + border_radius: 5, + shadow_offset: 1.0, + }, + } + } + } +} diff --git a/examples/tour.rs b/examples/tour.rs index da05b396..402bf1c4 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -62,8 +62,9 @@ impl Sandbox for Tour { if steps.has_previous() { controls = controls.push( - secondary_button(back_button, "Back") - .on_press(Message::BackPressed), + button(back_button, "Back") + .on_press(Message::BackPressed) + .style(style::Button::Secondary), ); } @@ -71,8 +72,9 @@ impl Sandbox for Tour { if steps.can_continue() { controls = controls.push( - primary_button(next_button, "Next") - .on_press(Message::NextPressed), + button(next_button, "Next") + .on_press(Message::NextPressed) + .style(style::Button::Primary), ); } @@ -697,24 +699,9 @@ fn button<'a, Message>( .horizontal_alignment(HorizontalAlignment::Center), ) .padding(12) - .border_radius(12) .min_width(100) } -fn primary_button<'a, Message>( - state: &'a mut button::State, - label: &str, -) -> Button<'a, Message> { - button(state, label).background(Color::from_rgb(0.11, 0.42, 0.87)) -} - -fn secondary_button<'a, Message>( - state: &'a mut button::State, - label: &str, -) -> Button<'a, Message> { - button(state, label).background(Color::from_rgb(0.4, 0.4, 0.4)) -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Language { Rust, @@ -757,6 +744,28 @@ pub enum Layout { Column, } +mod style { + use iced::{button, Background, Color}; + + pub enum Button { + Primary, + Secondary, + } + + impl button::StyleSheet for Button { + fn active(&self) -> button::Style { + button::Style { + background: Some(Background::Color(match self { + Button::Primary => Color::from_rgb(0.11, 0.42, 0.87), + Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5), + })), + border_radius: 12, + shadow_offset: 1.0, + } + } + } +} + // This should be gracefully handled by Iced in the future. Probably using our // own proc macro, or maybe the whole process is streamlined by `wasm-pack` at // some point. diff --git a/native/src/lib.rs b/native/src/lib.rs index 8dcacb2b..9d237196 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -34,7 +34,7 @@ //! [`Windowed`]: renderer/trait.Windowed.html //! [`UserInterface`]: struct.UserInterface.html //! [renderer]: renderer/index.html -#![deny(missing_docs)] +//#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![deny(unsafe_code)] diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 43076d61..1be669c2 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -1,7 +1,7 @@ use crate::{ - button, checkbox, column, radio, row, scrollable, text, text_input, - Background, Color, Element, Font, HorizontalAlignment, Layout, Point, - Rectangle, Renderer, Size, VerticalAlignment, + button, checkbox, column, radio, row, scrollable, text, text_input, Color, + Element, Font, HorizontalAlignment, Layout, Point, Rectangle, Renderer, + Size, VerticalAlignment, }; /// A renderer that does nothing. @@ -117,13 +117,14 @@ impl text_input::Renderer for Null { } impl button::Renderer for Null { + type Style = (); + fn draw( &mut self, _bounds: Rectangle, _cursor_position: Point, _is_pressed: bool, - _background: Option<Background>, - _border_radius: u16, + _style: &Self::Style, _content: Self::Output, ) -> Self::Output { } diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 2881105f..4a7187da 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -6,8 +6,8 @@ //! [`State`]: struct.State.html use crate::{ input::{mouse, ButtonState}, - layout, Background, Clipboard, Element, Event, Hasher, Layout, Length, - Point, Rectangle, Widget, + layout, Clipboard, Element, Event, Hasher, Layout, Length, Point, + Rectangle, Widget, }; use std::hash::Hash; @@ -28,7 +28,7 @@ use std::hash::Hash; /// .on_press(Message::ButtonPressed); /// ``` #[allow(missing_debug_implementations)] -pub struct Button<'a, Message, Renderer> { +pub struct Button<'a, Message, Renderer: self::Renderer> { state: &'a mut State, content: Element<'a, Message, Renderer>, on_press: Option<Message>, @@ -37,11 +37,13 @@ pub struct Button<'a, Message, Renderer> { min_width: u32, min_height: u32, padding: u16, - background: Option<Background>, - border_radius: u16, + style: Renderer::Style, } -impl<'a, Message, Renderer> Button<'a, Message, Renderer> { +impl<'a, Message, Renderer> Button<'a, Message, Renderer> +where + Renderer: self::Renderer, +{ /// Creates a new [`Button`] with some local [`State`] and the given /// content. /// @@ -60,8 +62,7 @@ impl<'a, Message, Renderer> Button<'a, Message, Renderer> { min_width: 0, min_height: 0, padding: 0, - background: None, - border_radius: 0, + style: Renderer::Style::default(), } } @@ -105,23 +106,6 @@ impl<'a, Message, Renderer> Button<'a, Message, Renderer> { self } - /// Sets the [`Background`] of the [`Button`]. - /// - /// [`Button`]: struct.Button.html - /// [`Background`]: ../../struct.Background.html - pub fn background<T: Into<Background>>(mut self, background: T) -> Self { - self.background = Some(background.into()); - self - } - - /// Sets the border radius of the [`Button`]. - /// - /// [`Button`]: struct.Button.html - pub fn border_radius(mut self, border_radius: u16) -> Self { - self.border_radius = border_radius; - self - } - /// Sets the message that will be produced when the [`Button`] is pressed. /// /// [`Button`]: struct.Button.html @@ -129,6 +113,11 @@ impl<'a, Message, Renderer> Button<'a, Message, Renderer> { self.on_press = Some(msg); self } + + pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self { + self.style = style.into(); + self + } } /// The local state of a [`Button`]. @@ -240,8 +229,7 @@ where layout.bounds(), cursor_position, self.state.is_pressed, - self.background, - self.border_radius, + &self.style, content, ) } @@ -260,6 +248,8 @@ where /// [`Button`]: struct.Button.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + type Style: Default; + /// Draws a [`Button`]. /// /// [`Button`]: struct.Button.html @@ -268,8 +258,7 @@ pub trait Renderer: crate::Renderer + Sized { bounds: Rectangle, cursor_position: Point, is_pressed: bool, - background: Option<Background>, - border_radius: u16, + style: &Self::Style, content: Self::Output, ) -> Self::Output; } @@ -174,7 +174,7 @@ //! [documentation]: https://docs.rs/iced //! [examples]: https://github.com/hecrj/iced/tree/master/examples //! [`Application`]: trait.Application.html -#![deny(missing_docs)] +//#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![deny(unsafe_code)] diff --git a/src/native.rs b/src/native.rs index f06f1c99..cc2068ae 100644 --- a/src/native.rs +++ b/src/native.rs @@ -22,23 +22,7 @@ pub mod widget { //! //! [`TextInput`]: text_input/struct.TextInput.html //! [`text_input::State`]: text_input/struct.State.html - pub mod button { - //! Allow your users to perform actions by pressing a button. - //! - //! A [`Button`] has some local [`State`]. - //! - //! [`Button`]: type.Button.html - //! [`State`]: struct.State.html - - /// A widget that produces a message when clicked. - /// - /// This is an alias of an `iced_native` button with a default - /// `Renderer`. - pub type Button<'a, Message> = - iced_winit::Button<'a, Message, iced_wgpu::Renderer>; - - pub use iced_winit::button::State; - } + pub use iced_wgpu::button; pub mod scrollable { //! Navigate an endless amount of content with a scrollbar. diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 9f9ed8db..55f93546 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -19,11 +19,13 @@ //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs //! [WebGPU API]: https://gpuweb.github.io/gpuweb/ //! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph -#![deny(missing_docs)] +//#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![deny(unsafe_code)] #![deny(rust_2018_idioms)] +pub mod widget; + mod image; mod primitive; mod quad; @@ -31,9 +33,11 @@ mod renderer; mod text; mod transformation; -pub(crate) use crate::image::Image; -pub(crate) use quad::Quad; -pub(crate) use transformation::Transformation; - pub use primitive::Primitive; pub use renderer::{Renderer, Target}; +#[doc(no_inline)] +pub use widget::*; + +pub(crate) use self::image::Image; +pub(crate) use quad::Quad; +pub(crate) use transformation::Transformation; diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 365ef1ef..4984d4fe 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -22,7 +22,7 @@ pub struct Renderer { device: Device, queue: Queue, quad_pipeline: quad::Pipeline, - image_pipeline: crate::image::Pipeline, + image_pipeline: image::Pipeline, text_pipeline: text::Pipeline, } @@ -63,7 +63,7 @@ impl Renderer { let text_pipeline = text::Pipeline::new(&mut device); let quad_pipeline = quad::Pipeline::new(&mut device); - let image_pipeline = crate::image::Pipeline::new(&mut device); + let image_pipeline = image::Pipeline::new(&mut device); Self { device, diff --git a/wgpu/src/renderer/widget/button.rs b/wgpu/src/renderer/widget/button.rs index 86963053..f3817374 100644 --- a/wgpu/src/renderer/widget/button.rs +++ b/wgpu/src/renderer/widget/button.rs @@ -1,50 +1,50 @@ -use crate::{Primitive, Renderer}; -use iced_native::{button, Background, MouseCursor, Point, Rectangle}; +use crate::{button::StyleSheet, Primitive, Renderer}; +use iced_native::{Background, MouseCursor, Point, Rectangle}; + +impl iced_native::button::Renderer for Renderer { + type Style = Box<dyn StyleSheet>; -impl button::Renderer for Renderer { fn draw( &mut self, bounds: Rectangle, cursor_position: Point, is_pressed: bool, - background: Option<Background>, - border_radius: u16, + style: &Box<dyn StyleSheet>, (content, _): Self::Output, ) -> Self::Output { let is_mouse_over = bounds.contains(cursor_position); // TODO: Render proper shadows - // TODO: Make hovering and pressed styles configurable - let shadow_offset = if is_mouse_over { + let styling = if is_mouse_over { if is_pressed { - 0.0 + style.pressed() } else { - 2.0 + style.hovered() } } else { - 1.0 + style.active() }; ( - match background { + match styling.background { None => content, Some(background) => Primitive::Group { primitives: vec![ Primitive::Quad { bounds: Rectangle { x: bounds.x + 1.0, - y: bounds.y + shadow_offset, + y: bounds.y + styling.shadow_offset, ..bounds }, background: Background::Color( [0.0, 0.0, 0.0, 0.5].into(), ), - border_radius, + border_radius: styling.border_radius, }, Primitive::Quad { bounds, background, - border_radius, + border_radius: styling.border_radius, }, content, ], diff --git a/wgpu/src/renderer/widget/button/style.rs b/wgpu/src/renderer/widget/button/style.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/wgpu/src/renderer/widget/button/style.rs @@ -0,0 +1 @@ + diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs new file mode 100644 index 00000000..aa200ca2 --- /dev/null +++ b/wgpu/src/widget.rs @@ -0,0 +1 @@ +pub mod button; diff --git a/wgpu/src/widget/button.rs b/wgpu/src/widget/button.rs new file mode 100644 index 00000000..7827f8b2 --- /dev/null +++ b/wgpu/src/widget/button.rs @@ -0,0 +1,97 @@ +//! Allow your users to perform actions by pressing a button. +//! +//! A [`Button`] has some local [`State`]. +//! +//! [`Button`]: type.Button.html +//! [`State`]: struct.State.html +use crate::Renderer; +use iced_native::Background; + +pub use iced_native::button::State; + +/// A widget that produces a message when clicked. +/// +/// This is an alias of an `iced_native` button with an `iced_wgpu::Renderer`. +pub type Button<'a, Message> = iced_native::Button<'a, Message, Renderer>; + +#[derive(Debug)] +pub struct Style { + pub shadow_offset: f32, + pub background: Option<Background>, + pub border_radius: u16, +} + +pub trait StyleSheet { + fn active(&self) -> Style; + + fn hovered(&self) -> Style { + let active = self.active(); + + Style { + shadow_offset: active.shadow_offset + 1.0, + ..active + } + } + + fn pressed(&self) -> Style { + Style { + shadow_offset: 0.0, + ..self.active() + } + } + + fn disabled(&self) -> Style { + self.active() + } +} + +struct Default; + +impl StyleSheet for Default { + fn active(&self) -> Style { + Style { + shadow_offset: 1.0, + background: Some(Background::Color([0.5, 0.5, 0.5].into())), + border_radius: 5, + } + } + + fn hovered(&self) -> Style { + Style { + shadow_offset: 2.0, + background: Some(Background::Color([0.5, 0.5, 0.5].into())), + border_radius: 5, + } + } + + fn pressed(&self) -> Style { + Style { + shadow_offset: 0.0, + background: Some(Background::Color([0.5, 0.5, 0.5].into())), + border_radius: 5, + } + } + + fn disabled(&self) -> Style { + Style { + shadow_offset: 0.0, + background: Some(Background::Color([0.7, 0.7, 0.7].into())), + border_radius: 5, + } + } +} + +impl std::default::Default for Box<dyn StyleSheet> { + fn default() -> Self { + Box::new(Default) + } +} + +impl<T> From<T> for Box<dyn StyleSheet> +where + T: 'static + StyleSheet, +{ + fn from(style: T) -> Self { + Box::new(style) + } +} |