Merge pull request #1702 from casperstorm/feat/text_input_handle

Added `Icon` to `text_input`
This commit is contained in:
Héctor Ramón 2023-04-11 06:18:52 +02:00 committed by GitHub
commit ff24f9040c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 190 additions and 30 deletions

Binary file not shown.

View file

@ -5,7 +5,7 @@ use iced::widget::{
scrollable, slider, text, text_input, toggler, vertical_space, scrollable, slider, text, text_input, toggler, vertical_space,
}; };
use iced::widget::{Button, Column, Container, Slider}; use iced::widget::{Button, Column, Container, Slider};
use iced::{Color, Element, Length, Renderer, Sandbox, Settings}; use iced::{Color, Element, Font, Length, Renderer, Sandbox, Settings};
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
env_logger::init(); env_logger::init();
@ -127,6 +127,7 @@ impl Steps {
Step::TextInput { Step::TextInput {
value: String::new(), value: String::new(),
is_secure: false, is_secure: false,
is_showing_icon: false,
}, },
Step::Debugger, Step::Debugger,
Step::End, Step::End,
@ -171,14 +172,32 @@ impl Steps {
enum Step { enum Step {
Welcome, Welcome,
Slider { value: u8 }, Slider {
RowsAndColumns { layout: Layout, spacing: u16 }, value: u8,
Text { size: u16, color: Color }, },
Radio { selection: Option<Language> }, RowsAndColumns {
Toggler { can_continue: bool }, layout: Layout,
Image { width: u16 }, spacing: u16,
},
Text {
size: u16,
color: Color,
},
Radio {
selection: Option<Language>,
},
Toggler {
can_continue: bool,
},
Image {
width: u16,
},
Scrollable, Scrollable,
TextInput { value: String, is_secure: bool }, TextInput {
value: String,
is_secure: bool,
is_showing_icon: bool,
},
Debugger, Debugger,
End, End,
} }
@ -194,6 +213,7 @@ pub enum StepMessage {
ImageWidthChanged(u16), ImageWidthChanged(u16),
InputChanged(String), InputChanged(String),
ToggleSecureInput(bool), ToggleSecureInput(bool),
ToggleTextInputIcon(bool),
DebugToggled(bool), DebugToggled(bool),
TogglerChanged(bool), TogglerChanged(bool),
} }
@ -256,6 +276,14 @@ impl<'a> Step {
*can_continue = value; *can_continue = value;
} }
} }
StepMessage::ToggleTextInputIcon(toggle) => {
if let Step::TextInput {
is_showing_icon, ..
} = self
{
*is_showing_icon = toggle
}
}
}; };
} }
@ -303,9 +331,11 @@ impl<'a> Step {
Self::rows_and_columns(*layout, *spacing) Self::rows_and_columns(*layout, *spacing)
} }
Step::Scrollable => Self::scrollable(), Step::Scrollable => Self::scrollable(),
Step::TextInput { value, is_secure } => { Step::TextInput {
Self::text_input(value, *is_secure) value,
} is_secure,
is_showing_icon,
} => Self::text_input(value, *is_secure, *is_showing_icon),
Step::Debugger => Self::debugger(debug), Step::Debugger => Self::debugger(debug),
Step::End => Self::end(), Step::End => Self::end(),
} }
@ -530,8 +560,17 @@ impl<'a> Step {
) )
} }
fn text_input(value: &str, is_secure: bool) -> Column<'a, StepMessage> { fn text_input(
let text_input = text_input( value: &str,
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...", "Type something to continue...",
value, value,
StepMessage::InputChanged, StepMessage::InputChanged,
@ -539,6 +578,16 @@ impl<'a> Step {
.padding(10) .padding(10)
.size(30); .size(30);
if is_showing_icon {
text_input = text_input.icon(text_input::Icon {
font: ICON_FONT,
code_point: '\u{E900}',
size: Some(35.0),
spacing: 10.0,
side: text_input::Side::Right,
});
}
Self::container("Text input") Self::container("Text input")
.push("Use a text input to ask for different kinds of information.") .push("Use a text input to ask for different kinds of information.")
.push(if is_secure { .push(if is_secure {
@ -551,6 +600,11 @@ impl<'a> Step {
is_secure, is_secure,
StepMessage::ToggleSecureInput, StepMessage::ToggleSecureInput,
)) ))
.push(checkbox(
"Show icon",
is_showing_icon,
StepMessage::ToggleTextInputIcon,
))
.push( .push(
"A text input produces a message every time it changes. It is \ "A text input produces a message every time it changes. It is \
very easy to keep track of its contents:", very easy to keep track of its contents:",

View file

@ -14,17 +14,6 @@ use crate::{
pub use iced_style::checkbox::{Appearance, StyleSheet}; pub use iced_style::checkbox::{Appearance, StyleSheet};
/// The icon in a [`Checkbox`].
#[derive(Debug, Clone, PartialEq)]
pub struct Icon<Font> {
/// 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<f32>,
}
/// A box that can be checked. /// A box that can be checked.
/// ///
/// # Example /// # Example
@ -319,3 +308,14 @@ where
Element::new(checkbox) Element::new(checkbox)
} }
} }
/// The icon in a [`Checkbox`].
#[derive(Debug, Clone, PartialEq)]
pub struct Icon<Font> {
/// 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<f32>,
}

View file

@ -68,6 +68,7 @@ where
on_change: Box<dyn Fn(String) -> Message + 'a>, on_change: Box<dyn Fn(String) -> Message + 'a>,
on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>, on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>,
on_submit: Option<Message>, on_submit: Option<Message>,
icon: Option<Icon<Renderer::Font>>,
style: <Renderer::Theme as StyleSheet>::Style, style: <Renderer::Theme as StyleSheet>::Style,
} }
@ -99,6 +100,7 @@ where
on_change: Box::new(on_change), on_change: Box::new(on_change),
on_paste: None, on_paste: None,
on_submit: None, on_submit: None,
icon: None,
style: Default::default(), style: Default::default(),
} }
} }
@ -132,6 +134,13 @@ where
self.font = font; self.font = font;
self self
} }
/// Sets the [`Icon`] of the [`TextInput`].
pub fn icon(mut self, icon: Icon<Renderer::Font>) -> Self {
self.icon = Some(icon);
self
}
/// Sets the width of the [`TextInput`]. /// Sets the width of the [`TextInput`].
pub fn width(mut self, width: impl Into<Length>) -> Self { pub fn width(mut self, width: impl Into<Length>) -> Self {
self.width = width.into(); self.width = width.into();
@ -190,6 +199,7 @@ where
self.size, self.size,
&self.font, &self.font,
self.is_secure, self.is_secure,
self.icon.as_ref(),
&self.style, &self.style,
) )
} }
@ -223,7 +233,14 @@ where
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> 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( fn operate(
@ -288,6 +305,7 @@ where
self.size, self.size,
&self.font, &self.font,
self.is_secure, self.is_secure,
self.icon.as_ref(),
&self.style, &self.style,
) )
} }
@ -318,6 +336,30 @@ where
} }
} }
/// The content of the [`Icon`].
#[derive(Debug, Clone)]
pub struct Icon<Font> {
/// 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<f32>,
/// The spacing between the [`Icon`] and the text in a [`TextInput`].
pub spacing: f32,
/// The side of a [`TextInput`] where to display the [`Icon`].
pub side: Side,
}
/// The side of a [`TextInput`].
#[derive(Debug, Clone)]
pub enum Side {
/// The left side of a [`TextInput`].
Left,
/// The right side of a [`TextInput`].
Right,
}
/// The identifier of a [`TextInput`]. /// The identifier of a [`TextInput`].
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Id(widget::Id); pub struct Id(widget::Id);
@ -380,6 +422,7 @@ pub fn layout<Renderer>(
width: Length, width: Length,
padding: Padding, padding: Padding,
size: Option<f32>, size: Option<f32>,
icon: Option<&Icon<Renderer::Font>>,
) -> layout::Node ) -> layout::Node
where where
Renderer: text::Renderer, Renderer: text::Renderer,
@ -389,10 +432,51 @@ where
let padding = padding.fit(Size::ZERO, limits.max()); let padding = padding.fit(Size::ZERO, limits.max());
let limits = limits.width(width).pad(padding).height(text_size); let limits = limits.width(width).pad(padding).height(text_size);
let mut text = layout::Node::new(limits.resolve(Size::ZERO)); let text_bounds = limits.resolve(Size::ZERO);
text.move_to(Point::new(padding.left, padding.top));
layout::Node::with_children(text.size().pad(padding), vec![text]) 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.side {
Side::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));
}
Side::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_bounds.pad(padding), vec![text])
}
} }
/// Processes an [`Event`] and updates the [`State`] of a [`TextInput`] /// Processes an [`Event`] and updates the [`State`] of a [`TextInput`]
@ -814,6 +898,7 @@ pub fn draw<Renderer>(
size: Option<f32>, size: Option<f32>,
font: &Renderer::Font, font: &Renderer::Font,
is_secure: bool, is_secure: bool,
icon: Option<&Icon<Renderer::Font>>,
style: &<Renderer::Theme as StyleSheet>::Style, style: &<Renderer::Theme as StyleSheet>::Style,
) where ) where
Renderer: text::Renderer, Renderer: text::Renderer,
@ -823,7 +908,9 @@ pub fn draw<Renderer>(
let value = secure_value.as_ref().unwrap_or(value); let value = secure_value.as_ref().unwrap_or(value);
let bounds = layout.bounds(); let bounds = layout.bounds();
let text_bounds = layout.children().next().unwrap().bounds();
let mut children_layout = layout.children();
let text_bounds = children_layout.next().unwrap().bounds();
let is_mouse_over = bounds.contains(cursor_position); let is_mouse_over = bounds.contains(cursor_position);
@ -845,6 +932,20 @@ pub fn draw<Renderer>(
appearance.background, appearance.background,
); );
if let Some(icon) = icon {
let icon_layout = children_layout.next().unwrap();
renderer.fill_text(Text {
content: &icon.code_point.to_string(),
size: icon.size.unwrap_or_else(|| renderer.default_size()),
font: icon.font.clone(),
color: appearance.icon_color,
bounds: icon_layout.bounds(),
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
});
}
let text = value.to_string(); let text = value.to_string();
let size = size.unwrap_or_else(|| renderer.default_size()); let size = size.unwrap_or_else(|| renderer.default_size());

View file

@ -124,7 +124,7 @@ pub mod text_input {
//! Display fields that can be filled with text. //! Display fields that can be filled with text.
pub use iced_native::widget::text_input::{ pub use iced_native::widget::text_input::{
focus, move_cursor_to, move_cursor_to_end, move_cursor_to_front, focus, move_cursor_to, move_cursor_to_end, move_cursor_to_front,
select_all, Appearance, Id, StyleSheet, select_all, Appearance, Icon, Id, Side, StyleSheet,
}; };
/// A field that can be filled with text. /// A field that can be filled with text.

View file

@ -12,6 +12,8 @@ pub struct Appearance {
pub border_width: f32, pub border_width: f32,
/// The border [`Color`] of the text input. /// The border [`Color`] of the text input.
pub border_color: Color, pub border_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. /// A set of rules that dictate the style of a text input.

View file

@ -1028,6 +1028,7 @@ impl text_input::StyleSheet for Theme {
border_radius: 2.0, border_radius: 2.0,
border_width: 1.0, border_width: 1.0,
border_color: palette.background.strong.color, border_color: palette.background.strong.color,
icon_color: palette.background.weak.text,
} }
} }
@ -1043,6 +1044,7 @@ impl text_input::StyleSheet for Theme {
border_radius: 2.0, border_radius: 2.0,
border_width: 1.0, border_width: 1.0,
border_color: palette.background.base.text, border_color: palette.background.base.text,
icon_color: palette.background.weak.text,
} }
} }
@ -1058,6 +1060,7 @@ impl text_input::StyleSheet for Theme {
border_radius: 2.0, border_radius: 2.0,
border_width: 1.0, border_width: 1.0,
border_color: palette.primary.strong.color, border_color: palette.primary.strong.color,
icon_color: palette.background.weak.text,
} }
} }