diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index 00a870ab80..b91702e9d7 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -565,7 +565,8 @@ where let mut local_shell = Shell::new(&mut local_messages); // Provide it to the widget - self.text_input.update( + Widget::update( + &mut self.text_input, &mut tree.children[0], event, layout, @@ -719,7 +720,8 @@ where // Unfocus the input let mut local_messages = Vec::new(); let mut local_shell = Shell::new(&mut local_messages); - self.text_input.update( + Widget::update( + &mut self.text_input, &mut tree.children[0], &Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)), layout, diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index e3a887bc5d..1156efde7c 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -586,62 +586,14 @@ where draw(renderer, text_bounds); } } -} - -impl Widget - for TextInput<'_, Message, Theme, Renderer> -where - Message: Clone, - Theme: Catalog, - Renderer: text::Renderer, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::>() - } - - fn state(&self) -> tree::State { - tree::State::new(State::::new()) - } - - fn diff(&self, tree: &mut Tree) { - let state = tree.state.downcast_mut::>(); - - // Stop pasting if input becomes disabled - if self.on_input.is_none() { - state.is_pasting = None; - } - } - - fn size(&self) -> Size { - Size { - width: self.width, - height: Length::Shrink, - } - } - fn layout( - &mut self, - tree: &mut Tree, - renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - self.layout(tree, renderer, limits, None) - } - - fn operate( - &mut self, - tree: &mut Tree, - layout: Layout<'_>, - _renderer: &Renderer, - operation: &mut dyn Operation, - ) { - let state = tree.state.downcast_mut::>(); - - operation.text_input(self.id.as_ref(), layout.bounds(), state); - operation.focusable(self.id.as_ref(), layout.bounds(), state); - } - - fn update( + /// Draws the [`TextInput`] with the given [`Renderer`], overriding its + /// [`Value`] if provided. + /// + /// [`Renderer`]: text::Renderer + /// + /// Update the [`TextInput`]'s value or, if provided, the external `value` [`Value`] + pub fn update( &mut self, tree: &mut Tree, event: &Event, @@ -649,6 +601,7 @@ where cursor: mouse::Cursor, renderer: &Renderer, shell: &mut Shell<'_, Message>, + value: Option<&mut Value>, _viewport: &Rectangle, ) { let update_cache = |state, value| { @@ -667,6 +620,8 @@ where Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) => { let state = state::(tree); + let value = value.unwrap_or(&mut self.value); + let cursor_before = state.cursor; let click_position = cursor.position_over(layout.bounds()); @@ -705,9 +660,9 @@ where click::Kind::Single => { let position = if target > 0.0 { let value = if self.is_secure { - self.value.secure() + value.secure() } else { - self.value.clone() + value.clone() }; find_cursor_position(text_layout.bounds(), &value, state, target) @@ -719,7 +674,7 @@ where if state.keyboard_modifiers.shift() { state .cursor - .select_range(state.cursor.start(&self.value), position); + .select_range(state.cursor.start(&value), position); } else { state.cursor.move_to(position); } @@ -728,28 +683,28 @@ where } click::Kind::Double => { if self.is_secure { - state.cursor.select_all(&self.value); + state.cursor.select_all(&value); state.is_dragging = None; } else { let position = find_cursor_position( text_layout.bounds(), - &self.value, + &value, state, target, ) .unwrap_or(0); state.cursor.select_range( - self.value.previous_start_of_word(position), - self.value.next_end_of_word(position), + value.previous_start_of_word(position), + value.next_end_of_word(position), ); state.is_dragging = Some(Drag::SelectWords { anchor: position }); } } click::Kind::Triple => { - state.cursor.select_all(&self.value); + state.cursor.select_all(&value); state.is_dragging = None; } } @@ -771,6 +726,7 @@ where Event::Mouse(mouse::Event::CursorMoved { position }) | Event::Touch(touch::Event::FingerMoved { position, .. }) => { let state = state::(tree); + let value = value.unwrap_or(&mut self.value); if let Some(is_dragging) = &state.is_dragging { let text_layout = layout.children().next().unwrap(); @@ -788,9 +744,9 @@ where }; let value = if self.is_secure { - self.value.secure() + value.secure() } else { - self.value.clone() + value.clone() }; let position = @@ -808,13 +764,13 @@ where Drag::SelectWords { anchor } => { if position < *anchor { state.cursor.select_range( - self.value.previous_start_of_word(position), - self.value.next_end_of_word(*anchor), + value.previous_start_of_word(position), + value.next_end_of_word(*anchor), ); } else { state.cursor.select_range( - self.value.previous_start_of_word(*anchor), - self.value.next_end_of_word(position), + value.previous_start_of_word(*anchor), + value.next_end_of_word(position), ); } } @@ -839,15 +795,16 @@ where .. }) => { let state = state::(tree); + let value = value.unwrap_or(&mut self.value); if let Some(focus) = &mut state.is_focused { let modifiers = state.keyboard_modifiers; match key.to_latin(*physical_key) { Some('c') if state.keyboard_modifiers.command() && !self.is_secure => { - if let Some((start, end)) = state.cursor.selection(&self.value) { + if let Some((start, end)) = state.cursor.selection(&value) { shell.write_clipboard(clipboard::Content::Text( - self.value.select(start, end).to_string(), + value.select(start, end).to_string(), )); } @@ -859,13 +816,13 @@ where return; }; - if let Some((start, end)) = state.cursor.selection(&self.value) { + if let Some((start, end)) = state.cursor.selection(&value) { shell.write_clipboard(clipboard::Content::Text( - self.value.select(start, end).to_string(), + value.select(start, end).to_string(), )); } - let mut editor = Editor::new(&mut self.value, &mut state.cursor); + let mut editor = Editor::new(value, &mut state.cursor); editor.delete(); let message = (on_input)(editor.contents()); @@ -873,7 +830,7 @@ where shell.capture_event(); focus.updated_at = Instant::now(); - update_cache(state, &self.value); + update_cache(state, &value); return; } Some('v') @@ -894,7 +851,7 @@ where } }; - let mut editor = Editor::new(&mut self.value, &mut state.cursor); + let mut editor = Editor::new(value, &mut state.cursor); editor.paste(content.clone()); let message = if let Some(paste) = &self.on_paste { @@ -906,13 +863,13 @@ where shell.capture_event(); focus.updated_at = Instant::now(); - update_cache(state, &self.value); + update_cache(state, &value); return; } Some('a') if state.keyboard_modifiers.command() => { let cursor_before = state.cursor; - state.cursor.select_all(&self.value); + state.cursor.select_all(&value); if cursor_before != state.cursor { focus.updated_at = Instant::now(); @@ -934,7 +891,7 @@ where state.is_pasting = None; if let Some(c) = text.chars().next().filter(|c| !c.is_control()) { - let mut editor = Editor::new(&mut self.value, &mut state.cursor); + let mut editor = Editor::new(value, &mut state.cursor); editor.insert(c); @@ -943,7 +900,7 @@ where shell.capture_event(); focus.updated_at = Instant::now(); - update_cache(state, &self.value); + update_cache(state, &value); return; } } @@ -966,18 +923,16 @@ where return; }; - if state.cursor.selection(&self.value).is_none() { + if state.cursor.selection(&value).is_none() { if (self.is_secure && modifiers.jump()) || modifiers.macos_command() { - state - .cursor - .select_range(state.cursor.start(&self.value), 0); + state.cursor.select_range(state.cursor.start(&value), 0); } else if modifiers.jump() { - state.cursor.select_left_by_words(&self.value); + state.cursor.select_left_by_words(&value); } } - let mut editor = Editor::new(&mut self.value, &mut state.cursor); + let mut editor = Editor::new(value, &mut state.cursor); editor.backspace(); let message = (on_input)(editor.contents()); @@ -985,26 +940,25 @@ where shell.capture_event(); focus.updated_at = Instant::now(); - update_cache(state, &self.value); + update_cache(state, &value); } keyboard::Key::Named(key::Named::Delete) => { let Some(on_input) = &self.on_input else { return; }; - if state.cursor.selection(&self.value).is_none() { + if state.cursor.selection(&value).is_none() { if (self.is_secure && modifiers.jump()) || modifiers.macos_command() { - state.cursor.select_range( - state.cursor.start(&self.value), - self.value.len(), - ); + state + .cursor + .select_range(state.cursor.start(&value), value.len()); } else if modifiers.jump() { - state.cursor.select_right_by_words(&self.value); + state.cursor.select_right_by_words(&value); } } - let mut editor = Editor::new(&mut self.value, &mut state.cursor); + let mut editor = Editor::new(value, &mut state.cursor); editor.delete(); let message = (on_input)(editor.contents()); @@ -1012,15 +966,13 @@ where shell.capture_event(); focus.updated_at = Instant::now(); - update_cache(state, &self.value); + update_cache(state, &value); } keyboard::Key::Named(key::Named::Home) => { let cursor_before = state.cursor; if modifiers.shift() { - state - .cursor - .select_range(state.cursor.start(&self.value), 0); + state.cursor.select_range(state.cursor.start(&value), 0); } else { state.cursor.move_to(0); } @@ -1037,12 +989,11 @@ where let cursor_before = state.cursor; if modifiers.shift() { - state.cursor.select_range( - state.cursor.start(&self.value), - self.value.len(), - ); + state + .cursor + .select_range(state.cursor.start(&value), value.len()); } else { - state.cursor.move_to(self.value.len()); + state.cursor.move_to(value.len()); } if cursor_before != state.cursor { @@ -1058,22 +1009,20 @@ where if (self.is_secure && modifiers.jump()) || modifiers.macos_command() { if modifiers.shift() { - state - .cursor - .select_range(state.cursor.start(&self.value), 0); + state.cursor.select_range(state.cursor.start(&value), 0); } else { state.cursor.move_to(0); } } else if modifiers.jump() { if modifiers.shift() { - state.cursor.select_left_by_words(&self.value); + state.cursor.select_left_by_words(&value); } else { - state.cursor.move_left_by_words(&self.value); + state.cursor.move_left_by_words(&value); } } else if modifiers.shift() { - state.cursor.select_left(&self.value); + state.cursor.select_left(&value); } else { - state.cursor.move_left(&self.value); + state.cursor.move_left(&value); } if cursor_before != state.cursor { @@ -1089,23 +1038,22 @@ where if (self.is_secure && modifiers.jump()) || modifiers.macos_command() { if modifiers.shift() { - state.cursor.select_range( - state.cursor.start(&self.value), - self.value.len(), - ); + state + .cursor + .select_range(state.cursor.start(&value), value.len()); } else { - state.cursor.move_to(self.value.len()); + state.cursor.move_to(value.len()); } } else if modifiers.jump() { if modifiers.shift() { - state.cursor.select_right_by_words(&self.value); + state.cursor.select_right_by_words(&value); } else { - state.cursor.move_right_by_words(&self.value); + state.cursor.move_right_by_words(&value); } } else if modifiers.shift() { - state.cursor.select_right(&self.value); + state.cursor.select_right(&value); } else { - state.cursor.move_right(&self.value); + state.cursor.move_right(&value); } if cursor_before != state.cursor { @@ -1152,6 +1100,7 @@ where }; let state = state::(tree); + let value = value.unwrap_or(&mut self.value); let Some(focus) = &mut state.is_focused else { return; @@ -1162,7 +1111,7 @@ where { state.is_pasting = Some(Paste::Pasting(Value::new(text))); - let mut editor = Editor::new(&mut self.value, &mut state.cursor); + let mut editor = Editor::new(value, &mut state.cursor); editor.paste(Value::new(text)); let message = if let Some(paste) = &self.on_paste { @@ -1174,7 +1123,7 @@ where shell.capture_event(); focus.updated_at = Instant::now(); - update_cache(state, &self.value); + update_cache(state, &value); return; } } @@ -1202,13 +1151,14 @@ where } input_method::Event::Commit(text) => { let state = state::(tree); + let value = value.unwrap_or(&mut self.value); if let Some(focus) = &mut state.is_focused { let Some(on_input) = &self.on_input else { return; }; - let mut editor = Editor::new(&mut self.value, &mut state.cursor); + let mut editor = Editor::new(value, &mut state.cursor); editor.paste(Value::new(text)); focus.updated_at = Instant::now(); @@ -1218,7 +1168,7 @@ where shell.publish(message); shell.capture_event(); - update_cache(state, &self.value); + update_cache(state, &value); } } }, @@ -1245,7 +1195,8 @@ where if let Some(focus) = &mut state.is_focused && focus.is_window_focused { - if matches!(state.cursor.state(&self.value), cursor::State::Index(_)) { + let value = value.map(|v| &*v).unwrap_or(&self.value); + if matches!(state.cursor.state(&value), cursor::State::Index(_)) { focus.now = *now; let millis_until_redraw = CURSOR_BLINK_INTERVAL_MILLIS @@ -1256,7 +1207,7 @@ where ); } - shell.request_input_method(&self.input_method(state, layout, &self.value)); + shell.request_input_method(&self.input_method(state, layout, &value)); } } _ => {} @@ -1286,6 +1237,73 @@ where shell.request_redraw(); } } +} + +impl Widget + for TextInput<'_, Message, Theme, Renderer> +where + Message: Clone, + Theme: Catalog, + Renderer: text::Renderer, +{ + fn tag(&self) -> tree::Tag { + tree::Tag::of::>() + } + + fn state(&self) -> tree::State { + tree::State::new(State::::new()) + } + + fn diff(&self, tree: &mut Tree) { + let state = tree.state.downcast_mut::>(); + + // Stop pasting if input becomes disabled + if self.on_input.is_none() { + state.is_pasting = None; + } + } + + fn size(&self) -> Size { + Size { + width: self.width, + height: Length::Shrink, + } + } + + fn layout( + &mut self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + self.layout(tree, renderer, limits, None) + } + + fn operate( + &mut self, + tree: &mut Tree, + layout: Layout<'_>, + _renderer: &Renderer, + operation: &mut dyn Operation, + ) { + let state = tree.state.downcast_mut::>(); + + operation.text_input(self.id.as_ref(), layout.bounds(), state); + operation.focusable(self.id.as_ref(), layout.bounds(), state); + } + + fn update( + &mut self, + tree: &mut Tree, + event: &Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) { + self.update(tree, event, layout, cursor, renderer, shell, None, viewport); + } fn draw( &self,