summaryrefslogtreecommitdiffstats
path: root/widget/src/text
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--widget/src/text/rich.rs20
-rw-r--r--widget/src/text_editor.rs332
-rw-r--r--widget/src/text_input.rs251
-rw-r--r--widget/src/text_input/cursor.rs4
4 files changed, 362 insertions, 245 deletions
diff --git a/widget/src/text/rich.rs b/widget/src/text/rich.rs
index 3d241375..7ef2707b 100644
--- a/widget/src/text/rich.rs
+++ b/widget/src/text/rich.rs
@@ -1,5 +1,4 @@
use crate::core::alignment;
-use crate::core::event;
use crate::core::layout;
use crate::core::mouse;
use crate::core::renderer;
@@ -355,7 +354,7 @@ where
);
}
- fn on_event(
+ fn update(
&mut self,
tree: &mut Tree,
event: Event,
@@ -365,7 +364,7 @@ where
_clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Link>,
_viewport: &Rectangle,
- ) -> event::Status {
+ ) {
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
if let Some(position) = cursor.position_in(layout.bounds()) {
@@ -374,9 +373,16 @@ where
.downcast_mut::<State<Link, Renderer::Paragraph>>();
if let Some(span) = state.paragraph.hit_span(position) {
- state.span_pressed = Some(span);
-
- return event::Status::Captured;
+ if self
+ .spans
+ .as_ref()
+ .as_ref()
+ .get(span)
+ .is_some_and(|span| span.link.is_some())
+ {
+ state.span_pressed = Some(span);
+ shell.capture_event();
+ }
}
}
}
@@ -409,8 +415,6 @@ where
}
_ => {}
}
-
- event::Status::Ignored
}
fn mouse_interaction(
diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs
index a298252a..b6e291e4 100644
--- a/widget/src/text_editor.rs
+++ b/widget/src/text_editor.rs
@@ -33,7 +33,6 @@
//! ```
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::{self, Layout};
@@ -47,7 +46,7 @@ use crate::core::widget::operation;
use crate::core::widget::{self, Widget};
use crate::core::window;
use crate::core::{
- Background, Border, Color, Element, Length, Padding, Pixels, Point,
+ Background, Border, Color, Element, Event, Length, Padding, Pixels, Point,
Rectangle, Shell, Size, SmolStr, Theme, Vector,
};
@@ -120,6 +119,7 @@ pub struct TextEditor<
&Highlighter::Highlight,
&Theme,
) -> highlighter::Format<Renderer::Font>,
+ last_status: Option<Status>,
}
impl<'a, Message, Theme, Renderer>
@@ -147,6 +147,7 @@ where
highlighter_format: |_highlight, _theme| {
highlighter::Format::default()
},
+ last_status: None,
}
}
}
@@ -270,6 +271,7 @@ where
on_edit: self.on_edit,
highlighter_settings: settings,
highlighter_format: to_format,
+ last_status: self.last_status,
}
}
@@ -596,7 +598,7 @@ where
}
}
- fn on_event(
+ fn update(
&mut self,
tree: &mut widget::Tree,
event: Event,
@@ -606,12 +608,16 @@ where
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
_viewport: &Rectangle,
- ) -> event::Status {
+ ) {
let Some(on_edit) = self.on_edit.as_ref() else {
- return event::Status::Ignored;
+ return;
};
let state = tree.state.downcast_mut::<State<Highlighter>>();
+ let is_redraw = matches!(
+ event,
+ Event::Window(window::Event::RedrawRequested(_now)),
+ );
match event {
Event::Window(window::Event::Unfocused) => {
@@ -624,7 +630,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)) => {
@@ -637,168 +643,193 @@ where
- (now - focus.updated_at).as_millis()
% Focus::CURSOR_BLINK_INTERVAL_MILLIS;
- shell.request_redraw(window::RedrawRequest::At(
+ shell.request_redraw_at(
now + Duration::from_millis(
millis_until_redraw as u64,
),
- ));
+ );
}
}
}
_ => {}
}
- let Some(update) = Update::from_event(
+ if let Some(update) = Update::from_event(
event,
state,
layout.bounds(),
self.padding,
cursor,
self.key_binding.as_deref(),
- ) else {
- return event::Status::Ignored;
- };
-
- match update {
- Update::Click(click) => {
- let action = match click.kind() {
- mouse::click::Kind::Single => {
- Action::Click(click.position())
- }
- mouse::click::Kind::Double => Action::SelectWord,
- mouse::click::Kind::Triple => Action::SelectLine,
- };
-
- state.focus = Some(Focus::now());
- state.last_click = Some(click);
- state.drag_click = Some(click.kind());
+ ) {
+ match update {
+ Update::Click(click) => {
+ let action = match click.kind() {
+ mouse::click::Kind::Single => {
+ Action::Click(click.position())
+ }
+ mouse::click::Kind::Double => Action::SelectWord,
+ mouse::click::Kind::Triple => Action::SelectLine,
+ };
- shell.publish(on_edit(action));
- }
- Update::Drag(position) => {
- shell.publish(on_edit(Action::Drag(position)));
- }
- Update::Release => {
- state.drag_click = None;
- }
- Update::Scroll(lines) => {
- let bounds = self.content.0.borrow().editor.bounds();
+ state.focus = Some(Focus::now());
+ state.last_click = Some(click);
+ state.drag_click = Some(click.kind());
- if bounds.height >= i32::MAX as f32 {
- return event::Status::Ignored;
+ shell.publish(on_edit(action));
+ shell.capture_event();
+ }
+ Update::Drag(position) => {
+ shell.publish(on_edit(Action::Drag(position)));
}
+ Update::Release => {
+ state.drag_click = None;
+ }
+ Update::Scroll(lines) => {
+ let bounds = self.content.0.borrow().editor.bounds();
- let lines = lines + state.partial_scroll;
- state.partial_scroll = lines.fract();
+ if bounds.height >= i32::MAX as f32 {
+ return;
+ }
- shell.publish(on_edit(Action::Scroll {
- lines: lines as i32,
- }));
- }
- Update::Binding(binding) => {
- fn apply_binding<
- H: text::Highlighter,
- R: text::Renderer,
- Message,
- >(
- binding: Binding<Message>,
- content: &Content<R>,
- state: &mut State<H>,
- on_edit: &dyn Fn(Action) -> Message,
- clipboard: &mut dyn Clipboard,
- shell: &mut Shell<'_, Message>,
- ) {
- let mut publish = |action| shell.publish(on_edit(action));
-
- match binding {
- Binding::Unfocus => {
- state.focus = None;
- state.drag_click = None;
- }
- Binding::Copy => {
- if let Some(selection) = content.selection() {
- clipboard.write(
- clipboard::Kind::Standard,
- selection,
- );
- }
- }
- Binding::Cut => {
- if let Some(selection) = content.selection() {
- clipboard.write(
- clipboard::Kind::Standard,
- selection,
- );
+ let lines = lines + state.partial_scroll;
+ state.partial_scroll = lines.fract();
+ shell.publish(on_edit(Action::Scroll {
+ lines: lines as i32,
+ }));
+ shell.capture_event();
+ }
+ Update::Binding(binding) => {
+ fn apply_binding<
+ H: text::Highlighter,
+ R: text::Renderer,
+ Message,
+ >(
+ binding: Binding<Message>,
+ content: &Content<R>,
+ state: &mut State<H>,
+ on_edit: &dyn Fn(Action) -> Message,
+ clipboard: &mut dyn Clipboard,
+ shell: &mut Shell<'_, Message>,
+ ) {
+ let mut publish =
+ |action| shell.publish(on_edit(action));
+
+ match binding {
+ Binding::Unfocus => {
+ state.focus = None;
+ state.drag_click = None;
+ }
+ Binding::Copy => {
+ if let Some(selection) = content.selection() {
+ clipboard.write(
+ clipboard::Kind::Standard,
+ selection,
+ );
+ }
+ }
+ Binding::Cut => {
+ if let Some(selection) = content.selection() {
+ clipboard.write(
+ clipboard::Kind::Standard,
+ selection,
+ );
+
+ publish(Action::Edit(Edit::Delete));
+ }
+ }
+ Binding::Paste => {
+ if let Some(contents) =
+ clipboard.read(clipboard::Kind::Standard)
+ {
+ publish(Action::Edit(Edit::Paste(
+ Arc::new(contents),
+ )));
+ }
+ }
+ Binding::Move(motion) => {
+ publish(Action::Move(motion));
+ }
+ Binding::Select(motion) => {
+ publish(Action::Select(motion));
+ }
+ Binding::SelectWord => {
+ publish(Action::SelectWord);
+ }
+ Binding::SelectLine => {
+ publish(Action::SelectLine);
+ }
+ Binding::SelectAll => {
+ publish(Action::SelectAll);
+ }
+ Binding::Insert(c) => {
+ publish(Action::Edit(Edit::Insert(c)));
+ }
+ Binding::Enter => {
+ publish(Action::Edit(Edit::Enter));
+ }
+ Binding::Backspace => {
+ publish(Action::Edit(Edit::Backspace));
+ }
+ Binding::Delete => {
publish(Action::Edit(Edit::Delete));
}
- }
- Binding::Paste => {
- if let Some(contents) =
- clipboard.read(clipboard::Kind::Standard)
- {
- publish(Action::Edit(Edit::Paste(Arc::new(
- contents,
- ))));
+ Binding::Sequence(sequence) => {
+ for binding in sequence {
+ apply_binding(
+ binding, content, state, on_edit,
+ clipboard, shell,
+ );
+ }
}
- }
- Binding::Move(motion) => {
- publish(Action::Move(motion));
- }
- Binding::Select(motion) => {
- publish(Action::Select(motion));
- }
- Binding::SelectWord => {
- publish(Action::SelectWord);
- }
- Binding::SelectLine => {
- publish(Action::SelectLine);
- }
- Binding::SelectAll => {
- publish(Action::SelectAll);
- }
- Binding::Insert(c) => {
- publish(Action::Edit(Edit::Insert(c)));
- }
- Binding::Enter => {
- publish(Action::Edit(Edit::Enter));
- }
- Binding::Backspace => {
- publish(Action::Edit(Edit::Backspace));
- }
- Binding::Delete => {
- publish(Action::Edit(Edit::Delete));
- }
- Binding::Sequence(sequence) => {
- for binding in sequence {
- apply_binding(
- binding, content, state, on_edit,
- clipboard, shell,
- );
+ Binding::Custom(message) => {
+ shell.publish(message);
}
}
- Binding::Custom(message) => {
- shell.publish(message);
- }
}
- }
- apply_binding(
- binding,
- self.content,
- state,
- on_edit,
- clipboard,
- shell,
- );
+ apply_binding(
+ binding,
+ self.content,
+ state,
+ on_edit,
+ clipboard,
+ shell,
+ );
+
+ if let Some(focus) = &mut state.focus {
+ focus.updated_at = Instant::now();
+ }
- if let Some(focus) = &mut state.focus {
- focus.updated_at = Instant::now();
+ shell.capture_event();
}
}
}
- event::Status::Captured
+ let status = {
+ let is_disabled = self.on_edit.is_none();
+ let is_hovered = cursor.is_over(layout.bounds());
+
+ if is_disabled {
+ Status::Disabled
+ } else if state.focus.is_some() {
+ Status::Focused { is_hovered }
+ } else if is_hovered {
+ Status::Hovered
+ } else {
+ Status::Active
+ }
+ };
+
+ if is_redraw {
+ self.last_status = Some(status);
+ } else if self
+ .last_status
+ .is_some_and(|last_status| status != last_status)
+ {
+ shell.request_redraw();
+ }
}
fn draw(
@@ -808,7 +839,7 @@ where
theme: &Theme,
_defaults: &renderer::Style,
layout: Layout<'_>,
- cursor: mouse::Cursor,
+ _cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
let bounds = layout.bounds();
@@ -824,20 +855,8 @@ where
|highlight| (self.highlighter_format)(highlight, theme),
);
- let is_disabled = self.on_edit.is_none();
- let is_mouse_over = cursor.is_over(bounds);
-
- let status = if is_disabled {
- Status::Disabled
- } else if state.focus.is_some() {
- 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::Active));
renderer.fill_quad(
renderer::Quad {
@@ -1036,7 +1055,7 @@ impl<Message> Binding<Message> {
status,
} = event;
- if status != Status::Focused {
+ if !matches!(status, Status::Focused { .. }) {
return None;
}
@@ -1176,7 +1195,9 @@ impl<Message> Update<Message> {
..
}) => {
let status = if state.focus.is_some() {
- Status::Focused
+ Status::Focused {
+ is_hovered: cursor.is_over(bounds),
+ }
} else {
Status::Active
};
@@ -1222,7 +1243,10 @@ pub enum Status {
/// The [`TextEditor`] is being hovered.
Hovered,
/// The [`TextEditor`] is focused.
- Focused,
+ Focused {
+ /// Whether the [`TextEditor`] is hovered, while focused.
+ is_hovered: bool,
+ },
/// The [`TextEditor`] cannot be interacted with.
Disabled,
}
@@ -1297,7 +1321,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
diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs
index ff413779..51e61cc0 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;
@@ -120,6 +119,7 @@ pub struct TextInput<
on_submit: Option<Message>,
icon: Option<Icon<Renderer::Font>>,
class: Theme::Class<'a>,
+ last_status: Option<Status>,
}
/// The default [`Padding`] of a [`TextInput`].
@@ -150,6 +150,7 @@ where
on_submit: None,
icon: None,
class: Theme::default(),
+ last_status: None,
}
}
@@ -400,7 +401,7 @@ where
renderer: &mut Renderer,
theme: &Theme,
layout: Layout<'_>,
- cursor: mouse::Cursor,
+ _cursor: mouse::Cursor,
value: Option<&Value>,
viewport: &Rectangle,
) {
@@ -416,19 +417,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 {
@@ -637,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,
@@ -647,7 +637,7 @@ where
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
_viewport: &Rectangle,
- ) -> event::Status {
+ ) {
let update_cache = |state, value| {
replace_paragraph(
renderer,
@@ -660,22 +650,21 @@ where
);
};
- match event {
+ match &event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
| Event::Touch(touch::Event::FingerPressed { .. }) => {
let state = state::<Renderer>(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,7 +749,11 @@ where
state.last_click = Some(click);
- return event::Status::Captured;
+ if cursor_before != state.cursor {
+ shell.request_redraw();
+ }
+
+ shell.capture_event();
}
}
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
@@ -801,11 +794,21 @@ where
)
.unwrap_or(0);
+ let selection_before = state.cursor.selection(&value);
+
state
.cursor
.select_range(state.cursor.start(&value), position);
- return event::Status::Captured;
+ if let Some(focus) = &mut state.is_focused {
+ focus.updated_at = Instant::now();
+ }
+
+ if selection_before != state.cursor.selection(&value) {
+ shell.request_redraw();
+ }
+
+ shell.capture_event();
}
}
Event::Keyboard(keyboard::Event::KeyPressed {
@@ -815,7 +818,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")
@@ -831,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)) =
@@ -856,17 +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() {
@@ -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 {
@@ -894,26 +897,35 @@ 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() =>
{
+ let cursor_before = state.cursor;
+
state.cursor.select_all(&self.value);
- return event::Status::Captured;
+ if cursor_before != state.cursor {
+ focus.updated_at = Instant::now();
+
+ shell.request_redraw();
+ }
+
+ 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;
@@ -928,12 +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;
}
}
@@ -941,11 +952,12 @@ where
keyboard::Key::Named(key::Named::Enter) => {
if let Some(on_submit) = self.on_submit.clone() {
shell.publish(on_submit);
+ 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()
@@ -968,12 +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);
}
keyboard::Key::Named(key::Named::Delete) => {
let Some(on_input) = &self.on_input else {
- return event::Status::Ignored;
+ return;
};
if modifiers.jump()
@@ -999,10 +1013,14 @@ where
let message = (on_input)(editor.contents());
shell.publish(message);
+ shell.capture_event();
+ focus.updated_at = Instant::now();
update_cache(state, &self.value);
}
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 +1029,18 @@ where
} else {
state.cursor.move_to(0);
}
+
+ if cursor_before != state.cursor {
+ focus.updated_at = Instant::now();
+
+ shell.request_redraw();
+ }
+
+ shell.capture_event();
}
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 +1049,20 @@ where
} else {
state.cursor.move_to(self.value.len());
}
+
+ if cursor_before != state.cursor {
+ focus.updated_at = Instant::now();
+
+ shell.request_redraw();
+ }
+
+ shell.capture_event();
}
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 +1071,20 @@ where
} else {
state.cursor.move_to(0);
}
+
+ if cursor_before != state.cursor {
+ focus.updated_at = Instant::now();
+
+ shell.request_redraw();
+ }
+
+ shell.capture_event();
}
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 +1093,18 @@ where
} else {
state.cursor.move_to(self.value.len());
}
+
+ if cursor_before != state.cursor {
+ focus.updated_at = Instant::now();
+
+ shell.request_redraw();
+ }
+
+ shell.capture_event();
}
keyboard::Key::Named(key::Named::ArrowLeft) => {
+ let cursor_before = state.cursor;
+
if modifiers.jump() && !self.is_secure {
if modifiers.shift() {
state
@@ -1062,8 +1120,18 @@ where
} else {
state.cursor.move_left(&self.value);
}
+
+ if cursor_before != state.cursor {
+ focus.updated_at = Instant::now();
+
+ shell.request_redraw();
+ }
+
+ shell.capture_event();
}
keyboard::Key::Named(key::Named::ArrowRight) => {
+ let cursor_before = state.cursor;
+
if modifiers.jump() && !self.is_secure {
if modifiers.shift() {
state
@@ -1079,6 +1147,14 @@ where
} else {
state.cursor.move_right(&self.value);
}
+
+ if cursor_before != state.cursor {
+ focus.updated_at = Instant::now();
+
+ shell.request_redraw();
+ }
+
+ shell.capture_event();
}
keyboard::Key::Named(key::Named::Escape) => {
state.is_focused = None;
@@ -1087,39 +1163,22 @@ where
state.keyboard_modifiers =
keyboard::Modifiers::default();
- }
- keyboard::Key::Named(
- key::Named::Tab
- | key::Named::ArrowUp
- | key::Named::ArrowDown,
- ) => {
- return event::Status::Ignored;
+
+ shell.capture_event();
}
_ => {}
}
-
- return event::Status::Captured;
}
}
Event::Keyboard(keyboard::Event::KeyReleased { key, .. }) => {
let state = state::<Renderer>(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;
+ shell.capture_event();
+ }
}
state.is_pasting = None;
@@ -1127,7 +1186,7 @@ where
Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
let state = state::<Renderer>(tree);
- state.keyboard_modifiers = modifiers;
+ state.keyboard_modifiers = *modifiers;
}
Event::Window(window::Event::Unfocused) => {
let state = state::<Renderer>(tree);
@@ -1143,32 +1202,59 @@ 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)) => {
let state = state::<Renderer>(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(
+ shell.request_redraw_at(
+ *now + Duration::from_millis(
millis_until_redraw as u64,
),
- ));
+ );
}
}
}
_ => {}
}
- event::Status::Ignored
+ let state = state::<Renderer>(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 if self
+ .last_status
+ .is_some_and(|last_status| status != last_status)
+ {
+ shell.request_redraw();
+ }
}
fn draw(
@@ -1535,7 +1621,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 +1701,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
diff --git a/widget/src/text_input/cursor.rs b/widget/src/text_input/cursor.rs
index f682b17d..a326fc8f 100644
--- a/widget/src/text_input/cursor.rs
+++ b/widget/src/text_input/cursor.rs
@@ -2,13 +2,13 @@
use crate::text_input::Value;
/// The cursor of a text input.
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Cursor {
state: State,
}
/// The state of a [`Cursor`].
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum State {
/// Cursor without a selection
Index(usize),