summaryrefslogtreecommitdiffstats
path: root/native/src/widget
diff options
context:
space:
mode:
Diffstat (limited to 'native/src/widget')
-rw-r--r--native/src/widget/action.rs59
-rw-r--r--native/src/widget/operation.rs5
-rw-r--r--native/src/widget/operation/text_input.rs131
-rw-r--r--native/src/widget/pane_grid.rs18
-rw-r--r--native/src/widget/pane_grid/content.rs29
-rw-r--r--native/src/widget/pane_grid/title_bar.rs40
-rw-r--r--native/src/widget/text_input.rs45
7 files changed, 315 insertions, 12 deletions
diff --git a/native/src/widget/action.rs b/native/src/widget/action.rs
index 0345fe2b..9aa79dec 100644
--- a/native/src/widget/action.rs
+++ b/native/src/widget/action.rs
@@ -1,8 +1,10 @@
-use crate::widget::operation::{self, Operation};
+use crate::widget::operation::{self, Focusable, Operation, Scrollable};
use crate::widget::Id;
use iced_futures::MaybeSend;
+use std::rc::Rc;
+
/// An operation to be performed on the widget tree.
#[allow(missing_debug_implementations)]
pub struct Action<T>(Box<dyn Operation<T>>);
@@ -24,7 +26,7 @@ impl<T> Action<T> {
{
Action(Box::new(Map {
operation: self.0,
- f: Box::new(f),
+ f: Rc::new(f),
}))
}
@@ -37,7 +39,7 @@ impl<T> Action<T> {
#[allow(missing_debug_implementations)]
struct Map<A, B> {
operation: Box<dyn Operation<A>>,
- f: Box<dyn Fn(A) -> B>,
+ f: Rc<dyn Fn(A) -> B>,
}
impl<A, B> Operation<B> for Map<A, B>
@@ -50,30 +52,44 @@ where
id: Option<&Id>,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
) {
- struct MapRef<'a, A, B> {
+ struct MapRef<'a, A> {
operation: &'a mut dyn Operation<A>,
- f: &'a dyn Fn(A) -> B,
}
- impl<'a, A, B> Operation<B> for MapRef<'a, A, B> {
+ impl<'a, A, B> Operation<B> for MapRef<'a, A> {
fn container(
&mut self,
id: Option<&Id>,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
) {
- let Self { operation, f } = self;
+ let Self { operation, .. } = self;
operation.container(id, &mut |operation| {
- operate_on_children(&mut MapRef { operation, f });
+ operate_on_children(&mut MapRef { operation });
});
}
+
+ fn scrollable(
+ &mut self,
+ state: &mut dyn Scrollable,
+ id: Option<&Id>,
+ ) {
+ self.operation.scrollable(state, id);
+ }
+
+ fn focusable(
+ &mut self,
+ state: &mut dyn Focusable,
+ id: Option<&Id>,
+ ) {
+ self.operation.focusable(state, id);
+ }
}
- let Self { operation, f } = self;
+ let Self { operation, .. } = self;
MapRef {
operation: operation.as_mut(),
- f,
}
.container(id, operate_on_children);
}
@@ -93,4 +109,27 @@ where
) {
self.operation.scrollable(state, id);
}
+
+ fn text_input(
+ &mut self,
+ state: &mut dyn operation::TextInput,
+ id: Option<&Id>,
+ ) {
+ self.operation.text_input(state, id);
+ }
+
+ fn finish(&self) -> operation::Outcome<B> {
+ match self.operation.finish() {
+ operation::Outcome::None => operation::Outcome::None,
+ operation::Outcome::Some(output) => {
+ operation::Outcome::Some((self.f)(output))
+ }
+ operation::Outcome::Chain(next) => {
+ operation::Outcome::Chain(Box::new(Map {
+ operation: next,
+ f: self.f.clone(),
+ }))
+ }
+ }
+ }
}
diff --git a/native/src/widget/operation.rs b/native/src/widget/operation.rs
index 2b1179f1..a0aa4117 100644
--- a/native/src/widget/operation.rs
+++ b/native/src/widget/operation.rs
@@ -1,9 +1,11 @@
//! Query or update internal widget state.
pub mod focusable;
pub mod scrollable;
+pub mod text_input;
pub use focusable::Focusable;
pub use scrollable::Scrollable;
+pub use text_input::TextInput;
use crate::widget::Id;
@@ -28,6 +30,9 @@ pub trait Operation<T> {
/// Operates on a widget that can be scrolled.
fn scrollable(&mut self, _state: &mut dyn Scrollable, _id: Option<&Id>) {}
+ /// Operates on a widget that has text input.
+ fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {}
+
/// Finishes the [`Operation`] and returns its [`Outcome`].
fn finish(&self) -> Outcome<T> {
Outcome::None
diff --git a/native/src/widget/operation/text_input.rs b/native/src/widget/operation/text_input.rs
new file mode 100644
index 00000000..4c773e99
--- /dev/null
+++ b/native/src/widget/operation/text_input.rs
@@ -0,0 +1,131 @@
+//! Operate on widgets that have text input.
+use crate::widget::operation::Operation;
+use crate::widget::Id;
+
+/// The internal state of a widget that has text input.
+pub trait TextInput {
+ /// Moves the cursor of the text input to the front of the input text.
+ fn move_cursor_to_front(&mut self);
+ /// Moves the cursor of the text input to the end of the input text.
+ fn move_cursor_to_end(&mut self);
+ /// Moves the cursor of the text input to an arbitrary location.
+ fn move_cursor_to(&mut self, position: usize);
+ /// Selects all the content of the text input.
+ fn select_all(&mut self);
+}
+
+/// Produces an [`Operation`] that moves the cursor of the widget with the given [`Id`] to the
+/// front.
+pub fn move_cursor_to_front<T>(target: Id) -> impl Operation<T> {
+ struct MoveCursor {
+ target: Id,
+ }
+
+ impl<T> Operation<T> for MoveCursor {
+ fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
+ match id {
+ Some(id) if id == &self.target => {
+ state.move_cursor_to_front();
+ }
+ _ => {}
+ }
+ }
+
+ fn container(
+ &mut self,
+ _id: Option<&Id>,
+ operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
+ ) {
+ operate_on_children(self)
+ }
+ }
+
+ MoveCursor { target }
+}
+
+/// Produces an [`Operation`] that moves the cursor of the widget with the given [`Id`] to the
+/// end.
+pub fn move_cursor_to_end<T>(target: Id) -> impl Operation<T> {
+ struct MoveCursor {
+ target: Id,
+ }
+
+ impl<T> Operation<T> for MoveCursor {
+ fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
+ match id {
+ Some(id) if id == &self.target => {
+ state.move_cursor_to_end();
+ }
+ _ => {}
+ }
+ }
+
+ fn container(
+ &mut self,
+ _id: Option<&Id>,
+ operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
+ ) {
+ operate_on_children(self)
+ }
+ }
+
+ MoveCursor { target }
+}
+
+/// Produces an [`Operation`] that moves the cursor of the widget with the given [`Id`] to the
+/// provided position.
+pub fn move_cursor_to<T>(target: Id, position: usize) -> impl Operation<T> {
+ struct MoveCursor {
+ target: Id,
+ position: usize,
+ }
+
+ impl<T> Operation<T> for MoveCursor {
+ fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
+ match id {
+ Some(id) if id == &self.target => {
+ state.move_cursor_to(self.position);
+ }
+ _ => {}
+ }
+ }
+
+ fn container(
+ &mut self,
+ _id: Option<&Id>,
+ operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
+ ) {
+ operate_on_children(self)
+ }
+ }
+
+ MoveCursor { target, position }
+}
+
+/// Produces an [`Operation`] that selects all the content of the widget with the given [`Id`].
+pub fn select_all<T>(target: Id) -> impl Operation<T> {
+ struct MoveCursor {
+ target: Id,
+ }
+
+ impl<T> Operation<T> for MoveCursor {
+ fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
+ match id {
+ Some(id) if id == &self.target => {
+ state.select_all();
+ }
+ _ => {}
+ }
+ }
+
+ fn container(
+ &mut self,
+ _id: Option<&Id>,
+ operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
+ ) {
+ operate_on_children(self)
+ }
+ }
+
+ MoveCursor { target }
+}
diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs
index 321e4e75..8f9065b0 100644
--- a/native/src/widget/pane_grid.rs
+++ b/native/src/widget/pane_grid.rs
@@ -38,6 +38,7 @@ use crate::mouse;
use crate::overlay;
use crate::renderer;
use crate::touch;
+use crate::widget;
use crate::widget::container;
use crate::widget::tree::{self, Tree};
use crate::{
@@ -289,6 +290,23 @@ where
)
}
+ fn operate(
+ &self,
+ tree: &mut Tree,
+ layout: Layout<'_>,
+ operation: &mut dyn widget::Operation<Message>,
+ ) {
+ operation.container(None, &mut |operation| {
+ self.contents
+ .iter()
+ .zip(&mut tree.children)
+ .zip(layout.children())
+ .for_each(|(((_pane, content), state), layout)| {
+ content.operate(state, layout, operation);
+ })
+ });
+ }
+
fn on_event(
&mut self,
tree: &mut Tree,
diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs
index 7e6c8148..5e843cff 100644
--- a/native/src/widget/pane_grid/content.rs
+++ b/native/src/widget/pane_grid/content.rs
@@ -5,7 +5,7 @@ use crate::overlay;
use crate::renderer;
use crate::widget::container;
use crate::widget::pane_grid::{Draggable, TitleBar};
-use crate::widget::Tree;
+use crate::widget::{self, Tree};
use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
/// The content of a [`Pane`].
@@ -183,6 +183,33 @@ where
}
}
+ pub(crate) fn operate(
+ &self,
+ tree: &mut Tree,
+ layout: Layout<'_>,
+ operation: &mut dyn widget::Operation<Message>,
+ ) {
+ let body_layout = if let Some(title_bar) = &self.title_bar {
+ let mut children = layout.children();
+
+ title_bar.operate(
+ &mut tree.children[1],
+ children.next().unwrap(),
+ operation,
+ );
+
+ children.next().unwrap()
+ } else {
+ layout
+ };
+
+ self.body.as_widget().operate(
+ &mut tree.children[0],
+ body_layout,
+ operation,
+ );
+ }
+
pub(crate) fn on_event(
&mut self,
tree: &mut Tree,
diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs
index 1b70e51b..115f6270 100644
--- a/native/src/widget/pane_grid/title_bar.rs
+++ b/native/src/widget/pane_grid/title_bar.rs
@@ -4,7 +4,7 @@ use crate::mouse;
use crate::overlay;
use crate::renderer;
use crate::widget::container;
-use crate::widget::Tree;
+use crate::widget::{self, Tree};
use crate::{
Clipboard, Element, Layout, Padding, Point, Rectangle, Shell, Size,
};
@@ -257,6 +257,44 @@ where
layout::Node::with_children(node.size().pad(self.padding), vec![node])
}
+ pub(crate) fn operate(
+ &self,
+ tree: &mut Tree,
+ layout: Layout<'_>,
+ operation: &mut dyn widget::Operation<Message>,
+ ) {
+ let mut children = layout.children();
+ let padded = children.next().unwrap();
+
+ let mut children = padded.children();
+ let title_layout = children.next().unwrap();
+ let mut show_title = true;
+
+ if let Some(controls) = &self.controls {
+ let controls_layout = children.next().unwrap();
+
+ if title_layout.bounds().width + controls_layout.bounds().width
+ > padded.bounds().width
+ {
+ show_title = false;
+ }
+
+ controls.as_widget().operate(
+ &mut tree.children[1],
+ controls_layout,
+ operation,
+ )
+ };
+
+ if show_title {
+ self.content.as_widget().operate(
+ &mut tree.children[0],
+ title_layout,
+ operation,
+ )
+ }
+ }
+
pub(crate) fn on_event(
&mut self,
tree: &mut Tree,
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index e57f41ea..ef2f9a17 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -233,6 +233,7 @@ where
let state = tree.state.downcast_mut::<State>();
operation.focusable(state, self.id.as_ref().map(|id| &id.0));
+ operation.text_input(state, self.id.as_ref().map(|id| &id.0));
}
fn on_event(
@@ -343,6 +344,32 @@ pub fn focus<Message: 'static>(id: Id) -> Command<Message> {
Command::widget(operation::focusable::focus(id.0))
}
+/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
+/// end.
+pub fn move_cursor_to_end<Message: 'static>(id: Id) -> Command<Message> {
+ Command::widget(operation::text_input::move_cursor_to_end(id.0))
+}
+
+/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
+/// front.
+pub fn move_cursor_to_front<Message: 'static>(id: Id) -> Command<Message> {
+ Command::widget(operation::text_input::move_cursor_to_front(id.0))
+}
+
+/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
+/// provided position.
+pub fn move_cursor_to<Message: 'static>(
+ id: Id,
+ position: usize,
+) -> Command<Message> {
+ Command::widget(operation::text_input::move_cursor_to(id.0, position))
+}
+
+/// Produces a [`Command`] that selects all the content of the [`TextInput`] with the given [`Id`].
+pub fn select_all<Message: 'static>(id: Id) -> Command<Message> {
+ Command::widget(operation::text_input::select_all(id.0))
+}
+
/// Computes the layout of a [`TextInput`].
pub fn layout<Renderer>(
renderer: &Renderer,
@@ -1007,6 +1034,24 @@ impl operation::Focusable for State {
}
}
+impl operation::TextInput for State {
+ fn move_cursor_to_front(&mut self) {
+ State::move_cursor_to_front(self)
+ }
+
+ fn move_cursor_to_end(&mut self) {
+ State::move_cursor_to_end(self)
+ }
+
+ fn move_cursor_to(&mut self, position: usize) {
+ State::move_cursor_to(self, position)
+ }
+
+ fn select_all(&mut self) {
+ State::select_all(self)
+ }
+}
+
mod platform {
use crate::keyboard;