From 52490397d64f187d55f51dc5883e3ba6c0da57a6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 22 Oct 2024 02:50:46 +0200 Subject: Implement `reactive-rendering` for `text_input` ... and fix the redraw queue logic in `iced_winit`. --- widget/src/text_input.rs | 223 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 166 insertions(+), 57 deletions(-) (limited to 'widget/src/text_input.rs') diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index ff413779..c18009a2 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -120,6 +120,7 @@ pub struct TextInput< on_submit: Option, icon: Option>, class: Theme::Class<'a>, + last_status: Option, } /// The default [`Padding`] of a [`TextInput`]. @@ -150,6 +151,7 @@ where on_submit: None, icon: None, class: Theme::default(), + last_status: None, } } @@ -400,7 +402,7 @@ where renderer: &mut Renderer, theme: &Theme, layout: Layout<'_>, - cursor: mouse::Cursor, + _cursor: mouse::Cursor, value: Option<&Value>, viewport: &Rectangle, ) { @@ -416,19 +418,8 @@ where let mut children_layout = layout.children(); let text_bounds = children_layout.next().unwrap().bounds(); - let is_mouse_over = cursor.is_over(bounds); - - let status = if is_disabled { - Status::Disabled - } else if state.is_focused() { - Status::Focused - } else if is_mouse_over { - Status::Hovered - } else { - Status::Active - }; - - let style = theme.style(&self.class, status); + let style = theme + .style(&self.class, self.last_status.unwrap_or(Status::Disabled)); renderer.fill_quad( renderer::Quad { @@ -660,22 +651,21 @@ where ); }; - match event { + match &event { Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) => { let state = state::(tree); + let cursor_before = state.cursor; let click_position = cursor.position_over(layout.bounds()); state.is_focused = if click_position.is_some() { - state.is_focused.or_else(|| { - let now = Instant::now(); - - Some(Focus { - updated_at: now, - now, - is_window_focused: true, - }) + let now = Instant::now(); + + Some(Focus { + updated_at: now, + now, + is_window_focused: true, }) } else { None @@ -760,6 +750,10 @@ where state.last_click = Some(click); + if cursor_before != state.cursor { + shell.request_redraw(window::RedrawRequest::NextFrame); + } + return event::Status::Captured; } } @@ -801,10 +795,20 @@ where ) .unwrap_or(0); + let selection_before = state.cursor.selection(&value); + state .cursor .select_range(state.cursor.start(&value), position); + if let Some(focus) = &mut state.is_focused { + focus.updated_at = Instant::now(); + } + + if selection_before != state.cursor.selection(&value) { + shell.request_redraw(window::RedrawRequest::NextFrame); + } + return event::Status::Captured; } } @@ -815,7 +819,6 @@ where if let Some(focus) = &mut state.is_focused { let modifiers = state.keyboard_modifiers; - focus.updated_at = Instant::now(); match key.as_ref() { keyboard::Key::Character("c") @@ -857,6 +860,7 @@ where let message = (on_input)(editor.contents()); shell.publish(message); + focus.updated_at = Instant::now(); update_cache(state, &self.value); return event::Status::Captured; @@ -885,7 +889,6 @@ where let mut editor = Editor::new(&mut self.value, &mut state.cursor); - editor.paste(content.clone()); let message = if let Some(paste) = &self.on_paste { @@ -896,7 +899,7 @@ where shell.publish(message); state.is_pasting = Some(content); - + focus.updated_at = Instant::now(); update_cache(state, &self.value); return event::Status::Captured; @@ -904,8 +907,18 @@ where keyboard::Key::Character("a") if state.keyboard_modifiers.command() => { + let cursor_before = state.cursor; + state.cursor.select_all(&self.value); + if cursor_before != state.cursor { + focus.updated_at = Instant::now(); + + shell.request_redraw( + window::RedrawRequest::NextFrame, + ); + } + return event::Status::Captured; } _ => {} @@ -930,7 +943,6 @@ where shell.publish(message); focus.updated_at = Instant::now(); - update_cache(state, &self.value); return event::Status::Captured; @@ -941,6 +953,8 @@ where keyboard::Key::Named(key::Named::Enter) => { if let Some(on_submit) = self.on_submit.clone() { shell.publish(on_submit); + + return event::Status::Captured; } } keyboard::Key::Named(key::Named::Backspace) => { @@ -969,7 +983,10 @@ where let message = (on_input)(editor.contents()); shell.publish(message); + focus.updated_at = Instant::now(); update_cache(state, &self.value); + + return event::Status::Captured; } keyboard::Key::Named(key::Named::Delete) => { let Some(on_input) = &self.on_input else { @@ -1000,9 +1017,14 @@ where let message = (on_input)(editor.contents()); shell.publish(message); + focus.updated_at = Instant::now(); update_cache(state, &self.value); + + return event::Status::Captured; } keyboard::Key::Named(key::Named::Home) => { + let cursor_before = state.cursor; + if modifiers.shift() { state.cursor.select_range( state.cursor.start(&self.value), @@ -1011,8 +1033,20 @@ where } else { state.cursor.move_to(0); } + + if cursor_before != state.cursor { + focus.updated_at = Instant::now(); + + shell.request_redraw( + window::RedrawRequest::NextFrame, + ); + } + + return event::Status::Captured; } keyboard::Key::Named(key::Named::End) => { + let cursor_before = state.cursor; + if modifiers.shift() { state.cursor.select_range( state.cursor.start(&self.value), @@ -1021,10 +1055,22 @@ where } else { state.cursor.move_to(self.value.len()); } + + if cursor_before != state.cursor { + focus.updated_at = Instant::now(); + + shell.request_redraw( + window::RedrawRequest::NextFrame, + ); + } + + return event::Status::Captured; } keyboard::Key::Named(key::Named::ArrowLeft) if modifiers.macos_command() => { + let cursor_before = state.cursor; + if modifiers.shift() { state.cursor.select_range( state.cursor.start(&self.value), @@ -1033,10 +1079,22 @@ where } else { state.cursor.move_to(0); } + + if cursor_before != state.cursor { + focus.updated_at = Instant::now(); + + shell.request_redraw( + window::RedrawRequest::NextFrame, + ); + } + + return event::Status::Captured; } keyboard::Key::Named(key::Named::ArrowRight) if modifiers.macos_command() => { + let cursor_before = state.cursor; + if modifiers.shift() { state.cursor.select_range( state.cursor.start(&self.value), @@ -1045,8 +1103,20 @@ where } else { state.cursor.move_to(self.value.len()); } + + if cursor_before != state.cursor { + focus.updated_at = Instant::now(); + + shell.request_redraw( + window::RedrawRequest::NextFrame, + ); + } + + return event::Status::Captured; } keyboard::Key::Named(key::Named::ArrowLeft) => { + let cursor_before = state.cursor; + if modifiers.jump() && !self.is_secure { if modifiers.shift() { state @@ -1062,8 +1132,20 @@ where } else { state.cursor.move_left(&self.value); } + + if cursor_before != state.cursor { + focus.updated_at = Instant::now(); + + shell.request_redraw( + window::RedrawRequest::NextFrame, + ); + } + + return event::Status::Captured; } keyboard::Key::Named(key::Named::ArrowRight) => { + let cursor_before = state.cursor; + if modifiers.jump() && !self.is_secure { if modifiers.shift() { state @@ -1079,6 +1161,16 @@ where } else { state.cursor.move_right(&self.value); } + + if cursor_before != state.cursor { + focus.updated_at = Instant::now(); + + shell.request_redraw( + window::RedrawRequest::NextFrame, + ); + } + + return event::Status::Captured; } keyboard::Key::Named(key::Named::Escape) => { state.is_focused = None; @@ -1087,39 +1179,22 @@ where state.keyboard_modifiers = keyboard::Modifiers::default(); - } - keyboard::Key::Named( - key::Named::Tab - | key::Named::ArrowUp - | key::Named::ArrowDown, - ) => { - return event::Status::Ignored; + + return event::Status::Captured; } _ => {} } - - return event::Status::Captured; } } Event::Keyboard(keyboard::Event::KeyReleased { key, .. }) => { let state = state::(tree); if state.is_focused.is_some() { - match key.as_ref() { - keyboard::Key::Character("v") => { - state.is_pasting = None; - } - keyboard::Key::Named( - key::Named::Tab - | key::Named::ArrowUp - | key::Named::ArrowDown, - ) => { - return event::Status::Ignored; - } - _ => {} - } + if let keyboard::Key::Character("v") = key.as_ref() { + state.is_pasting = None; - return event::Status::Captured; + return event::Status::Captured; + } } state.is_pasting = None; @@ -1127,7 +1202,7 @@ where Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { let state = state::(tree); - state.keyboard_modifiers = modifiers; + state.keyboard_modifiers = *modifiers; } Event::Window(window::Event::Unfocused) => { let state = state::(tree); @@ -1150,15 +1225,20 @@ where let state = state::(tree); if let Some(focus) = &mut state.is_focused { - if focus.is_window_focused { - focus.now = now; + if focus.is_window_focused + && matches!( + state.cursor.state(&self.value), + cursor::State::Index(_) + ) + { + focus.now = *now; let millis_until_redraw = CURSOR_BLINK_INTERVAL_MILLIS - - (now - focus.updated_at).as_millis() + - (*now - focus.updated_at).as_millis() % CURSOR_BLINK_INTERVAL_MILLIS; shell.request_redraw(window::RedrawRequest::At( - now + Duration::from_millis( + *now + Duration::from_millis( millis_until_redraw as u64, ), )); @@ -1168,6 +1248,32 @@ where _ => {} } + let state = state::(tree); + let is_disabled = self.on_input.is_none(); + + let status = if is_disabled { + Status::Disabled + } else if state.is_focused() { + Status::Focused { + is_hovered: cursor.is_over(layout.bounds()), + } + } else if cursor.is_over(layout.bounds()) { + Status::Hovered + } else { + Status::Active + }; + + if let Event::Window(window::Event::RedrawRequested(_now)) = event { + self.last_status = Some(status); + } else { + match self.last_status { + Some(last_status) if status != last_status => { + shell.request_redraw(window::RedrawRequest::NextFrame); + } + _ => {} + } + } + event::Status::Ignored } @@ -1535,7 +1641,10 @@ pub enum Status { /// The [`TextInput`] is being hovered. Hovered, /// The [`TextInput`] is focused. - Focused, + Focused { + /// Whether the [`TextInput`] is hovered, while focused. + is_hovered: bool, + }, /// The [`TextInput`] cannot be interacted with. Disabled, } @@ -1612,7 +1721,7 @@ pub fn default(theme: &Theme, status: Status) -> Style { }, ..active }, - Status::Focused => Style { + Status::Focused { .. } => Style { border: Border { color: palette.primary.strong.color, ..active.border -- cgit From 752403d70c851ece620c4007710062b158e8dec3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 25 Oct 2024 15:40:05 +0200 Subject: Split `Shell::request_redraw` into two different methods --- widget/src/text_input.rs | 50 ++++++++++++++++-------------------------------- 1 file changed, 17 insertions(+), 33 deletions(-) (limited to 'widget/src/text_input.rs') diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index c18009a2..8fa7889f 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -751,7 +751,7 @@ where state.last_click = Some(click); if cursor_before != state.cursor { - shell.request_redraw(window::RedrawRequest::NextFrame); + shell.request_redraw(); } return event::Status::Captured; @@ -806,7 +806,7 @@ where } if selection_before != state.cursor.selection(&value) { - shell.request_redraw(window::RedrawRequest::NextFrame); + shell.request_redraw(); } return event::Status::Captured; @@ -914,9 +914,7 @@ where if cursor_before != state.cursor { focus.updated_at = Instant::now(); - shell.request_redraw( - window::RedrawRequest::NextFrame, - ); + shell.request_redraw(); } return event::Status::Captured; @@ -1037,9 +1035,7 @@ where if cursor_before != state.cursor { focus.updated_at = Instant::now(); - shell.request_redraw( - window::RedrawRequest::NextFrame, - ); + shell.request_redraw(); } return event::Status::Captured; @@ -1059,9 +1055,7 @@ where if cursor_before != state.cursor { focus.updated_at = Instant::now(); - shell.request_redraw( - window::RedrawRequest::NextFrame, - ); + shell.request_redraw(); } return event::Status::Captured; @@ -1083,9 +1077,7 @@ where if cursor_before != state.cursor { focus.updated_at = Instant::now(); - shell.request_redraw( - window::RedrawRequest::NextFrame, - ); + shell.request_redraw(); } return event::Status::Captured; @@ -1107,9 +1099,7 @@ where if cursor_before != state.cursor { focus.updated_at = Instant::now(); - shell.request_redraw( - window::RedrawRequest::NextFrame, - ); + shell.request_redraw(); } return event::Status::Captured; @@ -1136,9 +1126,7 @@ where if cursor_before != state.cursor { focus.updated_at = Instant::now(); - shell.request_redraw( - window::RedrawRequest::NextFrame, - ); + shell.request_redraw(); } return event::Status::Captured; @@ -1165,9 +1153,7 @@ where if cursor_before != state.cursor { focus.updated_at = Instant::now(); - shell.request_redraw( - window::RedrawRequest::NextFrame, - ); + shell.request_redraw(); } return event::Status::Captured; @@ -1218,7 +1204,7 @@ where focus.is_window_focused = true; focus.updated_at = Instant::now(); - shell.request_redraw(window::RedrawRequest::NextFrame); + shell.request_redraw(); } } Event::Window(window::Event::RedrawRequested(now)) => { @@ -1237,11 +1223,11 @@ where - (*now - focus.updated_at).as_millis() % CURSOR_BLINK_INTERVAL_MILLIS; - shell.request_redraw(window::RedrawRequest::At( + shell.request_redraw_at( *now + Duration::from_millis( millis_until_redraw as u64, ), - )); + ); } } } @@ -1265,13 +1251,11 @@ where if let Event::Window(window::Event::RedrawRequested(_now)) = event { self.last_status = Some(status); - } else { - match self.last_status { - Some(last_status) if status != last_status => { - shell.request_redraw(window::RedrawRequest::NextFrame); - } - _ => {} - } + } else if self + .last_status + .is_some_and(|last_status| status != last_status) + { + shell.request_redraw(); } event::Status::Ignored -- cgit From dcc184b01b753dbecb500205391f6eaaa21c8683 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 25 Oct 2024 19:28:18 +0200 Subject: Replace `event::Status` in `Widget::on_event` with `Shell::capture_event` --- widget/src/text_input.rs | 66 +++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 35 deletions(-) (limited to 'widget/src/text_input.rs') diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 8fa7889f..87dffb98 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -42,7 +42,6 @@ use editor::Editor; use crate::core::alignment; use crate::core::clipboard::{self, Clipboard}; -use crate::core::event::{self, Event}; use crate::core::keyboard; use crate::core::keyboard::key; use crate::core::layout; @@ -57,8 +56,8 @@ use crate::core::widget::operation::{self, Operation}; use crate::core::widget::tree::{self, Tree}; use crate::core::window; use crate::core::{ - Background, Border, Color, Element, Layout, Length, Padding, Pixels, Point, - Rectangle, Shell, Size, Theme, Vector, Widget, + Background, Border, Color, Element, Event, Layout, Length, Padding, Pixels, + Point, Rectangle, Shell, Size, Theme, Vector, Widget, }; use crate::runtime::task::{self, Task}; use crate::runtime::Action; @@ -638,7 +637,7 @@ where clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, _viewport: &Rectangle, - ) -> event::Status { + ) { let update_cache = |state, value| { replace_paragraph( renderer, @@ -754,7 +753,7 @@ where shell.request_redraw(); } - return event::Status::Captured; + shell.capture_event(); } } Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) @@ -809,7 +808,7 @@ where shell.request_redraw(); } - return event::Status::Captured; + shell.capture_event(); } } Event::Keyboard(keyboard::Event::KeyPressed { @@ -834,14 +833,15 @@ where ); } - return event::Status::Captured; + shell.capture_event(); + return; } keyboard::Key::Character("x") if state.keyboard_modifiers.command() && !self.is_secure => { let Some(on_input) = &self.on_input else { - return event::Status::Ignored; + return; }; if let Some((start, end)) = @@ -859,18 +859,18 @@ where let message = (on_input)(editor.contents()); shell.publish(message); + shell.capture_event(); focus.updated_at = Instant::now(); update_cache(state, &self.value); - - return event::Status::Captured; + return; } keyboard::Key::Character("v") if state.keyboard_modifiers.command() && !state.keyboard_modifiers.alt() => { let Some(on_input) = &self.on_input else { - return event::Status::Ignored; + return; }; let content = match state.is_pasting.take() { @@ -897,12 +897,12 @@ where (on_input)(editor.contents()) }; shell.publish(message); + shell.capture_event(); state.is_pasting = Some(content); focus.updated_at = Instant::now(); update_cache(state, &self.value); - - return event::Status::Captured; + return; } keyboard::Key::Character("a") if state.keyboard_modifiers.command() => @@ -917,14 +917,15 @@ where shell.request_redraw(); } - return event::Status::Captured; + shell.capture_event(); + return; } _ => {} } if let Some(text) = text { let Some(on_input) = &self.on_input else { - return event::Status::Ignored; + return; }; state.is_pasting = None; @@ -939,11 +940,11 @@ where let message = (on_input)(editor.contents()); shell.publish(message); + shell.capture_event(); focus.updated_at = Instant::now(); update_cache(state, &self.value); - - return event::Status::Captured; + return; } } @@ -951,13 +952,12 @@ where keyboard::Key::Named(key::Named::Enter) => { if let Some(on_submit) = self.on_submit.clone() { shell.publish(on_submit); - - return event::Status::Captured; + shell.capture_event(); } } keyboard::Key::Named(key::Named::Backspace) => { let Some(on_input) = &self.on_input else { - return event::Status::Ignored; + return; }; if modifiers.jump() @@ -980,15 +980,14 @@ where let message = (on_input)(editor.contents()); shell.publish(message); + shell.capture_event(); focus.updated_at = Instant::now(); update_cache(state, &self.value); - - return event::Status::Captured; } keyboard::Key::Named(key::Named::Delete) => { let Some(on_input) = &self.on_input else { - return event::Status::Ignored; + return; }; if modifiers.jump() @@ -1014,11 +1013,10 @@ where let message = (on_input)(editor.contents()); shell.publish(message); + shell.capture_event(); focus.updated_at = Instant::now(); update_cache(state, &self.value); - - return event::Status::Captured; } keyboard::Key::Named(key::Named::Home) => { let cursor_before = state.cursor; @@ -1038,7 +1036,7 @@ where shell.request_redraw(); } - return event::Status::Captured; + shell.capture_event(); } keyboard::Key::Named(key::Named::End) => { let cursor_before = state.cursor; @@ -1058,7 +1056,7 @@ where shell.request_redraw(); } - return event::Status::Captured; + shell.capture_event(); } keyboard::Key::Named(key::Named::ArrowLeft) if modifiers.macos_command() => @@ -1080,7 +1078,7 @@ where shell.request_redraw(); } - return event::Status::Captured; + shell.capture_event(); } keyboard::Key::Named(key::Named::ArrowRight) if modifiers.macos_command() => @@ -1102,7 +1100,7 @@ where shell.request_redraw(); } - return event::Status::Captured; + shell.capture_event(); } keyboard::Key::Named(key::Named::ArrowLeft) => { let cursor_before = state.cursor; @@ -1129,7 +1127,7 @@ where shell.request_redraw(); } - return event::Status::Captured; + shell.capture_event(); } keyboard::Key::Named(key::Named::ArrowRight) => { let cursor_before = state.cursor; @@ -1156,7 +1154,7 @@ where shell.request_redraw(); } - return event::Status::Captured; + shell.capture_event(); } keyboard::Key::Named(key::Named::Escape) => { state.is_focused = None; @@ -1166,7 +1164,7 @@ where state.keyboard_modifiers = keyboard::Modifiers::default(); - return event::Status::Captured; + shell.capture_event(); } _ => {} } @@ -1179,7 +1177,7 @@ where if let keyboard::Key::Character("v") = key.as_ref() { state.is_pasting = None; - return event::Status::Captured; + shell.capture_event(); } } @@ -1257,8 +1255,6 @@ where { shell.request_redraw(); } - - event::Status::Ignored } fn draw( -- cgit From f02bfc3f68322bea0c56283d76888714be401ec2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 25 Oct 2024 22:06:06 +0200 Subject: Rename `Widget::on_event` to `update` --- widget/src/text_input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'widget/src/text_input.rs') diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 87dffb98..51e61cc0 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -627,7 +627,7 @@ where operation.text_input(state, self.id.as_ref().map(|id| &id.0)); } - fn on_event( + fn update( &mut self, tree: &mut Tree, event: Event, -- cgit