summaryrefslogtreecommitdiffstats
path: root/native
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2022-02-12 16:11:22 +0700
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2022-02-12 16:11:37 +0700
commite3108494e5886c34312184292ec05dddeb8bf3ca (patch)
tree34ef7a18b9bf169721263065d81bddd07656764b /native
parentb2670e8752eb96a4018f93b9cb8945da81a7ebff (diff)
downloadiced-e3108494e5886c34312184292ec05dddeb8bf3ca.tar.gz
iced-e3108494e5886c34312184292ec05dddeb8bf3ca.tar.bz2
iced-e3108494e5886c34312184292ec05dddeb8bf3ca.zip
Implement `TextInput` in `iced_pure`
Diffstat (limited to 'native')
-rw-r--r--native/src/widget/text_input.rs925
1 files changed, 477 insertions, 448 deletions
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index 03adeb12..83ce75f4 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -24,8 +24,6 @@ use crate::{
Rectangle, Shell, Size, Vector, Widget,
};
-use std::u32;
-
pub use iced_style::text_input::{Style, StyleSheet};
/// A field that can be filled with text.
@@ -61,10 +59,9 @@ pub struct TextInput<'a, Message, Renderer: text::Renderer> {
is_secure: bool,
font: Renderer::Font,
width: Length,
- max_width: u32,
padding: Padding,
size: Option<u16>,
- on_change: Box<dyn Fn(String) -> Message>,
+ on_change: Box<dyn Fn(String) -> Message + 'a>,
on_submit: Option<Message>,
style_sheet: Box<dyn StyleSheet + 'a>,
}
@@ -88,7 +85,7 @@ where
on_change: F,
) -> Self
where
- F: 'static + Fn(String) -> Message,
+ F: 'a + Fn(String) -> Message,
{
TextInput {
state,
@@ -97,7 +94,6 @@ where
is_secure: false,
font: Default::default(),
width: Length::Fill,
- max_width: u32::MAX,
padding: Padding::ZERO,
size: None,
on_change: Box::new(on_change),
@@ -126,12 +122,6 @@ where
self
}
- /// Sets the maximum width of the [`TextInput`].
- pub fn max_width(mut self, max_width: u32) -> Self {
- self.max_width = max_width;
- self
- }
-
/// Sets the [`Padding`] of the [`TextInput`].
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
self.padding = padding.into();
@@ -166,520 +156,313 @@ where
}
}
-impl<'a, Message, Renderer> TextInput<'a, Message, Renderer>
+/// Computes the layout of a [`TextInput`].
+pub fn layout<Renderer>(
+ renderer: &Renderer,
+ limits: &layout::Limits,
+ width: Length,
+ padding: Padding,
+ size: Option<u16>,
+) -> layout::Node
where
Renderer: text::Renderer,
{
- /// Draws the [`TextInput`] with the given [`Renderer`], overriding its
- /// [`Value`] if provided.
- pub fn draw(
- &self,
- renderer: &mut Renderer,
- layout: Layout<'_>,
- cursor_position: Point,
- value: Option<&Value>,
- ) {
- let value = value.unwrap_or(&self.value);
- let secure_value = self.is_secure.then(|| value.secure());
- let value = secure_value.as_ref().unwrap_or(&value);
+ let text_size = size.unwrap_or(renderer.default_size());
- let bounds = layout.bounds();
- let text_bounds = layout.children().next().unwrap().bounds();
+ let limits = limits
+ .pad(padding)
+ .width(width)
+ .height(Length::Units(text_size));
- let is_mouse_over = bounds.contains(cursor_position);
+ let mut text = layout::Node::new(limits.resolve(Size::ZERO));
+ text.move_to(Point::new(padding.left.into(), padding.top.into()));
- let style = if self.state.is_focused() {
- self.style_sheet.focused()
- } else if is_mouse_over {
- self.style_sheet.hovered()
- } else {
- self.style_sheet.active()
- };
-
- renderer.fill_quad(
- renderer::Quad {
- bounds,
- border_radius: style.border_radius,
- border_width: style.border_width,
- border_color: style.border_color,
- },
- style.background,
- );
-
- let text = value.to_string();
- let size = self.size.unwrap_or(renderer.default_size());
-
- let (cursor, offset) = if self.state.is_focused() {
- match self.state.cursor.state(&value) {
- cursor::State::Index(position) => {
- let (text_value_width, offset) =
- measure_cursor_and_scroll_offset(
- renderer,
- text_bounds,
- &value,
- size,
- position,
- self.font.clone(),
- );
-
- (
- Some((
- renderer::Quad {
- bounds: Rectangle {
- x: text_bounds.x + text_value_width,
- y: text_bounds.y,
- width: 1.0,
- height: text_bounds.height,
- },
- border_radius: 0.0,
- border_width: 0.0,
- border_color: Color::TRANSPARENT,
- },
- self.style_sheet.value_color(),
- )),
- offset,
- )
- }
- cursor::State::Selection { start, end } => {
- let left = start.min(end);
- let right = end.max(start);
-
- let (left_position, left_offset) =
- measure_cursor_and_scroll_offset(
- renderer,
- text_bounds,
- &value,
- size,
- left,
- self.font.clone(),
- );
-
- let (right_position, right_offset) =
- measure_cursor_and_scroll_offset(
- renderer,
- text_bounds,
- &value,
- size,
- right,
- self.font.clone(),
- );
-
- let width = right_position - left_position;
-
- (
- Some((
- renderer::Quad {
- bounds: Rectangle {
- x: text_bounds.x + left_position,
- y: text_bounds.y,
- width,
- height: text_bounds.height,
- },
- border_radius: 0.0,
- border_width: 0.0,
- border_color: Color::TRANSPARENT,
- },
- self.style_sheet.selection_color(),
- )),
- if end == right {
- right_offset
- } else {
- left_offset
- },
- )
- }
- }
- } else {
- (None, 0.0)
- };
-
- let text_width = renderer.measure_width(
- if text.is_empty() {
- &self.placeholder
- } else {
- &text
- },
- size,
- self.font.clone(),
- );
-
- let render = |renderer: &mut Renderer| {
- if let Some((cursor, color)) = cursor {
- renderer.fill_quad(cursor, color);
- }
-
- renderer.fill_text(Text {
- content: if text.is_empty() {
- &self.placeholder
- } else {
- &text
- },
- color: if text.is_empty() {
- self.style_sheet.placeholder_color()
- } else {
- self.style_sheet.value_color()
- },
- font: self.font.clone(),
- bounds: Rectangle {
- y: text_bounds.center_y(),
- width: f32::INFINITY,
- ..text_bounds
- },
- size: f32::from(size),
- horizontal_alignment: alignment::Horizontal::Left,
- vertical_alignment: alignment::Vertical::Center,
- });
- };
-
- if text_width > text_bounds.width {
- renderer.with_layer(text_bounds, |renderer| {
- renderer.with_translation(Vector::new(-offset, 0.0), render)
- });
- } else {
- render(renderer);
- }
- }
+ layout::Node::with_children(text.size().pad(padding), vec![text])
}
-impl<'a, Message, Renderer> Widget<Message, Renderer>
- for TextInput<'a, Message, Renderer>
+/// Processes an [`Event`] and updates the [`State`] of a [`TextInput`]
+/// accordingly.
+pub fn update<'a, Message, Renderer>(
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ renderer: &Renderer,
+ clipboard: &mut dyn Clipboard,
+ shell: &mut Shell<'_, Message>,
+ value: &mut Value,
+ size: Option<u16>,
+ font: &Renderer::Font,
+ is_secure: bool,
+ on_change: &dyn Fn(String) -> Message,
+ on_submit: &Option<Message>,
+ state: impl FnOnce() -> &'a mut State,
+) -> event::Status
where
Message: Clone,
Renderer: text::Renderer,
{
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- Length::Shrink
- }
-
- fn layout(
- &self,
- renderer: &Renderer,
- limits: &layout::Limits,
- ) -> layout::Node {
- let text_size = self.size.unwrap_or(renderer.default_size());
-
- let limits = limits
- .pad(self.padding)
- .width(self.width)
- .max_width(self.max_width)
- .height(Length::Units(text_size));
-
- let mut text = layout::Node::new(limits.resolve(Size::ZERO));
- text.move_to(Point::new(
- self.padding.left.into(),
- self.padding.top.into(),
- ));
-
- layout::Node::with_children(text.size().pad(self.padding), vec![text])
- }
-
- fn on_event(
- &mut self,
- event: Event,
- layout: Layout<'_>,
- cursor_position: Point,
- renderer: &Renderer,
- clipboard: &mut dyn Clipboard,
- shell: &mut Shell<'_, Message>,
- ) -> event::Status {
- match event {
- Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
- | Event::Touch(touch::Event::FingerPressed { .. }) => {
- let is_clicked = layout.bounds().contains(cursor_position);
-
- self.state.is_focused = is_clicked;
-
- if is_clicked {
- let text_layout = layout.children().next().unwrap();
- let target = cursor_position.x - text_layout.bounds().x;
-
- let click = mouse::Click::new(
- cursor_position,
- self.state.last_click,
- );
-
- match click.kind() {
- click::Kind::Single => {
- let position = if target > 0.0 {
- let value = if self.is_secure {
- self.value.secure()
- } else {
- self.value.clone()
- };
-
- find_cursor_position(
- renderer,
- text_layout.bounds(),
- self.font.clone(),
- self.size,
- &value,
- &self.state,
- target,
- )
+ match event {
+ Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
+ | Event::Touch(touch::Event::FingerPressed { .. }) => {
+ let state = state();
+ let is_clicked = layout.bounds().contains(cursor_position);
+
+ state.is_focused = is_clicked;
+
+ if is_clicked {
+ let text_layout = layout.children().next().unwrap();
+ let target = cursor_position.x - text_layout.bounds().x;
+
+ let click =
+ mouse::Click::new(cursor_position, state.last_click);
+
+ match click.kind() {
+ click::Kind::Single => {
+ let position = if target > 0.0 {
+ let value = if is_secure {
+ value.secure()
} else {
- None
+ value.clone()
};
- self.state.cursor.move_to(position.unwrap_or(0));
- self.state.is_dragging = true;
- }
- click::Kind::Double => {
- if self.is_secure {
- self.state.cursor.select_all(&self.value);
- } else {
- let position = find_cursor_position(
- renderer,
- text_layout.bounds(),
- self.font.clone(),
- self.size,
- &self.value,
- &self.state,
- target,
- )
- .unwrap_or(0);
-
- self.state.cursor.select_range(
- self.value.previous_start_of_word(position),
- self.value.next_end_of_word(position),
- );
- }
+ find_cursor_position(
+ renderer,
+ text_layout.bounds(),
+ font.clone(),
+ size,
+ &value,
+ state,
+ target,
+ )
+ } else {
+ None
+ };
- self.state.is_dragging = false;
- }
- click::Kind::Triple => {
- self.state.cursor.select_all(&self.value);
- self.state.is_dragging = false;
+ state.cursor.move_to(position.unwrap_or(0));
+ state.is_dragging = true;
+ }
+ click::Kind::Double => {
+ if is_secure {
+ state.cursor.select_all(value);
+ } else {
+ let position = find_cursor_position(
+ renderer,
+ text_layout.bounds(),
+ font.clone(),
+ size,
+ value,
+ state,
+ target,
+ )
+ .unwrap_or(0);
+
+ state.cursor.select_range(
+ value.previous_start_of_word(position),
+ value.next_end_of_word(position),
+ );
}
+
+ state.is_dragging = false;
+ }
+ click::Kind::Triple => {
+ state.cursor.select_all(value);
+ state.is_dragging = false;
}
+ }
- self.state.last_click = Some(click);
+ state.last_click = Some(click);
- return event::Status::Captured;
- }
- }
- Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
- | Event::Touch(touch::Event::FingerLifted { .. })
- | Event::Touch(touch::Event::FingerLost { .. }) => {
- self.state.is_dragging = false;
+ return event::Status::Captured;
}
- Event::Mouse(mouse::Event::CursorMoved { position })
- | Event::Touch(touch::Event::FingerMoved { position, .. }) => {
- if self.state.is_dragging {
- let text_layout = layout.children().next().unwrap();
- let target = position.x - text_layout.bounds().x;
-
- let value = if self.is_secure {
- self.value.secure()
- } else {
- self.value.clone()
- };
+ }
+ Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
+ | Event::Touch(touch::Event::FingerLifted { .. })
+ | Event::Touch(touch::Event::FingerLost { .. }) => {
+ state().is_dragging = false;
+ }
+ Event::Mouse(mouse::Event::CursorMoved { position })
+ | Event::Touch(touch::Event::FingerMoved { position, .. }) => {
+ let state = state();
- let position = find_cursor_position(
- renderer,
- text_layout.bounds(),
- self.font.clone(),
- self.size,
- &value,
- &self.state,
- target,
- )
- .unwrap_or(0);
+ if state.is_dragging {
+ let text_layout = layout.children().next().unwrap();
+ let target = position.x - text_layout.bounds().x;
- self.state.cursor.select_range(
- self.state.cursor.start(&value),
- position,
- );
+ let value = if is_secure {
+ value.secure()
+ } else {
+ value.clone()
+ };
+
+ let position = find_cursor_position(
+ renderer,
+ text_layout.bounds(),
+ font.clone(),
+ size,
+ &value,
+ state,
+ target,
+ )
+ .unwrap_or(0);
+
+ state
+ .cursor
+ .select_range(state.cursor.start(&value), position);
- return event::Status::Captured;
- }
+ return event::Status::Captured;
}
- Event::Keyboard(keyboard::Event::CharacterReceived(c))
- if self.state.is_focused
- && self.state.is_pasting.is_none()
- && !self.state.keyboard_modifiers.command()
- && !c.is_control() =>
+ }
+ Event::Keyboard(keyboard::Event::CharacterReceived(c)) => {
+ let state = state();
+
+ if state.is_focused
+ && state.is_pasting.is_none()
+ && !state.keyboard_modifiers.command()
+ && !c.is_control()
{
- let mut editor =
- Editor::new(&mut self.value, &mut self.state.cursor);
+ let mut editor = Editor::new(value, &mut state.cursor);
editor.insert(c);
- let message = (self.on_change)(editor.contents());
+ let message = (on_change)(editor.contents());
shell.publish(message);
return event::Status::Captured;
}
- Event::Keyboard(keyboard::Event::KeyPressed {
- key_code, ..
- }) if self.state.is_focused => {
- let modifiers = self.state.keyboard_modifiers;
+ }
+ Event::Keyboard(keyboard::Event::KeyPressed { key_code, .. }) => {
+ let state = state();
+
+ if state.is_focused {
+ let modifiers = state.keyboard_modifiers;
match key_code {
keyboard::KeyCode::Enter
| keyboard::KeyCode::NumpadEnter => {
- if let Some(on_submit) = self.on_submit.clone() {
+ if let Some(on_submit) = on_submit.clone() {
shell.publish(on_submit);
}
}
keyboard::KeyCode::Backspace => {
if platform::is_jump_modifier_pressed(modifiers)
- && self
- .state
- .cursor
- .selection(&self.value)
- .is_none()
+ && state.cursor.selection(value).is_none()
{
- if self.is_secure {
- let cursor_pos =
- self.state.cursor.end(&self.value);
- self.state.cursor.select_range(0, cursor_pos);
+ if is_secure {
+ let cursor_pos = state.cursor.end(value);
+ state.cursor.select_range(0, cursor_pos);
} else {
- self.state
- .cursor
- .select_left_by_words(&self.value);
+ state.cursor.select_left_by_words(value);
}
}
- let mut editor = Editor::new(
- &mut self.value,
- &mut self.state.cursor,
- );
-
+ let mut editor = Editor::new(value, &mut state.cursor);
editor.backspace();
- let message = (self.on_change)(editor.contents());
+ let message = (on_change)(editor.contents());
shell.publish(message);
}
keyboard::KeyCode::Delete => {
if platform::is_jump_modifier_pressed(modifiers)
- && self
- .state
- .cursor
- .selection(&self.value)
- .is_none()
+ && state.cursor.selection(value).is_none()
{
- if self.is_secure {
- let cursor_pos =
- self.state.cursor.end(&self.value);
- self.state
+ if is_secure {
+ let cursor_pos = state.cursor.end(value);
+ state
.cursor
- .select_range(cursor_pos, self.value.len());
+ .select_range(cursor_pos, value.len());
} else {
- self.state
- .cursor
- .select_right_by_words(&self.value);
+ state.cursor.select_right_by_words(value);
}
}
- let mut editor = Editor::new(
- &mut self.value,
- &mut self.state.cursor,
- );
-
+ let mut editor = Editor::new(value, &mut state.cursor);
editor.delete();
- let message = (self.on_change)(editor.contents());
+ let message = (on_change)(editor.contents());
shell.publish(message);
}
keyboard::KeyCode::Left => {
if platform::is_jump_modifier_pressed(modifiers)
- && !self.is_secure
+ && !is_secure
{
if modifiers.shift() {
- self.state
- .cursor
- .select_left_by_words(&self.value);
+ state.cursor.select_left_by_words(value);
} else {
- self.state
- .cursor
- .move_left_by_words(&self.value);
+ state.cursor.move_left_by_words(value);
}
} else if modifiers.shift() {
- self.state.cursor.select_left(&self.value)
+ state.cursor.select_left(value)
} else {
- self.state.cursor.move_left(&self.value);
+ state.cursor.move_left(value);
}
}
keyboard::KeyCode::Right => {
if platform::is_jump_modifier_pressed(modifiers)
- && !self.is_secure
+ && !is_secure
{
if modifiers.shift() {
- self.state
- .cursor
- .select_right_by_words(&self.value);
+ state.cursor.select_right_by_words(value);
} else {
- self.state
- .cursor
- .move_right_by_words(&self.value);
+ state.cursor.move_right_by_words(value);
}
} else if modifiers.shift() {
- self.state.cursor.select_right(&self.value)
+ state.cursor.select_right(value)
} else {
- self.state.cursor.move_right(&self.value);
+ state.cursor.move_right(value);
}
}
keyboard::KeyCode::Home => {
if modifiers.shift() {
- self.state.cursor.select_range(
- self.state.cursor.start(&self.value),
- 0,
- );
+ state
+ .cursor
+ .select_range(state.cursor.start(value), 0);
} else {
- self.state.cursor.move_to(0);
+ state.cursor.move_to(0);
}
}
keyboard::KeyCode::End => {
if modifiers.shift() {
- self.state.cursor.select_range(
- self.state.cursor.start(&self.value),
- self.value.len(),
+ state.cursor.select_range(
+ state.cursor.start(value),
+ value.len(),
);
} else {
- self.state.cursor.move_to(self.value.len());
+ state.cursor.move_to(value.len());
}
}
keyboard::KeyCode::C
- if self.state.keyboard_modifiers.command() =>
+ if state.keyboard_modifiers.command() =>
{
- match self.state.cursor.selection(&self.value) {
+ match state.cursor.selection(value) {
Some((start, end)) => {
clipboard.write(
- self.value.select(start, end).to_string(),
+ value.select(start, end).to_string(),
);
}
None => {}
}
}
keyboard::KeyCode::X
- if self.state.keyboard_modifiers.command() =>
+ if state.keyboard_modifiers.command() =>
{
- match self.state.cursor.selection(&self.value) {
+ match state.cursor.selection(value) {
Some((start, end)) => {
clipboard.write(
- self.value.select(start, end).to_string(),
+ value.select(start, end).to_string(),
);
}
None => {}
}
- let mut editor = Editor::new(
- &mut self.value,
- &mut self.state.cursor,
- );
-
+ let mut editor = Editor::new(value, &mut state.cursor);
editor.delete();
- let message = (self.on_change)(editor.contents());
+ let message = (on_change)(editor.contents());
shell.publish(message);
}
keyboard::KeyCode::V => {
- if self.state.keyboard_modifiers.command() {
- let content = match self.state.is_pasting.take() {
+ if state.keyboard_modifiers.command() {
+ let content = match state.is_pasting.take() {
Some(content) => content,
None => {
let content: String = clipboard
@@ -693,32 +476,30 @@ where
}
};
- let mut editor = Editor::new(
- &mut self.value,
- &mut self.state.cursor,
- );
+ let mut editor =
+ Editor::new(value, &mut state.cursor);
editor.paste(content.clone());
- let message = (self.on_change)(editor.contents());
+ let message = (on_change)(editor.contents());
shell.publish(message);
- self.state.is_pasting = Some(content);
+ state.is_pasting = Some(content);
} else {
- self.state.is_pasting = None;
+ state.is_pasting = None;
}
}
keyboard::KeyCode::A
- if self.state.keyboard_modifiers.command() =>
+ if state.keyboard_modifiers.command() =>
{
- self.state.cursor.select_all(&self.value);
+ state.cursor.select_all(value);
}
keyboard::KeyCode::Escape => {
- self.state.is_focused = false;
- self.state.is_dragging = false;
- self.state.is_pasting = None;
+ state.is_focused = false;
+ state.is_dragging = false;
+ state.is_pasting = None;
- self.state.keyboard_modifiers =
+ state.keyboard_modifiers =
keyboard::Modifiers::default();
}
keyboard::KeyCode::Tab
@@ -728,15 +509,17 @@ where
}
_ => {}
}
-
- return event::Status::Captured;
}
- Event::Keyboard(keyboard::Event::KeyReleased {
- key_code, ..
- }) if self.state.is_focused => {
+
+ return event::Status::Captured;
+ }
+ Event::Keyboard(keyboard::Event::KeyReleased { key_code, .. }) => {
+ let state = state();
+
+ if state.is_focused {
match key_code {
keyboard::KeyCode::V => {
- self.state.is_pasting = None;
+ state.is_pasting = None;
}
keyboard::KeyCode::Tab
| keyboard::KeyCode::Up
@@ -748,15 +531,261 @@ where
return event::Status::Captured;
}
- Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers))
- if self.state.is_focused =>
- {
- self.state.keyboard_modifiers = modifiers;
+ }
+ Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
+ let state = state();
+
+ if state.is_focused {
+ state.keyboard_modifiers = modifiers;
}
- _ => {}
}
+ _ => {}
+ }
+
+ event::Status::Ignored
+}
+
+/// Draws the [`TextInput`] with the given [`Renderer`], overriding its
+/// [`Value`] if provided.
+pub fn draw<Renderer>(
+ renderer: &mut Renderer,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ state: &State,
+ value: &Value,
+ placeholder: &str,
+ size: Option<u16>,
+ font: &Renderer::Font,
+ is_secure: bool,
+ style_sheet: &dyn StyleSheet,
+) where
+ Renderer: text::Renderer,
+{
+ let secure_value = is_secure.then(|| value.secure());
+ let value = secure_value.as_ref().unwrap_or(&value);
- event::Status::Ignored
+ let bounds = layout.bounds();
+ let text_bounds = layout.children().next().unwrap().bounds();
+
+ 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()
+ };
+
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds,
+ border_radius: style.border_radius,
+ border_width: style.border_width,
+ border_color: style.border_color,
+ },
+ style.background,
+ );
+
+ let text = value.to_string();
+ let size = size.unwrap_or(renderer.default_size());
+
+ let (cursor, offset) = if state.is_focused() {
+ match state.cursor.state(&value) {
+ cursor::State::Index(position) => {
+ let (text_value_width, offset) =
+ measure_cursor_and_scroll_offset(
+ renderer,
+ text_bounds,
+ &value,
+ size,
+ position,
+ font.clone(),
+ );
+
+ (
+ Some((
+ renderer::Quad {
+ bounds: Rectangle {
+ x: text_bounds.x + text_value_width,
+ y: text_bounds.y,
+ width: 1.0,
+ height: text_bounds.height,
+ },
+ border_radius: 0.0,
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ },
+ style_sheet.value_color(),
+ )),
+ offset,
+ )
+ }
+ cursor::State::Selection { start, end } => {
+ let left = start.min(end);
+ let right = end.max(start);
+
+ let (left_position, left_offset) =
+ measure_cursor_and_scroll_offset(
+ renderer,
+ text_bounds,
+ &value,
+ size,
+ left,
+ font.clone(),
+ );
+
+ let (right_position, right_offset) =
+ measure_cursor_and_scroll_offset(
+ renderer,
+ text_bounds,
+ &value,
+ size,
+ right,
+ font.clone(),
+ );
+
+ let width = right_position - left_position;
+
+ (
+ Some((
+ renderer::Quad {
+ bounds: Rectangle {
+ x: text_bounds.x + left_position,
+ y: text_bounds.y,
+ width,
+ height: text_bounds.height,
+ },
+ border_radius: 0.0,
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ },
+ style_sheet.selection_color(),
+ )),
+ if end == right {
+ right_offset
+ } else {
+ left_offset
+ },
+ )
+ }
+ }
+ } else {
+ (None, 0.0)
+ };
+
+ let text_width = renderer.measure_width(
+ if text.is_empty() { placeholder } else { &text },
+ size,
+ font.clone(),
+ );
+
+ let render = |renderer: &mut Renderer| {
+ if let Some((cursor, color)) = cursor {
+ renderer.fill_quad(cursor, color);
+ }
+
+ renderer.fill_text(Text {
+ content: if text.is_empty() { placeholder } else { &text },
+ color: if text.is_empty() {
+ style_sheet.placeholder_color()
+ } else {
+ style_sheet.value_color()
+ },
+ font: font.clone(),
+ bounds: Rectangle {
+ y: text_bounds.center_y(),
+ width: f32::INFINITY,
+ ..text_bounds
+ },
+ size: f32::from(size),
+ horizontal_alignment: alignment::Horizontal::Left,
+ vertical_alignment: alignment::Vertical::Center,
+ });
+ };
+
+ if text_width > text_bounds.width {
+ renderer.with_layer(text_bounds, |renderer| {
+ renderer.with_translation(Vector::new(-offset, 0.0), render)
+ });
+ } else {
+ render(renderer);
+ }
+}
+
+/// Computes the current [`mouse::Interaction`] of the [`TextInput`].
+pub fn mouse_interaction(
+ layout: Layout<'_>,
+ cursor_position: Point,
+) -> mouse::Interaction {
+ if layout.bounds().contains(cursor_position) {
+ mouse::Interaction::Text
+ } else {
+ mouse::Interaction::default()
+ }
+}
+
+/// Hashes the layout attributes of a [`TextInput`].
+pub fn hash_layout(
+ state: &mut Hasher,
+ width: Length,
+ padding: Padding,
+ size: Option<u16>,
+) {
+ use std::hash::Hash;
+
+ std::any::TypeId::of::<State>().hash(state);
+ width.hash(state);
+ padding.hash(state);
+ size.hash(state);
+}
+
+impl<'a, Message, Renderer> Widget<Message, Renderer>
+ for TextInput<'a, Message, Renderer>
+where
+ Message: Clone,
+ Renderer: text::Renderer,
+{
+ fn width(&self) -> Length {
+ self.width
+ }
+
+ fn height(&self) -> Length {
+ Length::Shrink
+ }
+
+ fn layout(
+ &self,
+ renderer: &Renderer,
+ limits: &layout::Limits,
+ ) -> layout::Node {
+ layout(renderer, limits, self.width, self.padding, self.size)
+ }
+
+ fn on_event(
+ &mut self,
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ renderer: &Renderer,
+ clipboard: &mut dyn Clipboard,
+ shell: &mut Shell<'_, Message>,
+ ) -> event::Status {
+ update(
+ event,
+ layout,
+ cursor_position,
+ renderer,
+ clipboard,
+ shell,
+ &mut self.value,
+ self.size,
+ &self.font,
+ self.is_secure,
+ self.on_change.as_ref(),
+ &self.on_submit,
+ || &mut self.state,
+ )
}
fn mouse_interaction(
@@ -766,11 +795,7 @@ where
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
- if layout.bounds().contains(cursor_position) {
- mouse::Interaction::Text
- } else {
- mouse::Interaction::default()
- }
+ mouse_interaction(layout, cursor_position)
}
fn draw(
@@ -781,18 +806,22 @@ where
cursor_position: Point,
_viewport: &Rectangle,
) {
- self.draw(renderer, layout, cursor_position, None)
+ draw(
+ renderer,
+ layout,
+ cursor_position,
+ &self.state,
+ &self.value,
+ &self.placeholder,
+ self.size,
+ &self.font,
+ self.is_secure,
+ self.style_sheet.as_ref(),
+ )
}
fn hash_layout(&self, state: &mut Hasher) {
- use std::{any::TypeId, hash::Hash};
- struct Marker;
- TypeId::of::<Marker>().hash(state);
-
- self.width.hash(state);
- self.max_width.hash(state);
- self.padding.hash(state);
- self.size.hash(state);
+ hash_layout(state, self.width, self.padding, self.size)
}
}