summaryrefslogtreecommitdiffstats
path: root/native/src/widget/text_input.rs
diff options
context:
space:
mode:
Diffstat (limited to 'native/src/widget/text_input.rs')
-rw-r--r--native/src/widget/text_input.rs229
1 files changed, 223 insertions, 6 deletions
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index 35e10000..bb5bb523 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -1,10 +1,95 @@
+//! Display fields that can be filled with text.
+//!
+//! A [`TextInput`] has some local [`State`].
+//!
+//! [`TextInput`]: struct.TextInput.html
+//! [`State`]: struct.State.html
use crate::{
input::{keyboard, mouse, ButtonState},
layout, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size,
Widget,
};
-pub use iced_core::{text_input::State, TextInput};
+/// A widget that can be filled with text by using a keyboard.
+pub struct TextInput<'a, Message> {
+ state: &'a mut State,
+ placeholder: String,
+ value: Value,
+ width: Length,
+ max_width: Length,
+ padding: u16,
+ size: Option<u16>,
+ on_change: Box<dyn Fn(String) -> Message>,
+ on_submit: Option<Message>,
+}
+
+impl<'a, Message> TextInput<'a, Message> {
+ /// Creates a new [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn new<F>(
+ state: &'a mut State,
+ placeholder: &str,
+ value: &str,
+ on_change: F,
+ ) -> Self
+ where
+ F: 'static + Fn(String) -> Message,
+ {
+ Self {
+ state,
+ placeholder: String::from(placeholder),
+ value: Value::new(value),
+ width: Length::Fill,
+ max_width: Length::Shrink,
+ padding: 0,
+ size: None,
+ on_change: Box::new(on_change),
+ on_submit: None,
+ }
+ }
+
+ /// Sets the width of the [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn width(mut self, width: Length) -> Self {
+ self.width = width;
+ self
+ }
+
+ /// Sets the maximum width of the [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn max_width(mut self, max_width: Length) -> Self {
+ self.max_width = max_width;
+ self
+ }
+
+ /// Sets the padding of the [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn padding(mut self, units: u16) -> Self {
+ self.padding = units;
+ self
+ }
+
+ /// Sets the text size of the [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn size(mut self, size: u16) -> Self {
+ self.size = Some(size);
+ self
+ }
+
+ /// Sets the message that should be produced when the [`TextInput`] is
+ /// focused and the enter key is pressed.
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn on_submit(mut self, message: Message) -> Self {
+ self.on_submit = Some(message);
+ self
+ }
+}
impl<'a, Message, Renderer> Widget<Message, Renderer> for TextInput<'a, Message>
where
@@ -120,12 +205,19 @@ where
let bounds = layout.bounds();
let text_bounds = layout.children().next().unwrap().bounds();
- renderer.draw(&self, bounds, text_bounds, cursor_position)
+ renderer.draw(
+ bounds,
+ text_bounds,
+ cursor_position,
+ self.size.unwrap_or(renderer.default_size()),
+ &self.placeholder,
+ &self.value,
+ &self.state,
+ )
}
fn hash_layout(&self, state: &mut Hasher) {
- use std::any::TypeId;
- use std::hash::Hash;
+ use std::{any::TypeId, hash::Hash};
TypeId::of::<TextInput<'static, ()>>().hash(state);
@@ -139,12 +231,15 @@ where
pub trait Renderer: crate::Renderer + Sized {
fn default_size(&self) -> u16;
- fn draw<Message>(
+ fn draw(
&mut self,
- text_input: &TextInput<'_, Message>,
bounds: Rectangle,
text_bounds: Rectangle,
cursor_position: Point,
+ size: u16,
+ placeholder: &str,
+ value: &Value,
+ state: &State,
) -> Self::Output;
}
@@ -160,3 +255,125 @@ where
Element::new(text_input)
}
}
+
+/// The state of a [`TextInput`].
+///
+/// [`TextInput`]: struct.TextInput.html
+#[derive(Debug, Default, Clone)]
+pub struct State {
+ is_focused: bool,
+ cursor_position: usize,
+}
+
+impl State {
+ /// Creates a new [`State`], representing an unfocused [`TextInput`].
+ ///
+ /// [`State`]: struct.State.html
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Creates a new [`State`], representing a focused [`TextInput`].
+ ///
+ /// [`State`]: struct.State.html
+ pub fn focused() -> Self {
+ use std::usize;
+
+ Self {
+ is_focused: true,
+ cursor_position: usize::MAX,
+ }
+ }
+
+ pub fn is_focused(&self) -> bool {
+ self.is_focused
+ }
+
+ /// Returns the cursor position of a [`TextInput`].
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn cursor_position(&self, value: &Value) -> usize {
+ self.cursor_position.min(value.len())
+ }
+
+ /// Moves the cursor of a [`TextInput`] to the right.
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn move_cursor_right(&mut self, value: &Value) {
+ let current = self.cursor_position(value);
+
+ if current < value.len() {
+ self.cursor_position = current + 1;
+ }
+ }
+
+ /// Moves the cursor of a [`TextInput`] to the left.
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn move_cursor_left(&mut self, value: &Value) {
+ let current = self.cursor_position(value);
+
+ if current > 0 {
+ self.cursor_position = current - 1;
+ }
+ }
+
+ /// Moves the cursor of a [`TextInput`] to the end.
+ ///
+ /// [`TextInput`]: struct.TextInput.html
+ pub fn move_cursor_to_end(&mut self, value: &Value) {
+ self.cursor_position = value.len();
+ }
+}
+
+/// The value of a [`TextInput`].
+///
+/// [`TextInput`]: struct.TextInput.html
+// TODO: Use `unicode-segmentation`
+#[derive(Debug)]
+pub struct Value(Vec<char>);
+
+impl Value {
+ /// Creates a new [`Value`] from a string slice.
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn new(string: &str) -> Self {
+ Self(string.chars().collect())
+ }
+
+ /// Returns the total amount of `char` in the [`Value`].
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ /// Returns a new [`Value`] containing the `char` until the given `index`.
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn until(&self, index: usize) -> Self {
+ Self(self.0[..index.min(self.len())].to_vec())
+ }
+
+ /// Converts the [`Value`] into a `String`.
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn to_string(&self) -> String {
+ use std::iter::FromIterator;
+ String::from_iter(self.0.iter())
+ }
+
+ /// Inserts a new `char` at the given `index`.
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn insert(&mut self, index: usize, c: char) {
+ self.0.insert(index, c);
+ }
+
+ /// Removes the `char` at the given `index`.
+ ///
+ /// [`Value`]: struct.Value.html
+ pub fn remove(&mut self, index: usize) {
+ let _ = self.0.remove(index);
+ }
+}