diff options
| author | 2022-02-12 16:11:22 +0700 | |
|---|---|---|
| committer | 2022-02-12 16:11:37 +0700 | |
| commit | e3108494e5886c34312184292ec05dddeb8bf3ca (patch) | |
| tree | 34ef7a18b9bf169721263065d81bddd07656764b /native/src | |
| parent | b2670e8752eb96a4018f93b9cb8945da81a7ebff (diff) | |
| download | iced-e3108494e5886c34312184292ec05dddeb8bf3ca.tar.gz iced-e3108494e5886c34312184292ec05dddeb8bf3ca.tar.bz2 iced-e3108494e5886c34312184292ec05dddeb8bf3ca.zip | |
Implement `TextInput` in `iced_pure`
Diffstat (limited to 'native/src')
| -rw-r--r-- | native/src/widget/text_input.rs | 925 | 
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)      }  } | 
