summaryrefslogtreecommitdiffstats
path: root/widget
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-11-04 23:21:06 +0100
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-11-05 23:52:59 +0100
commit3482ffecdcadf036b7d61ab3821c6ee661d0ec56 (patch)
tree4dd7e266839c7b1b10255c412cf3ee733aee31eb /widget
parentd5a886dbcb50964ce8c6132a4bb3504d6396d01b (diff)
downloadiced-3482ffecdcadf036b7d61ab3821c6ee661d0ec56.tar.gz
iced-3482ffecdcadf036b7d61ab3821c6ee661d0ec56.tar.bz2
iced-3482ffecdcadf036b7d61ab3821c6ee661d0ec56.zip
Implement `reactive-rendering` for `text_editor`
Diffstat (limited to '')
-rw-r--r--widget/src/text_editor.rs317
1 files changed, 170 insertions, 147 deletions
diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs
index 32e14946..3bd4c7f9 100644
--- a/widget/src/text_editor.rs
+++ b/widget/src/text_editor.rs
@@ -119,6 +119,7 @@ pub struct TextEditor<
&Highlighter::Highlight,
&Theme,
) -> highlighter::Format<Renderer::Font>,
+ last_status: Option<Status>,
}
impl<'a, Message, Theme, Renderer>
@@ -146,6 +147,7 @@ where
highlighter_format: |_highlight, _theme| {
highlighter::Format::default()
},
+ last_status: None,
}
}
}
@@ -269,6 +271,7 @@ where
on_edit: self.on_edit,
highlighter_settings: settings,
highlighter_format: to_format,
+ last_status: self.last_status,
}
}
@@ -611,6 +614,10 @@ where
};
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) => {
@@ -647,157 +654,180 @@ where
_ => {}
}
- 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;
- };
-
- 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());
+ ) {
+ shell.capture_event();
+
+ 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;
+ 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();
- 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,
+ }));
+ }
+ 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,
- );
- if let Some(focus) = &mut state.focus {
- focus.updated_at = Instant::now();
+ apply_binding(
+ binding,
+ self.content,
+ state,
+ on_edit,
+ clipboard,
+ shell,
+ );
+
+ if let Some(focus) = &mut state.focus {
+ focus.updated_at = Instant::now();
+ }
}
}
}
- shell.capture_event();
+ 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(
@@ -807,7 +837,7 @@ where
theme: &Theme,
_defaults: &renderer::Style,
layout: Layout<'_>,
- cursor: mouse::Cursor,
+ _cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
let bounds = layout.bounds();
@@ -823,20 +853,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 {
@@ -1035,7 +1053,7 @@ impl<Message> Binding<Message> {
status,
} = event;
- if status != Status::Focused {
+ if !matches!(status, Status::Focused { .. }) {
return None;
}
@@ -1175,7 +1193,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
};
@@ -1221,7 +1241,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,
}
@@ -1296,7 +1319,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