From 7b369842959511f17d5c27941fd0308484bff8ea Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Mon, 13 Feb 2023 11:38:05 +0100 Subject: feat: added handle to text_input --- examples/text_input/Cargo.toml | 9 +++ examples/text_input/README.md | 10 +++ examples/text_input/fonts/icons.ttf | Bin 0 -> 1612 bytes examples/text_input/src/main.rs | 93 ++++++++++++++++++++++++++++ native/src/widget/text_input.rs | 119 +++++++++++++++++++++++++++++++++++- src/widget.rs | 2 +- style/src/text_input.rs | 2 + style/src/theme.rs | 3 + 8 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 examples/text_input/Cargo.toml create mode 100644 examples/text_input/README.md create mode 100644 examples/text_input/fonts/icons.ttf create mode 100644 examples/text_input/src/main.rs diff --git a/examples/text_input/Cargo.toml b/examples/text_input/Cargo.toml new file mode 100644 index 00000000..5937ef8e --- /dev/null +++ b/examples/text_input/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "text_input" +version = "0.1.0" +authors = ["Casper Rogild Storm"] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../..", features = ["debug"] } diff --git a/examples/text_input/README.md b/examples/text_input/README.md new file mode 100644 index 00000000..2b2d8059 --- /dev/null +++ b/examples/text_input/README.md @@ -0,0 +1,10 @@ +## TextInput + +A `TextInput` is a field that can be filled with text. + +You can run it with `cargo run`: +``` +cargo run --package text_input +``` + +[`main`]: src/main.rs diff --git a/examples/text_input/fonts/icons.ttf b/examples/text_input/fonts/icons.ttf new file mode 100644 index 00000000..bfe8a24b Binary files /dev/null and b/examples/text_input/fonts/icons.ttf differ diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs new file mode 100644 index 00000000..b25ed7e1 --- /dev/null +++ b/examples/text_input/src/main.rs @@ -0,0 +1,93 @@ +use iced::widget::{checkbox, column, container, text_input}; +use iced::{Element, Font, Length, Sandbox, Settings}; + +const ICON_FONT: Font = Font::External { + name: "Icons", + bytes: include_bytes!("../fonts/icons.ttf"), +}; + +pub fn main() -> iced::Result { + Example::run(Settings::default()) +} + +#[derive(Default)] +struct Example { + value: String, + is_showing_handle: bool, +} + +#[derive(Debug, Clone)] +enum Message { + Changed(String), + ToggleHandle(bool), +} + +impl Sandbox for Example { + type Message = Message; + + fn new() -> Self { + Self::default() + } + + fn title(&self) -> String { + String::from("Text Input - Iced") + } + + fn update(&mut self, message: Message) { + match message { + Message::Changed(value) => self.value = value, + Message::ToggleHandle(_) => { + self.is_showing_handle = !self.is_showing_handle + } + } + } + + fn view(&self) -> Element { + let checkbox = + checkbox("Handle", self.is_showing_handle, Message::ToggleHandle) + .spacing(5) + .text_size(16); + + let mut text_input = + text_input("Placeholder", self.value.as_str(), Message::Changed); + + if self.is_showing_handle { + text_input = text_input.handle(text_input::Handle { + font: ICON_FONT, + text: String::from('\u{e900}'), + size: Some(18), + position: text_input::HandlePosition::Right, + }); + } + + let content = column!["What is blazing fast?", text_input, checkbox] + .width(Length::Units(200)) + .spacing(10); + + container(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } + + fn theme(&self) -> iced::Theme { + iced::Theme::default() + } + + fn style(&self) -> iced::theme::Application { + iced::theme::Application::default() + } + + fn scale_factor(&self) -> f64 { + 1.0 + } + + fn run(settings: Settings<()>) -> Result<(), iced::Error> + where + Self: 'static + Sized, + { + ::run(settings) + } +} diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index ee0473ea..7696da99 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -31,6 +31,47 @@ use crate::{ pub use iced_style::text_input::{Appearance, StyleSheet}; +/// The position of the [`Handle`]. +#[derive(Clone, Default, Debug)] +pub enum HandlePosition { + /// Position the handle to the left. + Left, + /// Position the handle to the left. + /// + /// This is the default. + #[default] + Right, +} + +/// The content of the [`Handle`]. +#[derive(Clone)] +pub struct Handle +where + Renderer: text::Renderer, +{ + /// Font that will be used to display the `text`. + pub font: Renderer::Font, + /// Text that will be shown. + pub text: String, + /// Font size of the content. + pub size: Option, + /// Position of the handle. + pub position: HandlePosition, +} + +impl std::fmt::Debug for Handle +where + Renderer: text::Renderer, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Handle") + .field("text", &self.text) + .field("size", &self.size) + .field("position", &self.position) + .finish() + } +} + /// A field that can be filled with text. /// /// # Example @@ -68,6 +109,7 @@ where on_change: Box Message + 'a>, on_paste: Option Message + 'a>>, on_submit: Option, + handle: Option>, style: ::Style, } @@ -99,6 +141,7 @@ where on_change: Box::new(on_change), on_paste: None, on_submit: None, + handle: None, style: Default::default(), } } @@ -132,6 +175,13 @@ where self.font = font; self } + + /// Sets the [`Handle`] of the [`TextInput`]. + pub fn handle(mut self, handle: Handle) -> Self { + self.handle = Some(handle); + self + } + /// Sets the width of the [`TextInput`]. pub fn width(mut self, width: impl Into) -> Self { self.width = width.into(); @@ -188,8 +238,10 @@ where value.unwrap_or(&self.value), &self.placeholder, self.size, + self.padding, &self.font, self.is_secure, + self.handle.as_ref(), &self.style, ) } @@ -286,8 +338,10 @@ where &self.value, &self.placeholder, self.size, + self.padding, &self.font, self.is_secure, + self.handle.as_ref(), &self.style, ) } @@ -812,8 +866,10 @@ pub fn draw( value: &Value, placeholder: &str, size: Option, + padding: Padding, font: &Renderer::Font, is_secure: bool, + handle: Option<&Handle>, style: &::Style, ) where Renderer: text::Renderer, @@ -823,7 +879,37 @@ pub fn draw( let value = secure_value.as_ref().unwrap_or(value); let bounds = layout.bounds(); - let text_bounds = layout.children().next().unwrap().bounds(); + let text_bounds = { + let bounds = layout.children().next().unwrap().bounds(); + if let Some(handle) = handle { + let Handle { + font, + size, + text, + position, + } = handle; + + let padding = f32::from(padding.horizontal()); + let size = size.unwrap_or_else(|| renderer.default_size()); + let width = + renderer.measure_width(text.as_str(), size, font.clone()); + + match position { + HandlePosition::Left => Rectangle { + x: bounds.x + (width + padding), + width: bounds.width - (width + padding), + ..bounds + }, + HandlePosition::Right => Rectangle { + x: bounds.x, + width: bounds.width - (width + padding), + ..bounds + }, + } + } else { + bounds + } + }; let is_mouse_over = bounds.contains(cursor_position); @@ -845,6 +931,37 @@ pub fn draw( appearance.background, ); + if let Some(handle) = handle { + let Handle { + size, + font, + text, + position, + } = handle; + + let padding = f32::from(padding.horizontal()); + let size = size.unwrap_or_else(|| renderer.default_size()); + let width = renderer.measure_width(text.as_str(), size, font.clone()); + + renderer.fill_text(Text { + content: text, + size: f32::from(size), + font: font.clone(), + color: appearance.handle_color, + bounds: Rectangle { + x: match position { + HandlePosition::Left => bounds.x + width + padding, + HandlePosition::Right => bounds.x + bounds.width - padding, + }, + y: bounds.center_y() - f32::from(size) / 2.0, + height: f32::from(size), + ..bounds + }, + horizontal_alignment: alignment::Horizontal::Right, + vertical_alignment: alignment::Vertical::Top, + }); + } + let text = value.to_string(); let size = size.unwrap_or_else(|| renderer.default_size()); diff --git a/src/widget.rs b/src/widget.rs index e2b0537e..948456d3 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -124,7 +124,7 @@ pub mod text_input { //! Display fields that can be filled with text. pub use iced_native::widget::text_input::{ focus, move_cursor_to, move_cursor_to_end, move_cursor_to_front, - select_all, Appearance, Id, StyleSheet, + select_all, Appearance, Handle, HandlePosition, Id, StyleSheet, }; /// A field that can be filled with text. diff --git a/style/src/text_input.rs b/style/src/text_input.rs index d97016dc..5e703e61 100644 --- a/style/src/text_input.rs +++ b/style/src/text_input.rs @@ -12,6 +12,8 @@ pub struct Appearance { pub border_width: f32, /// The border [`Color`] of the text input. pub border_color: Color, + /// The handle [`Color`] of the text input. + pub handle_color: Color, } /// A set of rules that dictate the style of a text input. diff --git a/style/src/theme.rs b/style/src/theme.rs index 0ebd82a4..de46d7d4 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -1028,6 +1028,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.background.strong.color, + handle_color: palette.background.weak.text, } } @@ -1043,6 +1044,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.background.base.text, + handle_color: palette.background.weak.text, } } @@ -1058,6 +1060,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.primary.strong.color, + handle_color: palette.primary.weak.text, } } -- cgit From bfc5db9009f10a67ed277ce9d1997bcdea3f6acd Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Mon, 13 Feb 2023 12:00:08 +0100 Subject: Updated `handle_color` for focused state --- style/src/theme.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style/src/theme.rs b/style/src/theme.rs index de46d7d4..4837e10b 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -1060,7 +1060,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.primary.strong.color, - handle_color: palette.primary.weak.text, + handle_color: palette.background.weak.text, } } -- cgit From d24a4a46895ed711ddfc3199a0445f0b69a812e4 Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Thu, 16 Feb 2023 14:32:59 +0100 Subject: Changed `Handle` to `Icon` to be consistent --- examples/text_input/src/main.rs | 18 ++++----- native/src/widget/text_input.rs | 87 +++++++++++++++++++++-------------------- src/widget.rs | 2 +- style/src/text_input.rs | 4 +- style/src/theme.rs | 6 +-- 5 files changed, 59 insertions(+), 58 deletions(-) diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs index b25ed7e1..e0ba1983 100644 --- a/examples/text_input/src/main.rs +++ b/examples/text_input/src/main.rs @@ -13,13 +13,13 @@ pub fn main() -> iced::Result { #[derive(Default)] struct Example { value: String, - is_showing_handle: bool, + is_showing_icon: bool, } #[derive(Debug, Clone)] enum Message { Changed(String), - ToggleHandle(bool), + ToggleIcon(bool), } impl Sandbox for Example { @@ -36,27 +36,27 @@ impl Sandbox for Example { fn update(&mut self, message: Message) { match message { Message::Changed(value) => self.value = value, - Message::ToggleHandle(_) => { - self.is_showing_handle = !self.is_showing_handle + Message::ToggleIcon(_) => { + self.is_showing_icon = !self.is_showing_icon } } } fn view(&self) -> Element { let checkbox = - checkbox("Handle", self.is_showing_handle, Message::ToggleHandle) + checkbox("Icon", self.is_showing_icon, Message::ToggleIcon) .spacing(5) .text_size(16); let mut text_input = text_input("Placeholder", self.value.as_str(), Message::Changed); - if self.is_showing_handle { - text_input = text_input.handle(text_input::Handle { + if self.is_showing_icon { + text_input = text_input.icon(text_input::Icon { font: ICON_FONT, - text: String::from('\u{e900}'), + code_point: '\u{e900}', size: Some(18), - position: text_input::HandlePosition::Right, + position: text_input::IconPosition::Right, }); } diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 7696da99..575f4436 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -31,41 +31,38 @@ use crate::{ pub use iced_style::text_input::{Appearance, StyleSheet}; -/// The position of the [`Handle`]. +/// The position of the [`Icon`]. #[derive(Clone, Default, Debug)] -pub enum HandlePosition { - /// Position the handle to the left. +pub enum IconPosition { + /// Position the [`Icon`] to the left. Left, - /// Position the handle to the left. + /// Position the [`Icon`] to the left. /// /// This is the default. #[default] Right, } -/// The content of the [`Handle`]. +/// The content of the [`Icon`]. #[derive(Clone)] -pub struct Handle -where - Renderer: text::Renderer, -{ - /// Font that will be used to display the `text`. - pub font: Renderer::Font, - /// Text that will be shown. - pub text: String, +pub struct Icon { + /// Font that will be used to display the `code_point`. + pub font: Font, + /// The unicode code point that will be used as the icon. + pub code_point: char, /// Font size of the content. pub size: Option, - /// Position of the handle. - pub position: HandlePosition, + /// Position of the icon. + pub position: IconPosition, } -impl std::fmt::Debug for Handle +impl std::fmt::Debug for Icon where Renderer: text::Renderer, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Handle") - .field("text", &self.text) + f.debug_struct("Icon") + .field("code_point", &self.code_point) .field("size", &self.size) .field("position", &self.position) .finish() @@ -109,7 +106,7 @@ where on_change: Box Message + 'a>, on_paste: Option Message + 'a>>, on_submit: Option, - handle: Option>, + icon: Option>, style: ::Style, } @@ -141,7 +138,7 @@ where on_change: Box::new(on_change), on_paste: None, on_submit: None, - handle: None, + icon: None, style: Default::default(), } } @@ -176,9 +173,9 @@ where self } - /// Sets the [`Handle`] of the [`TextInput`]. - pub fn handle(mut self, handle: Handle) -> Self { - self.handle = Some(handle); + /// Sets the [`Icon`] of the [`TextInput`]. + pub fn icon(mut self, icon: Icon) -> Self { + self.icon = Some(icon); self } @@ -241,7 +238,7 @@ where self.padding, &self.font, self.is_secure, - self.handle.as_ref(), + self.icon.as_ref(), &self.style, ) } @@ -341,7 +338,7 @@ where self.padding, &self.font, self.is_secure, - self.handle.as_ref(), + self.icon.as_ref(), &self.style, ) } @@ -869,7 +866,7 @@ pub fn draw( padding: Padding, font: &Renderer::Font, is_secure: bool, - handle: Option<&Handle>, + icon: Option<&Icon>, style: &::Style, ) where Renderer: text::Renderer, @@ -881,26 +878,29 @@ pub fn draw( let bounds = layout.bounds(); let text_bounds = { let bounds = layout.children().next().unwrap().bounds(); - if let Some(handle) = handle { - let Handle { + if let Some(icon) = icon { + let Icon { font, size, - text, + code_point, position, - } = handle; + } = icon; let padding = f32::from(padding.horizontal()); let size = size.unwrap_or_else(|| renderer.default_size()); - let width = - renderer.measure_width(text.as_str(), size, font.clone()); + let width = renderer.measure_width( + &code_point.to_string(), + size, + font.clone(), + ); match position { - HandlePosition::Left => Rectangle { + IconPosition::Left => Rectangle { x: bounds.x + (width + padding), width: bounds.width - (width + padding), ..bounds }, - HandlePosition::Right => Rectangle { + IconPosition::Right => Rectangle { x: bounds.x, width: bounds.width - (width + padding), ..bounds @@ -931,27 +931,28 @@ pub fn draw( appearance.background, ); - if let Some(handle) = handle { - let Handle { + if let Some(icon) = icon { + let Icon { size, font, - text, + code_point, position, - } = handle; + } = icon; let padding = f32::from(padding.horizontal()); let size = size.unwrap_or_else(|| renderer.default_size()); - let width = renderer.measure_width(text.as_str(), size, font.clone()); + let width = + renderer.measure_width(&code_point.to_string(), size, font.clone()); renderer.fill_text(Text { - content: text, + content: &code_point.to_string(), size: f32::from(size), font: font.clone(), - color: appearance.handle_color, + color: appearance.icon_color, bounds: Rectangle { x: match position { - HandlePosition::Left => bounds.x + width + padding, - HandlePosition::Right => bounds.x + bounds.width - padding, + IconPosition::Left => bounds.x + width + padding, + IconPosition::Right => bounds.x + bounds.width - padding, }, y: bounds.center_y() - f32::from(size) / 2.0, height: f32::from(size), diff --git a/src/widget.rs b/src/widget.rs index 948456d3..d3c16088 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -124,7 +124,7 @@ pub mod text_input { //! Display fields that can be filled with text. pub use iced_native::widget::text_input::{ focus, move_cursor_to, move_cursor_to_end, move_cursor_to_front, - select_all, Appearance, Handle, HandlePosition, Id, StyleSheet, + select_all, Appearance, Icon, IconPosition, Id, StyleSheet, }; /// A field that can be filled with text. diff --git a/style/src/text_input.rs b/style/src/text_input.rs index 5e703e61..73b05852 100644 --- a/style/src/text_input.rs +++ b/style/src/text_input.rs @@ -12,8 +12,8 @@ pub struct Appearance { pub border_width: f32, /// The border [`Color`] of the text input. pub border_color: Color, - /// The handle [`Color`] of the text input. - pub handle_color: Color, + /// The icon [`Color`] of the text input. + pub icon_color: Color, } /// A set of rules that dictate the style of a text input. diff --git a/style/src/theme.rs b/style/src/theme.rs index 4837e10b..0d974a19 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -1028,7 +1028,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.background.strong.color, - handle_color: palette.background.weak.text, + icon_color: palette.background.weak.text, } } @@ -1044,7 +1044,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.background.base.text, - handle_color: palette.background.weak.text, + icon_color: palette.background.weak.text, } } @@ -1060,7 +1060,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.primary.strong.color, - handle_color: palette.background.weak.text, + icon_color: palette.background.weak.text, } } -- cgit From 898307e9ac8e11de275d7d4d58b93a6f24a1e800 Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Mon, 20 Feb 2023 14:42:10 +0100 Subject: Removed text_input example in favor for Tour --- examples/text_input/Cargo.toml | 9 ---- examples/text_input/README.md | 10 ---- examples/text_input/fonts/icons.ttf | Bin 1612 -> 0 bytes examples/text_input/src/main.rs | 93 ------------------------------------ examples/tour/fonts/icons.ttf | Bin 0 -> 1612 bytes examples/tour/src/main.rs | 78 +++++++++++++++++++++++++----- 6 files changed, 66 insertions(+), 124 deletions(-) delete mode 100644 examples/text_input/Cargo.toml delete mode 100644 examples/text_input/README.md delete mode 100644 examples/text_input/fonts/icons.ttf delete mode 100644 examples/text_input/src/main.rs create mode 100644 examples/tour/fonts/icons.ttf diff --git a/examples/text_input/Cargo.toml b/examples/text_input/Cargo.toml deleted file mode 100644 index 5937ef8e..00000000 --- a/examples/text_input/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "text_input" -version = "0.1.0" -authors = ["Casper Rogild Storm"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../..", features = ["debug"] } diff --git a/examples/text_input/README.md b/examples/text_input/README.md deleted file mode 100644 index 2b2d8059..00000000 --- a/examples/text_input/README.md +++ /dev/null @@ -1,10 +0,0 @@ -## TextInput - -A `TextInput` is a field that can be filled with text. - -You can run it with `cargo run`: -``` -cargo run --package text_input -``` - -[`main`]: src/main.rs diff --git a/examples/text_input/fonts/icons.ttf b/examples/text_input/fonts/icons.ttf deleted file mode 100644 index bfe8a24b..00000000 Binary files a/examples/text_input/fonts/icons.ttf and /dev/null differ diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs deleted file mode 100644 index e0ba1983..00000000 --- a/examples/text_input/src/main.rs +++ /dev/null @@ -1,93 +0,0 @@ -use iced::widget::{checkbox, column, container, text_input}; -use iced::{Element, Font, Length, Sandbox, Settings}; - -const ICON_FONT: Font = Font::External { - name: "Icons", - bytes: include_bytes!("../fonts/icons.ttf"), -}; - -pub fn main() -> iced::Result { - Example::run(Settings::default()) -} - -#[derive(Default)] -struct Example { - value: String, - is_showing_icon: bool, -} - -#[derive(Debug, Clone)] -enum Message { - Changed(String), - ToggleIcon(bool), -} - -impl Sandbox for Example { - type Message = Message; - - fn new() -> Self { - Self::default() - } - - fn title(&self) -> String { - String::from("Text Input - Iced") - } - - fn update(&mut self, message: Message) { - match message { - Message::Changed(value) => self.value = value, - Message::ToggleIcon(_) => { - self.is_showing_icon = !self.is_showing_icon - } - } - } - - fn view(&self) -> Element { - let checkbox = - checkbox("Icon", self.is_showing_icon, Message::ToggleIcon) - .spacing(5) - .text_size(16); - - let mut text_input = - text_input("Placeholder", self.value.as_str(), Message::Changed); - - if self.is_showing_icon { - text_input = text_input.icon(text_input::Icon { - font: ICON_FONT, - code_point: '\u{e900}', - size: Some(18), - position: text_input::IconPosition::Right, - }); - } - - let content = column!["What is blazing fast?", text_input, checkbox] - .width(Length::Units(200)) - .spacing(10); - - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() - } - - fn theme(&self) -> iced::Theme { - iced::Theme::default() - } - - fn style(&self) -> iced::theme::Application { - iced::theme::Application::default() - } - - fn scale_factor(&self) -> f64 { - 1.0 - } - - fn run(settings: Settings<()>) -> Result<(), iced::Error> - where - Self: 'static + Sized, - { - ::run(settings) - } -} diff --git a/examples/tour/fonts/icons.ttf b/examples/tour/fonts/icons.ttf new file mode 100644 index 00000000..bfe8a24b Binary files /dev/null and b/examples/tour/fonts/icons.ttf differ diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index de063d00..5edee850 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -5,8 +5,14 @@ use iced::widget::{ scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; +use iced::Font; use iced::{Color, Element, Length, Renderer, Sandbox, Settings}; +const ICON_FONT: Font = Font::External { + name: "Icons", + bytes: include_bytes!("../fonts/icons.ttf"), +}; + pub fn main() -> iced::Result { env_logger::init(); @@ -127,6 +133,7 @@ impl Steps { Step::TextInput { value: String::new(), is_secure: false, + is_showing_icon: false, }, Step::Debugger, Step::End, @@ -171,14 +178,32 @@ impl Steps { enum Step { Welcome, - Slider { value: u8 }, - RowsAndColumns { layout: Layout, spacing: u16 }, - Text { size: u16, color: Color }, - Radio { selection: Option }, - Toggler { can_continue: bool }, - Image { width: u16 }, + Slider { + value: u8, + }, + RowsAndColumns { + layout: Layout, + spacing: u16, + }, + Text { + size: u16, + color: Color, + }, + Radio { + selection: Option, + }, + Toggler { + can_continue: bool, + }, + Image { + width: u16, + }, Scrollable, - TextInput { value: String, is_secure: bool }, + TextInput { + value: String, + is_secure: bool, + is_showing_icon: bool, + }, Debugger, End, } @@ -194,6 +219,7 @@ pub enum StepMessage { ImageWidthChanged(u16), InputChanged(String), ToggleSecureInput(bool), + ToggleTextInputIcon(bool), DebugToggled(bool), TogglerChanged(bool), } @@ -256,6 +282,14 @@ impl<'a> Step { *can_continue = value; } } + StepMessage::ToggleTextInputIcon(toggle) => { + if let Step::TextInput { + is_showing_icon, .. + } = self + { + *is_showing_icon = toggle + } + } }; } @@ -303,9 +337,11 @@ impl<'a> Step { Self::rows_and_columns(*layout, *spacing) } Step::Scrollable => Self::scrollable(), - Step::TextInput { value, is_secure } => { - Self::text_input(value, *is_secure) - } + Step::TextInput { + value, + is_secure, + is_showing_icon, + } => Self::text_input(value, *is_secure, *is_showing_icon), Step::Debugger => Self::debugger(debug), Step::End => Self::end(), } @@ -530,8 +566,12 @@ impl<'a> Step { ) } - fn text_input(value: &str, is_secure: bool) -> Column<'a, StepMessage> { - let text_input = text_input( + fn text_input( + value: &str, + is_secure: bool, + is_showing_icon: bool, + ) -> Column<'a, StepMessage> { + let mut text_input = text_input( "Type something to continue...", value, StepMessage::InputChanged, @@ -539,6 +579,15 @@ impl<'a> Step { .padding(10) .size(30); + if is_showing_icon { + text_input = text_input.icon(text_input::Icon { + font: ICON_FONT, + code_point: '\u{e900}', + size: Some(35), + position: text_input::IconPosition::Right, + }); + } + Self::container("Text input") .push("Use a text input to ask for different kinds of information.") .push(if is_secure { @@ -551,6 +600,11 @@ impl<'a> Step { is_secure, StepMessage::ToggleSecureInput, )) + .push(checkbox( + "Show icon", + is_showing_icon, + StepMessage::ToggleTextInputIcon, + )) .push( "A text input produces a message every time it changes. It is \ very easy to keep track of its contents:", -- cgit From 0e2fc99eb864800d2d1522c015054d84cad078f4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:13:56 +0200 Subject: Use `f32` for `Icon::size` and remove unnecessary conversions --- examples/tour/src/main.rs | 2 +- native/src/widget/text_input.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 5edee850..6a1380d7 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -583,7 +583,7 @@ impl<'a> Step { text_input = text_input.icon(text_input::Icon { font: ICON_FONT, code_point: '\u{e900}', - size: Some(35), + size: Some(35.0), position: text_input::IconPosition::Right, }); } diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 575f4436..95e3b720 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -51,7 +51,7 @@ pub struct Icon { /// The unicode code point that will be used as the icon. pub code_point: char, /// Font size of the content. - pub size: Option, + pub size: Option, /// Position of the icon. pub position: IconPosition, } @@ -886,7 +886,7 @@ pub fn draw( position, } = icon; - let padding = f32::from(padding.horizontal()); + let padding = padding.horizontal(); let size = size.unwrap_or_else(|| renderer.default_size()); let width = renderer.measure_width( &code_point.to_string(), @@ -939,14 +939,14 @@ pub fn draw( position, } = icon; - let padding = f32::from(padding.horizontal()); + let padding = padding.horizontal(); let size = size.unwrap_or_else(|| renderer.default_size()); let width = renderer.measure_width(&code_point.to_string(), size, font.clone()); renderer.fill_text(Text { content: &code_point.to_string(), - size: f32::from(size), + size, font: font.clone(), color: appearance.icon_color, bounds: Rectangle { @@ -954,8 +954,8 @@ pub fn draw( IconPosition::Left => bounds.x + width + padding, IconPosition::Right => bounds.x + bounds.width - padding, }, - y: bounds.center_y() - f32::from(size) / 2.0, - height: f32::from(size), + y: bounds.center_y() - size / 2.0, + height: size, ..bounds }, horizontal_alignment: alignment::Horizontal::Right, -- cgit From 9852b4b36442ef036f0b308f798e892ddaa06c2d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:45:55 +0200 Subject: Move `Icon` layout logic to `layout` in `text_input` Also add `Icon::spacing` field. --- examples/tour/src/main.rs | 5 +- native/src/widget/text_input.rs | 129 ++++++++++++++++++++-------------------- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 6a1380d7..8a1b8d5a 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -138,7 +138,7 @@ impl Steps { Step::Debugger, Step::End, ], - current: 0, + current: 8, } } @@ -582,8 +582,9 @@ impl<'a> Step { if is_showing_icon { text_input = text_input.icon(text_input::Icon { font: ICON_FONT, - code_point: '\u{e900}', + code_point: '\u{E900}', size: Some(35.0), + spacing: 10.0, position: text_input::IconPosition::Right, }); } diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 95e3b720..f331f05a 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -52,6 +52,8 @@ pub struct Icon { pub code_point: char, /// Font size of the content. pub size: Option, + /// The spacing between the [`Icon`] and the text in a [`TextInput`]. + pub spacing: f32, /// Position of the icon. pub position: IconPosition, } @@ -235,7 +237,6 @@ where value.unwrap_or(&self.value), &self.placeholder, self.size, - self.padding, &self.font, self.is_secure, self.icon.as_ref(), @@ -272,7 +273,14 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - layout(renderer, limits, self.width, self.padding, self.size) + layout( + renderer, + limits, + self.width, + self.padding, + self.size, + self.icon.as_ref(), + ) } fn operate( @@ -335,7 +343,6 @@ where &self.value, &self.placeholder, self.size, - self.padding, &self.font, self.is_secure, self.icon.as_ref(), @@ -431,6 +438,7 @@ pub fn layout( width: Length, padding: Padding, size: Option, + icon: Option<&Icon>, ) -> layout::Node where Renderer: text::Renderer, @@ -440,10 +448,51 @@ where let padding = padding.fit(Size::ZERO, limits.max()); let limits = limits.width(width).pad(padding).height(text_size); - let mut text = layout::Node::new(limits.resolve(Size::ZERO)); - text.move_to(Point::new(padding.left, padding.top)); + let text_bounds = limits.resolve(Size::ZERO); + + if let Some(icon) = icon { + let icon_width = renderer.measure_width( + &icon.code_point.to_string(), + icon.size.unwrap_or_else(|| renderer.default_size()), + icon.font.clone(), + ); + + let mut text_node = layout::Node::new( + text_bounds - Size::new(icon_width + icon.spacing, 0.0), + ); + + let mut icon_node = + layout::Node::new(Size::new(icon_width, text_bounds.height)); + + match icon.position { + IconPosition::Left => { + text_node.move_to(Point::new( + padding.left + icon_width + icon.spacing, + padding.top, + )); + + icon_node.move_to(Point::new(padding.left, padding.top)); + } + IconPosition::Right => { + text_node.move_to(Point::new(padding.left, padding.top)); + + icon_node.move_to(Point::new( + padding.left + text_bounds.width - icon_width, + padding.top, + )); + } + }; + + layout::Node::with_children( + text_bounds.pad(padding), + vec![text_node, icon_node], + ) + } else { + let mut text = layout::Node::new(text_bounds); + text.move_to(Point::new(padding.left, padding.top)); - layout::Node::with_children(text.size().pad(padding), vec![text]) + layout::Node::with_children(text.size().pad(padding), vec![text]) + } } /// Processes an [`Event`] and updates the [`State`] of a [`TextInput`] @@ -863,7 +912,6 @@ pub fn draw( value: &Value, placeholder: &str, size: Option, - padding: Padding, font: &Renderer::Font, is_secure: bool, icon: Option<&Icon>, @@ -876,40 +924,9 @@ pub fn draw( let value = secure_value.as_ref().unwrap_or(value); let bounds = layout.bounds(); - let text_bounds = { - let bounds = layout.children().next().unwrap().bounds(); - if let Some(icon) = icon { - let Icon { - font, - size, - code_point, - position, - } = icon; - - let padding = padding.horizontal(); - let size = size.unwrap_or_else(|| renderer.default_size()); - let width = renderer.measure_width( - &code_point.to_string(), - size, - font.clone(), - ); - - match position { - IconPosition::Left => Rectangle { - x: bounds.x + (width + padding), - width: bounds.width - (width + padding), - ..bounds - }, - IconPosition::Right => Rectangle { - x: bounds.x, - width: bounds.width - (width + padding), - ..bounds - }, - } - } else { - bounds - } - }; + + let mut children_layout = layout.children(); + let text_bounds = children_layout.next().unwrap().bounds(); let is_mouse_over = bounds.contains(cursor_position); @@ -932,33 +949,15 @@ pub fn draw( ); if let Some(icon) = icon { - let Icon { - size, - font, - code_point, - position, - } = icon; - - let padding = padding.horizontal(); - let size = size.unwrap_or_else(|| renderer.default_size()); - let width = - renderer.measure_width(&code_point.to_string(), size, font.clone()); + let icon_layout = children_layout.next().unwrap(); renderer.fill_text(Text { - content: &code_point.to_string(), - size, - font: font.clone(), + content: &icon.code_point.to_string(), + size: icon.size.unwrap_or_else(|| renderer.default_size()), + font: icon.font.clone(), color: appearance.icon_color, - bounds: Rectangle { - x: match position { - IconPosition::Left => bounds.x + width + padding, - IconPosition::Right => bounds.x + bounds.width - padding, - }, - y: bounds.center_y() - size / 2.0, - height: size, - ..bounds - }, - horizontal_alignment: alignment::Horizontal::Right, + bounds: icon_layout.bounds(), + horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, }); } -- cgit From 870b2fe513bd5b3fbcf3ba369afb14d68324aaa2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:48:12 +0200 Subject: Derive `Debug` for `text_input::Icon` --- native/src/widget/text_input.rs | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index f331f05a..a7fdcb1c 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -32,19 +32,16 @@ use crate::{ pub use iced_style::text_input::{Appearance, StyleSheet}; /// The position of the [`Icon`]. -#[derive(Clone, Default, Debug)] +#[derive(Debug, Clone)] pub enum IconPosition { /// Position the [`Icon`] to the left. Left, - /// Position the [`Icon`] to the left. - /// - /// This is the default. - #[default] + /// Position the [`Icon`] to the right. Right, } /// The content of the [`Icon`]. -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Icon { /// Font that will be used to display the `code_point`. pub font: Font, @@ -58,19 +55,6 @@ pub struct Icon { pub position: IconPosition, } -impl std::fmt::Debug for Icon -where - Renderer: text::Renderer, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Icon") - .field("code_point", &self.code_point) - .field("size", &self.size) - .field("position", &self.position) - .finish() - } -} - /// A field that can be filled with text. /// /// # Example -- cgit From 57265ff211e8d040dce2a13e71409bdd6482204c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:53:22 +0200 Subject: Move `Icon` definitions after `Widget` implementation --- native/src/widget/checkbox.rs | 22 +++++++++---------- native/src/widget/text_input.rs | 48 ++++++++++++++++++++--------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 9b69e574..ad05a8e7 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -14,17 +14,6 @@ use crate::{ pub use iced_style::checkbox::{Appearance, StyleSheet}; -/// The icon in a [`Checkbox`]. -#[derive(Debug, Clone, PartialEq)] -pub struct Icon { - /// Font that will be used to display the `code_point`, - pub font: Font, - /// The unicode code point that will be used as the icon. - pub code_point: char, - /// Font size of the content. - pub size: Option, -} - /// A box that can be checked. /// /// # Example @@ -319,3 +308,14 @@ where Element::new(checkbox) } } + +/// The icon in a [`Checkbox`]. +#[derive(Debug, Clone, PartialEq)] +pub struct Icon { + /// Font that will be used to display the `code_point`, + pub font: Font, + /// The unicode code point that will be used as the icon. + pub code_point: char, + /// Font size of the content. + pub size: Option, +} diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index a7fdcb1c..c43b735c 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -31,30 +31,6 @@ use crate::{ pub use iced_style::text_input::{Appearance, StyleSheet}; -/// The position of the [`Icon`]. -#[derive(Debug, Clone)] -pub enum IconPosition { - /// Position the [`Icon`] to the left. - Left, - /// Position the [`Icon`] to the right. - Right, -} - -/// The content of the [`Icon`]. -#[derive(Debug, Clone)] -pub struct Icon { - /// Font that will be used to display the `code_point`. - pub font: Font, - /// The unicode code point that will be used as the icon. - pub code_point: char, - /// Font size of the content. - pub size: Option, - /// The spacing between the [`Icon`] and the text in a [`TextInput`]. - pub spacing: f32, - /// Position of the icon. - pub position: IconPosition, -} - /// A field that can be filled with text. /// /// # Example @@ -360,6 +336,30 @@ where } } +/// The content of the [`Icon`]. +#[derive(Debug, Clone)] +pub struct Icon { + /// The font that will be used to display the `code_point`. + pub font: Font, + /// The unicode code point that will be used as the icon. + pub code_point: char, + /// The font size of the content. + pub size: Option, + /// The spacing between the [`Icon`] and the text in a [`TextInput`]. + pub spacing: f32, + /// The position of the icon. + pub position: IconPosition, +} + +/// The position of an [`Icon`]. +#[derive(Debug, Clone)] +pub enum IconPosition { + /// Position the [`Icon`] on the left side of a [`TextInput`]. + Left, + /// Position the [`Icon`] on the right side of a [`TextInput`]. + Right, +} + /// The identifier of a [`TextInput`]. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Id(widget::Id); -- cgit From cf9d8e01048845ee503a62eb55e634a76a0e9163 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:54:51 +0200 Subject: Rename `IconPosition` to `Side` in `text_input` --- examples/tour/src/main.rs | 2 +- native/src/widget/text_input.rs | 18 +++++++++--------- src/widget.rs | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 8a1b8d5a..40ab33c2 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -585,7 +585,7 @@ impl<'a> Step { code_point: '\u{E900}', size: Some(35.0), spacing: 10.0, - position: text_input::IconPosition::Right, + side: text_input::Side::Right, }); } diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index c43b735c..bb397645 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -347,16 +347,16 @@ pub struct Icon { pub size: Option, /// The spacing between the [`Icon`] and the text in a [`TextInput`]. pub spacing: f32, - /// The position of the icon. - pub position: IconPosition, + /// The side of a [`TextInput`] where to display the [`Icon`]. + pub side: Side, } -/// The position of an [`Icon`]. +/// The side of a [`TextInput`]. #[derive(Debug, Clone)] -pub enum IconPosition { - /// Position the [`Icon`] on the left side of a [`TextInput`]. +pub enum Side { + /// The left side of a [`TextInput`]. Left, - /// Position the [`Icon`] on the right side of a [`TextInput`]. + /// The right side of a [`TextInput`]. Right, } @@ -448,8 +448,8 @@ where let mut icon_node = layout::Node::new(Size::new(icon_width, text_bounds.height)); - match icon.position { - IconPosition::Left => { + match icon.side { + Side::Left => { text_node.move_to(Point::new( padding.left + icon_width + icon.spacing, padding.top, @@ -457,7 +457,7 @@ where icon_node.move_to(Point::new(padding.left, padding.top)); } - IconPosition::Right => { + Side::Right => { text_node.move_to(Point::new(padding.left, padding.top)); icon_node.move_to(Point::new( diff --git a/src/widget.rs b/src/widget.rs index d3c16088..c0ac716f 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -124,7 +124,7 @@ pub mod text_input { //! Display fields that can be filled with text. pub use iced_native::widget::text_input::{ focus, move_cursor_to, move_cursor_to_end, move_cursor_to_front, - select_all, Appearance, Icon, IconPosition, Id, StyleSheet, + select_all, Appearance, Icon, Id, Side, StyleSheet, }; /// A field that can be filled with text. -- cgit From c794d8ba788b388d4fb7a8ef1bba208b98e1bd02 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:56:34 +0200 Subject: Collapse `Font` import in `tour` example --- examples/tour/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 40ab33c2..9ee386d4 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -5,8 +5,7 @@ use iced::widget::{ scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; -use iced::Font; -use iced::{Color, Element, Length, Renderer, Sandbox, Settings}; +use iced::{Color, Element, Font, Length, Renderer, Sandbox, Settings}; const ICON_FONT: Font = Font::External { name: "Icons", -- cgit From aa0be30656e30d116e60a5d06557aea27442ce76 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:57:01 +0200 Subject: Move `ICON_FONT` constant in `tour` to `text_input` helper --- examples/tour/src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 9ee386d4..e9b9fb3a 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -7,11 +7,6 @@ use iced::widget::{ use iced::widget::{Button, Column, Container, Slider}; use iced::{Color, Element, Font, Length, Renderer, Sandbox, Settings}; -const ICON_FONT: Font = Font::External { - name: "Icons", - bytes: include_bytes!("../fonts/icons.ttf"), -}; - pub fn main() -> iced::Result { env_logger::init(); @@ -570,6 +565,11 @@ impl<'a> Step { is_secure: bool, is_showing_icon: bool, ) -> Column<'a, StepMessage> { + const ICON_FONT: Font = Font::External { + name: "Icons", + bytes: include_bytes!("../fonts/icons.ttf"), + }; + let mut text_input = text_input( "Type something to continue...", value, -- cgit From 45015e37d4123f01b546337e741820975fccf66a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:57:31 +0200 Subject: Fix `current` step in `tour` --- examples/tour/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index e9b9fb3a..90868877 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -132,7 +132,7 @@ impl Steps { Step::Debugger, Step::End, ], - current: 8, + current: 0, } } -- cgit From 927c3a8caaefc241c48d6200266d286e20dc2076 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:59:08 +0200 Subject: Reuse `text_bounds` in `text_input::layout` --- native/src/widget/text_input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index bb397645..fd61a849 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -475,7 +475,7 @@ where let mut text = layout::Node::new(text_bounds); text.move_to(Point::new(padding.left, padding.top)); - layout::Node::with_children(text.size().pad(padding), vec![text]) + layout::Node::with_children(text_bounds.pad(padding), vec![text]) } } -- cgit