From 7b369842959511f17d5c27941fd0308484bff8ea Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Mon, 13 Feb 2023 11:38:05 +0100 Subject: [PATCH] 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 0000000000000000000000000000000000000000..bfe8a24b966869737a85aac1242d5a35f32381b2 GIT binary patch literal 1612 zcmZQzWME+6W@unwW-#y%);Ho8VqL?)z_5pbfgvF|H?crG>{2HK1EUNB14Br9VsQZj z1A_nq10#s$NYANEn|YgSF9QQ(1p@<9OGav9in7qU;|vVUe;61T%rY`k6WOOTpI~5M zF=1d}P|3(GsSsjd_`$%y62ZX0Ad{1yoX9A`c!z<3r37SdZem3N0}q260|QG70|SFX zUSe+QOfSyg3=Aww7#JAa3i69f7?>Ft7#P@eK=KUC%ngha7+4t?m}WAaVPIhBhtQ0# z8H^b@LF$k&qW}^I%yeL2c*(?k;Qs*z2j&9|4h#(7umDSeO$V_+wt=t=10&dG83tys zDj5bA1`7rT1}+8;1||k321W*jmrVcv{{Iis#Gvq!0nBFn|5%&_ssk(mwuABie+GWA zDG)Ix1_pCWP&5!N#=?-#;J`eQS&M<6L6yOf!I^=9(MU{OSd2|sQAy2Q*pAWIREdq9 znU7Iij?q}r$jsbKO`TbfQCV1oO<76J)Wpo#NX*bxrL?N5G#d|_ zPFk7{8xI2m0~3Sx|JTe{nWr+Sf&8MXZpUb9qOQlNuBNQS&d12kCMqH>$0#mlY-DB* z@{y65sfn68qlvj4qq&)}5m-)C1guh739QeQdCyr{c{#ZZH@AQQH@6HqISAV_UFR&X zynwP15AR<|K>;gubzL2Gbt?fuFk43#%&s}BlWvKu3&NI{J0)kfR>N+5| z2?`*y&&p=H3u_7}IIF2SD+p)`yJyOZTNp4cGO!S5$~M*4cNGy45pmVmH#ODMcNG;8 z5p~tq`}@%DH!S*FoZFrG88cs zGh~7!7!()`8T1$ou5X6wmkj_xbki(G3P=r;bTYg@NLV9XmYEfcI zYKlTqrGjU2zHfeho*si6*zOVr1%`BnRE9jTixL@17*ZKh7!(+i7%CYQ7(Brq@P&AQ PL63m}Tmqs*3n~WyV}A4q literal 0 HcmV?d00001 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, } }