Implement explicit text caching in the widget state tree

This commit is contained in:
Héctor Ramón Jiménez 2023-08-30 04:31:21 +02:00
parent c9bd48704d
commit ed3454301e
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
79 changed files with 1910 additions and 1705 deletions

View file

@ -144,11 +144,6 @@ where
self
}
/// Returns whether the [`ComboBox`] is currently focused or not.
pub fn is_focused(&self) -> bool {
self.state.is_focused()
}
/// Sets the text sixe of the [`ComboBox`].
pub fn size(mut self, size: f32) -> Self {
self.text_input = self.text_input.size(size);
@ -179,7 +174,6 @@ pub struct State<T>(RefCell<Inner<T>>);
#[derive(Debug, Clone)]
struct Inner<T> {
text_input: text_input::State,
value: String,
options: Vec<T>,
option_matchers: Vec<String>,
@ -216,7 +210,6 @@ where
);
Self(RefCell::new(Inner {
text_input: text_input::State::new(),
value,
options,
option_matchers,
@ -224,51 +217,12 @@ where
}))
}
/// Focuses the [`ComboBox`].
pub fn focused(self) -> Self {
self.focus();
self
}
/// Focuses the [`ComboBox`].
pub fn focus(&self) {
let mut inner = self.0.borrow_mut();
inner.text_input.focus();
}
/// Unfocuses the [`ComboBox`].
pub fn unfocus(&self) {
let mut inner = self.0.borrow_mut();
inner.text_input.unfocus();
}
/// Returns whether the [`ComboBox`] is currently focused or not.
pub fn is_focused(&self) -> bool {
let inner = self.0.borrow();
inner.text_input.is_focused()
}
fn value(&self) -> String {
let inner = self.0.borrow();
inner.value.clone()
}
fn text_input_tree(&self) -> widget::Tree {
let inner = self.0.borrow();
inner.text_input_tree()
}
fn update_text_input(&self, tree: widget::Tree) {
let mut inner = self.0.borrow_mut();
inner.update_text_input(tree)
}
fn with_inner<O>(&self, f: impl FnOnce(&Inner<T>) -> O) -> O {
let inner = self.0.borrow();
@ -288,21 +242,6 @@ where
}
}
impl<T> Inner<T> {
fn text_input_tree(&self) -> widget::Tree {
widget::Tree {
tag: widget::tree::Tag::of::<text_input::State>(),
state: widget::tree::State::new(self.text_input.clone()),
children: vec![],
}
}
fn update_text_input(&mut self, tree: widget::Tree) {
self.text_input =
tree.state.downcast_ref::<text_input::State>().clone();
}
}
impl<T> Filtered<T>
where
T: Clone,
@ -366,10 +305,11 @@ where
fn layout(
&self,
tree: &widget::Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.text_input.layout(renderer, limits)
self.text_input.layout(tree, renderer, limits)
}
fn tag(&self) -> widget::tree::Tag {
@ -385,6 +325,10 @@ where
})
}
fn children(&self) -> Vec<widget::Tree> {
vec![widget::Tree::new(&self.text_input as &dyn Widget<_, _>)]
}
fn on_event(
&mut self,
tree: &mut widget::Tree,
@ -398,7 +342,13 @@ where
) -> event::Status {
let menu = tree.state.downcast_mut::<Menu<T>>();
let started_focused = self.state.is_focused();
let started_focused = {
let text_input_state = tree.children[0]
.state
.downcast_ref::<text_input::State<Renderer::Paragraph>>();
text_input_state.is_focused()
};
// This is intended to check whether or not the message buffer was empty,
// since `Shell` does not expose such functionality.
let mut published_message_to_shell = false;
@ -408,9 +358,8 @@ where
let mut local_shell = Shell::new(&mut local_messages);
// Provide it to the widget
let mut tree = self.state.text_input_tree();
let mut event_status = self.text_input.on_event(
&mut tree,
&mut tree.children[0],
event.clone(),
layout,
cursor,
@ -419,7 +368,6 @@ where
&mut local_shell,
viewport,
);
self.state.update_text_input(tree);
// Then finally react to them here
for message in local_messages {
@ -450,7 +398,15 @@ where
shell.invalidate_layout();
}
if self.state.is_focused() {
let is_focused = {
let text_input_state = tree.children[0]
.state
.downcast_ref::<text_input::State<Renderer::Paragraph>>();
text_input_state.is_focused()
};
if is_focused {
self.state.with_inner(|state| {
if !started_focused {
if let Some(on_option_hovered) = &mut self.on_option_hovered
@ -589,9 +545,8 @@ where
published_message_to_shell = true;
// Unfocus the input
let mut tree = state.text_input_tree();
let _ = self.text_input.on_event(
&mut tree,
&mut tree.children[0],
Event::Mouse(mouse::Event::ButtonPressed(
mouse::Button::Left,
)),
@ -602,21 +557,25 @@ where
&mut Shell::new(&mut vec![]),
viewport,
);
state.update_text_input(tree);
}
});
if started_focused
&& !self.state.is_focused()
&& !published_message_to_shell
{
let is_focused = {
let text_input_state = tree.children[0]
.state
.downcast_ref::<text_input::State<Renderer::Paragraph>>();
text_input_state.is_focused()
};
if started_focused && !is_focused && !published_message_to_shell {
if let Some(message) = self.on_close.take() {
shell.publish(message);
}
}
// Focus changed, invalidate widget tree to force a fresh `view`
if started_focused != self.state.is_focused() {
if started_focused != is_focused {
shell.invalidate_widgets();
}
@ -625,20 +584,24 @@ where
fn mouse_interaction(
&self,
_tree: &widget::Tree,
tree: &widget::Tree,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
let tree = self.state.text_input_tree();
self.text_input
.mouse_interaction(&tree, layout, cursor, viewport, renderer)
self.text_input.mouse_interaction(
&tree.children[0],
layout,
cursor,
viewport,
renderer,
)
}
fn draw(
&self,
_tree: &widget::Tree,
tree: &widget::Tree,
renderer: &mut Renderer,
theme: &Renderer::Theme,
_style: &renderer::Style,
@ -646,16 +609,28 @@ where
cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
let selection = if self.state.is_focused() || self.selection.is_empty()
{
let is_focused = {
let text_input_state = tree.children[0]
.state
.downcast_ref::<text_input::State<Renderer::Paragraph>>();
text_input_state.is_focused()
};
let selection = if is_focused || self.selection.is_empty() {
None
} else {
Some(&self.selection)
};
let tree = self.state.text_input_tree();
self.text_input
.draw(&tree, renderer, theme, layout, cursor, selection);
self.text_input.draw(
&tree.children[0],
renderer,
theme,
layout,
cursor,
selection,
);
}
fn overlay<'b>(
@ -664,14 +639,22 @@ where
layout: Layout<'_>,
_renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
let Menu {
menu,
filtered_options,
hovered_option,
..
} = tree.state.downcast_mut::<Menu<T>>();
let is_focused = {
let text_input_state = tree.children[0]
.state
.downcast_ref::<text_input::State<Renderer::Paragraph>>();
text_input_state.is_focused()
};
if is_focused {
let Menu {
menu,
filtered_options,
hovered_option,
..
} = tree.state.downcast_mut::<Menu<T>>();
if self.state.is_focused() {
let bounds = layout.bounds();
self.state.sync_filtered_options(filtered_options);