Skip to content

Commit

Permalink
Merge pull request #1702 from casperstorm/feat/text_input_handle
Browse files Browse the repository at this point in the history
Added `Icon` to `text_input`
  • Loading branch information
hecrj authored Apr 11, 2023
2 parents 931b30d + 927c3a8 commit ff24f90
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 30 deletions.
Binary file added examples/tour/fonts/icons.ttf
Binary file not shown.
80 changes: 67 additions & 13 deletions examples/tour/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use iced::widget::{
scrollable, slider, text, text_input, toggler, vertical_space,
};
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 {
env_logger::init();
Expand Down Expand Up @@ -127,6 +127,7 @@ impl Steps {
Step::TextInput {
value: String::new(),
is_secure: false,
is_showing_icon: false,
},
Step::Debugger,
Step::End,
Expand Down Expand Up @@ -171,14 +172,32 @@ impl Steps {

enum Step {
Welcome,
Slider { value: u8 },
RowsAndColumns { layout: Layout, spacing: u16 },
Text { size: u16, color: Color },
Radio { selection: Option<Language> },
Toggler { can_continue: bool },
Image { width: u16 },
Slider {
value: u8,
},
RowsAndColumns {
layout: Layout,
spacing: u16,
},
Text {
size: u16,
color: Color,
},
Radio {
selection: Option<Language>,
},
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,
}
Expand All @@ -194,6 +213,7 @@ pub enum StepMessage {
ImageWidthChanged(u16),
InputChanged(String),
ToggleSecureInput(bool),
ToggleTextInputIcon(bool),
DebugToggled(bool),
TogglerChanged(bool),
}
Expand Down Expand Up @@ -256,6 +276,14 @@ impl<'a> Step {
*can_continue = value;
}
}
StepMessage::ToggleTextInputIcon(toggle) => {
if let Step::TextInput {
is_showing_icon, ..
} = self
{
*is_showing_icon = toggle
}
}
};
}

Expand Down Expand Up @@ -303,9 +331,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(),
}
Expand Down Expand Up @@ -530,15 +560,34 @@ 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> {
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,
StepMessage::InputChanged,
)
.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.0),
spacing: 10.0,
side: text_input::Side::Right,
});
}

Self::container("Text input")
.push("Use a text input to ask for different kinds of information.")
.push(if is_secure {
Expand All @@ -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:",
Expand Down
22 changes: 11 additions & 11 deletions native/src/widget/checkbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
/// 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.
///
/// # Example
Expand Down Expand Up @@ -319,3 +308,14 @@ where
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>,
}
111 changes: 106 additions & 5 deletions native/src/widget/text_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ where
on_change: Box<dyn Fn(String) -> Message + 'a>,
on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>,
on_submit: Option<Message>,
icon: Option<Icon<Renderer::Font>>,
style: <Renderer::Theme as StyleSheet>::Style,
}

Expand Down Expand Up @@ -99,6 +100,7 @@ where
on_change: Box::new(on_change),
on_paste: None,
on_submit: None,
icon: None,
style: Default::default(),
}
}
Expand Down Expand Up @@ -132,6 +134,13 @@ where
self.font = font;
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`].
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.width = width.into();
Expand Down Expand Up @@ -190,6 +199,7 @@ where
self.size,
&self.font,
self.is_secure,
self.icon.as_ref(),
&self.style,
)
}
Expand Down Expand Up @@ -223,7 +233,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(
Expand Down Expand Up @@ -288,6 +305,7 @@ where
self.size,
&self.font,
self.is_secure,
self.icon.as_ref(),
&self.style,
)
}
Expand Down Expand Up @@ -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`].
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Id(widget::Id);
Expand Down Expand Up @@ -380,6 +422,7 @@ pub fn layout<Renderer>(
width: Length,
padding: Padding,
size: Option<f32>,
icon: Option<&Icon<Renderer::Font>>,
) -> layout::Node
where
Renderer: text::Renderer,
Expand All @@ -389,10 +432,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.side {
Side::Left => {
text_node.move_to(Point::new(
padding.left + icon_width + icon.spacing,
padding.top,
));

layout::Node::with_children(text.size().pad(padding), vec![text])
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`]
Expand Down Expand Up @@ -814,6 +898,7 @@ pub fn draw<Renderer>(
size: Option<f32>,
font: &Renderer::Font,
is_secure: bool,
icon: Option<&Icon<Renderer::Font>>,
style: &<Renderer::Theme as StyleSheet>::Style,
) where
Renderer: text::Renderer,
Expand All @@ -823,7 +908,9 @@ pub fn draw<Renderer>(
let value = secure_value.as_ref().unwrap_or(value);

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);

Expand All @@ -845,6 +932,20 @@ pub fn draw<Renderer>(
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 size = size.unwrap_or_else(|| renderer.default_size());

Expand Down
2 changes: 1 addition & 1 deletion src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, Icon, Id, Side, StyleSheet,
};

/// A field that can be filled with text.
Expand Down
2 changes: 2 additions & 0 deletions style/src/text_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub struct Appearance {
pub border_width: f32,
/// The border [`Color`] of the text input.
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.
Expand Down
Loading

0 comments on commit ff24f90

Please sign in to comment.