From 5af4159848341b14f6ff9ae14a9a222d8d8b0fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 1 Jan 2020 18:26:49 +0100 Subject: [PATCH] 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>;