From c7b170da6d180f80e539910cccb543720fa3713c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 29 Dec 2019 10:57:01 +0100 Subject: Draft `Style` and `StyleSheet` for `Button` --- examples/pokedex.rs | 23 +++++++- examples/stopwatch.rs | 43 ++++++++++---- examples/todos.rs | 51 ++++++++++++++--- examples/tour.rs | 47 +++++++++------- native/src/lib.rs | 2 +- native/src/renderer/null.rs | 11 ++-- native/src/widget/button.rs | 47 ++++++---------- src/lib.rs | 2 +- src/native.rs | 18 +----- wgpu/src/lib.rs | 14 +++-- wgpu/src/renderer.rs | 4 +- wgpu/src/renderer/widget/button.rs | 28 ++++----- wgpu/src/renderer/widget/button/style.rs | 1 + wgpu/src/widget.rs | 1 + wgpu/src/widget/button.rs | 97 ++++++++++++++++++++++++++++++++ 15 files changed, 275 insertions(+), 114 deletions(-) create mode 100644 wgpu/src/renderer/widget/button/style.rs create mode 100644 wgpu/src/widget.rs create mode 100644 wgpu/src/widget/button.rs 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 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, - _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, @@ -37,11 +37,13 @@ pub struct Button<'a, Message, Renderer> { min_width: u32, min_height: u32, padding: u16, - background: Option, - 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>(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) -> 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, - border_radius: u16, + style: &Self::Style, content: Self::Output, ) -> Self::Output; } diff --git a/src/lib.rs b/src/lib.rs index 1ef11378..579ff43d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; -impl button::Renderer for Renderer { fn draw( &mut self, bounds: Rectangle, cursor_position: Point, is_pressed: bool, - background: Option, - border_radius: u16, + style: &Box, (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, + 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 { + fn default() -> Self { + Box::new(Default) + } +} + +impl From for Box +where + T: 'static + StyleSheet, +{ + fn from(style: T) -> Self { + Box::new(style) + } +} -- cgit From f74ab463d44dd0bb025b0cea466d2861576253dd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 29 Dec 2019 12:29:47 +0100 Subject: Add `background_color` to `Settings` --- native/src/renderer.rs | 7 ++++--- native/src/renderer/windowed.rs | 3 ++- src/settings.rs | 18 +++++++++++++++++- wgpu/src/renderer.rs | 18 ++++++++++++------ winit/src/application.rs | 8 ++++++-- winit/src/settings/mod.rs | 15 ++++++++++++++- 6 files changed, 55 insertions(+), 14 deletions(-) diff --git a/native/src/renderer.rs b/native/src/renderer.rs index 7a68ada4..023dd42b 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -21,14 +21,15 @@ //! [`checkbox::Renderer`]: ../widget/checkbox/trait.Renderer.html mod debugger; -#[cfg(debug_assertions)] -mod null; mod windowed; pub use debugger::Debugger; +pub use windowed::{Target, Windowed}; + +#[cfg(debug_assertions)] +mod null; #[cfg(debug_assertions)] pub use null::Null; -pub use windowed::{Target, Windowed}; use crate::{layout, Element}; diff --git a/native/src/renderer/windowed.rs b/native/src/renderer/windowed.rs index 813a03f2..89f80bbe 100644 --- a/native/src/renderer/windowed.rs +++ b/native/src/renderer/windowed.rs @@ -1,4 +1,4 @@ -use crate::MouseCursor; +use crate::{Color, MouseCursor}; use raw_window_handle::HasRawWindowHandle; @@ -19,6 +19,7 @@ pub trait Windowed: super::Renderer + Sized { /// top of the GUI on most scenarios. fn draw>( &mut self, + clear_color: Color, output: &Self::Output, overlay: &[T], target: &mut Self::Target, diff --git a/src/settings.rs b/src/settings.rs index 62a1a614..8da8948c 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,7 +1,8 @@ //! Configure your application. +use crate::Color; /// The settings of an application. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct Settings { /// The [`Window`] settings. /// @@ -9,6 +10,20 @@ pub struct Settings { /// /// [`Window`]: struct.Window.html pub window: Window, + + /// The default background [`Color`] of the application + /// + /// [`Color`]: ../struct.Color.html + pub background_color: Color, +} + +impl Default for Settings { + fn default() -> Settings { + Settings { + window: Window::default(), + background_color: Color::WHITE, + } + } } /// The window settings of an application. @@ -44,6 +59,7 @@ impl From for iced_winit::Settings { decorations: settings.window.decorations, platform_specific: Default::default(), }, + background_color: settings.background_color, } } } diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 4984d4fe..47b258ed 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -76,6 +76,7 @@ impl Renderer { fn draw>( &mut self, + clear_color: Color, (primitive, mouse_cursor): &(Primitive, MouseCursor), overlay: &[T], target: &mut Target, @@ -97,11 +98,15 @@ impl Renderer { resolve_target: None, load_op: wgpu::LoadOp::Clear, store_op: wgpu::StoreOp::Store, - clear_color: wgpu::Color { - r: 1.0, - g: 1.0, - b: 1.0, - a: 1.0, + clear_color: { + let [r, g, b, a] = clear_color.into_linear(); + + wgpu::Color { + r: f64::from(r), + g: f64::from(g), + b: f64::from(b), + a: f64::from(a), + } }, }], depth_stencil_attachment: None, @@ -428,11 +433,12 @@ impl Windowed for Renderer { fn draw>( &mut self, + clear_color: Color, output: &Self::Output, overlay: &[T], target: &mut Target, ) -> MouseCursor { - self.draw(output, overlay, target) + self.draw(clear_color, output, overlay, target) } } diff --git a/winit/src/application.rs b/winit/src/application.rs index a8612b1a..50060b11 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -279,8 +279,12 @@ pub trait Application: Sized { resized = false; } - let new_mouse_cursor = - renderer.draw(&primitive, &debug.overlay(), &mut target); + let new_mouse_cursor = renderer.draw( + settings.background_color, + &primitive, + &debug.overlay(), + &mut target, + ); debug.render_finished(); diff --git a/winit/src/settings/mod.rs b/winit/src/settings/mod.rs index 58e3d879..1f9f1502 100644 --- a/winit/src/settings/mod.rs +++ b/winit/src/settings/mod.rs @@ -1,4 +1,5 @@ //! Configure your application. +use crate::Color; #[cfg(target_os = "windows")] #[path = "windows.rs"] @@ -10,12 +11,24 @@ mod platform; pub use platform::PlatformSpecific; /// The settings of an application. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct Settings { /// The [`Window`] settings /// /// [`Window`]: struct.Window.html pub window: Window, + + /// The default background color of the application + pub background_color: Color, +} + +impl Default for Settings { + fn default() -> Settings { + Settings { + window: Window::default(), + background_color: Color::WHITE, + } + } } /// The window settings of an application. -- cgit From 89a6b8a9a173e767753ec777fd83c912c1be5ea3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 29 Dec 2019 12:31:47 +0100 Subject: Rename `Settings::background_color` to `background` --- src/settings.rs | 6 +++--- winit/src/application.rs | 2 +- winit/src/settings/mod.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/settings.rs b/src/settings.rs index 8da8948c..4ae18a14 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -14,14 +14,14 @@ pub struct Settings { /// The default background [`Color`] of the application /// /// [`Color`]: ../struct.Color.html - pub background_color: Color, + pub background: Color, } impl Default for Settings { fn default() -> Settings { Settings { window: Window::default(), - background_color: Color::WHITE, + background: Color::WHITE, } } } @@ -59,7 +59,7 @@ impl From for iced_winit::Settings { decorations: settings.window.decorations, platform_specific: Default::default(), }, - background_color: settings.background_color, + background: settings.background, } } } diff --git a/winit/src/application.rs b/winit/src/application.rs index 50060b11..56f17573 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -280,7 +280,7 @@ pub trait Application: Sized { } let new_mouse_cursor = renderer.draw( - settings.background_color, + settings.background, &primitive, &debug.overlay(), &mut target, diff --git a/winit/src/settings/mod.rs b/winit/src/settings/mod.rs index 1f9f1502..0384df32 100644 --- a/winit/src/settings/mod.rs +++ b/winit/src/settings/mod.rs @@ -19,14 +19,14 @@ pub struct Settings { pub window: Window, /// The default background color of the application - pub background_color: Color, + pub background: Color, } impl Default for Settings { fn default() -> Settings { Settings { window: Window::default(), - background_color: Color::WHITE, + background: Color::WHITE, } } } -- cgit From 8caa66be2708b1c83e20d905d69902c2567c4692 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 30 Dec 2019 12:14:26 +0100 Subject: Add `Renderer::Defaults` and style inheritance --- examples/custom_widget.rs | 3 +- examples/pokedex.rs | 7 +-- examples/stopwatch.rs | 6 +-- examples/todos.rs | 86 ++++++++++++++++++++------------ examples/tour.rs | 13 +++-- native/src/element.rs | 10 +++- native/src/renderer.rs | 2 + native/src/renderer/debugger.rs | 1 + native/src/renderer/null.rs | 19 ++++++- native/src/user_interface.rs | 58 ++------------------- native/src/widget.rs | 1 + native/src/widget/button.rs | 19 +++---- native/src/widget/checkbox.rs | 2 + native/src/widget/column.rs | 4 +- native/src/widget/container.rs | 2 + native/src/widget/image.rs | 1 + native/src/widget/radio.rs | 2 + native/src/widget/row.rs | 4 +- native/src/widget/scrollable.rs | 8 ++- native/src/widget/slider.rs | 1 + native/src/widget/svg.rs | 1 + native/src/widget/text.rs | 3 ++ native/src/widget/text_input.rs | 1 + wgpu/src/defaults.rs | 27 ++++++++++ wgpu/src/lib.rs | 2 + wgpu/src/renderer.rs | 9 +++- wgpu/src/renderer/widget/button.rs | 27 ++++++++-- wgpu/src/renderer/widget/button/style.rs | 1 - wgpu/src/renderer/widget/column.rs | 3 +- wgpu/src/renderer/widget/row.rs | 3 +- wgpu/src/renderer/widget/text.rs | 3 +- wgpu/src/widget/button.rs | 45 +++++++---------- 32 files changed, 224 insertions(+), 150 deletions(-) create mode 100644 wgpu/src/defaults.rs delete mode 100644 wgpu/src/renderer/widget/button/style.rs diff --git a/examples/custom_widget.rs b/examples/custom_widget.rs index cf2f7792..ca562ead 100644 --- a/examples/custom_widget.rs +++ b/examples/custom_widget.rs @@ -13,7 +13,7 @@ mod circle { layout, Background, Color, Element, Hasher, Layout, Length, MouseCursor, Point, Size, Widget, }; - use iced_wgpu::{Primitive, Renderer}; + use iced_wgpu::{Defaults, Primitive, Renderer}; pub struct Circle { radius: u16, @@ -54,6 +54,7 @@ mod circle { fn draw( &self, _renderer: &mut Renderer, + _defaults: &Defaults, layout: Layout<'_>, _cursor_position: Point, ) -> (Primitive, MouseCursor) { diff --git a/examples/pokedex.rs b/examples/pokedex.rs index 0dcf6981..35d38251 100644 --- a/examples/pokedex.rs +++ b/examples/pokedex.rs @@ -1,6 +1,6 @@ use iced::{ - button, image, Align, Application, Button, Color, Column, Command, - Container, Element, Image, Length, Row, Settings, Text, + button, image, Align, Application, Button, Column, Command, Container, + Element, Image, Length, Row, Settings, Text, }; pub fn main() { @@ -219,7 +219,7 @@ impl From for Error { } fn button<'a>(state: &'a mut button::State, text: &str) -> Button<'a, Message> { - Button::new(state, Text::new(text).color(Color::WHITE)) + Button::new(state, Text::new(text)) .padding(10) .style(style::Button::Primary) } @@ -239,6 +239,7 @@ mod style { })), border_radius: 12, shadow_offset: 1.0, + text_color: Color::WHITE, } } } diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index 0e0cdba5..99746609 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -1,6 +1,6 @@ use iced::{ - button, Align, Application, Button, Color, Column, Command, Container, - Element, HorizontalAlignment, Length, Row, Settings, Subscription, Text, + button, Align, Application, Button, Column, Command, Container, Element, + HorizontalAlignment, Length, Row, Settings, Subscription, Text, }; use std::time::{Duration, Instant}; @@ -102,7 +102,6 @@ impl Application for Stopwatch { Button::new( state, Text::new(label) - .color(Color::WHITE) .horizontal_alignment(HorizontalAlignment::Center), ) .min_width(80) @@ -199,6 +198,7 @@ mod style { })), border_radius: 12, shadow_offset: 1.0, + text_color: Color::WHITE, } } } diff --git a/examples/todos.rs b/examples/todos.rs index 00edd7fb..ca20183f 100644 --- a/examples/todos.rs +++ b/examples/todos.rs @@ -1,7 +1,7 @@ use iced::{ button, scrollable, text_input, Align, Application, Button, Checkbox, - Color, Column, Command, Container, Element, Font, HorizontalAlignment, - Length, Row, Scrollable, Settings, Text, TextInput, + Column, Command, Container, Element, Font, HorizontalAlignment, Length, + Row, Scrollable, Settings, Text, TextInput, }; use serde::{Deserialize, Serialize}; @@ -291,13 +291,10 @@ impl Task { .align_items(Align::Center) .push(checkbox) .push( - Button::new( - edit_button, - edit_icon().color([0.5, 0.5, 0.5]), - ) - .on_press(TaskMessage::Edit) - .padding(10) - .style(style::Button::NoBackground), + Button::new(edit_button, edit_icon()) + .on_press(TaskMessage::Edit) + .padding(10) + .style(style::Button::Icon), ) .into() } @@ -321,14 +318,9 @@ impl Task { .push( Button::new( delete_button, - Row::new() - .spacing(10) - .push(delete_icon().color(Color::WHITE)) - .push( - Text::new("Delete") - .width(Length::Shrink) - .color(Color::WHITE), - ), + Row::new().spacing(10).push(delete_icon()).push( + Text::new("Delete").width(Length::Shrink), + ), ) .on_press(TaskMessage::Delete) .padding(10) @@ -359,12 +351,10 @@ impl Controls { let filter_button = |state, label, filter, current_filter| { let label = Text::new(label).size(16).width(Length::Shrink); - let button = if filter == current_filter { - Button::new(state, label.color(Color::WHITE)) - .style(style::Button::FilterSelected) - } else { - Button::new(state, label).style(style::Button::NoBackground) - }; + let button = + Button::new(state, label).style(style::Button::Filter { + selected: filter == current_filter, + }); button.on_press(Message::FilterChanged(filter)).padding(8) }; @@ -564,25 +554,38 @@ mod style { use iced::{button, Background, Color}; pub enum Button { - FilterSelected, - NoBackground, + Filter { selected: bool }, + Icon, 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 { + Button::Filter { selected } => { + if *selected { + button::Style { + background: Some(Background::Color( + Color::from_rgb(0.2, 0.2, 0.7), + )), + border_radius: 10, + shadow_offset: 0.0, + text_color: Color::WHITE, + } + } else { + button::Style { + background: None, + border_radius: 0, + shadow_offset: 0.0, + text_color: Color::BLACK, + } + } + } + Button::Icon => button::Style { background: None, border_radius: 0, shadow_offset: 0.0, + text_color: Color::from_rgb(0.5, 0.5, 0.5), }, Button::Destructive => button::Style { background: Some(Background::Color(Color::from_rgb( @@ -590,7 +593,24 @@ mod style { ))), border_radius: 5, shadow_offset: 1.0, + text_color: Color::WHITE, + }, + } + } + + fn hovered(&self) -> button::Style { + let active = self.active(); + + button::Style { + text_color: match self { + Button::Icon => Color::from_rgb(0.2, 0.2, 0.7), + Button::Filter { selected } if !selected => { + Color::from_rgb(0.2, 0.2, 0.7) + } + _ => active.text_color, }, + shadow_offset: active.shadow_offset + 1.0, + ..active } } } diff --git a/examples/tour.rs b/examples/tour.rs index 402bf1c4..2429ca4f 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -694,9 +694,7 @@ fn button<'a, Message>( ) -> Button<'a, Message> { Button::new( state, - Text::new(label) - .color(Color::WHITE) - .horizontal_alignment(HorizontalAlignment::Center), + Text::new(label).horizontal_alignment(HorizontalAlignment::Center), ) .padding(12) .min_width(100) @@ -761,6 +759,15 @@ mod style { })), border_radius: 12, shadow_offset: 1.0, + text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE), + } + } + + fn hovered(&self) -> button::Style { + button::Style { + text_color: Color::WHITE, + shadow_offset: 2.0, + ..self.active() } } } diff --git a/native/src/element.rs b/native/src/element.rs index 63d2de0c..9b5adb9c 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -235,10 +235,12 @@ where pub fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { - self.widget.draw(renderer, layout, cursor_position) + self.widget + .draw(renderer, defaults, layout, cursor_position) } pub(crate) fn hash_layout(&self, state: &mut Hasher) { @@ -316,10 +318,12 @@ where fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { - self.widget.draw(renderer, layout, cursor_position) + self.widget + .draw(renderer, defaults, layout, cursor_position) } fn hash_layout(&self, state: &mut Hasher) { @@ -384,10 +388,12 @@ where fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { renderer.explain( + defaults, self.element.widget.as_ref(), layout, cursor_position, diff --git a/native/src/renderer.rs b/native/src/renderer.rs index 023dd42b..90cec6c8 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -44,6 +44,8 @@ pub trait Renderer: Sized { /// [`Renderer`]: trait.Renderer.html type Output; + type Defaults: Default; + /// Lays out the elements of a user interface. /// /// You should override this if you need to perform any operations before or diff --git a/native/src/renderer/debugger.rs b/native/src/renderer/debugger.rs index 4cc50661..30f3d9a0 100644 --- a/native/src/renderer/debugger.rs +++ b/native/src/renderer/debugger.rs @@ -17,6 +17,7 @@ pub trait Debugger: super::Renderer { /// [`Element::explain`]: struct.Element.html#method.explain fn explain( &mut self, + defaults: &Self::Defaults, widget: &dyn Widget, layout: Layout<'_>, cursor_position: Point, diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 1be669c2..56d7e472 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -5,16 +5,26 @@ use crate::{ }; /// A renderer that does nothing. +/// +/// It can be useful if you are writing tests! #[derive(Debug, Clone, Copy)] pub struct Null; +impl Null { + pub fn new() -> Null { + Null + } +} + impl Renderer for Null { type Output = (); + type Defaults = (); } impl column::Renderer for Null { fn draw( &mut self, + _defaults: &Self::Defaults, _content: &[Element<'_, Message, Self>], _layout: Layout<'_>, _cursor_position: Point, @@ -25,6 +35,7 @@ impl column::Renderer for Null { impl row::Renderer for Null { fn draw( &mut self, + _defaults: &Self::Defaults, _content: &[Element<'_, Message, Self>], _layout: Layout<'_>, _cursor_position: Point, @@ -49,6 +60,7 @@ impl text::Renderer for Null { fn draw( &mut self, + _defaults: &Self::Defaults, _bounds: Rectangle, _content: &str, _size: u16, @@ -119,13 +131,16 @@ impl text_input::Renderer for Null { impl button::Renderer for Null { type Style = (); - fn draw( + fn draw( &mut self, + _defaults: &Self::Defaults, _bounds: Rectangle, _cursor_position: Point, + _is_disabled: bool, _is_pressed: bool, _style: &Self::Style, - _content: Self::Output, + _content: &Element<'_, Message, Self>, + _content_layout: Layout<'_>, ) -> Self::Output { } } diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 07b936a9..970bf0c1 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -43,24 +43,7 @@ where /// use iced_wgpu::Renderer; /// /// # mod iced_wgpu { - /// # pub struct Renderer; - /// # - /// # impl Renderer { - /// # pub fn new() -> Self { Renderer } - /// # } - /// # - /// # impl iced_native::Renderer for Renderer { type Output = (); } - /// # - /// # impl iced_native::column::Renderer for Renderer { - /// # fn draw( - /// # &mut self, - /// # _children: &[iced_native::Element<'_, Message, Self>], - /// # _layout: iced_native::Layout<'_>, - /// # _cursor_position: iced_native::Point, - /// # ) -> Self::Output { - /// # () - /// # } - /// # } + /// # pub use iced_native::renderer::Null as Renderer; /// # } /// # /// # use iced_native::Column; @@ -139,24 +122,7 @@ where /// use iced_wgpu::Renderer; /// /// # mod iced_wgpu { - /// # pub struct Renderer; - /// # - /// # impl Renderer { - /// # pub fn new() -> Self { Renderer } - /// # } - /// # - /// # impl iced_native::Renderer for Renderer { type Output = (); } - /// # - /// # impl iced_native::column::Renderer for Renderer { - /// # fn draw( - /// # &mut self, - /// # _children: &[iced_native::Element<'_, Message, Self>], - /// # _layout: iced_native::Layout<'_>, - /// # _cursor_position: iced_native::Point, - /// # ) -> Self::Output { - /// # () - /// # } - /// # } + /// # pub use iced_native::renderer::Null as Renderer; /// # } /// # /// # use iced_native::Column; @@ -241,24 +207,7 @@ where /// use iced_wgpu::Renderer; /// /// # mod iced_wgpu { - /// # pub struct Renderer; - /// # - /// # impl Renderer { - /// # pub fn new() -> Self { Renderer } - /// # } - /// # - /// # impl iced_native::Renderer for Renderer { type Output = (); } - /// # - /// # impl iced_native::column::Renderer for Renderer { - /// # fn draw( - /// # &mut self, - /// # _children: &[iced_native::Element<'_, Message, Self>], - /// # _layout: iced_native::Layout<'_>, - /// # _cursor_position: iced_native::Point, - /// # ) -> Self::Output { - /// # () - /// # } - /// # } + /// # pub use iced_native::renderer::Null as Renderer; /// # } /// # /// # use iced_native::Column; @@ -304,6 +253,7 @@ where pub fn draw(&self, renderer: &mut Renderer) -> Renderer::Output { self.root.widget.draw( renderer, + &Renderer::Defaults::default(), Layout::new(&self.layout), self.cursor_position, ) diff --git a/native/src/widget.rs b/native/src/widget.rs index 26889280..4aa7e7f0 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -101,6 +101,7 @@ where fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output; diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 4a7187da..75ef2693 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -216,21 +216,19 @@ where fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { - let content = self.content.draw( - renderer, - layout.children().next().unwrap(), - cursor_position, - ); - renderer.draw( + defaults, layout.bounds(), cursor_position, + self.on_press.is_none(), self.state.is_pressed, &self.style, - content, + &self.content, + layout.children().next().unwrap(), ) } @@ -253,13 +251,16 @@ pub trait Renderer: crate::Renderer + Sized { /// Draws a [`Button`]. /// /// [`Button`]: struct.Button.html - fn draw( + fn draw( &mut self, + defaults: &Self::Defaults, bounds: Rectangle, cursor_position: Point, + is_disabled: bool, is_pressed: bool, style: &Self::Style, - content: Self::Output, + content: &Element<'_, Message, Self>, + content_layout: Layout<'_>, ) -> Self::Output; } diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 0dcac712..87a7f629 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -134,6 +134,7 @@ where fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { @@ -146,6 +147,7 @@ where let label = text::Renderer::draw( renderer, + defaults, label_layout.bounds(), &self.label, text::Renderer::default_size(renderer), diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index 4b5d631c..3418d4b0 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -173,10 +173,11 @@ where fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { - renderer.draw(&self.children, layout, cursor_position) + renderer.draw(defaults, &self.children, layout, cursor_position) } fn hash_layout(&self, state: &mut Hasher) { @@ -213,6 +214,7 @@ pub trait Renderer: crate::Renderer + Sized { /// [`Layout`]: ../layout/struct.Layout.html fn draw( &mut self, + defaults: &Self::Defaults, content: &[Element<'_, Message, Self>], layout: Layout<'_>, cursor_position: Point, diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 74f0e0ef..93804c99 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -147,11 +147,13 @@ where fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { self.content.draw( renderer, + defaults, layout.children().next().unwrap(), cursor_position, ) diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 20375822..1efe4570 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -95,6 +95,7 @@ where fn draw( &self, renderer: &mut Renderer, + _defaults: &Renderer::Defaults, layout: Layout<'_>, _cursor_position: Point, ) -> Renderer::Output { diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index a9995b86..6ac00770 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -131,6 +131,7 @@ where fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { @@ -143,6 +144,7 @@ where let label = text::Renderer::draw( renderer, + defaults, label_layout.bounds(), &self.label, text::Renderer::default_size(renderer), diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index 3de65deb..76cca3d0 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -174,10 +174,11 @@ where fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { - renderer.draw(&self.children, layout, cursor_position) + renderer.draw(defaults, &self.children, layout, cursor_position) } fn hash_layout(&self, state: &mut Hasher) { @@ -215,6 +216,7 @@ pub trait Renderer: crate::Renderer + Sized { /// [`Layout`]: ../layout/struct.Layout.html fn draw( &mut self, + defaults: &Self::Defaults, children: &[Element<'_, Message, Self>], layout: Layout<'_>, cursor_position: Point, diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 9fa602d5..9df09b14 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -255,6 +255,7 @@ where fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { @@ -277,7 +278,12 @@ where Point::new(cursor_position.x, -1.0) }; - self.content.draw(renderer, content_layout, cursor_position) + self.content.draw( + renderer, + defaults, + content_layout, + cursor_position, + ) }; self::Renderer::draw( diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index f446f7e8..ea66a347 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -178,6 +178,7 @@ where fn draw( &self, renderer: &mut Renderer, + _defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs index 9580f195..f6202f72 100644 --- a/native/src/widget/svg.rs +++ b/native/src/widget/svg.rs @@ -91,6 +91,7 @@ where fn draw( &self, renderer: &mut Renderer, + _defaults: &Renderer::Defaults, layout: Layout<'_>, _cursor_position: Point, ) -> Renderer::Output { diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index cf9c9565..3a3db3cd 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -146,10 +146,12 @@ where fn draw( &self, renderer: &mut Renderer, + defaults: &Renderer::Defaults, layout: Layout<'_>, _cursor_position: Point, ) -> Renderer::Output { renderer.draw( + defaults, layout.bounds(), &self.content, self.size.unwrap_or(renderer.default_size()), @@ -209,6 +211,7 @@ pub trait Renderer: crate::Renderer { /// [`VerticalAlignment`]: enum.VerticalAlignment.html fn draw( &mut self, + defaults: &Self::Defaults, bounds: Rectangle, content: &str, size: u16, diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 1d1c32a2..e2114f00 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -343,6 +343,7 @@ where fn draw( &self, renderer: &mut Renderer, + _defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { diff --git a/wgpu/src/defaults.rs b/wgpu/src/defaults.rs new file mode 100644 index 00000000..8de8258b --- /dev/null +++ b/wgpu/src/defaults.rs @@ -0,0 +1,27 @@ +use iced_native::Color; + +#[derive(Debug, Clone, Copy)] +pub struct Defaults { + pub text: Text, +} + +impl Default for Defaults { + fn default() -> Defaults { + Defaults { + text: Text::default(), + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Text { + pub color: Color, +} + +impl Default for Text { + fn default() -> Text { + Text { + color: Color::BLACK, + } + } +} diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 55f93546..786b6872 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -24,6 +24,7 @@ #![deny(unused_results)] #![deny(unsafe_code)] #![deny(rust_2018_idioms)] +pub mod defaults; pub mod widget; mod image; @@ -33,6 +34,7 @@ mod renderer; mod text; mod transformation; +pub use defaults::Defaults; pub use primitive::Primitive; pub use renderer::{Renderer, Target}; #[doc(no_inline)] diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 47b258ed..1b143d90 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -1,4 +1,6 @@ -use crate::{image, quad, text, Image, Primitive, Quad, Transformation}; +use crate::{ + image, quad, text, Defaults, Image, Primitive, Quad, Transformation, +}; use iced_native::{ renderer::{Debugger, Windowed}, Background, Color, Layout, MouseCursor, Point, Rectangle, Vector, Widget, @@ -411,6 +413,7 @@ impl Renderer { impl iced_native::Renderer for Renderer { type Output = (Primitive, MouseCursor); + type Defaults = Defaults; fn layout<'a, Message>( &mut self, @@ -445,13 +448,15 @@ impl Windowed for Renderer { impl Debugger for Renderer { fn explain( &mut self, + defaults: &Defaults, widget: &dyn Widget, layout: Layout<'_>, cursor_position: Point, color: Color, ) -> Self::Output { let mut primitives = Vec::new(); - let (primitive, cursor) = widget.draw(self, layout, cursor_position); + let (primitive, cursor) = + widget.draw(self, defaults, layout, cursor_position); explain_layout(layout, color, &mut primitives); primitives.push(primitive); diff --git a/wgpu/src/renderer/widget/button.rs b/wgpu/src/renderer/widget/button.rs index f3817374..4fbd90d7 100644 --- a/wgpu/src/renderer/widget/button.rs +++ b/wgpu/src/renderer/widget/button.rs @@ -1,21 +1,26 @@ -use crate::{button::StyleSheet, Primitive, Renderer}; -use iced_native::{Background, MouseCursor, Point, Rectangle}; +use crate::{button::StyleSheet, defaults, Defaults, Primitive, Renderer}; +use iced_native::{Background, Element, Layout, MouseCursor, Point, Rectangle}; impl iced_native::button::Renderer for Renderer { type Style = Box; - fn draw( + fn draw( &mut self, + defaults: &Defaults, bounds: Rectangle, cursor_position: Point, + is_disabled: bool, is_pressed: bool, style: &Box, - (content, _): Self::Output, + content: &Element<'_, Message, Self>, + content_layout: Layout<'_>, ) -> Self::Output { let is_mouse_over = bounds.contains(cursor_position); // TODO: Render proper shadows - let styling = if is_mouse_over { + let styling = if is_disabled { + style.disabled() + } else if is_mouse_over { if is_pressed { style.pressed() } else { @@ -25,6 +30,18 @@ impl iced_native::button::Renderer for Renderer { style.active() }; + let (content, _) = content.draw( + self, + &Defaults { + text: defaults::Text { + color: styling.text_color, + }, + ..*defaults + }, + content_layout, + cursor_position, + ); + ( match styling.background { None => content, diff --git a/wgpu/src/renderer/widget/button/style.rs b/wgpu/src/renderer/widget/button/style.rs deleted file mode 100644 index 8b137891..00000000 --- a/wgpu/src/renderer/widget/button/style.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/wgpu/src/renderer/widget/column.rs b/wgpu/src/renderer/widget/column.rs index 6c31af90..95a7463a 100644 --- a/wgpu/src/renderer/widget/column.rs +++ b/wgpu/src/renderer/widget/column.rs @@ -4,6 +4,7 @@ use iced_native::{column, Element, Layout, MouseCursor, Point}; impl column::Renderer for Renderer { fn draw( &mut self, + defaults: &Self::Defaults, content: &[Element<'_, Message, Self>], layout: Layout<'_>, cursor_position: Point, @@ -17,7 +18,7 @@ impl column::Renderer for Renderer { .zip(layout.children()) .map(|(child, layout)| { let (primitive, new_mouse_cursor) = - child.draw(self, layout, cursor_position); + child.draw(self, defaults, layout, cursor_position); if new_mouse_cursor > mouse_cursor { mouse_cursor = new_mouse_cursor; diff --git a/wgpu/src/renderer/widget/row.rs b/wgpu/src/renderer/widget/row.rs index f082dc61..bd9f1a04 100644 --- a/wgpu/src/renderer/widget/row.rs +++ b/wgpu/src/renderer/widget/row.rs @@ -4,6 +4,7 @@ use iced_native::{row, Element, Layout, MouseCursor, Point}; impl row::Renderer for Renderer { fn draw( &mut self, + defaults: &Self::Defaults, children: &[Element<'_, Message, Self>], layout: Layout<'_>, cursor_position: Point, @@ -17,7 +18,7 @@ impl row::Renderer for Renderer { .zip(layout.children()) .map(|(child, layout)| { let (primitive, new_mouse_cursor) = - child.draw(self, layout, cursor_position); + child.draw(self, defaults, layout, cursor_position); if new_mouse_cursor > mouse_cursor { mouse_cursor = new_mouse_cursor; diff --git a/wgpu/src/renderer/widget/text.rs b/wgpu/src/renderer/widget/text.rs index 08a162ba..d61c5523 100644 --- a/wgpu/src/renderer/widget/text.rs +++ b/wgpu/src/renderer/widget/text.rs @@ -27,6 +27,7 @@ impl text::Renderer for Renderer { fn draw( &mut self, + defaults: &Self::Defaults, bounds: Rectangle, content: &str, size: u16, @@ -40,7 +41,7 @@ impl text::Renderer for Renderer { content: content.to_string(), size: f32::from(size), bounds, - color: color.unwrap_or(Color::BLACK), + color: color.unwrap_or(defaults.text.color), font, horizontal_alignment, vertical_alignment, diff --git a/wgpu/src/widget/button.rs b/wgpu/src/widget/button.rs index 7827f8b2..2c4e174f 100644 --- a/wgpu/src/widget/button.rs +++ b/wgpu/src/widget/button.rs @@ -5,7 +5,7 @@ //! [`Button`]: type.Button.html //! [`State`]: struct.State.html use crate::Renderer; -use iced_native::Background; +use iced_native::{Background, Color}; pub use iced_native::button::State; @@ -19,6 +19,7 @@ pub struct Style { pub shadow_offset: f32, pub background: Option, pub border_radius: u16, + pub text_color: Color, } pub trait StyleSheet { @@ -41,7 +42,22 @@ pub trait StyleSheet { } fn disabled(&self) -> Style { - self.active() + let active = self.active(); + + Style { + shadow_offset: 0.0, + background: active.background.map(|background| match background { + Background::Color(color) => Background::Color(Color { + a: color.a * 0.5, + ..color + }), + }), + text_color: Color { + a: active.text_color.a * 0.5, + ..active.text_color + }, + ..active + } } } @@ -53,30 +69,7 @@ impl StyleSheet for Default { 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, + text_color: Color::BLACK, } } } -- cgit From 2ff0e48142c302cb93130164d083589bb2ac4979 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 30 Dec 2019 20:54:04 +0100 Subject: Make `Row`, `Column`, and `Checkbox` shrink by default --- examples/custom_widget.rs | 5 +---- examples/events.rs | 12 +++--------- examples/pokedex.rs | 21 ++++++++------------- examples/stopwatch.rs | 3 --- examples/svg.rs | 12 ++++++------ examples/todos.rs | 14 ++++++-------- examples/tour.rs | 8 +++++++- native/src/widget/checkbox.rs | 2 +- native/src/widget/column.rs | 2 +- native/src/widget/radio.rs | 1 + native/src/widget/row.rs | 2 +- native/src/widget/svg.rs | 2 +- native/src/widget/text.rs | 2 +- native/src/widget/text_input.rs | 4 ++-- web/src/widget/column.rs | 2 +- web/src/widget/row.rs | 2 +- web/src/widget/text.rs | 2 +- 17 files changed, 42 insertions(+), 54 deletions(-) diff --git a/examples/custom_widget.rs b/examples/custom_widget.rs index cf2f7792..c0038cd9 100644 --- a/examples/custom_widget.rs +++ b/examples/custom_widget.rs @@ -124,10 +124,7 @@ impl Sandbox for Example { .max_width(500) .align_items(Align::Center) .push(Circle::new(self.radius)) - .push( - Text::new(format!("Radius: {}", self.radius.to_string())) - .width(Length::Shrink), - ) + .push(Text::new(format!("Radius: {}", self.radius.to_string()))) .push(Slider::new( &mut self.slider, 1.0..=100.0, diff --git a/examples/events.rs b/examples/events.rs index 7d83fbd8..74542171 100644 --- a/examples/events.rs +++ b/examples/events.rs @@ -57,13 +57,9 @@ impl Application for Events { fn view(&mut self) -> Element { let events = self.last.iter().fold( - Column::new().width(Length::Shrink).spacing(10), + Column::new().spacing(10), |column, event| { - column.push( - Text::new(format!("{:?}", event)) - .size(40) - .width(Length::Shrink), - ) + column.push(Text::new(format!("{:?}", event)).size(40)) }, ); @@ -71,11 +67,9 @@ impl Application for Events { self.enabled, "Listen to runtime events", Message::Toggled, - ) - .width(Length::Shrink); + ); let content = Column::new() - .width(Length::Shrink) .align_items(Align::Center) .spacing(20) .push(events) diff --git a/examples/pokedex.rs b/examples/pokedex.rs index 2d595ec4..f44b6163 100644 --- a/examples/pokedex.rs +++ b/examples/pokedex.rs @@ -77,11 +77,8 @@ impl Application for Pokedex { fn view(&mut self) -> Element { let content = match self { - Pokedex::Loading => Column::new().width(Length::Shrink).push( - Text::new("Searching for Pokémon...") - .width(Length::Shrink) - .size(40), - ), + Pokedex::Loading => Column::new() + .push(Text::new("Searching for Pokémon...").size(40)), Pokedex::Loaded { pokemon, search } => Column::new() .max_width(500) .spacing(20) @@ -91,14 +88,9 @@ impl Application for Pokedex { button(search, "Keep searching!").on_press(Message::Search), ), Pokedex::Errored { try_again, .. } => Column::new() - .width(Length::Shrink) .spacing(20) .align_items(Align::End) - .push( - Text::new("Whoops! Something went wrong...") - .width(Length::Shrink) - .size(40), - ) + .push(Text::new("Whoops! Something went wrong...").size(40)) .push(button(try_again, "Try again").on_press(Message::Search)), }; @@ -134,10 +126,13 @@ impl Pokemon { Row::new() .align_items(Align::Center) .spacing(20) - .push(Text::new(&self.name).size(30)) + .push( + Text::new(&self.name) + .size(30) + .width(Length::Fill), + ) .push( Text::new(format!("#{}", self.number)) - .width(Length::Shrink) .size(20) .color([0.5, 0.5, 0.5]), ), diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index 7a7f0793..f4d485e2 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -96,7 +96,6 @@ impl Application for Stopwatch { seconds % MINUTE, self.duration.subsec_millis() / 10, )) - .width(Length::Shrink) .size(40); let button = |state, label, color: [f32; 3]| { @@ -125,13 +124,11 @@ impl Application for Stopwatch { .on_press(Message::Reset); let controls = Row::new() - .width(Length::Shrink) .spacing(20) .push(toggle_button) .push(reset_button); let content = Column::new() - .width(Length::Shrink) .align_items(Align::Center) .spacing(20) .push(duration) diff --git a/examples/svg.rs b/examples/svg.rs index cdf238f0..1895039d 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -25,13 +25,14 @@ impl Sandbox for Tiger { let content = { use iced::{Column, Svg}; - Column::new() - .width(Length::Shrink) - .padding(20) - .push(Svg::new(format!( + Column::new().padding(20).push( + Svg::new(format!( "{}/examples/resources/tiger.svg", env!("CARGO_MANIFEST_DIR") - ))) + )) + .width(Length::Fill) + .height(Length::Fill), + ) }; #[cfg(not(feature = "svg"))] @@ -39,7 +40,6 @@ impl Sandbox for Tiger { use iced::{HorizontalAlignment, Text}; Text::new("You need to enable the `svg` feature!") - .width(Length::Shrink) .horizontal_alignment(HorizontalAlignment::Center) .size(30) }; diff --git a/examples/todos.rs b/examples/todos.rs index 42e88f65..f5f2f459 100644 --- a/examples/todos.rs +++ b/examples/todos.rs @@ -146,6 +146,7 @@ impl Application for Todos { .. }) => { let title = Text::new("todos") + .width(Length::Fill) .size(100) .color([0.5, 0.5, 0.5]) .horizontal_alignment(HorizontalAlignment::Center); @@ -284,7 +285,8 @@ impl Task { self.completed, &self.description, TaskMessage::Completed, - ); + ) + .width(Length::Fill); Row::new() .spacing(20) @@ -323,11 +325,7 @@ impl Task { Row::new() .spacing(10) .push(delete_icon().color(Color::WHITE)) - .push( - Text::new("Delete") - .width(Length::Shrink) - .color(Color::WHITE), - ), + .push(Text::new("Delete").color(Color::WHITE)), ) .on_press(TaskMessage::Delete) .padding(10) @@ -358,7 +356,7 @@ impl Controls { let tasks_left = tasks.iter().filter(|task| !task.completed).count(); let filter_button = |state, label, filter, current_filter| { - let label = Text::new(label).size(16).width(Length::Shrink); + let label = Text::new(label).size(16); let button = if filter == current_filter { Button::new(state, label.color(Color::WHITE)) .background(Color::from_rgb(0.2, 0.2, 0.7)) @@ -381,11 +379,11 @@ impl Controls { tasks_left, if tasks_left == 1 { "task" } else { "tasks" } )) + .width(Length::Fill) .size(16), ) .push( Row::new() - .width(Length::Shrink) .spacing(10) .push(filter_button( all_button, diff --git a/examples/tour.rs b/examples/tour.rs index da05b396..dc5ccb5d 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -67,7 +67,7 @@ impl Sandbox for Tour { ); } - controls = controls.push(Column::new()); + controls = controls.push(Column::new().width(Length::Fill)); if steps.can_continue() { controls = controls.push( @@ -401,6 +401,7 @@ impl<'a> Step { )) .push( Text::new(&value.to_string()) + .width(Length::Fill) .horizontal_alignment(HorizontalAlignment::Center), ) } @@ -447,6 +448,7 @@ impl<'a> Step { )) .push( Text::new(&format!("{} px", spacing)) + .width(Length::Fill) .horizontal_alignment(HorizontalAlignment::Center), ); @@ -561,6 +563,7 @@ impl<'a> Step { )) .push( Text::new(&format!("Width: {} px", width.to_string())) + .width(Length::Fill) .horizontal_alignment(HorizontalAlignment::Center), ) } @@ -580,6 +583,7 @@ impl<'a> Step { .push(Column::new().height(Length::Units(4096))) .push( Text::new("You are halfway there!") + .width(Length::Fill) .size(30) .horizontal_alignment(HorizontalAlignment::Center), ) @@ -587,6 +591,7 @@ impl<'a> Step { .push(ferris(300)) .push( Text::new("You made it!") + .width(Length::Fill) .size(50) .horizontal_alignment(HorizontalAlignment::Center), ) @@ -629,6 +634,7 @@ impl<'a> Step { } else { value }) + .width(Length::Fill) .horizontal_alignment(HorizontalAlignment::Center), ) } diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 0dcac712..06a484c7 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -54,7 +54,7 @@ impl Checkbox { on_toggle: Box::new(f), label: String::from(label), label_color: None, - width: Length::Fill, + width: Length::Shrink, } } diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index 4b5d631c..0901bff4 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -33,7 +33,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { Column { spacing: 0, padding: 0, - width: Length::Fill, + width: Length::Shrink, height: Length::Shrink, max_width: u32::MAX, max_height: u32::MAX, diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index a9995b86..876f4f48 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -95,6 +95,7 @@ where let size = self::Renderer::default_size(renderer); Row::<(), Renderer>::new() + .width(Length::Fill) .spacing(15) .align_items(Align::Center) .push( diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index 3de65deb..7d951968 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -33,7 +33,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> { Row { spacing: 0, padding: 0, - width: Length::Fill, + width: Length::Shrink, height: Length::Shrink, max_width: u32::MAX, max_height: u32::MAX, diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs index 9580f195..25587ffa 100644 --- a/native/src/widget/svg.rs +++ b/native/src/widget/svg.rs @@ -30,7 +30,7 @@ impl Svg { Svg { handle: handle.into(), width: Length::Fill, - height: Length::Fill, + height: Length::Shrink, } } diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index cf9c9565..caa81db0 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -41,7 +41,7 @@ impl Text { size: None, color: None, font: Font::Default, - width: Length::Fill, + width: Length::Shrink, height: Length::Shrink, horizontal_alignment: HorizontalAlignment::Left, vertical_alignment: VerticalAlignment::Top, diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 1d1c32a2..d3c45fba 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -64,11 +64,11 @@ impl<'a, Message> TextInput<'a, Message> { placeholder: &str, value: &str, on_change: F, - ) -> Self + ) -> TextInput<'a, Message> where F: 'static + Fn(String) -> Message, { - Self { + TextInput { state, placeholder: String::from(placeholder), value: Value::new(value), diff --git a/web/src/widget/column.rs b/web/src/widget/column.rs index e0e49148..9aa988ff 100644 --- a/web/src/widget/column.rs +++ b/web/src/widget/column.rs @@ -28,7 +28,7 @@ impl<'a, Message> Column<'a, Message> { Column { spacing: 0, padding: 0, - width: Length::Fill, + width: Length::Shrink, height: Length::Shrink, max_width: u32::MAX, max_height: u32::MAX, diff --git a/web/src/widget/row.rs b/web/src/widget/row.rs index 02754e2e..c26cb91b 100644 --- a/web/src/widget/row.rs +++ b/web/src/widget/row.rs @@ -28,7 +28,7 @@ impl<'a, Message> Row<'a, Message> { Row { spacing: 0, padding: 0, - width: Length::Fill, + width: Length::Shrink, height: Length::Shrink, max_width: u32::MAX, max_height: u32::MAX, diff --git a/web/src/widget/text.rs b/web/src/widget/text.rs index 2fdbc0a6..5b0bee55 100644 --- a/web/src/widget/text.rs +++ b/web/src/widget/text.rs @@ -36,7 +36,7 @@ impl Text { size: None, color: None, font: Font::Default, - width: Length::Fill, + width: Length::Shrink, height: Length::Shrink, horizontal_alignment: HorizontalAlignment::Left, vertical_alignment: VerticalAlignment::Top, -- cgit From fb9cc0262b30a953e8188897b74abb5106ea1fd8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 31 Dec 2019 11:36:54 +0100 Subject: Draft basic styling for `Container` --- native/src/widget/container.rs | 48 ++++++++++++++++++++++++++++------- wgpu/src/renderer/widget.rs | 1 + wgpu/src/renderer/widget/container.rs | 46 +++++++++++++++++++++++++++++++++ wgpu/src/widget.rs | 1 + wgpu/src/widget/container.rs | 37 +++++++++++++++++++++++++++ winit/src/application.rs | 4 +-- 6 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 wgpu/src/renderer/widget/container.rs create mode 100644 wgpu/src/widget/container.rs diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 93804c99..75c2d6b3 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -3,7 +3,7 @@ use std::hash::Hash; use crate::{ layout, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, - Widget, + Rectangle, Widget, }; use std::u32; @@ -12,17 +12,21 @@ use std::u32; /// /// It is normally used for alignment purposes. #[allow(missing_debug_implementations)] -pub struct Container<'a, Message, Renderer> { +pub struct Container<'a, Message, Renderer: self::Renderer> { width: Length, height: Length, max_width: u32, max_height: u32, horizontal_alignment: Align, vertical_alignment: Align, + style: Renderer::Style, content: Element<'a, Message, Renderer>, } -impl<'a, Message, Renderer> Container<'a, Message, Renderer> { +impl<'a, Message, Renderer> Container<'a, Message, Renderer> +where + Renderer: self::Renderer, +{ /// Creates an empty [`Container`]. /// /// [`Container`]: struct.Container.html @@ -37,6 +41,7 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> { max_height: u32::MAX, horizontal_alignment: Align::Start, vertical_alignment: Align::Start, + style: Renderer::Style::default(), content: content.into(), } } @@ -78,7 +83,6 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> { /// [`Container`]: struct.Container.html pub fn center_x(mut self) -> Self { self.horizontal_alignment = Align::Center; - self } @@ -87,7 +91,14 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> { /// [`Container`]: struct.Container.html pub fn center_y(mut self) -> Self { self.vertical_alignment = Align::Center; + self + } + /// Sets the style the [`Container`]. + /// + /// [`Container`]: struct.Container.html + pub fn style(mut self, style: impl Into) -> Self { + self.style = style.into(); self } } @@ -95,7 +106,7 @@ impl<'a, Message, Renderer> Container<'a, Message, Renderer> { impl<'a, Message, Renderer> Widget for Container<'a, Message, Renderer> where - Renderer: crate::Renderer, + Renderer: self::Renderer, { fn width(&self) -> Length { self.width @@ -151,11 +162,13 @@ where layout: Layout<'_>, cursor_position: Point, ) -> Renderer::Output { - self.content.draw( - renderer, + renderer.draw( defaults, - layout.children().next().unwrap(), + layout.bounds(), cursor_position, + &self.style, + &self.content, + layout.children().next().unwrap(), ) } @@ -170,10 +183,27 @@ where } } +pub trait Renderer: crate::Renderer { + type Style: Default; + + /// Draws a [`Container`]. + /// + /// [`Container`]: struct.Container.html + fn draw( + &mut self, + defaults: &Self::Defaults, + bounds: Rectangle, + cursor_position: Point, + style: &Self::Style, + content: &Element<'_, Message, Self>, + content_layout: Layout<'_>, + ) -> Self::Output; +} + impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + crate::Renderer, + Renderer: 'a + self::Renderer, Message: 'static, { fn from( diff --git a/wgpu/src/renderer/widget.rs b/wgpu/src/renderer/widget.rs index 91f107e8..8cf79cb0 100644 --- a/wgpu/src/renderer/widget.rs +++ b/wgpu/src/renderer/widget.rs @@ -1,6 +1,7 @@ mod button; mod checkbox; mod column; +mod container; mod image; mod radio; mod row; diff --git a/wgpu/src/renderer/widget/container.rs b/wgpu/src/renderer/widget/container.rs new file mode 100644 index 00000000..29c709f8 --- /dev/null +++ b/wgpu/src/renderer/widget/container.rs @@ -0,0 +1,46 @@ +use crate::{container, defaults, Defaults, Primitive, Renderer}; +use iced_native::{Element, Layout, Point, Rectangle}; + +impl iced_native::container::Renderer for Renderer { + type Style = Box; + + fn draw( + &mut self, + defaults: &Defaults, + bounds: Rectangle, + cursor_position: Point, + style_sheet: &Self::Style, + content: &Element<'_, Message, Self>, + content_layout: Layout<'_>, + ) -> Self::Output { + let style = style_sheet.style(); + + let defaults = Defaults { + text: defaults::Text { + color: style.text_color.unwrap_or(defaults.text.color), + }, + ..*defaults + }; + + let (content, mouse_cursor) = + content.draw(self, &defaults, content_layout, cursor_position); + + match style.background { + Some(background) => { + let quad = Primitive::Quad { + bounds, + background, + border_radius: style.border_radius, + }; + + ( + Primitive::Group { + primitives: vec![quad, content], + }, + mouse_cursor, + ) + } + None => (content, mouse_cursor), + } + } +} diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs index aa200ca2..c6f34301 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -1 +1,2 @@ pub mod button; +pub mod container; diff --git a/wgpu/src/widget/container.rs b/wgpu/src/widget/container.rs new file mode 100644 index 00000000..1fc0ec98 --- /dev/null +++ b/wgpu/src/widget/container.rs @@ -0,0 +1,37 @@ +use iced_native::{Background, Color}; + +#[derive(Debug, Clone, Copy)] +pub struct Style { + pub text_color: Option, + pub background: Option, + pub border_radius: u16, +} + +pub trait StyleSheet { + fn style(&self) -> Style { + Style { + text_color: None, + background: None, + border_radius: 0, + } + } +} + +struct Default; + +impl StyleSheet for Default {} + +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/winit/src/application.rs b/winit/src/application.rs index 56f17573..02fa3780 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -1,5 +1,5 @@ use crate::{ - conversion, + container, conversion, input::{keyboard, mouse}, renderer::{Target, Windowed}, subscription, Cache, Clipboard, Command, Container, Debug, Element, Event, @@ -18,7 +18,7 @@ pub trait Application: Sized { /// The renderer to use to draw the [`Application`]. /// /// [`Application`]: trait.Application.html - type Renderer: Windowed; + type Renderer: Windowed + container::Renderer; /// The type of __messages__ your [`Application`] will produce. /// -- cgit From 649d72e7de88e593255075957e65414ed1b4d0d6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 31 Dec 2019 11:38:35 +0100 Subject: Fix `Widget::draw` for `Space` widget --- native/src/widget/space.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/native/src/widget/space.rs b/native/src/widget/space.rs index 2029c52f..24c94bf6 100644 --- a/native/src/widget/space.rs +++ b/native/src/widget/space.rs @@ -68,6 +68,7 @@ where fn draw( &self, renderer: &mut Renderer, + _defaults: &Renderer::Defaults, layout: Layout<'_>, _cursor_position: Point, ) -> Renderer::Output { -- cgit From 9ab7c47dc7d834ee73bc068f9f34eea4d6946436 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 31 Dec 2019 21:35:42 +0100 Subject: Add `border_width` and `border_color` to `Quad` --- core/src/color.rs | 8 +++++ examples/custom_widget.rs | 2 ++ examples/tour.rs | 2 +- wgpu/src/primitive.rs | 4 +++ wgpu/src/quad.rs | 17 ++++++++-- wgpu/src/renderer.rs | 9 +++-- wgpu/src/renderer/widget/button.rs | 8 ++++- wgpu/src/renderer/widget/checkbox.rs | 44 ++++++++++--------------- wgpu/src/renderer/widget/container.rs | 4 ++- wgpu/src/renderer/widget/radio.rs | 46 +++++++++++--------------- wgpu/src/renderer/widget/scrollable.rs | 8 ++++- wgpu/src/renderer/widget/slider.rs | 58 +++++++++++++++------------------ wgpu/src/renderer/widget/text_input.rs | 33 +++++++------------ wgpu/src/shader/quad.frag | 51 ++++++++++++++++++++++++----- wgpu/src/shader/quad.frag.spv | Bin 3044 -> 4212 bytes wgpu/src/shader/quad.vert | 14 +++++--- wgpu/src/shader/quad.vert.spv | Bin 3020 -> 3372 bytes 17 files changed, 180 insertions(+), 128 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index c28e784f..d72651d9 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -25,6 +25,14 @@ impl Color { a: 1.0, }; + /// A color with no opacity. + pub const TRANSPARENT: Color = Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 0.0, + }; + /// Creates a [`Color`] from its RGB8 components. /// /// [`Color`]: struct.Color.html diff --git a/examples/custom_widget.rs b/examples/custom_widget.rs index ca562ead..1f51ee54 100644 --- a/examples/custom_widget.rs +++ b/examples/custom_widget.rs @@ -63,6 +63,8 @@ mod circle { bounds: layout.bounds(), background: Background::Color(Color::BLACK), border_radius: self.radius, + border_width: 0, + border_color: Color::TRANSPARENT, }, MouseCursor::OutOfBounds, ) diff --git a/examples/tour.rs b/examples/tour.rs index c7f866e8..d006d397 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -27,7 +27,7 @@ impl Sandbox for Tour { scroll: scrollable::State::new(), back_button: button::State::new(), next_button: button::State::new(), - debug: false, + debug: true, } } diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index 6c61f800..f4609151 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -38,6 +38,10 @@ pub enum Primitive { background: Background, /// The border radius of the quad border_radius: u16, + /// The border width of the quad + border_width: u16, + /// The border color of the quad + border_color: Color, }, /// An image primitive Image { diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index c292dec3..fe3276a3 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -125,9 +125,19 @@ impl Pipeline { }, wgpu::VertexAttributeDescriptor { shader_location: 4, - format: wgpu::VertexFormat::Float, + format: wgpu::VertexFormat::Float4, offset: 4 * (2 + 2 + 4), }, + wgpu::VertexAttributeDescriptor { + shader_location: 5, + format: wgpu::VertexFormat::Float, + offset: 4 * (2 + 2 + 4 + 4), + }, + wgpu::VertexAttributeDescriptor { + shader_location: 6, + format: wgpu::VertexFormat::Float, + offset: 4 * (2 + 2 + 4 + 4 + 1), + }, ], }, ], @@ -233,7 +243,8 @@ impl Pipeline { bounds.x, bounds.y, bounds.width, - bounds.height, + // TODO: Address anti-aliasing adjustments properly + bounds.height + 1, ); render_pass.draw_indexed( @@ -277,7 +288,9 @@ pub struct Quad { pub position: [f32; 2], pub scale: [f32; 2], pub color: [f32; 4], + pub border_color: [f32; 4], pub border_radius: f32, + pub border_width: f32, } impl Quad { diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 1b143d90..efda046b 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -223,6 +223,8 @@ impl Renderer { bounds, background, border_radius, + border_width, + border_color, } => { // TODO: Move some of this computations to the GPU (?) layer.quads.push(Quad { @@ -235,6 +237,8 @@ impl Renderer { Background::Color(color) => color.into_linear(), }, border_radius: *border_radius as f32, + border_width: *border_width as f32, + border_color: border_color.into_linear(), }); } Primitive::Image { handle, bounds } => { @@ -470,11 +474,12 @@ fn explain_layout( color: Color, primitives: &mut Vec, ) { - // TODO: Draw borders instead primitives.push(Primitive::Quad { bounds: layout.bounds(), - background: Background::Color([0.0, 0.0, 0.0, 0.05].into()), + background: Background::Color(Color::TRANSPARENT), border_radius: 0, + border_width: 1, + border_color: [0.6, 0.6, 0.6, 0.5].into(), }); for child in layout.children() { diff --git a/wgpu/src/renderer/widget/button.rs b/wgpu/src/renderer/widget/button.rs index 4fbd90d7..d00a43a5 100644 --- a/wgpu/src/renderer/widget/button.rs +++ b/wgpu/src/renderer/widget/button.rs @@ -1,5 +1,7 @@ use crate::{button::StyleSheet, defaults, Defaults, Primitive, Renderer}; -use iced_native::{Background, Element, Layout, MouseCursor, Point, Rectangle}; +use iced_native::{ + Background, Color, Element, Layout, MouseCursor, Point, Rectangle, +}; impl iced_native::button::Renderer for Renderer { type Style = Box; @@ -57,11 +59,15 @@ impl iced_native::button::Renderer for Renderer { [0.0, 0.0, 0.0, 0.5].into(), ), border_radius: styling.border_radius, + border_width: 0, + border_color: Color::TRANSPARENT, }, Primitive::Quad { bounds, background, border_radius: styling.border_radius, + border_width: 0, + border_color: Color::TRANSPARENT, }, content, ], diff --git a/wgpu/src/renderer/widget/checkbox.rs b/wgpu/src/renderer/widget/checkbox.rs index 54b4b1cc..1ed27ff7 100644 --- a/wgpu/src/renderer/widget/checkbox.rs +++ b/wgpu/src/renderer/widget/checkbox.rs @@ -1,6 +1,6 @@ use crate::{Primitive, Renderer}; use iced_native::{ - checkbox, Background, HorizontalAlignment, MouseCursor, Rectangle, + checkbox, Background, Color, HorizontalAlignment, MouseCursor, Rectangle, VerticalAlignment, }; @@ -18,30 +18,20 @@ impl checkbox::Renderer for Renderer { is_mouse_over: bool, (label, _): Self::Output, ) -> Self::Output { - let (checkbox_border, checkbox_box) = ( - Primitive::Quad { - bounds, - background: Background::Color([0.6, 0.6, 0.6].into()), - border_radius: 6, - }, - Primitive::Quad { - bounds: Rectangle { - x: bounds.x + 1.0, - y: bounds.y + 1.0, - width: bounds.width - 2.0, - height: bounds.height - 2.0, - }, - background: Background::Color( - if is_mouse_over { - [0.90, 0.90, 0.90] - } else { - [0.95, 0.95, 0.95] - } - .into(), - ), - border_radius: 5, - }, - ); + 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), + }; ( Primitive::Group { @@ -56,9 +46,9 @@ impl checkbox::Renderer for Renderer { vertical_alignment: VerticalAlignment::Center, }; - vec![checkbox_border, checkbox_box, check, label] + vec![checkbox, check, label] } else { - vec![checkbox_border, checkbox_box, label] + vec![checkbox, label] }, }, if is_mouse_over { diff --git a/wgpu/src/renderer/widget/container.rs b/wgpu/src/renderer/widget/container.rs index 29c709f8..18908571 100644 --- a/wgpu/src/renderer/widget/container.rs +++ b/wgpu/src/renderer/widget/container.rs @@ -1,5 +1,5 @@ use crate::{container, defaults, Defaults, Primitive, Renderer}; -use iced_native::{Element, Layout, Point, Rectangle}; +use iced_native::{Color, Element, Layout, Point, Rectangle}; impl iced_native::container::Renderer for Renderer { type Style = Box; @@ -31,6 +31,8 @@ impl iced_native::container::Renderer for Renderer { bounds, background, border_radius: style.border_radius, + border_width: 0, + border_color: Color::TRANSPARENT, }; ( diff --git a/wgpu/src/renderer/widget/radio.rs b/wgpu/src/renderer/widget/radio.rs index 3c00a4c2..aa1dbadc 100644 --- a/wgpu/src/renderer/widget/radio.rs +++ b/wgpu/src/renderer/widget/radio.rs @@ -1,5 +1,5 @@ use crate::{Primitive, Renderer}; -use iced_native::{radio, Background, MouseCursor, Rectangle}; +use iced_native::{radio, Background, Color, MouseCursor, Rectangle}; const SIZE: f32 = 28.0; const DOT_SIZE: f32 = SIZE / 2.0; @@ -16,30 +16,20 @@ impl radio::Renderer for Renderer { is_mouse_over: bool, (label, _): Self::Output, ) -> Self::Output { - let (radio_border, radio_box) = ( - Primitive::Quad { - bounds, - background: Background::Color([0.6, 0.6, 0.6].into()), - border_radius: (SIZE / 2.0) as u16, - }, - Primitive::Quad { - bounds: Rectangle { - x: bounds.x + 1.0, - y: bounds.y + 1.0, - width: bounds.width - 2.0, - height: bounds.height - 2.0, - }, - background: Background::Color( - if is_mouse_over { - [0.90, 0.90, 0.90] - } else { - [0.95, 0.95, 0.95] - } - .into(), - ), - border_radius: (SIZE / 2.0 - 1.0) as u16, - }, - ); + 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(), + ), + border_radius: (SIZE / 2.0) as u16, + border_width: 1, + border_color: Color::from_rgb(0.6, 0.6, 0.6), + }; ( Primitive::Group { @@ -53,11 +43,13 @@ impl radio::Renderer for Renderer { }, background: Background::Color([0.3, 0.3, 0.3].into()), border_radius: (DOT_SIZE / 2.0) as u16, + border_width: 0, + border_color: Color::TRANSPARENT, }; - vec![radio_border, radio_box, radio_circle, label] + vec![radio, radio_circle, label] } else { - vec![radio_border, radio_box, label] + vec![radio, label] }, }, if is_mouse_over { diff --git a/wgpu/src/renderer/widget/scrollable.rs b/wgpu/src/renderer/widget/scrollable.rs index 6ef57185..42a4a743 100644 --- a/wgpu/src/renderer/widget/scrollable.rs +++ b/wgpu/src/renderer/widget/scrollable.rs @@ -1,5 +1,7 @@ use crate::{Primitive, Renderer}; -use iced_native::{scrollable, Background, MouseCursor, Rectangle, Vector}; +use iced_native::{ + scrollable, Background, Color, MouseCursor, Rectangle, Vector, +}; const SCROLLBAR_WIDTH: u16 = 10; const SCROLLBAR_MARGIN: u16 = 2; @@ -68,6 +70,8 @@ impl scrollable::Renderer for Renderer { [0.0, 0.0, 0.0, 0.7].into(), ), border_radius: 5, + border_width: 0, + border_color: Color::TRANSPARENT, }; if is_mouse_over_scrollbar || state.is_scroller_grabbed() { @@ -83,6 +87,8 @@ impl scrollable::Renderer for Renderer { [0.0, 0.0, 0.0, 0.3].into(), ), border_radius: 5, + border_width: 0, + border_color: Color::TRANSPARENT, }; Primitive::Group { diff --git a/wgpu/src/renderer/widget/slider.rs b/wgpu/src/renderer/widget/slider.rs index c73a4e56..386decb5 100644 --- a/wgpu/src/renderer/widget/slider.rs +++ b/wgpu/src/renderer/widget/slider.rs @@ -29,8 +29,10 @@ impl slider::Renderer for Renderer { width: bounds.width, height: 2.0, }, - background: Color::from_rgb(0.6, 0.6, 0.6).into(), + background: Background::Color([0.6, 0.6, 0.6, 0.5].into()), border_radius: 0, + border_width: 0, + border_color: Color::TRANSPARENT, }, Primitive::Quad { bounds: Rectangle { @@ -41,6 +43,8 @@ impl slider::Renderer for Renderer { }, background: Background::Color(Color::WHITE), border_radius: 0, + border_width: 0, + border_color: Color::TRANSPARENT, }, ); @@ -49,41 +53,31 @@ impl slider::Renderer for Renderer { let handle_offset = (bounds.width - HANDLE_WIDTH) * ((value - range_start) / (range_end - range_start).max(1.0)); - let (handle_border, handle) = ( - Primitive::Quad { - bounds: Rectangle { - x: bounds.x + handle_offset.round() - 1.0, - y: rail_y - HANDLE_HEIGHT / 2.0 - 1.0, - width: HANDLE_WIDTH + 2.0, - height: HANDLE_HEIGHT + 2.0, - }, - background: Color::from_rgb(0.6, 0.6, 0.6).into(), - border_radius: 5, - }, - Primitive::Quad { - bounds: Rectangle { - x: bounds.x + handle_offset.round(), - y: rail_y - HANDLE_HEIGHT / 2.0, - width: HANDLE_WIDTH, - height: HANDLE_HEIGHT, - }, - background: Background::Color( - if is_dragging { - [0.85, 0.85, 0.85] - } else if is_mouse_over { - [0.90, 0.90, 0.90] - } else { - [0.95, 0.95, 0.95] - } - .into(), - ), - border_radius: 4, + let handle = Primitive::Quad { + bounds: Rectangle { + x: bounds.x + handle_offset.round(), + y: rail_y - HANDLE_HEIGHT / 2.0, + width: HANDLE_WIDTH, + height: HANDLE_HEIGHT, }, - ); + background: Background::Color( + if is_dragging { + [0.85, 0.85, 0.85] + } else if is_mouse_over { + [0.90, 0.90, 0.90] + } else { + [0.95, 0.95, 0.95] + } + .into(), + ), + border_radius: 4, + border_width: 1, + border_color: Color::from_rgb(0.6, 0.6, 0.6), + }; ( Primitive::Group { - primitives: vec![rail_top, rail_bottom, handle_border, handle], + primitives: vec![rail_top, rail_bottom, handle], }, if is_dragging { MouseCursor::Grabbing diff --git a/wgpu/src/renderer/widget/text_input.rs b/wgpu/src/renderer/widget/text_input.rs index 929f94db..cf3a31ab 100644 --- a/wgpu/src/renderer/widget/text_input.rs +++ b/wgpu/src/renderer/widget/text_input.rs @@ -64,28 +64,17 @@ impl text_input::Renderer for Renderer { ) -> Self::Output { let is_mouse_over = bounds.contains(cursor_position); - let border = Primitive::Quad { - bounds, - background: Background::Color( - if is_mouse_over || state.is_focused() { - [0.5, 0.5, 0.5] - } else { - [0.7, 0.7, 0.7] - } - .into(), - ), - border_radius: 5, - }; - let input = Primitive::Quad { - bounds: Rectangle { - x: bounds.x + 1.0, - y: bounds.y + 1.0, - width: bounds.width - 2.0, - height: bounds.height - 2.0, - }, + bounds, background: Background::Color(Color::WHITE), - border_radius: 4, + border_radius: 5, + border_width: 1, + border_color: if is_mouse_over || state.is_focused() { + [0.5, 0.5, 0.5] + } else { + [0.7, 0.7, 0.7] + } + .into(), }; let text = value.to_string(); @@ -130,6 +119,8 @@ impl text_input::Renderer for Renderer { }, background: Background::Color(Color::BLACK), border_radius: 0, + border_width: 0, + border_color: Color::TRANSPARENT, }; ( @@ -150,7 +141,7 @@ impl text_input::Renderer for Renderer { ( Primitive::Group { - primitives: vec![border, input, contents], + primitives: vec![input, contents], }, if is_mouse_over { MouseCursor::Text diff --git a/wgpu/src/shader/quad.frag b/wgpu/src/shader/quad.frag index 2ee77e71..ad1af1ad 100644 --- a/wgpu/src/shader/quad.frag +++ b/wgpu/src/shader/quad.frag @@ -1,14 +1,17 @@ #version 450 layout(location = 0) in vec4 v_Color; -layout(location = 1) in vec2 v_Pos; -layout(location = 2) in vec2 v_Scale; -layout(location = 3) in float v_BorderRadius; +layout(location = 1) in vec4 v_BorderColor; +layout(location = 2) in vec2 v_Pos; +layout(location = 3) in vec2 v_Scale; +layout(location = 4) in float v_BorderRadius; +layout(location = 5) in float v_BorderWidth; layout(location = 0) out vec4 o_Color; -float rounded(in vec2 frag_coord, in vec2 position, in vec2 size, float radius, float s) +float distance(in vec2 frag_coord, in vec2 position, in vec2 size, float radius) { + // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN vec2 inner_size = size - vec2(radius, radius) * 2.0; vec2 top_left = position + vec2(radius, radius); vec2 bottom_right = top_left + inner_size; @@ -21,13 +24,43 @@ float rounded(in vec2 frag_coord, in vec2 position, in vec2 size, float radius, max(max(top_left_distance.y, bottom_right_distance.y), 0) ); - float d = sqrt(distance.x * distance.x + distance.y * distance.y); - - return 1.0 - smoothstep(radius - s, radius + s, d); + return sqrt(distance.x * distance.x + distance.y * distance.y); } void main() { - float radius_alpha = rounded(gl_FragCoord.xy, v_Pos, v_Scale, v_BorderRadius, 0.5); + vec4 mixed_color; + + // TODO: Remove branching (?) + if(v_BorderWidth > 0) { + float internal_border = max(v_BorderRadius - v_BorderWidth, 0); + + float internal_distance = distance( + gl_FragCoord.xy, + v_Pos + vec2(v_BorderWidth), + v_Scale - vec2(v_BorderWidth * 2.0), + internal_border + ); + + float border_mix = smoothstep( + max(internal_border - 0.5, 0.0), + internal_border + 0.5, + internal_distance + ); + + mixed_color = mix(v_Color, v_BorderColor, border_mix); + } else { + mixed_color = v_Color; + } + + float d = distance( + gl_FragCoord.xy, + v_Pos, + v_Scale, + v_BorderRadius + ); + + float radius_alpha = + 1.0 - smoothstep(max(v_BorderRadius - 0.5, 0), v_BorderRadius + 0.5, d); - o_Color = vec4(v_Color.xyz, v_Color.w * radius_alpha); + o_Color = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); } diff --git a/wgpu/src/shader/quad.frag.spv b/wgpu/src/shader/quad.frag.spv index 17bd8f46..519f5f01 100644 Binary files a/wgpu/src/shader/quad.frag.spv and b/wgpu/src/shader/quad.frag.spv differ diff --git a/wgpu/src/shader/quad.vert b/wgpu/src/shader/quad.vert index 539755cb..1d9a4fd2 100644 --- a/wgpu/src/shader/quad.vert +++ b/wgpu/src/shader/quad.vert @@ -4,7 +4,9 @@ layout(location = 0) in vec2 v_Pos; layout(location = 1) in vec2 i_Pos; layout(location = 2) in vec2 i_Scale; layout(location = 3) in vec4 i_Color; -layout(location = 4) in float i_BorderRadius; +layout(location = 4) in vec4 i_BorderColor; +layout(location = 5) in float i_BorderRadius; +layout(location = 6) in float i_BorderWidth; layout (set = 0, binding = 0) uniform Globals { mat4 u_Transform; @@ -12,9 +14,11 @@ layout (set = 0, binding = 0) uniform Globals { }; layout(location = 0) out vec4 o_Color; -layout(location = 1) out vec2 o_Pos; -layout(location = 2) out vec2 o_Scale; -layout(location = 3) out float o_BorderRadius; +layout(location = 1) out vec4 o_BorderColor; +layout(location = 2) out vec2 o_Pos; +layout(location = 3) out vec2 o_Scale; +layout(location = 4) out float o_BorderRadius; +layout(location = 5) out float o_BorderWidth; void main() { vec2 p_Pos = i_Pos * u_Scale; @@ -28,9 +32,11 @@ void main() { ); o_Color = i_Color; + o_BorderColor = i_BorderColor; o_Pos = p_Pos; o_Scale = p_Scale; o_BorderRadius = i_BorderRadius * u_Scale; + o_BorderWidth = i_BorderWidth * u_Scale; gl_Position = u_Transform * i_Transform * vec4(v_Pos, 0.0, 1.0); } diff --git a/wgpu/src/shader/quad.vert.spv b/wgpu/src/shader/quad.vert.spv index 9050adfb..7059b51b 100644 Binary files a/wgpu/src/shader/quad.vert.spv and b/wgpu/src/shader/quad.vert.spv differ -- cgit From e1062a02d17f5748e4809b76ddcc132f1c912886 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 Jan 2020 14:16:10 +0100 Subject: Move styling to a brand new `iced_style` crate --- Cargo.toml | 1 + src/native.rs | 14 +++----- style/Cargo.toml | 14 ++++++++ style/src/button.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++ style/src/container.rs | 41 +++++++++++++++++++++++ style/src/lib.rs | 2 ++ wgpu/Cargo.toml | 1 + wgpu/src/widget/button.rs | 77 +----------------------------------------- wgpu/src/widget/container.rs | 43 +++++------------------- winit/Cargo.toml | 3 ++ 10 files changed, 154 insertions(+), 121 deletions(-) create mode 100644 style/Cargo.toml create mode 100644 style/src/button.rs create mode 100644 style/src/container.rs create mode 100644 style/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index ebd6412e..79a9007a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ maintenance = { status = "actively-developed" } members = [ "core", "native", + "style", "web", "wgpu", "winit", diff --git a/src/native.rs b/src/native.rs index d5c9349a..54afee4b 100644 --- a/src/native.rs +++ b/src/native.rs @@ -22,7 +22,7 @@ pub mod widget { //! //! [`TextInput`]: text_input/struct.TextInput.html //! [`text_input::State`]: text_input/struct.State.html - pub use iced_wgpu::button; + pub use iced_wgpu::widget::*; pub mod scrollable { //! Navigate an endless amount of content with a scrollbar. @@ -73,8 +73,9 @@ pub mod widget { #[doc(no_inline)] pub use { - button::Button, image::Image, scrollable::Scrollable, slider::Slider, - svg::Svg, text_input::TextInput, + button::Button, container::Container, image::Image, + scrollable::Scrollable, slider::Slider, svg::Svg, + text_input::TextInput, }; /// A container that distributes its contents vertically. @@ -88,13 +89,6 @@ pub mod widget { /// This is an alias of an `iced_native` row with a default `Renderer`. pub type Row<'a, Message> = iced_winit::Row<'a, Message, iced_wgpu::Renderer>; - - /// An element decorating some content. - /// - /// This is an alias of an `iced_native` container with a default - /// `Renderer`. - pub type Container<'a, Message> = - iced_winit::Container<'a, Message, iced_wgpu::Renderer>; } #[doc(no_inline)] diff --git a/style/Cargo.toml b/style/Cargo.toml new file mode 100644 index 00000000..5928c60d --- /dev/null +++ b/style/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "iced_style" +version = "0.1.0-alpha" +authors = ["Héctor Ramón Jiménez "] +edition = "2018" +description = "The default set of styles of Iced" +license = "MIT" +repository = "https://github.com/hecrj/iced" +documentation = "https://docs.rs/iced_style" +keywords = ["gui", "ui", "graphics", "interface", "widgets"] +categories = ["gui"] + +[dependencies] +iced_core = { version = "0.1.0", path = "../core" } diff --git a/style/src/button.rs b/style/src/button.rs new file mode 100644 index 00000000..42286897 --- /dev/null +++ b/style/src/button.rs @@ -0,0 +1,79 @@ +//! Allow your users to perform actions by pressing a button. +use iced_core::{Background, Color}; + +/// The appearance of a button. +#[derive(Debug)] +pub struct Style { + pub shadow_offset: f32, + pub background: Option, + pub border_radius: u16, + pub text_color: Color, +} + +/// A set of rules that dictate the style of a button. +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 { + let active = self.active(); + + Style { + shadow_offset: 0.0, + background: active.background.map(|background| match background { + Background::Color(color) => Background::Color(Color { + a: color.a * 0.5, + ..color + }), + }), + text_color: Color { + a: active.text_color.a * 0.5, + ..active.text_color + }, + ..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, + text_color: Color::BLACK, + } + } +} + +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/container.rs b/style/src/container.rs new file mode 100644 index 00000000..756ea0f9 --- /dev/null +++ b/style/src/container.rs @@ -0,0 +1,41 @@ +//! Decorate content and apply alignment. +use iced_core::{Background, Color}; + +/// The appearance of a container. +#[derive(Debug, Clone, Copy)] +pub struct Style { + pub text_color: Option, + pub background: Option, + pub border_radius: u16, +} + +/// A set of rules that dictate the style of a container. +pub trait StyleSheet { + /// Produces the style of a container. + fn style(&self) -> Style { + Style { + text_color: None, + background: None, + border_radius: 0, + } + } +} + +struct Default; + +impl StyleSheet for Default {} + +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 new file mode 100644 index 00000000..c6f34301 --- /dev/null +++ b/style/src/lib.rs @@ -0,0 +1,2 @@ +pub mod button; +pub mod container; diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index bb241914..19d41bba 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -12,6 +12,7 @@ svg = ["resvg"] [dependencies] iced_native = { version = "0.1.0", path = "../native" } +iced_style = { version = "0.1.0-alpha", path = "../style" } wgpu = "0.4" glyph_brush = "0.6" wgpu_glyph = { version = "0.7", git = "https://github.com/hecrj/wgpu_glyph", branch = "fix/font-load-panic" } diff --git a/wgpu/src/widget/button.rs b/wgpu/src/widget/button.rs index 2c4e174f..b738c55e 100644 --- a/wgpu/src/widget/button.rs +++ b/wgpu/src/widget/button.rs @@ -5,86 +5,11 @@ //! [`Button`]: type.Button.html //! [`State`]: struct.State.html use crate::Renderer; -use iced_native::{Background, Color}; pub use iced_native::button::State; +pub use iced_style::button::{Style, StyleSheet}; /// 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, - pub border_radius: u16, - pub text_color: Color, -} - -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 { - let active = self.active(); - - Style { - shadow_offset: 0.0, - background: active.background.map(|background| match background { - Background::Color(color) => Background::Color(Color { - a: color.a * 0.5, - ..color - }), - }), - text_color: Color { - a: active.text_color.a * 0.5, - ..active.text_color - }, - ..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, - text_color: Color::BLACK, - } - } -} - -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/container.rs b/wgpu/src/widget/container.rs index 1fc0ec98..9a93a246 100644 --- a/wgpu/src/widget/container.rs +++ b/wgpu/src/widget/container.rs @@ -1,37 +1,10 @@ -use iced_native::{Background, Color}; +//! Decorate content and apply alignment. +use crate::Renderer; -#[derive(Debug, Clone, Copy)] -pub struct Style { - pub text_color: Option, - pub background: Option, - pub border_radius: u16, -} +pub use iced_style::container::{Style, StyleSheet}; -pub trait StyleSheet { - fn style(&self) -> Style { - Style { - text_color: None, - background: None, - border_radius: 0, - } - } -} - -struct Default; - -impl StyleSheet for Default {} - -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) - } -} +/// An element decorating some content. +/// +/// This is an alias of an `iced_native` container with a default +/// `Renderer`. +pub type Container<'a, Message> = iced_native::Container<'a, Message, Renderer>; diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 60e3f2d0..5727f8cf 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -6,6 +6,9 @@ edition = "2018" description = "A winit runtime for Iced" license = "MIT" repository = "https://github.com/hecrj/iced" +documentation = "https://docs.rs/iced_winit" +keywords = ["gui", "ui", "graphics", "interface", "widgets"] +categories = ["gui"] [features] debug = [] -- cgit From d96ced8e2da703117a43399110ef2b8fa21a7546 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 Jan 2020 17:49:48 +0100 Subject: Allow configuration of default font --- native/src/renderer/windowed.rs | 4 +++- src/application.rs | 7 ++++++- src/settings.rs | 4 ++++ wgpu/src/lib.rs | 2 ++ wgpu/src/renderer.rs | 13 ++++++++----- wgpu/src/settings.rs | 4 ++++ wgpu/src/text.rs | 11 +++++++---- winit/src/application.rs | 8 +++++--- 8 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 wgpu/src/settings.rs diff --git a/native/src/renderer/windowed.rs b/native/src/renderer/windowed.rs index 89f80bbe..30e08cb8 100644 --- a/native/src/renderer/windowed.rs +++ b/native/src/renderer/windowed.rs @@ -4,13 +4,15 @@ use raw_window_handle::HasRawWindowHandle; /// A renderer that can target windows. pub trait Windowed: super::Renderer + Sized { + type Settings: Default; + /// The type of target. type Target: Target; /// Creates a new [`Windowed`] renderer. /// /// [`Windowed`]: trait.Windowed.html - fn new() -> Self; + fn new(settings: Self::Settings) -> Self; /// Performs the drawing operations described in the output on the given /// target. diff --git a/src/application.rs b/src/application.rs index a7e826fb..7dd76774 100644 --- a/src/application.rs +++ b/src/application.rs @@ -151,7 +151,12 @@ pub trait Application: Sized { Self: 'static, { #[cfg(not(target_arch = "wasm32"))] - as iced_winit::Application>::run(_settings.into()); + as iced_winit::Application>::run( + _settings.into(), + iced_wgpu::Settings { + default_font: _settings.default_font, + }, + ); #[cfg(target_arch = "wasm32")] as iced_web::Application>::run(); diff --git a/src/settings.rs b/src/settings.rs index 4ae18a14..b01e6fc8 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -15,6 +15,9 @@ pub struct Settings { /// /// [`Color`]: ../struct.Color.html pub background: Color, + + // TODO: Add `name` for web compatibility + pub default_font: Option<&'static [u8]>, } impl Default for Settings { @@ -22,6 +25,7 @@ impl Default for Settings { Settings { window: Window::default(), background: Color::WHITE, + default_font: None, } } } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 786b6872..80ebc2a7 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -31,12 +31,14 @@ mod image; mod primitive; mod quad; mod renderer; +mod settings; mod text; mod transformation; pub use defaults::Defaults; pub use primitive::Primitive; pub use renderer::{Renderer, Target}; +pub use settings::Settings; #[doc(no_inline)] pub use widget::*; diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index efda046b..7f0f0b89 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -1,5 +1,6 @@ use crate::{ - image, quad, text, Defaults, Image, Primitive, Quad, Transformation, + image, quad, text, Defaults, Image, Primitive, Quad, Settings, + Transformation, }; use iced_native::{ renderer::{Debugger, Windowed}, @@ -49,7 +50,7 @@ impl<'a> Layer<'a> { } impl Renderer { - fn new() -> Self { + fn new(settings: Settings) -> Self { let adapter = Adapter::request(&RequestAdapterOptions { power_preference: PowerPreference::Default, backends: BackendBit::all(), @@ -63,7 +64,8 @@ impl Renderer { limits: Limits { max_bind_groups: 2 }, }); - let text_pipeline = text::Pipeline::new(&mut device); + let text_pipeline = + text::Pipeline::new(&mut device, settings.default_font); let quad_pipeline = quad::Pipeline::new(&mut device); let image_pipeline = image::Pipeline::new(&mut device); @@ -432,10 +434,11 @@ impl iced_native::Renderer for Renderer { } impl Windowed for Renderer { + type Settings = Settings; type Target = Target; - fn new() -> Self { - Self::new() + fn new(settings: Settings) -> Self { + Self::new(settings) } fn draw>( diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs new file mode 100644 index 00000000..c6d8369b --- /dev/null +++ b/wgpu/src/settings.rs @@ -0,0 +1,4 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct Settings { + pub default_font: Option<&'static [u8]>, +} diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 880ad1a6..ab9a2f71 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -22,13 +22,16 @@ pub struct Pipeline { } impl Pipeline { - pub fn new(device: &mut wgpu::Device) -> Self { + pub fn new(device: &mut wgpu::Device, default_font: Option<&[u8]>) -> Self { // TODO: Font customization let font_source = font::Source::new(); - let default_font = font_source - .load(&[font::Family::SansSerif, font::Family::Serif]) - .unwrap_or_else(|_| FALLBACK_FONT.to_vec()); + let default_font = + default_font.map(|slice| slice.to_vec()).unwrap_or_else(|| { + font_source + .load(&[font::Family::SansSerif, font::Family::Serif]) + .unwrap_or_else(|_| FALLBACK_FONT.to_vec()) + }); let load_glyph_brush = |font: Vec| { let builder = diff --git a/winit/src/application.rs b/winit/src/application.rs index 02fa3780..d16c209c 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -81,8 +81,10 @@ pub trait Application: Sized { /// It should probably be that last thing you call in your `main` function. /// /// [`Application`]: trait.Application.html - fn run(settings: Settings) - where + fn run( + settings: Settings, + renderer_settings: ::Settings, + ) where Self: 'static, { use winit::{ @@ -140,7 +142,7 @@ pub trait Application: Sized { let mut resized = false; let clipboard = Clipboard::new(&window); - let mut renderer = Self::Renderer::new(); + let mut renderer = Self::Renderer::new(renderer_settings); let mut target = { let (width, height) = to_physical(size, dpi); -- cgit From 5af4159848341b14f6ff9ae14a9a222d8d8b0fb8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 Jan 2020 18:26:49 +0100 Subject: Draft basic styling for `TextInput` --- native/src/renderer/null.rs | 3 ++ native/src/widget/container.rs | 2 +- native/src/widget/text_input.rs | 33 ++++++++++++---- src/native.rs | 10 ----- style/src/container.rs | 10 +++-- style/src/lib.rs | 1 + style/src/text_input.rs | 72 ++++++++++++++++++++++++++++++++++ wgpu/src/renderer/widget/text_input.rs | 32 +++++++++------ wgpu/src/widget.rs | 1 + wgpu/src/widget/text_input.rs | 15 +++++++ 10 files changed, 143 insertions(+), 36 deletions(-) create mode 100644 style/src/text_input.rs create mode 100644 wgpu/src/widget/text_input.rs diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 56d7e472..96aa132c 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -97,6 +97,8 @@ impl scrollable::Renderer for Null { } impl text_input::Renderer for Null { + type Style = (); + fn default_size(&self) -> u16 { 20 } @@ -124,6 +126,7 @@ impl text_input::Renderer for Null { _placeholder: &str, _value: &text_input::Value, _state: &text_input::State, + _style: &Self::Style, ) -> Self::Output { } } diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 75c2d6b3..abe83264 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -94,7 +94,7 @@ where self } - /// Sets the style the [`Container`]. + /// Sets the style of the [`Container`]. /// /// [`Container`]: struct.Container.html pub fn style(mut self, style: impl Into) -> Self { diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index e2114f00..9952a9bf 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -15,8 +15,9 @@ use unicode_segmentation::UnicodeSegmentation; /// /// # Example /// ``` -/// # use iced_native::{text_input, TextInput}; +/// # use iced_native::{text_input, renderer::Null}; /// # +/// # pub type TextInput<'a, Message> = iced_native::TextInput<'a, Message, Null>; /// #[derive(Debug, Clone)] /// enum Message { /// TextInputChanged(String), @@ -35,7 +36,7 @@ use unicode_segmentation::UnicodeSegmentation; /// ``` /// ![Text input drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true) #[allow(missing_debug_implementations)] -pub struct TextInput<'a, Message> { +pub struct TextInput<'a, Message, Renderer: self::Renderer> { state: &'a mut State, placeholder: String, value: Value, @@ -46,9 +47,10 @@ pub struct TextInput<'a, Message> { size: Option, on_change: Box Message>, on_submit: Option, + style: Renderer::Style, } -impl<'a, Message> TextInput<'a, Message> { +impl<'a, Message, Renderer: self::Renderer> TextInput<'a, Message, Renderer> { /// Creates a new [`TextInput`]. /// /// It expects: @@ -79,6 +81,7 @@ impl<'a, Message> TextInput<'a, Message> { size: None, on_change: Box::new(on_change), on_submit: None, + style: Renderer::Style::default(), } } @@ -130,11 +133,20 @@ impl<'a, Message> TextInput<'a, Message> { self.on_submit = Some(message); self } + + /// Sets the style of the [`TextInput`]. + /// + /// [`TextInput`]: struct.TextInput.html + pub fn style(mut self, style: impl Into) -> Self { + self.style = style.into(); + self + } } -impl<'a, Message, Renderer> Widget for TextInput<'a, Message> +impl<'a, Message, Renderer> Widget + for TextInput<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: 'static + self::Renderer, Message: Clone + std::fmt::Debug, { fn width(&self) -> Length { @@ -359,6 +371,7 @@ where &self.placeholder, &self.value.secure(), &self.state, + &self.style, ) } else { renderer.draw( @@ -369,6 +382,7 @@ where &self.placeholder, &self.value, &self.state, + &self.style, ) } } @@ -376,7 +390,7 @@ where fn hash_layout(&self, state: &mut Hasher) { use std::{any::TypeId, hash::Hash}; - TypeId::of::>().hash(state); + TypeId::of::>().hash(state); self.width.hash(state); self.max_width.hash(state); @@ -393,6 +407,8 @@ where /// [`TextInput`]: struct.TextInput.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + type Style: Default; + /// Returns the default size of the text of the [`TextInput`]. /// /// [`TextInput`]: struct.TextInput.html @@ -441,17 +457,18 @@ pub trait Renderer: crate::Renderer + Sized { placeholder: &str, value: &Value, state: &State, + style: &Self::Style, ) -> Self::Output; } -impl<'a, Message, Renderer> From> +impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Renderer: 'static + self::Renderer, Message: 'static + Clone + std::fmt::Debug, { fn from( - text_input: TextInput<'a, Message>, + text_input: TextInput<'a, Message, Renderer>, ) -> Element<'a, Message, Renderer> { Element::new(text_input) } diff --git a/src/native.rs b/src/native.rs index 54afee4b..3535ab6a 100644 --- a/src/native.rs +++ b/src/native.rs @@ -38,16 +38,6 @@ pub mod widget { pub use iced_winit::scrollable::State; } - pub mod text_input { - //! Ask for information using text fields. - //! - //! A [`TextInput`] has some local [`State`]. - //! - //! [`TextInput`]: struct.TextInput.html - //! [`State`]: struct.State.html - pub use iced_winit::text_input::{State, TextInput}; - } - pub mod slider { //! Display an interactive selector of a single value from a range of //! values. diff --git a/style/src/container.rs b/style/src/container.rs index 756ea0f9..a9cd3ccc 100644 --- a/style/src/container.rs +++ b/style/src/container.rs @@ -12,6 +12,12 @@ pub struct Style { /// A set of rules that dictate the style of a container. pub trait StyleSheet { /// Produces the style of a container. + fn style(&self) -> Style; +} + +struct Default; + +impl StyleSheet for Default { fn style(&self) -> Style { Style { text_color: None, @@ -21,10 +27,6 @@ pub trait StyleSheet { } } -struct Default; - -impl StyleSheet for Default {} - impl std::default::Default for Box { fn default() -> Self { Box::new(Default) diff --git a/style/src/lib.rs b/style/src/lib.rs index c6f34301..1d7e57c4 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -1,2 +1,3 @@ pub mod button; pub mod container; +pub mod text_input; diff --git a/style/src/text_input.rs b/style/src/text_input.rs new file mode 100644 index 00000000..75f427a7 --- /dev/null +++ b/style/src/text_input.rs @@ -0,0 +1,72 @@ +//! Display fields that can be filled with text. +use iced_core::{Background, Color}; + +/// The appearance of a text input. +#[derive(Debug, Clone, Copy)] +pub struct Style { + pub background: Background, + pub border_radius: u16, + pub border_width: u16, + pub border_color: Color, +} + +/// A set of rules that dictate the style of a text input. +pub trait StyleSheet { + /// Produces the style of an active text input. + fn active(&self) -> Style; + + /// Produces the style of a focused text input. + fn focused(&self) -> Style; + + /// Produces the style of an hovered text input. + fn hovered(&self) -> Style { + self.focused() + } + + fn placeholder_color(&self) -> Color; + + fn value_color(&self) -> Color; +} + +struct Default; + +impl StyleSheet for Default { + fn active(&self) -> Style { + Style { + background: Background::Color(Color::WHITE), + border_radius: 5, + border_width: 1, + border_color: Color::from_rgb(0.7, 0.7, 0.7), + } + } + + fn focused(&self) -> Style { + Style { + border_color: Color::from_rgb(0.5, 0.5, 0.5), + ..self.active() + } + } + + fn placeholder_color(&self) -> Color { + Color::from_rgb(0.7, 0.7, 0.7) + } + + fn value_color(&self) -> Color { + Color::from_rgb(0.3, 0.3, 0.3) + } +} + +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/text_input.rs b/wgpu/src/renderer/widget/text_input.rs index cf3a31ab..8b774a48 100644 --- a/wgpu/src/renderer/widget/text_input.rs +++ b/wgpu/src/renderer/widget/text_input.rs @@ -1,4 +1,4 @@ -use crate::{Primitive, Renderer}; +use crate::{text_input::StyleSheet, Primitive, Renderer}; use iced_native::{ text_input, Background, Color, Font, HorizontalAlignment, MouseCursor, @@ -7,6 +7,8 @@ use iced_native::{ use std::f32; impl text_input::Renderer for Renderer { + type Style = Box; + fn default_size(&self) -> u16 { // TODO: Make this configurable 20 @@ -61,20 +63,24 @@ impl text_input::Renderer for Renderer { placeholder: &str, value: &text_input::Value, state: &text_input::State, + style_sheet: &Self::Style, ) -> Self::Output { let is_mouse_over = bounds.contains(cursor_position); + let style = if state.is_focused() { + style_sheet.focused() + } else if is_mouse_over { + style_sheet.hovered() + } else { + style_sheet.active() + }; + let input = Primitive::Quad { bounds, - background: Background::Color(Color::WHITE), - border_radius: 5, - border_width: 1, - border_color: if is_mouse_over || state.is_focused() { - [0.5, 0.5, 0.5] - } else { - [0.7, 0.7, 0.7] - } - .into(), + background: style.background, + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, }; let text = value.to_string(); @@ -86,9 +92,9 @@ impl text_input::Renderer for Renderer { text.clone() }, color: if text.is_empty() { - [0.7, 0.7, 0.7] + style_sheet.placeholder_color() } else { - [0.3, 0.3, 0.3] + style_sheet.value_color() } .into(), font: Font::Default, @@ -117,7 +123,7 @@ impl text_input::Renderer for Renderer { width: 1.0, height: text_bounds.height, }, - background: Background::Color(Color::BLACK), + background: Background::Color(style_sheet.value_color()), border_radius: 0, border_width: 0, border_color: Color::TRANSPARENT, diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs index c6f34301..1d7e57c4 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -1,2 +1,3 @@ pub mod button; pub mod container; +pub mod text_input; diff --git a/wgpu/src/widget/text_input.rs b/wgpu/src/widget/text_input.rs new file mode 100644 index 00000000..260fe3a6 --- /dev/null +++ b/wgpu/src/widget/text_input.rs @@ -0,0 +1,15 @@ +//! Display fields that can be filled with text. +//! +//! A [`TextInput`] has some local [`State`]. +//! +//! [`TextInput`]: struct.TextInput.html +//! [`State`]: struct.State.html +use crate::Renderer; + +pub use iced_native::text_input::State; +pub use iced_style::text_input::{Style, StyleSheet}; + +/// A field that can be filled with text. +/// +/// This is an alias of an `iced_native` text input with an `iced_wgpu::Renderer`. +pub type TextInput<'a, Message> = iced_native::TextInput<'a, Message, Renderer>; -- cgit From 8d6f86b317303c06a0daf1ca3ce91c29670dd674 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 5 Jan 2020 18:11:54 +0100 Subject: Remove `background` from `Settings` --- native/src/renderer/windowed.rs | 3 +-- src/settings.rs | 8 -------- wgpu/src/renderer.rs | 18 ++++++------------ winit/src/application.rs | 8 ++------ winit/src/settings/mod.rs | 6 ------ 5 files changed, 9 insertions(+), 34 deletions(-) diff --git a/native/src/renderer/windowed.rs b/native/src/renderer/windowed.rs index 30e08cb8..c3266e6c 100644 --- a/native/src/renderer/windowed.rs +++ b/native/src/renderer/windowed.rs @@ -1,4 +1,4 @@ -use crate::{Color, MouseCursor}; +use crate::MouseCursor; use raw_window_handle::HasRawWindowHandle; @@ -21,7 +21,6 @@ pub trait Windowed: super::Renderer + Sized { /// top of the GUI on most scenarios. fn draw>( &mut self, - clear_color: Color, output: &Self::Output, overlay: &[T], target: &mut Self::Target, diff --git a/src/settings.rs b/src/settings.rs index b01e6fc8..b725ef9f 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,5 +1,4 @@ //! Configure your application. -use crate::Color; /// The settings of an application. #[derive(Debug, Clone, Copy, PartialEq)] @@ -11,11 +10,6 @@ pub struct Settings { /// [`Window`]: struct.Window.html pub window: Window, - /// The default background [`Color`] of the application - /// - /// [`Color`]: ../struct.Color.html - pub background: Color, - // TODO: Add `name` for web compatibility pub default_font: Option<&'static [u8]>, } @@ -24,7 +18,6 @@ impl Default for Settings { fn default() -> Settings { Settings { window: Window::default(), - background: Color::WHITE, default_font: None, } } @@ -63,7 +56,6 @@ impl From for iced_winit::Settings { decorations: settings.window.decorations, platform_specific: Default::default(), }, - background: settings.background, } } } diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index 7f0f0b89..8f0b2020 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -80,7 +80,6 @@ impl Renderer { fn draw>( &mut self, - clear_color: Color, (primitive, mouse_cursor): &(Primitive, MouseCursor), overlay: &[T], target: &mut Target, @@ -102,15 +101,11 @@ impl Renderer { resolve_target: None, load_op: wgpu::LoadOp::Clear, store_op: wgpu::StoreOp::Store, - clear_color: { - let [r, g, b, a] = clear_color.into_linear(); - - wgpu::Color { - r: f64::from(r), - g: f64::from(g), - b: f64::from(b), - a: f64::from(a), - } + clear_color: wgpu::Color { + r: 1.0, + g: 1.0, + b: 1.0, + a: 1.0, }, }], depth_stencil_attachment: None, @@ -443,12 +438,11 @@ impl Windowed for Renderer { fn draw>( &mut self, - clear_color: Color, output: &Self::Output, overlay: &[T], target: &mut Target, ) -> MouseCursor { - self.draw(clear_color, output, overlay, target) + self.draw(output, overlay, target) } } diff --git a/winit/src/application.rs b/winit/src/application.rs index d16c209c..da943660 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -281,12 +281,8 @@ pub trait Application: Sized { resized = false; } - let new_mouse_cursor = renderer.draw( - settings.background, - &primitive, - &debug.overlay(), - &mut target, - ); + let new_mouse_cursor = + renderer.draw(&primitive, &debug.overlay(), &mut target); debug.render_finished(); diff --git a/winit/src/settings/mod.rs b/winit/src/settings/mod.rs index 0384df32..b2290b46 100644 --- a/winit/src/settings/mod.rs +++ b/winit/src/settings/mod.rs @@ -1,6 +1,4 @@ //! Configure your application. -use crate::Color; - #[cfg(target_os = "windows")] #[path = "windows.rs"] mod platform; @@ -17,16 +15,12 @@ pub struct Settings { /// /// [`Window`]: struct.Window.html pub window: Window, - - /// The default background color of the application - pub background: Color, } impl Default for Settings { fn default() -> Settings { Settings { window: Window::default(), - background: Color::WHITE, } } } -- cgit From 2116fbb3c2412030a676c60d65784b9dfa467a0a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 5 Jan 2020 18:38:03 +0100 Subject: Add border styling to `Container` --- style/src/container.rs | 4 ++++ wgpu/src/renderer/widget/container.rs | 37 ++++++++++++++++++----------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/style/src/container.rs b/style/src/container.rs index a9cd3ccc..484fdfda 100644 --- a/style/src/container.rs +++ b/style/src/container.rs @@ -7,6 +7,8 @@ pub struct Style { pub text_color: Option, pub background: Option, pub border_radius: u16, + pub border_width: u16, + pub border_color: Color, } /// A set of rules that dictate the style of a container. @@ -23,6 +25,8 @@ impl StyleSheet for Default { text_color: None, background: None, border_radius: 0, + border_width: 0, + border_color: Color::TRANSPARENT, } } } diff --git a/wgpu/src/renderer/widget/container.rs b/wgpu/src/renderer/widget/container.rs index 18908571..2d4d1db8 100644 --- a/wgpu/src/renderer/widget/container.rs +++ b/wgpu/src/renderer/widget/container.rs @@ -1,5 +1,5 @@ use crate::{container, defaults, Defaults, Primitive, Renderer}; -use iced_native::{Color, Element, Layout, Point, Rectangle}; +use iced_native::{Background, Color, Element, Layout, Point, Rectangle}; impl iced_native::container::Renderer for Renderer { type Style = Box; @@ -25,24 +25,25 @@ impl iced_native::container::Renderer for Renderer { let (content, mouse_cursor) = content.draw(self, &defaults, content_layout, cursor_position); - match style.background { - Some(background) => { - let quad = Primitive::Quad { - bounds, - background, - border_radius: style.border_radius, - border_width: 0, - border_color: Color::TRANSPARENT, - }; + if style.background.is_some() || style.border_width > 0 { + let quad = Primitive::Quad { + bounds, + background: style + .background + .unwrap_or(Background::Color(Color::TRANSPARENT)), + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, + }; - ( - Primitive::Group { - primitives: vec![quad, content], - }, - mouse_cursor, - ) - } - None => (content, mouse_cursor), + ( + Primitive::Group { + primitives: vec![quad, content], + }, + mouse_cursor, + ) + } else { + (content, mouse_cursor) } } } -- cgit From 1a0effa961344677daf17b4192243423a154f1bf Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 5 Jan 2020 19:29:12 +0100 Subject: Add border and shadow styling to `Button` --- core/src/vector.rs | 12 ++++++++++++ examples/pokedex.rs | 5 +++-- examples/stopwatch.rs | 5 +++-- examples/todos.rs | 20 +++++++------------- examples/tour.rs | 9 +++++---- src/native.rs | 2 +- style/src/button.rs | 29 +++++++++++++++++++++++------ wgpu/src/renderer/widget/button.rs | 8 ++++---- 8 files changed, 58 insertions(+), 32 deletions(-) diff --git a/core/src/vector.rs b/core/src/vector.rs index 7d87343a..1c09ee3e 100644 --- a/core/src/vector.rs +++ b/core/src/vector.rs @@ -31,3 +31,15 @@ where Self::new(self.x + b.x, self.y + b.y) } } + +impl Default for Vector +where + T: Default, +{ + fn default() -> Self { + Self { + x: T::default(), + y: T::default(), + } + } +} diff --git a/examples/pokedex.rs b/examples/pokedex.rs index 35d38251..2a696ffe 100644 --- a/examples/pokedex.rs +++ b/examples/pokedex.rs @@ -225,7 +225,7 @@ fn button<'a>(state: &'a mut button::State, text: &str) -> Button<'a, Message> { } mod style { - use iced::{button, Background, Color}; + use iced::{button, Background, Color, Vector}; pub enum Button { Primary, @@ -238,8 +238,9 @@ mod style { Button::Primary => Color::from_rgb(0.11, 0.42, 0.87), })), border_radius: 12, - shadow_offset: 1.0, + shadow_offset: Vector::new(1.0, 1.0), text_color: Color::WHITE, + ..button::Style::default() } } } diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index 99746609..9b69f7ca 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -180,7 +180,7 @@ mod time { } mod style { - use iced::{button, Background, Color}; + use iced::{button, Background, Color, Vector}; pub enum Button { Primary, @@ -197,8 +197,9 @@ mod style { Button::Destructive => Color::from_rgb(0.8, 0.2, 0.2), })), border_radius: 12, - shadow_offset: 1.0, + shadow_offset: Vector::new(1.0, 1.0), text_color: Color::WHITE, + ..button::Style::default() } } } diff --git a/examples/todos.rs b/examples/todos.rs index ca20183f..1563aad5 100644 --- a/examples/todos.rs +++ b/examples/todos.rs @@ -551,7 +551,7 @@ impl SavedState { } mod style { - use iced::{button, Background, Color}; + use iced::{button, Background, Color, Vector}; pub enum Button { Filter { selected: bool }, @@ -569,31 +569,25 @@ mod style { Color::from_rgb(0.2, 0.2, 0.7), )), border_radius: 10, - shadow_offset: 0.0, text_color: Color::WHITE, + ..button::Style::default() } } else { - button::Style { - background: None, - border_radius: 0, - shadow_offset: 0.0, - text_color: Color::BLACK, - } + button::Style::default() } } Button::Icon => button::Style { - background: None, - border_radius: 0, - shadow_offset: 0.0, text_color: Color::from_rgb(0.5, 0.5, 0.5), + ..button::Style::default() }, Button::Destructive => button::Style { background: Some(Background::Color(Color::from_rgb( 0.8, 0.2, 0.2, ))), border_radius: 5, - shadow_offset: 1.0, text_color: Color::WHITE, + shadow_offset: Vector::new(1.0, 1.0), + ..button::Style::default() }, } } @@ -609,7 +603,7 @@ mod style { } _ => active.text_color, }, - shadow_offset: active.shadow_offset + 1.0, + shadow_offset: active.shadow_offset + Vector::new(0.0, 1.0), ..active } } diff --git a/examples/tour.rs b/examples/tour.rs index d006d397..84e5d516 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -27,7 +27,7 @@ impl Sandbox for Tour { scroll: scrollable::State::new(), back_button: button::State::new(), next_button: button::State::new(), - debug: true, + debug: false, } } @@ -743,7 +743,7 @@ pub enum Layout { } mod style { - use iced::{button, Background, Color}; + use iced::{button, Background, Color, Vector}; pub enum Button { Primary, @@ -758,15 +758,16 @@ mod style { Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5), })), border_radius: 12, - shadow_offset: 1.0, + shadow_offset: Vector::new(1.0, 1.0), text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE), + ..button::Style::default() } } fn hovered(&self) -> button::Style { button::Style { text_color: Color::WHITE, - shadow_offset: 2.0, + shadow_offset: Vector::new(1.0, 2.0), ..self.active() } } diff --git a/src/native.rs b/src/native.rs index 3535ab6a..b7becdc8 100644 --- a/src/native.rs +++ b/src/native.rs @@ -1,6 +1,6 @@ pub use iced_winit::{ Align, Background, Color, Command, Font, HorizontalAlignment, Length, - Space, Subscription, VerticalAlignment, + Space, Subscription, Vector, VerticalAlignment, }; pub mod widget { diff --git a/style/src/button.rs b/style/src/button.rs index 42286897..4a9dac45 100644 --- a/style/src/button.rs +++ b/style/src/button.rs @@ -1,15 +1,30 @@ //! Allow your users to perform actions by pressing a button. -use iced_core::{Background, Color}; +use iced_core::{Background, Color, Vector}; /// The appearance of a button. #[derive(Debug)] pub struct Style { - pub shadow_offset: f32, + pub shadow_offset: Vector, pub background: Option, pub border_radius: u16, + pub border_width: u16, + pub border_color: Color, pub text_color: Color, } +impl std::default::Default for Style { + fn default() -> Self { + Style { + shadow_offset: Vector::default(), + background: None, + border_radius: 0, + border_width: 0, + border_color: Color::TRANSPARENT, + text_color: Color::BLACK, + } + } +} + /// A set of rules that dictate the style of a button. pub trait StyleSheet { fn active(&self) -> Style; @@ -18,14 +33,14 @@ pub trait StyleSheet { let active = self.active(); Style { - shadow_offset: active.shadow_offset + 1.0, + shadow_offset: active.shadow_offset + Vector::new(0.0, 1.0), ..active } } fn pressed(&self) -> Style { Style { - shadow_offset: 0.0, + shadow_offset: Vector::default(), ..self.active() } } @@ -34,7 +49,7 @@ pub trait StyleSheet { let active = self.active(); Style { - shadow_offset: 0.0, + shadow_offset: Vector::default(), background: active.background.map(|background| match background { Background::Color(color) => Background::Color(Color { a: color.a * 0.5, @@ -55,9 +70,11 @@ struct Default; impl StyleSheet for Default { fn active(&self) -> Style { Style { - shadow_offset: 1.0, + shadow_offset: Vector::new(0.0, 1.0), background: Some(Background::Color([0.5, 0.5, 0.5].into())), border_radius: 5, + border_width: 0, + border_color: Color::TRANSPARENT, text_color: Color::BLACK, } } diff --git a/wgpu/src/renderer/widget/button.rs b/wgpu/src/renderer/widget/button.rs index d00a43a5..63a53473 100644 --- a/wgpu/src/renderer/widget/button.rs +++ b/wgpu/src/renderer/widget/button.rs @@ -51,8 +51,8 @@ impl iced_native::button::Renderer for Renderer { primitives: vec![ Primitive::Quad { bounds: Rectangle { - x: bounds.x + 1.0, - y: bounds.y + styling.shadow_offset, + x: bounds.x + styling.shadow_offset.x, + y: bounds.y + styling.shadow_offset.y, ..bounds }, background: Background::Color( @@ -66,8 +66,8 @@ impl iced_native::button::Renderer for Renderer { bounds, background, border_radius: styling.border_radius, - border_width: 0, - border_color: Color::TRANSPARENT, + border_width: styling.border_width, + border_color: styling.border_color, }, content, ], -- cgit From 07ef59af78107db103b7a9640a660ecae95b1156 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 5 Jan 2020 19:34:38 +0100 Subject: Implement `Default` for `container::Style` --- style/src/button.rs | 2 +- style/src/container.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/style/src/button.rs b/style/src/button.rs index 4a9dac45..03c9ab32 100644 --- a/style/src/button.rs +++ b/style/src/button.rs @@ -14,7 +14,7 @@ pub struct Style { impl std::default::Default for Style { fn default() -> Self { - Style { + Self { shadow_offset: Vector::default(), background: None, border_radius: 0, diff --git a/style/src/container.rs b/style/src/container.rs index 484fdfda..d2247342 100644 --- a/style/src/container.rs +++ b/style/src/container.rs @@ -11,6 +11,18 @@ pub struct Style { pub border_color: Color, } +impl std::default::Default for Style { + fn default() -> Self { + Self { + text_color: None, + background: None, + border_radius: 0, + border_width: 0, + border_color: Color::TRANSPARENT, + } + } +} + /// A set of rules that dictate the style of a container. pub trait StyleSheet { /// Produces the style of a container. -- cgit From 7c4dba29c7614428041bda049924569d751cbb1a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 5 Jan 2020 19:36:44 +0100 Subject: Implement `Default` for `text_input::Style` --- style/src/text_input.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/style/src/text_input.rs b/style/src/text_input.rs index 75f427a7..269b28db 100644 --- a/style/src/text_input.rs +++ b/style/src/text_input.rs @@ -10,6 +10,17 @@ pub struct Style { pub border_color: Color, } +impl std::default::Default for Style { + fn default() -> Self { + Self { + background: Background::Color(Color::WHITE), + border_radius: 0, + border_width: 0, + border_color: Color::TRANSPARENT, + } + } +} + /// A set of rules that dictate the style of a text input. pub trait StyleSheet { /// Produces the style of an active text input. -- cgit From a848306b89053ef4ba2aeb4eb7899bec94d93cb3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 5 Jan 2020 19:48:27 +0100 Subject: Fix styling in `button::Renderer` implementation --- wgpu/src/renderer/widget/button.rs | 64 ++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/wgpu/src/renderer/widget/button.rs b/wgpu/src/renderer/widget/button.rs index 63a53473..eb5d7c09 100644 --- a/wgpu/src/renderer/widget/button.rs +++ b/wgpu/src/renderer/widget/button.rs @@ -1,6 +1,6 @@ use crate::{button::StyleSheet, defaults, Defaults, Primitive, Renderer}; use iced_native::{ - Background, Color, Element, Layout, MouseCursor, Point, Rectangle, + Background, Color, Element, Layout, MouseCursor, Point, Rectangle, Vector, }; impl iced_native::button::Renderer for Renderer { @@ -45,33 +45,43 @@ impl iced_native::button::Renderer for Renderer { ); ( - match styling.background { - None => content, - Some(background) => Primitive::Group { - primitives: vec![ - Primitive::Quad { - bounds: Rectangle { - x: bounds.x + styling.shadow_offset.x, - y: bounds.y + styling.shadow_offset.y, - ..bounds - }, - background: Background::Color( - [0.0, 0.0, 0.0, 0.5].into(), - ), - border_radius: styling.border_radius, - border_width: 0, - border_color: Color::TRANSPARENT, - }, - Primitive::Quad { - bounds, - background, - border_radius: styling.border_radius, - border_width: styling.border_width, - border_color: styling.border_color, + if styling.background.is_some() || styling.border_width > 0 { + let background = Primitive::Quad { + bounds, + background: styling + .background + .unwrap_or(Background::Color(Color::TRANSPARENT)), + border_radius: styling.border_radius, + border_width: styling.border_width, + border_color: styling.border_color, + }; + + if styling.shadow_offset == Vector::default() { + Primitive::Group { + primitives: vec![background, content], + } + } else { + // TODO: Implement proper shadow support + let shadow = Primitive::Quad { + bounds: Rectangle { + x: bounds.x + styling.shadow_offset.x, + y: bounds.y + styling.shadow_offset.y, + ..bounds }, - content, - ], - }, + background: Background::Color( + [0.0, 0.0, 0.0, 0.5].into(), + ), + border_radius: styling.border_radius, + border_width: 0, + border_color: Color::TRANSPARENT, + }; + + Primitive::Group { + primitives: vec![shadow, background, content], + } + } + } else { + content }, if is_mouse_over { MouseCursor::Pointer -- cgit From 2bbd395d5dcdf9c92ffb354b8b05444478e4b344 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 6 Jan 2020 18:44:45 +0100 Subject: Draft `styling` example --- core/src/vector.rs | 11 ++ examples/styling.rs | 282 +++++++++++++++++++++++++++++++++++++ style/src/button.rs | 2 +- style/src/text_input.rs | 8 +- wgpu/src/renderer/widget/button.rs | 1 - 5 files changed, 298 insertions(+), 6 deletions(-) create mode 100644 examples/styling.rs diff --git a/core/src/vector.rs b/core/src/vector.rs index 1c09ee3e..75c77dbd 100644 --- a/core/src/vector.rs +++ b/core/src/vector.rs @@ -32,6 +32,17 @@ where } } +impl std::ops::Mul for Vector +where + T: std::ops::Mul, +{ + type Output = Self; + + fn mul(self, b: Self) -> Self { + Self::new(self.x * b.x, self.y * b.y) + } +} + impl Default for Vector where T: Default, diff --git a/examples/styling.rs b/examples/styling.rs new file mode 100644 index 00000000..95590f2c --- /dev/null +++ b/examples/styling.rs @@ -0,0 +1,282 @@ +use iced::{ + button, text_input, Button, Column, Container, Element, Length, Radio, Row, + Sandbox, Settings, Text, TextInput, +}; + +pub fn main() { + Styling::run(Settings::default()) +} + +struct Styling { + theme: style::Theme, + input: text_input::State, + input_value: String, + button: button::State, +} + +#[derive(Debug, Clone)] +enum Message { + ThemeChanged(style::Theme), + InputChanged(String), + ButtonPressed, +} + +impl Sandbox for Styling { + type Message = Message; + + fn new() -> Self { + Styling { + theme: style::Theme::Light, + input: text_input::State::default(), + input_value: String::new(), + button: button::State::default(), + } + } + + fn title(&self) -> String { + String::from("Styling - Iced") + } + + fn update(&mut self, message: Message) { + match message { + Message::ThemeChanged(theme) => self.theme = theme, + Message::InputChanged(value) => self.input_value = value, + Message::ButtonPressed => (), + } + } + + fn view(&mut self) -> Element { + let choose_theme = style::Theme::ALL.iter().fold( + Column::new() + .width(Length::Shrink) + .spacing(10) + .push(Text::new("Choose a theme:").width(Length::Shrink)), + |column, theme| { + column.push(Radio::new( + *theme, + &format!("{:?}", theme), + Some(self.theme), + Message::ThemeChanged, + )) + }, + ); + + let text_input = TextInput::new( + &mut self.input, + "Type something...", + &self.input_value, + Message::InputChanged, + ) + .padding(10) + .size(20) + .style(self.theme); + + let button = Button::new(&mut self.button, Text::new("Submit")) + .padding(10) + .on_press(Message::ButtonPressed) + .style(self.theme); + + let content = Column::new() + .spacing(20) + .padding(20) + .max_width(600) + .push(choose_theme) + .push(Row::new().spacing(10).push(text_input).push(button)); + + Container::new(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .style(self.theme) + .into() + } +} + +mod style { + use iced::{button, container, text_input}; + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum Theme { + Light, + Dark, + } + + impl Theme { + pub const ALL: [Theme; 2] = [Theme::Light, Theme::Dark]; + } + + impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => light::Container.into(), + Theme::Dark => dark::Container.into(), + } + } + } + + impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => light::TextInput.into(), + Theme::Dark => dark::TextInput.into(), + } + } + } + + impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => light::Button.into(), + Theme::Dark => dark::Button.into(), + } + } + } + + mod light { + use iced::{button, container, text_input, Background, Color, Vector}; + + pub struct Container; + + impl container::StyleSheet for Container { + fn style(&self) -> container::Style { + container::Style::default() + } + } + + pub struct TextInput; + + impl text_input::StyleSheet for TextInput { + fn active(&self) -> text_input::Style { + text_input::Style { + background: Background::Color(Color::WHITE), + border_radius: 5, + border_width: 1, + border_color: Color::from_rgb(0.7, 0.7, 0.7), + } + } + + fn focused(&self) -> text_input::Style { + text_input::Style { + border_width: 1, + border_color: Color::from_rgb(0.5, 0.5, 0.5), + ..self.active() + } + } + + fn placeholder_color(&self) -> Color { + Color::from_rgb(0.7, 0.7, 0.7) + } + + fn value_color(&self) -> Color { + Color::from_rgb(0.3, 0.3, 0.3) + } + } + + pub struct Button; + + impl button::StyleSheet for Button { + fn active(&self) -> button::Style { + button::Style { + background: Some(Background::Color(Color::from_rgb( + 0.11, 0.42, 0.87, + ))), + border_radius: 12, + shadow_offset: Vector::new(1.0, 1.0), + text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE), + ..button::Style::default() + } + } + + fn hovered(&self) -> button::Style { + button::Style { + text_color: Color::WHITE, + shadow_offset: Vector::new(1.0, 2.0), + ..self.active() + } + } + } + } + + mod dark { + use iced::{button, container, text_input, Background, Color}; + + pub struct Container; + + impl container::StyleSheet for Container { + fn style(&self) -> container::Style { + container::Style { + background: Some(Background::Color(Color::from_rgb8( + 0x36, 0x39, 0x3F, + ))), + text_color: Some(Color::WHITE), + ..container::Style::default() + } + } + } + + pub struct TextInput; + + impl text_input::StyleSheet for TextInput { + fn active(&self) -> text_input::Style { + text_input::Style { + background: Background::Color(Color::from_rgb8( + 0x40, 0x44, 0x4B, + )), + border_radius: 2, + border_width: 0, + border_color: Color::TRANSPARENT, + } + } + + fn focused(&self) -> text_input::Style { + text_input::Style { + border_width: 1, + border_color: Color::from_rgb8(0x6F, 0xFF, 0xE9), + ..self.active() + } + } + + fn hovered(&self) -> text_input::Style { + text_input::Style { + border_width: 1, + border_color: Color::from_rgb8(0x5B, 0xC0, 0xBE), + ..self.focused() + } + } + + fn placeholder_color(&self) -> Color { + Color::from_rgb(0.7, 0.7, 0.7) + } + + fn value_color(&self) -> Color { + Color::WHITE + } + } + + pub struct Button; + + impl button::StyleSheet for Button { + fn active(&self) -> button::Style { + button::Style { + background: Some(Background::Color(Color::from_rgb8( + 0x72, 0x89, 0xDA, + ))), + border_radius: 3, + text_color: Color::WHITE, + ..button::Style::default() + } + } + + fn hovered(&self) -> button::Style { + button::Style { + background: Some(Background::Color(Color::from_rgb8( + 0x67, 0x7B, 0xC4, + ))), + text_color: Color::WHITE, + ..self.active() + } + } + } + } +} diff --git a/style/src/button.rs b/style/src/button.rs index 03c9ab32..93c27860 100644 --- a/style/src/button.rs +++ b/style/src/button.rs @@ -75,7 +75,7 @@ impl StyleSheet for Default { border_radius: 5, border_width: 0, border_color: Color::TRANSPARENT, - text_color: Color::BLACK, + text_color: Color::WHITE, } } } diff --git a/style/src/text_input.rs b/style/src/text_input.rs index 269b28db..c5123b20 100644 --- a/style/src/text_input.rs +++ b/style/src/text_input.rs @@ -29,14 +29,14 @@ pub trait StyleSheet { /// Produces the style of a focused text input. fn focused(&self) -> Style; + fn placeholder_color(&self) -> Color; + + fn value_color(&self) -> Color; + /// Produces the style of an hovered text input. fn hovered(&self) -> Style { self.focused() } - - fn placeholder_color(&self) -> Color; - - fn value_color(&self) -> Color; } struct Default; diff --git a/wgpu/src/renderer/widget/button.rs b/wgpu/src/renderer/widget/button.rs index eb5d7c09..a9209f64 100644 --- a/wgpu/src/renderer/widget/button.rs +++ b/wgpu/src/renderer/widget/button.rs @@ -19,7 +19,6 @@ impl iced_native::button::Renderer for Renderer { ) -> Self::Output { let is_mouse_over = bounds.contains(cursor_position); - // TODO: Render proper shadows let styling = if is_disabled { style.disabled() } else if is_mouse_over { -- cgit From fbc25f8d3d94be148663dbbe1de6ceb9c162b308 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 6 Jan 2020 18:46:47 +0100 Subject: Implement `Default` for `Theme` in `styling` example --- examples/styling.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/styling.rs b/examples/styling.rs index 95590f2c..9f8cc53f 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -7,6 +7,7 @@ pub fn main() { Styling::run(Settings::default()) } +#[derive(Default)] struct Styling { theme: style::Theme, input: text_input::State, @@ -25,12 +26,7 @@ impl Sandbox for Styling { type Message = Message; fn new() -> Self { - Styling { - theme: style::Theme::Light, - input: text_input::State::default(), - input_value: String::new(), - button: button::State::default(), - } + Styling::default() } fn title(&self) -> String { @@ -106,6 +102,12 @@ mod style { pub const ALL: [Theme; 2] = [Theme::Light, Theme::Dark]; } + impl Default for Theme { + fn default() -> Theme { + Theme::Light + } + } + impl From for Box { fn from(theme: Theme) -> Self { match theme { -- cgit From f7dfd6537429f94af63d75617c27bc998d72d9c8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 6 Jan 2020 19:05:00 +0100 Subject: Use default styles in `styling` example --- examples/styling.rs | 48 ++++-------------------------------------------- 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/examples/styling.rs b/examples/styling.rs index 9f8cc53f..c368ea07 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -43,10 +43,7 @@ impl Sandbox for Styling { fn view(&mut self) -> Element { let choose_theme = style::Theme::ALL.iter().fold( - Column::new() - .width(Length::Shrink) - .spacing(10) - .push(Text::new("Choose a theme:").width(Length::Shrink)), + Column::new().spacing(10).push(Text::new("Choose a theme:")), |column, theme| { column.push(Radio::new( *theme, @@ -111,7 +108,7 @@ mod style { impl From for Box { fn from(theme: Theme) -> Self { match theme { - Theme::Light => light::Container.into(), + Theme::Light => Default::default(), Theme::Dark => dark::Container.into(), } } @@ -120,7 +117,7 @@ mod style { impl From for Box { fn from(theme: Theme) -> Self { match theme { - Theme::Light => light::TextInput.into(), + Theme::Light => Default::default(), Theme::Dark => dark::TextInput.into(), } } @@ -136,44 +133,7 @@ mod style { } mod light { - use iced::{button, container, text_input, Background, Color, Vector}; - - pub struct Container; - - impl container::StyleSheet for Container { - fn style(&self) -> container::Style { - container::Style::default() - } - } - - pub struct TextInput; - - impl text_input::StyleSheet for TextInput { - fn active(&self) -> text_input::Style { - text_input::Style { - background: Background::Color(Color::WHITE), - border_radius: 5, - border_width: 1, - border_color: Color::from_rgb(0.7, 0.7, 0.7), - } - } - - fn focused(&self) -> text_input::Style { - text_input::Style { - border_width: 1, - border_color: Color::from_rgb(0.5, 0.5, 0.5), - ..self.active() - } - } - - fn placeholder_color(&self) -> Color { - Color::from_rgb(0.7, 0.7, 0.7) - } - - fn value_color(&self) -> Color { - Color::from_rgb(0.3, 0.3, 0.3) - } - } + use iced::{button, Background, Color, Vector}; pub struct Button; -- cgit From d0dc7cebf9c352f4d14827fe47489788f59e61a1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 6 Jan 2020 21:01:09 +0100 Subject: Implement styling for `Scrollable` --- examples/styling.rs | 63 +++++++++++++++++++++++++--- native/src/renderer/null.rs | 3 ++ native/src/widget/scrollable.rs | 24 ++++++++--- src/native.rs | 14 ------- style/src/lib.rs | 1 + style/src/scrollable.rs | 76 ++++++++++++++++++++++++++++++++++ wgpu/src/renderer/widget/scrollable.rs | 33 +++++++++------ wgpu/src/widget.rs | 1 + wgpu/src/widget/scrollable.rs | 13 ++++++ 9 files changed, 191 insertions(+), 37 deletions(-) create mode 100644 style/src/scrollable.rs create mode 100644 wgpu/src/widget/scrollable.rs diff --git a/examples/styling.rs b/examples/styling.rs index c368ea07..61c8bcba 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -1,6 +1,6 @@ use iced::{ - button, text_input, Button, Column, Container, Element, Length, Radio, Row, - Sandbox, Settings, Text, TextInput, + button, scrollable, text_input, Button, Column, Container, Element, Length, + Radio, Row, Sandbox, Scrollable, Settings, Text, TextInput, }; pub fn main() { @@ -10,6 +10,7 @@ pub fn main() { #[derive(Default)] struct Styling { theme: style::Theme, + scroll: scrollable::State, input: text_input::State, input_value: String, button: button::State, @@ -76,10 +77,13 @@ impl Sandbox for Styling { .push(choose_theme) .push(Row::new().spacing(10).push(text_input).push(button)); - Container::new(content) + let scrollable = Scrollable::new(&mut self.scroll) + .style(self.theme) + .push(Container::new(content).width(Length::Fill).center_x()); + + Container::new(scrollable) .width(Length::Fill) .height(Length::Fill) - .center_x() .center_y() .style(self.theme) .into() @@ -87,7 +91,7 @@ impl Sandbox for Styling { } mod style { - use iced::{button, container, text_input}; + use iced::{button, container, scrollable, text_input}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Theme { @@ -132,6 +136,15 @@ mod style { } } + impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Scrollable.into(), + } + } + } + mod light { use iced::{button, Background, Color, Vector}; @@ -161,7 +174,9 @@ mod style { } mod dark { - use iced::{button, container, text_input, Background, Color}; + use iced::{ + button, container, scrollable, text_input, Background, Color, + }; pub struct Container; @@ -239,6 +254,42 @@ mod style { ..self.active() } } + + fn pressed(&self) -> button::Style { + button::Style { + border_width: 1, + border_color: Color::WHITE, + ..self.hovered() + } + } + } + + pub struct Scrollable; + + impl scrollable::StyleSheet for Scrollable { + fn active(&self) -> scrollable::Scrollbar { + scrollable::Scrollbar { + background: None, + border_radius: 2, + border_width: 0, + border_color: Color::TRANSPARENT, + scroller: scrollable::Scroller { + color: [1.0, 1.0, 1.0, 0.7].into(), + border_radius: 2, + border_width: 1, + border_color: Color::WHITE, + }, + } + } + + fn hovered(&self) -> scrollable::Scrollbar { + scrollable::Scrollbar { + background: Some(Background::Color( + [1.0, 1.0, 1.0, 0.3].into(), + )), + ..self.active() + } + } } } } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 96aa132c..0184ac00 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -73,6 +73,8 @@ impl text::Renderer for Null { } impl scrollable::Renderer for Null { + type Style = (); + fn scrollbar( &self, _bounds: Rectangle, @@ -91,6 +93,7 @@ impl scrollable::Renderer for Null { _is_mouse_over_scrollbar: bool, _scrollbar: Option, _offset: u32, + _style: &Self::Style, _content: Self::Output, ) { } diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 9df09b14..a062abd0 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -11,14 +11,15 @@ use std::{f32, hash::Hash, u32}; /// A widget that can vertically display an infinite amount of content with a /// scrollbar. #[allow(missing_debug_implementations)] -pub struct Scrollable<'a, Message, Renderer> { +pub struct Scrollable<'a, Message, Renderer: self::Renderer> { state: &'a mut State, height: Length, max_height: u32, content: Column<'a, Message, Renderer>, + style: Renderer::Style, } -impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> { +impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> { /// Creates a new [`Scrollable`] with the given [`State`]. /// /// [`Scrollable`]: struct.Scrollable.html @@ -29,6 +30,7 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> { height: Length::Shrink, max_height: u32::MAX, content: Column::new(), + style: Renderer::Style::default(), } } @@ -90,6 +92,14 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> { self } + /// Sets the style of the [`Scrollable`] . + /// + /// [`Scrollable`]: struct.Scrollable.html + pub fn style(mut self, style: impl Into) -> Self { + self.style = style.into(); + self + } + /// Adds an element to the [`Scrollable`]. /// /// [`Scrollable`]: struct.Scrollable.html @@ -105,7 +115,7 @@ impl<'a, Message, Renderer> Scrollable<'a, Message, Renderer> { impl<'a, Message, Renderer> Widget for Scrollable<'a, Message, Renderer> where - Renderer: self::Renderer + column::Renderer, + Renderer: 'static + self::Renderer + column::Renderer, { fn width(&self) -> Length { Length::Fill @@ -295,12 +305,13 @@ where is_mouse_over_scrollbar, scrollbar, offset, + &self.style, content, ) } fn hash_layout(&self, state: &mut Hasher) { - std::any::TypeId::of::>().hash(state); + std::any::TypeId::of::>().hash(state); self.height.hash(state); self.max_height.hash(state); @@ -447,6 +458,8 @@ pub struct Scroller { /// [`Scrollable`]: struct.Scrollable.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + type Style: Default; + /// Returns the [`Scrollbar`] given the bounds and content bounds of a /// [`Scrollable`]. /// @@ -483,6 +496,7 @@ pub trait Renderer: crate::Renderer + Sized { is_mouse_over_scrollbar: bool, scrollbar: Option, offset: u32, + style: &Self::Style, content: Self::Output, ) -> Self::Output; } @@ -490,7 +504,7 @@ pub trait Renderer: crate::Renderer + Sized { impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer + column::Renderer, + Renderer: 'static + self::Renderer + column::Renderer, Message: 'static, { fn from( diff --git a/src/native.rs b/src/native.rs index 67f85c20..1061a730 100644 --- a/src/native.rs +++ b/src/native.rs @@ -24,20 +24,6 @@ pub mod widget { //! [`text_input::State`]: text_input/struct.State.html pub use iced_wgpu::widget::*; - pub mod scrollable { - //! Navigate an endless amount of content with a scrollbar. - - /// A widget that can vertically display an infinite amount of content - /// with a scrollbar. - /// - /// This is an alias of an `iced_native` scrollable with a default - /// `Renderer`. - pub type Scrollable<'a, Message> = - iced_winit::Scrollable<'a, Message, iced_wgpu::Renderer>; - - pub use iced_winit::scrollable::State; - } - pub mod slider { //! Display an interactive selector of a single value from a range of //! values. diff --git a/style/src/lib.rs b/style/src/lib.rs index 1d7e57c4..fb90b2b5 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -1,3 +1,4 @@ pub mod button; pub mod container; +pub mod scrollable; pub mod text_input; diff --git a/style/src/scrollable.rs b/style/src/scrollable.rs new file mode 100644 index 00000000..690c14a2 --- /dev/null +++ b/style/src/scrollable.rs @@ -0,0 +1,76 @@ +//! Navigate an endless amount of content with a scrollbar. +use iced_core::{Background, Color}; + +/// The appearance of a scrollable. +#[derive(Debug, Clone, Copy)] +pub struct Scrollbar { + pub background: Option, + pub border_radius: u16, + pub border_width: u16, + pub border_color: Color, + pub scroller: Scroller, +} + +/// The appearance of the scroller of a scrollable. +#[derive(Debug, Clone, Copy)] +pub struct Scroller { + pub color: Color, + pub border_radius: u16, + pub border_width: u16, + pub border_color: Color, +} + +/// A set of rules that dictate the style of a scrollable. +pub trait StyleSheet { + /// Produces the style of an active scrollbar. + fn active(&self) -> Scrollbar; + + /// Produces the style of an hovered scrollbar. + fn hovered(&self) -> Scrollbar; + + /// Produces the style of a scrollbar that is being dragged. + fn dragging(&self) -> Scrollbar { + self.hovered() + } +} + +struct Default; + +impl StyleSheet for Default { + fn active(&self) -> Scrollbar { + Scrollbar { + background: None, + border_radius: 5, + border_width: 0, + border_color: Color::TRANSPARENT, + scroller: Scroller { + color: [0.0, 0.0, 0.0, 0.7].into(), + border_radius: 5, + border_width: 0, + border_color: Color::TRANSPARENT, + }, + } + } + + fn hovered(&self) -> Scrollbar { + Scrollbar { + background: Some(Background::Color([0.0, 0.0, 0.0, 0.3].into())), + ..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/scrollable.rs b/wgpu/src/renderer/widget/scrollable.rs index 42a4a743..30d7f337 100644 --- a/wgpu/src/renderer/widget/scrollable.rs +++ b/wgpu/src/renderer/widget/scrollable.rs @@ -7,6 +7,8 @@ const SCROLLBAR_WIDTH: u16 = 10; const SCROLLBAR_MARGIN: u16 = 2; impl scrollable::Renderer for Renderer { + type Style = Box; + fn scrollbar( &self, bounds: Rectangle, @@ -53,6 +55,7 @@ impl scrollable::Renderer for Renderer { is_mouse_over_scrollbar: bool, scrollbar: Option, offset: u32, + style_sheet: &Self::Style, (content, mouse_cursor): Self::Output, ) -> Self::Output { let clip = Primitive::Clip { @@ -64,17 +67,23 @@ impl scrollable::Renderer for Renderer { ( if let Some(scrollbar) = scrollbar { if is_mouse_over || state.is_scroller_grabbed() { + let style = if state.is_scroller_grabbed() { + style_sheet.dragging() + } else if is_mouse_over_scrollbar { + style_sheet.hovered() + } else { + style_sheet.active() + }; + let scroller = Primitive::Quad { bounds: scrollbar.scroller.bounds, - background: Background::Color( - [0.0, 0.0, 0.0, 0.7].into(), - ), - border_radius: 5, - border_width: 0, - border_color: Color::TRANSPARENT, + background: Background::Color(style.scroller.color), + border_radius: style.scroller.border_radius, + border_width: style.scroller.border_width, + border_color: style.scroller.border_color, }; - if is_mouse_over_scrollbar || state.is_scroller_grabbed() { + if style.background.is_some() || style.border_width > 0 { let scrollbar = Primitive::Quad { bounds: Rectangle { x: scrollbar.bounds.x @@ -83,12 +92,12 @@ impl scrollable::Renderer for Renderer { - f32::from(2 * SCROLLBAR_MARGIN), ..scrollbar.bounds }, - background: Background::Color( - [0.0, 0.0, 0.0, 0.3].into(), + background: style.background.unwrap_or( + Background::Color(Color::TRANSPARENT), ), - border_radius: 5, - border_width: 0, - border_color: Color::TRANSPARENT, + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, }; Primitive::Group { diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs index 1d7e57c4..fb90b2b5 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -1,3 +1,4 @@ pub mod button; pub mod container; +pub mod scrollable; pub mod text_input; diff --git a/wgpu/src/widget/scrollable.rs b/wgpu/src/widget/scrollable.rs new file mode 100644 index 00000000..1d236105 --- /dev/null +++ b/wgpu/src/widget/scrollable.rs @@ -0,0 +1,13 @@ +//! Navigate an endless amount of content with a scrollbar. +use crate::Renderer; + +pub use iced_native::scrollable::State; +pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; + +/// A widget that can vertically display an infinite amount of content +/// with a scrollbar. +/// +/// This is an alias of an `iced_native` scrollable with a default +/// `Renderer`. +pub type Scrollable<'a, Message> = + iced_native::Scrollable<'a, Message, Renderer>; -- cgit From b329003c8fdcdc3378c8fda93af54be5686fc9ae Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Jan 2020 00:28:08 +0100 Subject: Implement styling for `Slider` --- examples/styling.rs | 75 ++++++++++++++++++++++++++++-- native/src/renderer/null.rs | 25 ++++++++-- native/src/widget/slider.rs | 32 ++++++++++--- src/native.rs | 11 ----- style/src/lib.rs | 1 + style/src/slider.rs | 95 ++++++++++++++++++++++++++++++++++++++ wgpu/src/renderer/widget/slider.rs | 57 ++++++++++++++--------- wgpu/src/widget.rs | 1 + wgpu/src/widget/slider.rs | 16 +++++++ 9 files changed, 266 insertions(+), 47 deletions(-) create mode 100644 style/src/slider.rs create mode 100644 wgpu/src/widget/slider.rs diff --git a/examples/styling.rs b/examples/styling.rs index 61c8bcba..b0cdbcf0 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -1,6 +1,6 @@ use iced::{ - button, scrollable, text_input, Button, Column, Container, Element, Length, - Radio, Row, Sandbox, Scrollable, Settings, Text, TextInput, + button, scrollable, slider, text_input, Button, Column, Container, Element, + Length, Radio, Row, Sandbox, Scrollable, Settings, Slider, Text, TextInput, }; pub fn main() { @@ -14,6 +14,8 @@ struct Styling { input: text_input::State, input_value: String, button: button::State, + slider: slider::State, + slider_value: f32, } #[derive(Debug, Clone)] @@ -21,6 +23,7 @@ enum Message { ThemeChanged(style::Theme), InputChanged(String), ButtonPressed, + SliderChanged(f32), } impl Sandbox for Styling { @@ -39,6 +42,7 @@ impl Sandbox for Styling { Message::ThemeChanged(theme) => self.theme = theme, Message::InputChanged(value) => self.input_value = value, Message::ButtonPressed => (), + Message::SliderChanged(value) => self.slider_value = value, } } @@ -70,12 +74,21 @@ impl Sandbox for Styling { .on_press(Message::ButtonPressed) .style(self.theme); + let slider = Slider::new( + &mut self.slider, + 0.0..=100.0, + self.slider_value, + Message::SliderChanged, + ) + .style(self.theme); + let content = Column::new() .spacing(20) .padding(20) .max_width(600) .push(choose_theme) - .push(Row::new().spacing(10).push(text_input).push(button)); + .push(Row::new().spacing(10).push(text_input).push(button)) + .push(slider); let scrollable = Scrollable::new(&mut self.scroll) .style(self.theme) @@ -91,7 +104,7 @@ impl Sandbox for Styling { } mod style { - use iced::{button, container, scrollable, text_input}; + use iced::{button, container, scrollable, slider, text_input}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Theme { @@ -145,6 +158,15 @@ mod style { } } + impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Slider.into(), + } + } + } + mod light { use iced::{button, Background, Color, Vector}; @@ -175,7 +197,8 @@ mod style { mod dark { use iced::{ - button, container, scrollable, text_input, Background, Color, + button, container, scrollable, slider, text_input, Background, + Color, }; pub struct Container; @@ -291,5 +314,47 @@ mod style { } } } + + pub struct Slider; + + impl slider::StyleSheet for Slider { + fn active(&self) -> slider::Style { + let blue = Color::from_rgb8(0x72, 0x89, 0xDA); + + slider::Style { + rail_colors: (blue, Color { a: 0.1, ..blue }), + handle: slider::Handle { + shape: slider::HandleShape::Circle { radius: 9 }, + color: blue, + border_width: 0, + border_color: Color::TRANSPARENT, + }, + } + } + + fn hovered(&self) -> slider::Style { + let active = self.active(); + + slider::Style { + handle: slider::Handle { + color: Color::from_rgb(0.90, 0.90, 0.90), + ..active.handle + }, + ..active + } + } + + fn dragging(&self) -> slider::Style { + let active = self.active(); + + slider::Style { + handle: slider::Handle { + color: Color::from_rgb(0.85, 0.85, 0.85), + ..active.handle + }, + ..active + } + } + } } } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 0184ac00..72f06a87 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, Color, - Element, Font, HorizontalAlignment, Layout, Point, Rectangle, Renderer, - Size, VerticalAlignment, + button, checkbox, column, radio, row, scrollable, slider, text, text_input, + Color, Element, Font, HorizontalAlignment, Layout, Point, Rectangle, + Renderer, Size, VerticalAlignment, }; /// A renderer that does nothing. @@ -180,3 +180,22 @@ impl checkbox::Renderer for Null { ) { } } + +impl slider::Renderer for Null { + type Style = (); + + fn height(&self) -> u32 { + 30 + } + + fn draw( + &mut self, + _bounds: Rectangle, + _cursor_position: Point, + _range: std::ops::RangeInclusive, + _value: f32, + _is_dragging: bool, + _style_sheet: &Self::Style, + ) -> Self::Output { + } +} diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index ea66a347..c35a933e 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -21,8 +21,9 @@ use std::{hash::Hash, ops::RangeInclusive}; /// /// # Example /// ``` -/// # use iced_native::{slider, Slider}; +/// # use iced_native::{slider, renderer::Null}; /// # +/// # pub type Slider<'a, Message> = iced_native::Slider<'a, Message, Null>; /// pub enum Message { /// SliderChanged(f32), /// } @@ -35,15 +36,16 @@ use std::{hash::Hash, ops::RangeInclusive}; /// /// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true) #[allow(missing_debug_implementations)] -pub struct Slider<'a, Message> { +pub struct Slider<'a, Message, Renderer: self::Renderer> { state: &'a mut State, range: RangeInclusive, value: f32, on_change: Box Message>, width: Length, + style: Renderer::Style, } -impl<'a, Message> Slider<'a, Message> { +impl<'a, Message, Renderer: self::Renderer> Slider<'a, Message, Renderer> { /// Creates a new [`Slider`]. /// /// It expects: @@ -71,6 +73,7 @@ impl<'a, Message> Slider<'a, Message> { range, on_change: Box::new(on_change), width: Length::Fill, + style: Renderer::Style::default(), } } @@ -81,6 +84,14 @@ impl<'a, Message> Slider<'a, Message> { self.width = width; self } + + /// Sets the style of the [`Slider`]. + /// + /// [`Slider`]: struct.Slider.html + pub fn style(mut self, style: impl Into) -> Self { + self.style = style.into(); + self + } } /// The local state of a [`Slider`]. @@ -100,7 +111,8 @@ impl State { } } -impl<'a, Message, Renderer> Widget for Slider<'a, Message> +impl<'a, Message, Renderer> Widget + for Slider<'a, Message, Renderer> where Renderer: self::Renderer, { @@ -188,6 +200,7 @@ where self.range.clone(), self.value, self.state.is_dragging, + &self.style, ) } @@ -204,6 +217,8 @@ where /// [`Slider`]: struct.Slider.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + type Style: Default; + /// Returns the height of the [`Slider`]. /// /// [`Slider`]: struct.Slider.html @@ -228,16 +243,19 @@ pub trait Renderer: crate::Renderer { range: RangeInclusive, value: f32, is_dragging: bool, + style: &Self::Style, ) -> Self::Output; } -impl<'a, Message, Renderer> From> +impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: 'static + self::Renderer, Message: 'static, { - fn from(slider: Slider<'a, Message>) -> Element<'a, Message, Renderer> { + fn from( + slider: Slider<'a, Message, Renderer>, + ) -> Element<'a, Message, Renderer> { Element::new(slider) } } diff --git a/src/native.rs b/src/native.rs index 1061a730..c2584fdd 100644 --- a/src/native.rs +++ b/src/native.rs @@ -24,17 +24,6 @@ pub mod widget { //! [`text_input::State`]: text_input/struct.State.html pub use iced_wgpu::widget::*; - pub mod slider { - //! Display an interactive selector of a single value from a range of - //! values. - //! - //! A [`Slider`] has some local [`State`]. - //! - //! [`Slider`]: struct.Slider.html - //! [`State`]: struct.State.html - pub use iced_winit::slider::{Slider, State}; - } - pub mod image { //! Display images in your user interface. pub use iced_winit::image::{Handle, Image}; diff --git a/style/src/lib.rs b/style/src/lib.rs index fb90b2b5..4b8d339e 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -1,4 +1,5 @@ pub mod button; pub mod container; pub mod scrollable; +pub mod slider; pub mod text_input; diff --git a/style/src/slider.rs b/style/src/slider.rs new file mode 100644 index 00000000..776e180c --- /dev/null +++ b/style/src/slider.rs @@ -0,0 +1,95 @@ +//! Display an interactive selector of a single value from a range of values. +use iced_core::Color; + +/// The appearance of a slider. +#[derive(Debug, Clone, Copy)] +pub struct Style { + pub rail_colors: (Color, Color), + pub handle: Handle, +} + +/// The appearance of the handle of a slider. +#[derive(Debug, Clone, Copy)] +pub struct Handle { + pub shape: HandleShape, + pub color: Color, + pub border_width: u16, + pub border_color: Color, +} + +/// The shape of the handle of a slider. +#[derive(Debug, Clone, Copy)] +pub enum HandleShape { + Circle { radius: u16 }, + Rectangle { width: u16, border_radius: u16 }, +} + +/// A set of rules that dictate the style of a slider. +pub trait StyleSheet { + /// Produces the style of an active slider. + fn active(&self) -> Style; + + /// Produces the style of an hovered slider. + fn hovered(&self) -> Style; + + /// Produces the style of a slider that is being dragged. + fn dragging(&self) -> Style; +} + +struct Default; + +impl StyleSheet for Default { + fn active(&self) -> Style { + Style { + rail_colors: ([0.6, 0.6, 0.6, 0.5].into(), Color::WHITE), + handle: Handle { + shape: HandleShape::Rectangle { + width: 8, + border_radius: 4, + }, + color: Color::from_rgb(0.95, 0.95, 0.95), + border_color: Color::from_rgb(0.6, 0.6, 0.6), + border_width: 1, + }, + } + } + + fn hovered(&self) -> Style { + let active = self.active(); + + Style { + handle: Handle { + color: Color::from_rgb(0.90, 0.90, 0.90), + ..active.handle + }, + ..active + } + } + + fn dragging(&self) -> Style { + let active = self.active(); + + Style { + handle: Handle { + color: Color::from_rgb(0.85, 0.85, 0.85), + ..active.handle + }, + ..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/slider.rs b/wgpu/src/renderer/widget/slider.rs index 386decb5..c8ebd0da 100644 --- a/wgpu/src/renderer/widget/slider.rs +++ b/wgpu/src/renderer/widget/slider.rs @@ -1,10 +1,14 @@ -use crate::{Primitive, Renderer}; +use crate::{ + slider::{HandleShape, StyleSheet}, + Primitive, Renderer, +}; use iced_native::{slider, Background, Color, MouseCursor, Point, Rectangle}; -const HANDLE_WIDTH: f32 = 8.0; const HANDLE_HEIGHT: f32 = 22.0; impl slider::Renderer for Renderer { + type Style = Box; + fn height(&self) -> u32 { 30 } @@ -16,9 +20,18 @@ impl slider::Renderer for Renderer { range: std::ops::RangeInclusive, value: f32, is_dragging: bool, + style_sheet: &Self::Style, ) -> Self::Output { let is_mouse_over = bounds.contains(cursor_position); + let style = if is_dragging { + style_sheet.dragging() + } else if is_mouse_over { + style_sheet.hovered() + } else { + style_sheet.active() + }; + let rail_y = bounds.y + (bounds.height / 2.0).round(); let (rail_top, rail_bottom) = ( @@ -29,7 +42,7 @@ impl slider::Renderer for Renderer { width: bounds.width, height: 2.0, }, - background: Background::Color([0.6, 0.6, 0.6, 0.5].into()), + background: Background::Color(style.rail_colors.0), border_radius: 0, border_width: 0, border_color: Color::TRANSPARENT, @@ -41,7 +54,7 @@ impl slider::Renderer for Renderer { width: bounds.width, height: 2.0, }, - background: Background::Color(Color::WHITE), + background: Background::Color(style.rail_colors.1), border_radius: 0, border_width: 0, border_color: Color::TRANSPARENT, @@ -50,29 +63,31 @@ impl slider::Renderer for Renderer { let (range_start, range_end) = range.into_inner(); - let handle_offset = (bounds.width - HANDLE_WIDTH) + let (handle_width, handle_height, handle_border_radius) = + match style.handle.shape { + HandleShape::Circle { radius } => { + (f32::from(radius * 2), f32::from(radius * 2), radius) + } + HandleShape::Rectangle { + width, + border_radius, + } => (f32::from(width), HANDLE_HEIGHT, border_radius), + }; + + let handle_offset = (bounds.width - handle_width) * ((value - range_start) / (range_end - range_start).max(1.0)); let handle = Primitive::Quad { bounds: Rectangle { x: bounds.x + handle_offset.round(), - y: rail_y - HANDLE_HEIGHT / 2.0, - width: HANDLE_WIDTH, - height: HANDLE_HEIGHT, + y: rail_y - handle_height / 2.0, + width: handle_width, + height: handle_height, }, - background: Background::Color( - if is_dragging { - [0.85, 0.85, 0.85] - } else if is_mouse_over { - [0.90, 0.90, 0.90] - } else { - [0.95, 0.95, 0.95] - } - .into(), - ), - border_radius: 4, - border_width: 1, - border_color: Color::from_rgb(0.6, 0.6, 0.6), + background: Background::Color(style.handle.color), + border_radius: handle_border_radius, + border_width: style.handle.border_width, + border_color: style.handle.border_color, }; ( diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs index fb90b2b5..4b8d339e 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -1,4 +1,5 @@ pub mod button; pub mod container; pub mod scrollable; +pub mod slider; pub mod text_input; diff --git a/wgpu/src/widget/slider.rs b/wgpu/src/widget/slider.rs new file mode 100644 index 00000000..4e47978f --- /dev/null +++ b/wgpu/src/widget/slider.rs @@ -0,0 +1,16 @@ +//! Display an interactive selector of a single value from a range of values. +//! +//! A [`Slider`] has some local [`State`]. +//! +//! [`Slider`]: struct.Slider.html +//! [`State`]: struct.State.html +use crate::Renderer; + +pub use iced_native::slider::State; +pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet}; + +/// An horizontal bar and a handle that selects a single value from a range of +/// values. +/// +/// This is an alias of an `iced_native` slider with an `iced_wgpu::Renderer`. +pub type Slider<'a, Message> = iced_native::Slider<'a, Message, Renderer>; -- cgit From fce89d0ffe36111cdbf42480c28e67811afb42e6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Jan 2020 01:17:32 +0100 Subject: Use constants for colors in `styling` example --- core/src/color.rs | 14 +++++++------- examples/styling.rs | 48 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index d72651d9..d6bdd365 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -33,6 +33,13 @@ impl Color { a: 0.0, }; + /// Creates a [`Color`] from its RGB components. + /// + /// [`Color`]: struct.Color.html + pub const fn from_rgb(r: f32, g: f32, b: f32) -> Color { + Color { r, g, b, a: 1.0 } + } + /// Creates a [`Color`] from its RGB8 components. /// /// [`Color`]: struct.Color.html @@ -45,13 +52,6 @@ impl Color { } } - /// Creates a [`Color`] from its RGB components. - /// - /// [`Color`]: struct.Color.html - pub fn from_rgb(r: f32, g: f32, b: f32) -> Color { - Color { r, g, b, a: 1.0 } - } - /// Converts the [`Color`] into its linear values. /// /// [`Color`]: struct.Color.html diff --git a/examples/styling.rs b/examples/styling.rs index b0cdbcf0..215185e3 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -201,6 +201,30 @@ mod style { Color, }; + const SURFACE: Color = Color::from_rgb( + 0x40 as f32 / 255.0, + 0x44 as f32 / 255.0, + 0x4B as f32 / 255.0, + ); + + const ACCENT: Color = Color::from_rgb( + 0x6F as f32 / 255.0, + 0xFF as f32 / 255.0, + 0xE9 as f32 / 255.0, + ); + + const ACTIVE: Color = Color::from_rgb( + 0x72 as f32 / 255.0, + 0x89 as f32 / 255.0, + 0xDA as f32 / 255.0, + ); + + const HOVERED: Color = Color::from_rgb( + 0x67 as f32 / 255.0, + 0x7B as f32 / 255.0, + 0xC4 as f32 / 255.0, + ); + pub struct Container; impl container::StyleSheet for Container { @@ -220,9 +244,7 @@ mod style { impl text_input::StyleSheet for TextInput { fn active(&self) -> text_input::Style { text_input::Style { - background: Background::Color(Color::from_rgb8( - 0x40, 0x44, 0x4B, - )), + background: Background::Color(SURFACE), border_radius: 2, border_width: 0, border_color: Color::TRANSPARENT, @@ -232,7 +254,7 @@ mod style { fn focused(&self) -> text_input::Style { text_input::Style { border_width: 1, - border_color: Color::from_rgb8(0x6F, 0xFF, 0xE9), + border_color: ACCENT, ..self.active() } } @@ -240,7 +262,7 @@ mod style { fn hovered(&self) -> text_input::Style { text_input::Style { border_width: 1, - border_color: Color::from_rgb8(0x5B, 0xC0, 0xBE), + border_color: Color { a: 0.3, ..ACCENT }, ..self.focused() } } @@ -259,9 +281,7 @@ mod style { impl button::StyleSheet for Button { fn active(&self) -> button::Style { button::Style { - background: Some(Background::Color(Color::from_rgb8( - 0x72, 0x89, 0xDA, - ))), + background: Some(Background::Color(ACTIVE)), border_radius: 3, text_color: Color::WHITE, ..button::Style::default() @@ -270,9 +290,7 @@ mod style { fn hovered(&self) -> button::Style { button::Style { - background: Some(Background::Color(Color::from_rgb8( - 0x67, 0x7B, 0xC4, - ))), + background: Some(Background::Color(HOVERED)), text_color: Color::WHITE, ..self.active() } @@ -319,13 +337,11 @@ mod style { impl slider::StyleSheet for Slider { fn active(&self) -> slider::Style { - let blue = Color::from_rgb8(0x72, 0x89, 0xDA); - slider::Style { - rail_colors: (blue, Color { a: 0.1, ..blue }), + rail_colors: (ACTIVE, Color { a: 0.1, ..ACTIVE }), handle: slider::Handle { shape: slider::HandleShape::Circle { radius: 9 }, - color: blue, + color: ACTIVE, border_width: 0, border_color: Color::TRANSPARENT, }, @@ -337,7 +353,7 @@ mod style { slider::Style { handle: slider::Handle { - color: Color::from_rgb(0.90, 0.90, 0.90), + color: HOVERED, ..active.handle }, ..active -- cgit From 48b3b78a3840778eef1035f4585d5ba9dd3d6291 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Jan 2020 01:53:26 +0100 Subject: Implement styling for `ProgressBar` --- examples/progress_bar.rs | 14 ++------- examples/styling.rs | 40 ++++++++++++++++++++++---- native/src/renderer/null.rs | 23 ++++++++++++--- native/src/widget/progress_bar.rs | 49 ++++++++++++++------------------ src/native.rs | 6 ++-- style/src/lib.rs | 1 + style/src/progress_bar.rs | 42 +++++++++++++++++++++++++++ wgpu/src/renderer/widget/progress_bar.rs | 27 +++++++++--------- wgpu/src/widget.rs | 1 + wgpu/src/widget/progress_bar.rs | 15 ++++++++++ 10 files changed, 151 insertions(+), 67 deletions(-) create mode 100644 style/src/progress_bar.rs create mode 100644 wgpu/src/widget/progress_bar.rs diff --git a/examples/progress_bar.rs b/examples/progress_bar.rs index 901428de..43b09928 100644 --- a/examples/progress_bar.rs +++ b/examples/progress_bar.rs @@ -1,7 +1,4 @@ -use iced::{ - slider, Background, Color, Column, Element, Length, ProgressBar, Sandbox, - Settings, Slider, -}; +use iced::{slider, Column, Element, ProgressBar, Sandbox, Settings, Slider}; pub fn main() { Progress::run(Settings::default()) @@ -38,14 +35,7 @@ impl Sandbox for Progress { fn view(&mut self) -> Element { Column::new() .padding(20) - .push( - ProgressBar::new(0.0..=100.0, self.value) - .background(Background::Color(Color::from_rgb( - 0.6, 0.6, 0.6, - ))) - .active_color(Color::from_rgb(0.0, 0.95, 0.0)) - .height(Length::Units(30)), - ) + .push(ProgressBar::new(0.0..=100.0, self.value)) .push(Slider::new( &mut self.progress_bar_slider, 0.0..=100.0, diff --git a/examples/styling.rs b/examples/styling.rs index 215185e3..426e0638 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -1,6 +1,7 @@ use iced::{ button, scrollable, slider, text_input, Button, Column, Container, Element, - Length, Radio, Row, Sandbox, Scrollable, Settings, Slider, Text, TextInput, + Length, ProgressBar, Radio, Row, Sandbox, Scrollable, Settings, Slider, + Text, TextInput, }; pub fn main() { @@ -82,13 +83,17 @@ impl Sandbox for Styling { ) .style(self.theme); + let progress_bar = + ProgressBar::new(0.0..=100.0, self.slider_value).style(self.theme); + let content = 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(slider) + .push(progress_bar); let scrollable = Scrollable::new(&mut self.scroll) .style(self.theme) @@ -104,7 +109,9 @@ impl Sandbox for Styling { } mod style { - use iced::{button, container, scrollable, slider, text_input}; + use iced::{ + button, container, progress_bar, scrollable, slider, text_input, + }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Theme { @@ -167,6 +174,15 @@ mod style { } } + impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::ProgressBar.into(), + } + } + } + mod light { use iced::{button, Background, Color, Vector}; @@ -197,8 +213,8 @@ mod style { mod dark { use iced::{ - button, container, scrollable, slider, text_input, Background, - Color, + button, container, progress_bar, scrollable, slider, text_input, + Background, Color, }; const SURFACE: Color = Color::from_rgb( @@ -268,7 +284,7 @@ mod style { } fn placeholder_color(&self) -> Color { - Color::from_rgb(0.7, 0.7, 0.7) + Color::from_rgb(0.4, 0.4, 0.4) } fn value_color(&self) -> Color { @@ -372,5 +388,17 @@ mod style { } } } + + pub struct ProgressBar; + + impl progress_bar::StyleSheet for ProgressBar { + fn style(&self) -> progress_bar::Style { + progress_bar::Style { + background: Background::Color(SURFACE), + bar: Background::Color(ACTIVE), + border_radius: 10, + } + } + } } } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 72f06a87..b721e29e 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, slider, text, text_input, - Color, Element, Font, HorizontalAlignment, Layout, Point, Rectangle, - Renderer, Size, VerticalAlignment, + button, checkbox, column, progress_bar, radio, row, scrollable, slider, + text, text_input, Color, Element, Font, HorizontalAlignment, Layout, Point, + Rectangle, Renderer, Size, VerticalAlignment, }; /// A renderer that does nothing. @@ -196,6 +196,21 @@ impl slider::Renderer for Null { _value: f32, _is_dragging: bool, _style_sheet: &Self::Style, - ) -> Self::Output { + ) { + } +} + +impl progress_bar::Renderer for Null { + type Style = (); + + const DEFAULT_HEIGHT: u16 = 30; + + fn draw( + &self, + _bounds: Rectangle, + _range: std::ops::RangeInclusive, + _value: f32, + _style: &Self::Style, + ) { } } diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs index fe517404..0bc860dd 100644 --- a/native/src/widget/progress_bar.rs +++ b/native/src/widget/progress_bar.rs @@ -1,7 +1,6 @@ //! Provide progress feedback to your users. use crate::{ - layout, Background, Color, Element, Hasher, Layout, Length, Point, - Rectangle, Size, Widget, + layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; use std::{hash::Hash, ops::RangeInclusive}; @@ -10,8 +9,9 @@ use std::{hash::Hash, ops::RangeInclusive}; /// /// # Example /// ``` -/// # use iced_native::ProgressBar; +/// # use iced_native::renderer::Null; /// # +/// # pub type ProgressBar = iced_native::ProgressBar; /// let value = 50.0; /// /// ProgressBar::new(0.0..=100.0, value); @@ -19,16 +19,15 @@ use std::{hash::Hash, ops::RangeInclusive}; /// /// ![Progress bar drawn with `iced_wgpu`](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png) #[allow(missing_debug_implementations)] -pub struct ProgressBar { +pub struct ProgressBar { range: RangeInclusive, value: f32, width: Length, height: Option, - background: Option, - active_color: Option, + style: Renderer::Style, } -impl ProgressBar { +impl ProgressBar { /// Creates a new [`ProgressBar`]. /// /// It expects: @@ -42,8 +41,7 @@ impl ProgressBar { range, width: Length::Fill, height: None, - background: None, - active_color: None, + style: Renderer::Style::default(), } } @@ -63,24 +61,16 @@ impl ProgressBar { self } - /// Sets the background of the [`ProgressBar`]. + /// Sets the style of the [`ProgressBar`]. /// /// [`ProgressBar`]: struct.ProgressBar.html - pub fn background(mut self, background: Background) -> Self { - self.background = Some(background); - self - } - - /// Sets the active color of the [`ProgressBar`]. - /// - /// [`ProgressBar`]: struct.ProgressBar.html - pub fn active_color(mut self, active_color: Color) -> Self { - self.active_color = Some(active_color); + pub fn style(mut self, style: impl Into) -> Self { + self.style = style.into(); self } } -impl Widget for ProgressBar +impl Widget for ProgressBar where Renderer: self::Renderer, { @@ -119,8 +109,7 @@ where layout.bounds(), self.range.clone(), self.value, - self.background, - self.active_color, + &self.style, ) } @@ -138,6 +127,8 @@ where /// [`ProgressBar`]: struct.ProgressBar.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + type Style: Default; + /// The default height of a [`ProgressBar`]. /// /// [`ProgressBar`]: struct.ProgressBar.html @@ -158,17 +149,19 @@ pub trait Renderer: crate::Renderer { bounds: Rectangle, range: RangeInclusive, value: f32, - background: Option, - active_color: Option, + style: &Self::Style, ) -> Self::Output; } -impl<'a, Message, Renderer> From for Element<'a, Message, Renderer> +impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: 'static + self::Renderer, Message: 'static, { - fn from(progress_bar: ProgressBar) -> Element<'a, Message, Renderer> { + fn from( + progress_bar: ProgressBar, + ) -> Element<'a, Message, Renderer> { Element::new(progress_bar) } } diff --git a/src/native.rs b/src/native.rs index c2584fdd..5a4db8f6 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, ProgressBar, Radio, Text}; + pub use iced_winit::{Checkbox, Radio, Text}; #[doc(no_inline)] pub use { button::Button, container::Container, image::Image, - scrollable::Scrollable, slider::Slider, svg::Svg, - text_input::TextInput, + progress_bar::ProgressBar, 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 4b8d339e..3f3dc0b0 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -1,5 +1,6 @@ pub mod button; pub mod container; +pub mod progress_bar; pub mod scrollable; pub mod slider; pub mod text_input; diff --git a/style/src/progress_bar.rs b/style/src/progress_bar.rs new file mode 100644 index 00000000..73503fa8 --- /dev/null +++ b/style/src/progress_bar.rs @@ -0,0 +1,42 @@ +//! Provide progress feedback to your users. +use iced_core::{Background, Color}; + +/// The appearance of a progress bar. +#[derive(Debug)] +pub struct Style { + pub background: Background, + pub bar: Background, + pub border_radius: u16, +} + +/// A set of rules that dictate the style of a progress bar. +pub trait StyleSheet { + fn style(&self) -> Style; +} + +struct Default; + +impl StyleSheet for Default { + fn style(&self) -> Style { + Style { + background: Background::Color(Color::from_rgb(0.6, 0.6, 0.6)), + bar: Background::Color(Color::from_rgb(0.3, 0.9, 0.3)), + border_radius: 5, + } + } +} + +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/progress_bar.rs b/wgpu/src/renderer/widget/progress_bar.rs index e9346fda..8bb9db03 100644 --- a/wgpu/src/renderer/widget/progress_bar.rs +++ b/wgpu/src/renderer/widget/progress_bar.rs @@ -1,7 +1,9 @@ -use crate::{Primitive, Renderer}; -use iced_native::{progress_bar, Background, Color, MouseCursor, Rectangle}; +use crate::{progress_bar::StyleSheet, Primitive, Renderer}; +use iced_native::{progress_bar, Color, MouseCursor, Rectangle}; impl progress_bar::Renderer for Renderer { + type Style = Box; + const DEFAULT_HEIGHT: u16 = 30; fn draw( @@ -9,9 +11,10 @@ impl progress_bar::Renderer for Renderer { bounds: Rectangle, range: std::ops::RangeInclusive, value: f32, - background: Option, - active_color: Option, + style_sheet: &Self::Style, ) -> Self::Output { + let style = style_sheet.style(); + let (range_start, range_end) = range.into_inner(); let active_progress_width = bounds.width * ((value - range_start) / (range_end - range_start).max(1.0)); @@ -19,31 +22,27 @@ impl progress_bar::Renderer for Renderer { let background = Primitive::Group { primitives: vec![Primitive::Quad { bounds: Rectangle { ..bounds }, - background: background - .unwrap_or(Background::Color([0.6, 0.6, 0.6].into())) - .into(), - border_radius: 5, + background: style.background, + border_radius: style.border_radius, border_width: 0, border_color: Color::TRANSPARENT, }], }; - let active_progress = Primitive::Quad { + let bar = Primitive::Quad { bounds: Rectangle { width: active_progress_width, ..bounds }, - background: Background::Color( - active_color.unwrap_or([0.0, 0.95, 0.0].into()), - ), - border_radius: 5, + background: style.bar, + border_radius: style.border_radius, border_width: 0, border_color: Color::TRANSPARENT, }; ( Primitive::Group { - primitives: vec![background, active_progress], + primitives: vec![background, bar], }, MouseCursor::OutOfBounds, ) diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs index 4b8d339e..3f3dc0b0 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -1,5 +1,6 @@ pub mod button; pub mod container; +pub mod progress_bar; pub mod scrollable; pub mod slider; pub mod text_input; diff --git a/wgpu/src/widget/progress_bar.rs b/wgpu/src/widget/progress_bar.rs new file mode 100644 index 00000000..34450b5e --- /dev/null +++ b/wgpu/src/widget/progress_bar.rs @@ -0,0 +1,15 @@ +//! 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; + +pub use iced_style::progress_bar::{Style, StyleSheet}; + +/// A bar that displays progress. +/// +/// This is an alias of an `iced_native` progress bar with an +/// `iced_wgpu::Renderer`. +pub type ProgressBar = iced_native::ProgressBar; -- cgit From 387fc0be26ccd1adc66c1dc80420f9b08d0c023a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Jan 2020 02:25:57 +0100 Subject: 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; -- cgit From ed30b487d649ffa0967ab8bfd66f4820ee2150fd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Jan 2020 02:54:54 +0100 Subject: Implement styling for `Checkbox` --- examples/styling.rs | 81 ++++++++++++++++++++++++++++++------ native/src/renderer/null.rs | 3 ++ native/src/widget/checkbox.rs | 41 ++++++++++-------- 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, 180 insertions(+), 46 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); - - let scrollable = Scrollable::new(&mut self.scroll) - .style(self.theme) - .push(Container::new(content).width(Length::Fill).center_x()); + .push(progress_bar) + .push(checkbox) + .into(); + + 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,29 +53,30 @@ 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`]. + /// Sets the width of the [`Checkbox`]. /// /// [`Checkbox`]: struct.Checkbox.html - pub fn label_color>(mut self, color: C) -> Self { - self.label_color = Some(color.into()); + pub fn width(mut self, width: Length) -> Self { + self.width = width; self } - /// Sets the width of the [`Checkbox`]. + /// Sets the style of the [`Checkbox`]. /// /// [`Checkbox`]: struct.Checkbox.html - pub fn width(mut self, width: Length) -> Self { - self.width = width; + 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; -- cgit From 3d26eb79c2b6a4ed4d186552b052c31235bd0b83 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Jan 2020 03:18:39 +0100 Subject: Always show scroller if scrollbar is visible --- wgpu/src/renderer/widget/scrollable.rs | 73 ++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/wgpu/src/renderer/widget/scrollable.rs b/wgpu/src/renderer/widget/scrollable.rs index 30d7f337..bfee7411 100644 --- a/wgpu/src/renderer/widget/scrollable.rs +++ b/wgpu/src/renderer/widget/scrollable.rs @@ -66,50 +66,53 @@ impl scrollable::Renderer for Renderer { ( if let Some(scrollbar) = scrollbar { - if is_mouse_over || state.is_scroller_grabbed() { - let style = if state.is_scroller_grabbed() { - style_sheet.dragging() - } else if is_mouse_over_scrollbar { - style_sheet.hovered() - } else { - style_sheet.active() - }; + let style = if state.is_scroller_grabbed() { + style_sheet.dragging() + } else if is_mouse_over_scrollbar { + style_sheet.hovered() + } else { + style_sheet.active() + }; + + let is_scrollbar_visible = + style.background.is_some() || style.border_width > 0; - let scroller = Primitive::Quad { + let scroller = if is_mouse_over + || state.is_scroller_grabbed() + || is_scrollbar_visible + { + Primitive::Quad { bounds: scrollbar.scroller.bounds, background: Background::Color(style.scroller.color), border_radius: style.scroller.border_radius, border_width: style.scroller.border_width, border_color: style.scroller.border_color, - }; - - if style.background.is_some() || style.border_width > 0 { - let scrollbar = Primitive::Quad { - bounds: Rectangle { - x: scrollbar.bounds.x - + f32::from(SCROLLBAR_MARGIN), - width: scrollbar.bounds.width - - f32::from(2 * SCROLLBAR_MARGIN), - ..scrollbar.bounds - }, - background: style.background.unwrap_or( - Background::Color(Color::TRANSPARENT), - ), - border_radius: style.border_radius, - border_width: style.border_width, - border_color: style.border_color, - }; + } + } else { + Primitive::None + }; - Primitive::Group { - primitives: vec![clip, scrollbar, scroller], - } - } else { - Primitive::Group { - primitives: vec![clip, scroller], - } + let scrollbar = if is_scrollbar_visible { + Primitive::Quad { + bounds: Rectangle { + x: scrollbar.bounds.x + f32::from(SCROLLBAR_MARGIN), + width: scrollbar.bounds.width + - f32::from(2 * SCROLLBAR_MARGIN), + ..scrollbar.bounds + }, + background: style + .background + .unwrap_or(Background::Color(Color::TRANSPARENT)), + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, } } else { - clip + Primitive::None + }; + + Primitive::Group { + primitives: vec![clip, scrollbar, scroller], } } else { clip -- cgit From 3e3f426af882d9b19815cb3a8cf7ac6fe65c88ea Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Jan 2020 03:19:00 +0100 Subject: Add `Scrollable` to `styling` example --- examples/styling.rs | 81 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/examples/styling.rs b/examples/styling.rs index 97095cff..a11bf155 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -1,7 +1,7 @@ use iced::{ - button, scrollable, slider, text_input, Button, Checkbox, Column, + button, scrollable, slider, text_input, Align, Button, Checkbox, Column, Container, Element, Length, ProgressBar, Radio, Row, Sandbox, Scrollable, - Settings, Slider, Text, TextInput, + Settings, Slider, Space, Text, TextInput, }; pub fn main() { @@ -17,7 +17,7 @@ struct Styling { button: button::State, slider: slider::State, slider_value: f32, - debug: bool, + toggle_value: bool, } #[derive(Debug, Clone)] @@ -26,7 +26,7 @@ enum Message { InputChanged(String), ButtonPressed, SliderChanged(f32), - DebugToggled(bool), + CheckboxToggled(bool), } impl Sandbox for Styling { @@ -46,7 +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, + Message::CheckboxToggled(value) => self.toggle_value = value, } } @@ -92,14 +92,21 @@ impl Sandbox for Styling { let progress_bar = ProgressBar::new(0.0..=100.0, self.slider_value).style(self.theme); + let scrollable = Scrollable::new(&mut self.scroll) + .height(Length::Units(100)) + .style(self.theme) + .push(Text::new("Scroll me!")) + .push(Space::with_height(Length::Units(800))) + .push(Text::new("You did it!")); + let checkbox = Checkbox::new( - self.debug, - "Enable layout debugger", - Message::DebugToggled, + self.toggle_value, + "Toggle me!", + Message::CheckboxToggled, ) .style(self.theme); - let content: Element<_> = Column::new() + let content = Column::new() .spacing(20) .padding(20) .max_width(600) @@ -107,23 +114,18 @@ impl Sandbox for Styling { .push(Row::new().spacing(10).push(text_input).push(button)) .push(slider) .push(progress_bar) - .push(checkbox) - .into(); - - 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(), + .push( + Row::new() + .spacing(10) + .align_items(Align::Center) + .push(scrollable) + .push(checkbox), ); - Container::new(scrollable) + Container::new(content) .width(Length::Fill) .height(Length::Fill) + .center_x() .center_y() .style(self.theme) .into() @@ -394,25 +396,44 @@ mod style { impl scrollable::StyleSheet for Scrollable { fn active(&self) -> scrollable::Scrollbar { scrollable::Scrollbar { - background: None, + background: Some(Background::Color(SURFACE)), border_radius: 2, border_width: 0, border_color: Color::TRANSPARENT, scroller: scrollable::Scroller { - color: [1.0, 1.0, 1.0, 0.7].into(), + color: ACTIVE, border_radius: 2, - border_width: 1, - border_color: Color::WHITE, + border_width: 0, + border_color: Color::TRANSPARENT, }, } } fn hovered(&self) -> scrollable::Scrollbar { + let active = self.active(); + scrollable::Scrollbar { - background: Some(Background::Color( - [1.0, 1.0, 1.0, 0.3].into(), - )), - ..self.active() + background: Some(Background::Color(Color { + a: 0.5, + ..SURFACE + })), + scroller: scrollable::Scroller { + color: HOVERED, + ..active.scroller + }, + ..active + } + } + + fn dragging(&self) -> scrollable::Scrollbar { + let hovered = self.hovered(); + + scrollable::Scrollbar { + scroller: scrollable::Scroller { + color: Color::from_rgb(0.85, 0.85, 0.85), + ..hovered.scroller + }, + ..hovered } } } -- cgit From f7a8b6983c2af491803bf84f341d8069045ffb5e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Jan 2020 04:56:32 +0100 Subject: Remove `Mul` implementation for `Vector` --- core/src/vector.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/core/src/vector.rs b/core/src/vector.rs index 75c77dbd..1c09ee3e 100644 --- a/core/src/vector.rs +++ b/core/src/vector.rs @@ -32,17 +32,6 @@ where } } -impl std::ops::Mul for Vector -where - T: std::ops::Mul, -{ - type Output = Self; - - fn mul(self, b: Self) -> Self { - Self::new(self.x * b.x, self.y * b.y) - } -} - impl Default for Vector where T: Default, -- cgit From 08faaaf6234e93acb3b0dc8e10bf9c4cb266f21c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Jan 2020 05:19:01 +0100 Subject: Color borders of `Checkbox` and `Radio` in `styling` example --- examples/styling.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/styling.rs b/examples/styling.rs index a11bf155..f0edfd82 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -311,9 +311,9 @@ mod style { fn active(&self) -> radio::Style { radio::Style { background: Background::Color(SURFACE), - dot_color: Color::WHITE, - border_width: 0, - border_color: Color::TRANSPARENT, + dot_color: ACTIVE, + border_width: 1, + border_color: ACTIVE, } } @@ -496,10 +496,10 @@ mod style { fn active(&self) -> checkbox::Style { checkbox::Style { background: Background::Color(SURFACE), - checkmark_color: Color::WHITE, + checkmark_color: ACTIVE, border_radius: 2, - border_width: 0, - border_color: Color::TRANSPARENT, + border_width: 1, + border_color: ACTIVE, } } -- cgit From cc529a1803972604b122c19c0104e71532fff993 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 8 Jan 2020 03:22:05 +0100 Subject: Add a note about cross-compatibility in `master` Fixes #145 --- web/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web/README.md b/web/README.md index 762f6c83..6a3da7b4 100644 --- a/web/README.md +++ b/web/README.md @@ -39,7 +39,11 @@ cargo build --example tour --target wasm32-unknown-unknown wasm-bindgen ../target/wasm32-unknown-unknown/debug/examples/tour.wasm --out-dir tour --web ``` -Then, we need to create an `.html` file to load our application: +*__Note:__ Keep in mind that Iced is still in early exploration stages and most of the work needs to happen on the native side of the ecosystem. At this stage, it is important to be able to batch work without having to constantly jump back and forth. Because of this, there is currently no requirement for the `master` branch to contain a cross-platform API at all times. If you hit an issue when building an example and want to help, it may be a good way to [start contributing]!* + +[start contributing]: ../CONTRIBUTING.md + +Once the example is compiled, we need to create an `.html` file to load our application: ```html -- cgit From cae4463e8379cddefbd8322a40ad8957bce07215 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 8 Jan 2020 03:30:15 +0100 Subject: Allow `Checkbox` style to change based on its state --- examples/styling.rs | 19 +++++++++++++------ style/src/checkbox.rs | 10 +++++----- wgpu/src/renderer/widget/checkbox.rs | 4 ++-- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/examples/styling.rs b/examples/styling.rs index f0edfd82..59c9c734 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -493,20 +493,27 @@ mod style { pub struct Checkbox; impl checkbox::StyleSheet for Checkbox { - fn active(&self) -> checkbox::Style { + fn active(&self, is_checked: bool) -> checkbox::Style { checkbox::Style { - background: Background::Color(SURFACE), - checkmark_color: ACTIVE, + background: Background::Color(if is_checked { + ACTIVE + } else { + SURFACE + }), + checkmark_color: Color::WHITE, border_radius: 2, border_width: 1, border_color: ACTIVE, } } - fn hovered(&self) -> checkbox::Style { + fn hovered(&self, is_checked: bool) -> checkbox::Style { checkbox::Style { - background: Background::Color(Color { a: 0.5, ..SURFACE }), - ..self.active() + background: Background::Color(Color { + a: 0.8, + ..if is_checked { ACTIVE } else { SURFACE } + }), + ..self.active(is_checked) } } } diff --git a/style/src/checkbox.rs b/style/src/checkbox.rs index e84dfd18..3c645f15 100644 --- a/style/src/checkbox.rs +++ b/style/src/checkbox.rs @@ -13,15 +13,15 @@ pub struct Style { /// A set of rules that dictate the style of a checkbox. pub trait StyleSheet { - fn active(&self) -> Style; + fn active(&self, is_checked: bool) -> Style; - fn hovered(&self) -> Style; + fn hovered(&self, is_checked: bool) -> Style; } struct Default; impl StyleSheet for Default { - fn active(&self) -> Style { + fn active(&self, _is_checked: bool) -> 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), @@ -31,10 +31,10 @@ impl StyleSheet for Default { } } - fn hovered(&self) -> Style { + fn hovered(&self, is_checked: bool) -> Style { Style { background: Background::Color(Color::from_rgb(0.90, 0.90, 0.90)), - ..self.active() + ..self.active(is_checked) } } } diff --git a/wgpu/src/renderer/widget/checkbox.rs b/wgpu/src/renderer/widget/checkbox.rs index cd90be5e..17121eea 100644 --- a/wgpu/src/renderer/widget/checkbox.rs +++ b/wgpu/src/renderer/widget/checkbox.rs @@ -21,9 +21,9 @@ impl checkbox::Renderer for Renderer { style_sheet: &Self::Style, ) -> Self::Output { let style = if is_mouse_over { - style_sheet.hovered() + style_sheet.hovered(is_checked) } else { - style_sheet.active() + style_sheet.active(is_checked) }; let checkbox = Primitive::Quad { -- cgit From 89b1ac6eac03bec9e2acb9b4d59da86e59d26153 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 8 Jan 2020 03:32:38 +0100 Subject: Fix drawing empty `Quad` on empty `ProgressBar` --- wgpu/src/renderer/widget/progress_bar.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/wgpu/src/renderer/widget/progress_bar.rs b/wgpu/src/renderer/widget/progress_bar.rs index 8bb9db03..34e33276 100644 --- a/wgpu/src/renderer/widget/progress_bar.rs +++ b/wgpu/src/renderer/widget/progress_bar.rs @@ -29,20 +29,24 @@ impl progress_bar::Renderer for Renderer { }], }; - let bar = Primitive::Quad { - bounds: Rectangle { - width: active_progress_width, - ..bounds - }, - background: style.bar, - border_radius: style.border_radius, - border_width: 0, - border_color: Color::TRANSPARENT, - }; - ( - Primitive::Group { - primitives: vec![background, bar], + if active_progress_width > 0.0 { + let bar = Primitive::Quad { + bounds: Rectangle { + width: active_progress_width, + ..bounds + }, + background: style.bar, + border_radius: style.border_radius, + border_width: 0, + border_color: Color::TRANSPARENT, + }; + + Primitive::Group { + primitives: vec![background, bar], + } + } else { + background }, MouseCursor::OutOfBounds, ) -- cgit From 775500cf1f5a14afacdc0bb6875136a4fd3369a4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 9 Jan 2020 06:05:26 +0100 Subject: Remove leftover `debug_color` in `styling` example --- examples/styling.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/examples/styling.rs b/examples/styling.rs index 59c9c734..50095ec7 100644 --- a/examples/styling.rs +++ b/examples/styling.rs @@ -135,7 +135,7 @@ impl Sandbox for Styling { mod style { use iced::{ button, checkbox, container, progress_bar, radio, scrollable, slider, - text_input, Color, + text_input, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -146,13 +146,6 @@ 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 { -- cgit From 7b278755fc7929633b5771824beac4d39b16e82e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 9 Jan 2020 18:31:07 +0100 Subject: Write missing docs and reenable deny statements --- native/src/lib.rs | 2 +- native/src/renderer.rs | 5 +++++ native/src/renderer/null.rs | 3 +++ native/src/renderer/windowed.rs | 1 + native/src/widget/button.rs | 4 ++++ native/src/widget/checkbox.rs | 1 + native/src/widget/container.rs | 8 ++++++++ native/src/widget/progress_bar.rs | 1 + native/src/widget/radio.rs | 1 + native/src/widget/scrollable.rs | 1 + native/src/widget/slider.rs | 1 + native/src/widget/text_input.rs | 1 + src/lib.rs | 2 +- src/settings.rs | 14 ++++---------- wgpu/src/defaults.rs | 5 +++++ wgpu/src/lib.rs | 2 +- wgpu/src/settings.rs | 6 ++++++ wgpu/src/widget.rs | 26 ++++++++++++++++++++++++++ 18 files changed, 71 insertions(+), 13 deletions(-) diff --git a/native/src/lib.rs b/native/src/lib.rs index 9d237196..8dcacb2b 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.rs b/native/src/renderer.rs index 90cec6c8..284c95f6 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -44,6 +44,11 @@ pub trait Renderer: Sized { /// [`Renderer`]: trait.Renderer.html type Output; + /// The default styling attributes of the [`Renderer`]. + /// + /// This type can be leveraged to implement style inheritance. + /// + /// [`Renderer`]: trait.Renderer.html type Defaults: Default; /// Lays out the elements of a user interface. diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index bd5bba23..df261cdc 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -11,6 +11,9 @@ use crate::{ pub struct Null; impl Null { + /// Creates a new [`Null`] renderer. + /// + /// [`Null`]: struct.Null.html pub fn new() -> Null { Null } diff --git a/native/src/renderer/windowed.rs b/native/src/renderer/windowed.rs index c3266e6c..ee020ab1 100644 --- a/native/src/renderer/windowed.rs +++ b/native/src/renderer/windowed.rs @@ -4,6 +4,7 @@ use raw_window_handle::HasRawWindowHandle; /// A renderer that can target windows. pub trait Windowed: super::Renderer + Sized { + /// The settings of the renderer. type Settings: Default; /// The type of target. diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 75ef2693..51b02172 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -114,6 +114,9 @@ where self } + /// Sets the style of the [`Button`]. + /// + /// [`Button`]: struct.Button.html pub fn style(mut self, style: impl Into) -> Self { self.style = style.into(); self @@ -246,6 +249,7 @@ where /// [`Button`]: struct.Button.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + /// The style supported by this renderer. type Style: Default; /// Draws a [`Button`]. diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index d88a59f4..95165997 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -183,6 +183,7 @@ where /// [`Checkbox`]: struct.Checkbox.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + /// The style supported by this renderer. type Style: Default; /// Returns the default size of a [`Checkbox`]. diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index abe83264..5682fc87 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -183,7 +183,15 @@ where } } +/// The renderer of a [`Container`]. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`Container`] in your user interface. +/// +/// [`Container`]: struct.Container.html +/// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + /// The style supported by this renderer. type Style: Default; /// Draws a [`Container`]. diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs index 0bc860dd..67d1ab83 100644 --- a/native/src/widget/progress_bar.rs +++ b/native/src/widget/progress_bar.rs @@ -127,6 +127,7 @@ where /// [`ProgressBar`]: struct.ProgressBar.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + /// The style supported by this renderer. type Style: Default; /// The default height of a [`ProgressBar`]. diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index b0a6080b..99743ec3 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -181,6 +181,7 @@ where /// [`Radio`]: struct.Radio.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + /// The style supported by this renderer. type Style: Default; /// Returns the default size of a [`Radio`] button. diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index a062abd0..e83f25af 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -458,6 +458,7 @@ pub struct Scroller { /// [`Scrollable`]: struct.Scrollable.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + /// The style supported by this renderer. type Style: Default; /// Returns the [`Scrollbar`] given the bounds and content bounds of a diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index c35a933e..008203fe 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -217,6 +217,7 @@ where /// [`Slider`]: struct.Slider.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer { + /// The style supported by this renderer. type Style: Default; /// Returns the height of the [`Slider`]. diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index efbd65c8..25032559 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -407,6 +407,7 @@ where /// [`TextInput`]: struct.TextInput.html /// [renderer]: ../../renderer/index.html pub trait Renderer: crate::Renderer + Sized { + /// The style supported by this renderer. type Style: Default; /// Returns the default size of the text of the [`TextInput`]. diff --git a/src/lib.rs b/src/lib.rs index 579ff43d..1ef11378 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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/settings.rs b/src/settings.rs index b725ef9f..e20edc97 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,7 +1,7 @@ //! Configure your application. /// The settings of an application. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct Settings { /// The [`Window`] settings. /// @@ -10,19 +10,13 @@ pub struct Settings { /// [`Window`]: struct.Window.html pub window: Window, + /// The bytes of the font that will be used by default. + /// + /// If `None` is provided, a default system font will be chosen. // TODO: Add `name` for web compatibility pub default_font: Option<&'static [u8]>, } -impl Default for Settings { - fn default() -> Settings { - Settings { - window: Window::default(), - default_font: None, - } - } -} - /// The window settings of an application. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Window { diff --git a/wgpu/src/defaults.rs b/wgpu/src/defaults.rs index 8de8258b..11718a87 100644 --- a/wgpu/src/defaults.rs +++ b/wgpu/src/defaults.rs @@ -1,7 +1,10 @@ +//! Use default styling attributes to inherit styles. use iced_native::Color; +/// Some default styling attributes. #[derive(Debug, Clone, Copy)] pub struct Defaults { + /// Text styling pub text: Text, } @@ -13,8 +16,10 @@ impl Default for Defaults { } } +/// Some default text styling attributes. #[derive(Debug, Clone, Copy)] pub struct Text { + /// The default color of text pub color: Color, } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index dda4f322..ab14987c 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -19,7 +19,7 @@ //! [`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)] diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs index c6d8369b..dbe81830 100644 --- a/wgpu/src/settings.rs +++ b/wgpu/src/settings.rs @@ -1,4 +1,10 @@ +/// The settings of a [`Renderer`]. +/// +/// [`Renderer`]: struct.Renderer.html #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct Settings { + /// The bytes of the font that will be used by default. + /// + /// If `None` is provided, a default system font will be chosen. pub default_font: Option<&'static [u8]>, } diff --git a/wgpu/src/widget.rs b/wgpu/src/widget.rs index e0f56594..e3edda0b 100644 --- a/wgpu/src/widget.rs +++ b/wgpu/src/widget.rs @@ -1,3 +1,12 @@ +//! Use the widgets supported out-of-the-box. +//! +//! # Re-exports +//! For convenience, the contents of this module are available at the root +//! module. Therefore, you can directly type: +//! +//! ``` +//! use iced_wgpu::{button, Button}; +//! ``` pub mod button; pub mod checkbox; pub mod container; @@ -6,3 +15,20 @@ pub mod radio; pub mod scrollable; pub mod slider; pub mod text_input; + +#[doc(no_inline)] +pub use button::Button; +#[doc(no_inline)] +pub use checkbox::Checkbox; +#[doc(no_inline)] +pub use container::Container; +#[doc(no_inline)] +pub use progress_bar::ProgressBar; +#[doc(no_inline)] +pub use radio::Radio; +#[doc(no_inline)] +pub use scrollable::Scrollable; +#[doc(no_inline)] +pub use slider::Slider; +#[doc(no_inline)] +pub use text_input::TextInput; -- cgit From 7ab6ed7ef912011318e07533e40fc98f4a846511 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 10 Jan 2020 01:28:45 +0100 Subject: Add `window::Event::Resized` to `iced_native` --- native/src/event.rs | 8 +++++++- native/src/lib.rs | 1 + native/src/window.rs | 4 ++++ native/src/window/event.rs | 12 ++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 native/src/window.rs create mode 100644 native/src/window/event.rs diff --git a/native/src/event.rs b/native/src/event.rs index 71f06006..1d28aa7b 100644 --- a/native/src/event.rs +++ b/native/src/event.rs @@ -1,4 +1,7 @@ -use crate::input::{keyboard, mouse}; +use crate::{ + input::{keyboard, mouse}, + window, +}; /// A user interface event. /// @@ -13,4 +16,7 @@ pub enum Event { /// A mouse event Mouse(mouse::Event), + + /// A window event + Window(window::Event), } diff --git a/native/src/lib.rs b/native/src/lib.rs index 8dcacb2b..e65c6596 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -44,6 +44,7 @@ pub mod layout; pub mod renderer; pub mod subscription; pub mod widget; +pub mod window; mod clipboard; mod element; diff --git a/native/src/window.rs b/native/src/window.rs new file mode 100644 index 00000000..220bb3be --- /dev/null +++ b/native/src/window.rs @@ -0,0 +1,4 @@ +//! Build window-based GUI applications. +mod event; + +pub use event::Event; diff --git a/native/src/window/event.rs b/native/src/window/event.rs new file mode 100644 index 00000000..89ec0a0c --- /dev/null +++ b/native/src/window/event.rs @@ -0,0 +1,12 @@ +/// A window-related event. +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum Event { + /// A window was resized + Resized { + /// The new width of the window (in units) + width: u32, + + /// The new height of the window (in units) + height: u32, + }, +} -- cgit From d15d1156bd0c7fa111d1c59bea11fd58b9cc63b1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 10 Jan 2020 01:34:41 +0100 Subject: Produce `window::Event::Resized` in `iced_winit` --- winit/src/application.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/winit/src/application.rs b/winit/src/application.rs index da943660..8529c99c 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -2,8 +2,8 @@ use crate::{ container, conversion, input::{keyboard, mouse}, renderer::{Target, Windowed}, - subscription, Cache, Clipboard, Command, Container, Debug, Element, Event, - Length, MouseCursor, Settings, Subscription, UserInterface, + subscription, window, Cache, Clipboard, Command, Container, Debug, Element, + Event, Length, MouseCursor, Settings, Subscription, UserInterface, }; /// An interactive, native cross-platform application. @@ -373,10 +373,13 @@ pub trait Application: Sized { *control_flow = ControlFlow::Exit; } WindowEvent::Resized(new_size) => { + events.push(Event::Window(window::Event::Resized { + width: new_size.width.round() as u32, + height: new_size.height.round() as u32, + })); + size = new_size; resized = true; - - log::debug!("Resized: {:?}", new_size); } _ => {} }, -- cgit From d1bf3f02c7161e35e40f89fd0a6b3da9834a1616 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 10 Jan 2020 04:58:13 +0100 Subject: Fix flex layout cross-alignment when not filled --- native/src/layout/flex.rs | 2 +- native/src/layout/limits.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/native/src/layout/flex.rs b/native/src/layout/flex.rs index 03b13e38..bd37b75a 100644 --- a/native/src/layout/flex.rs +++ b/native/src/layout/flex.rs @@ -77,7 +77,7 @@ where let max_cross = axis.cross(limits.max()); let mut fill_sum = 0; - let mut cross = axis.cross(limits.min()); + let mut cross = axis.cross(limits.min()).max(axis.cross(limits.fill())); let mut available = axis.main(limits.max()) - total_spacing; let mut nodes: Vec = Vec::with_capacity(items.len()); diff --git a/native/src/layout/limits.rs b/native/src/layout/limits.rs index a35f7ff7..b674b58a 100644 --- a/native/src/layout/limits.rs +++ b/native/src/layout/limits.rs @@ -44,6 +44,14 @@ impl Limits { self.max } + /// Returns the fill [`Size`] of the [`Limits`]. + /// + /// [`Limits`]: struct.Limits.html + /// [`Size`]: ../struct.Size.html + pub fn fill(&self) -> Size { + self.fill + } + /// Applies a width constraint to the current [`Limits`]. /// /// [`Limits`]: struct.Limits.html -- cgit