summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--native/src/input/mouse.rs64
-rw-r--r--native/src/widget/text_input.rs309
-rw-r--r--native/src/widget/text_input/cursor.rs176
-rw-r--r--wgpu/src/renderer/widget/text_input.rs50
4 files changed, 315 insertions, 284 deletions
diff --git a/native/src/input/mouse.rs b/native/src/input/mouse.rs
index 69dc6b4c..22c81e15 100644
--- a/native/src/input/mouse.rs
+++ b/native/src/input/mouse.rs
@@ -2,5 +2,69 @@
mod button;
mod event;
+use crate::Point;
pub use button::Button;
pub use event::{Event, ScrollDelta};
+use std::time::{Duration, SystemTime};
+
+/// enum to track the type of the last click
+#[derive(Debug, Copy, Clone)]
+pub enum Interaction {
+ /// Last Click was a single click
+ Click(Point),
+ /// Last Click was a double click
+ DoubleClick(Point),
+ /// Last Click was a triple click
+ TripleClick(Point),
+}
+
+/// Compiler bully
+#[derive(Debug, Copy, Clone)]
+pub struct State {
+ last_click: Option<Interaction>,
+ last_click_timestamp: Option<SystemTime>,
+}
+
+impl Default for State {
+ fn default() -> Self {
+ State {
+ last_click: None,
+ last_click_timestamp: None,
+ }
+ }
+}
+
+impl State {
+ /// processes left click to check for double/triple clicks
+ /// return amount of repetitive mouse clicks
+ /// (1 -> double click, 2 -> triple click)
+ pub fn update(&mut self, position: Point) -> Interaction {
+ self.last_click_timestamp = Some(SystemTime::now());
+ self.last_click = match self.last_click {
+ None => Some(Interaction::Click(position)),
+ Some(x) => match x {
+ Interaction::Click(p) if self.process_click(p, position) => {
+ Some(Interaction::DoubleClick(position))
+ }
+ Interaction::DoubleClick(p)
+ if self.process_click(p, position) =>
+ {
+ Some(Interaction::TripleClick(position))
+ }
+ _ => Some(Interaction::Click(position)),
+ },
+ };
+ self.last_click.unwrap_or(Interaction::Click(position))
+ }
+
+ fn process_click(&self, old_position: Point, new_position: Point) -> bool {
+ old_position == new_position
+ && SystemTime::now()
+ .duration_since(
+ self.last_click_timestamp.unwrap_or(SystemTime::UNIX_EPOCH),
+ )
+ .unwrap_or(Duration::from_secs(1))
+ .as_millis()
+ <= 500
+ }
+}
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index 59d86f63..4e7906a8 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -4,10 +4,13 @@
//!
//! [`TextInput`]: struct.TextInput.html
//! [`State`]: struct.State.html
+mod cursor;
use crate::{
- input::{keyboard, mouse, ButtonState},
- layout, Clipboard, Element, Event, Font, Hasher, Layout, Length, Point,
- Rectangle, Size, Widget,
+ input::{keyboard, mouse, mouse::Interaction, ButtonState},
+ layout,
+ widget::text_input::cursor::Cursor,
+ Clipboard, Element, Event, Font, Hasher, Layout, Length, Point, Rectangle,
+ Size, Widget,
};
use std::u32;
@@ -209,16 +212,20 @@ where
let text_layout = layout.children().next().unwrap();
let target = cursor_position.x - text_layout.bounds().x;
- match self.state.cursor.process_click(cursor_position) {
- 1 => self.state.cursor.select_range(
- self.value.previous_start_of_word(
- self.state.cursor.end(),
- ),
- self.value
- .next_end_of_word(self.state.cursor.end()),
- ),
- 2 => self.state.cursor.select_all(self.value.len()),
- _ => {
+ match self.state.mouse.update(cursor_position) {
+ Interaction::DoubleClick(_) => {
+ self.state.cursor.select_range(
+ self.value.previous_start_of_word(
+ self.state.cursor.end(),
+ ),
+ self.value
+ .next_end_of_word(self.state.cursor.end()),
+ )
+ }
+ Interaction::TripleClick(_) => {
+ self.state.cursor.select_all(&self.value)
+ }
+ Interaction::Click(_) => {
if target > 0.0 {
let value = if self.is_secure {
self.value.secure()
@@ -308,17 +315,14 @@ where
&& self.state.is_pasting.is_none()
&& !c.is_control() =>
{
- if !self.state.cursor.is_selection() {
- self.value.insert(self.state.cursor.end(), c);
- } else {
- self.value.remove_many(
- self.state.cursor.left(),
- self.state.cursor.right(),
- );
- self.state.cursor.move_left();
- self.value.insert(self.state.cursor.end(), c);
+ match self.state.cursor.selection_position() {
+ Some((left, right)) => {
+ self.value.remove_many(left, right);
+ self.state.cursor.move_left();
+ }
+ _ => (),
}
-
+ self.value.insert(self.state.cursor.end(), c);
self.state.cursor.move_right(&self.value);
let message = (self.on_change)(self.value.to_string());
@@ -335,42 +339,38 @@ where
}
}
keyboard::KeyCode::Backspace => {
- if !self.state.cursor.is_selection() {
- if self.state.cursor.start() > 0 {
+ match self.state.cursor.selection_position() {
+ Some((start, end)) => {
+ self.value.remove_many(start, end);
self.state.cursor.move_left();
- let _ =
- self.value.remove(self.state.cursor.end() - 1);
- let message =
- (self.on_change)(self.value.to_string());
- messages.push(message);
}
- } else {
- self.value.remove_many(
- self.state.cursor.left(),
- self.state.cursor.right(),
- );
- self.state.cursor.move_left();
- let message = (self.on_change)(self.value.to_string());
- messages.push(message);
+ None => {
+ if self.state.cursor.start() > 0 {
+ self.state.cursor.move_left();
+ let _ = self
+ .value
+ .remove(self.state.cursor.start() - 1);
+ }
+ }
}
+ let message = (self.on_change)(self.value.to_string());
+ messages.push(message);
}
keyboard::KeyCode::Delete => {
- if !self.state.cursor.is_selection() {
- if self.state.cursor.end() < self.value.len() {
- let _ = self.value.remove(self.state.cursor.end());
- let message =
- (self.on_change)(self.value.to_string());
- messages.push(message);
+ match self.state.cursor.selection_position() {
+ Some((start, end)) => {
+ self.value.remove_many(start, end);
+ self.state.cursor.move_left();
+ }
+ None => {
+ if self.state.cursor.end() < self.value.len() {
+ let _ =
+ self.value.remove(self.state.cursor.end());
+ }
}
- } else {
- self.value.remove_many(
- self.state.cursor.left(),
- self.state.cursor.right(),
- );
- self.state.cursor.move_left();
- let message = (self.on_change)(self.value.to_string());
- messages.push(message);
}
+ let message = (self.on_change)(self.value.to_string());
+ messages.push(message);
}
keyboard::KeyCode::Left => {
if platform::is_jump_modifier_pressed(modifiers)
@@ -413,12 +413,12 @@ where
}
};
- if self.state.cursor.is_selection() {
- self.value.remove_many(
- self.state.cursor.left(),
- self.state.cursor.right(),
- );
- self.state.cursor.move_left();
+ match self.state.cursor.selection_position() {
+ Some((left, right)) => {
+ self.value.remove_many(left, right);
+ self.state.cursor.move_left();
+ }
+ _ => (),
}
self.value.insert_many(
@@ -442,7 +442,7 @@ where
}
keyboard::KeyCode::A => {
if platform::is_copy_paste_modifier_pressed(modifiers) {
- self.state.cursor.select_range(0, self.value.len());
+ self.state.cursor.select_all(&self.value);
}
}
_ => {}
@@ -596,8 +596,8 @@ pub struct State {
is_focused: bool,
is_pressed: bool,
is_pasting: Option<Value>,
- /// TODO: Compiler wants documentation here
- pub cursor: cursor::Cursor,
+ cursor: Cursor,
+ mouse: crate::input::mouse::State,
// TODO: Add stateful horizontal scrolling offset
}
@@ -617,7 +617,8 @@ impl State {
is_focused: true,
is_pressed: false,
is_pasting: None,
- cursor: cursor::Cursor::default(),
+ cursor: Cursor::default(),
+ mouse: crate::input::mouse::State::default(),
}
}
@@ -627,6 +628,11 @@ impl State {
pub fn is_focused(&self) -> bool {
self.is_focused
}
+
+ /// getter for cursor
+ pub fn cursor(&self) -> Cursor {
+ self.cursor
+ }
}
/// The value of a [`TextInput`].
@@ -841,182 +847,3 @@ mod platform {
}
}
}
-
-mod cursor {
- use crate::widget::text_input::Value;
- use iced_core::Point;
- use std::time::{Duration, SystemTime};
-
- /// Even the compiler bullies me for not writing documentation
- #[derive(Debug, Copy, Clone)]
- pub struct Cursor {
- start: usize,
- end: usize,
- click_count: usize,
- last_click_position: Option<crate::Point>,
- last_click_timestamp: Option<SystemTime>,
- }
-
- impl Default for Cursor {
- fn default() -> Self {
- Cursor {
- start: 0,
- end: 0,
- click_count: 0,
- last_click_position: None,
- last_click_timestamp: None,
- }
- }
- }
-
- impl Cursor {
- /* Move section */
- pub fn move_to(&mut self, position: usize) {
- self.start = position;
- self.end = position;
- }
-
- pub fn move_right(&mut self, value: &Value) {
- if self.is_selection() {
- let dest = self.right();
- self.start = dest;
- self.end = dest;
- } else if self.end < value.len() {
- self.start += 1;
- self.end += 1;
- }
- }
-
- pub fn move_left(&mut self) {
- if self.is_selection() {
- let dest = self.left();
- self.start = dest;
- self.end = dest;
- } else if self.left() > 0 {
- self.start -= 1;
- self.end -= 1;
- }
- }
-
- pub fn move_right_by_amount(&mut self, value: &Value, amount: usize) {
- self.start = self.start.saturating_add(amount).min(value.len());
- self.end = self.end.saturating_add(amount).min(value.len());
- }
-
- pub fn move_left_by_words(&mut self, value: &Value) {
- let (left, _) = self.cursor_position(value);
-
- self.move_to(value.previous_start_of_word(left));
- }
-
- pub fn move_right_by_words(&mut self, value: &Value) {
- let (_, right) = self.cursor_position(value);
-
- self.move_to(value.next_end_of_word(right));
- }
- /* Move section end */
-
- /* Selection section */
- pub fn select_range(&mut self, start: usize, end: usize) {
- self.start = start;
- self.end = end;
- }
-
- pub fn select_left(&mut self) {
- if self.end > 0 {
- self.end -= 1;
- }
- }
-
- pub fn select_right(&mut self, value: &Value) {
- if self.end < value.len() {
- self.end += 1;
- }
- }
-
- pub fn select_left_by_words(&mut self, value: &Value) {
- self.end = value.previous_start_of_word(self.start);
- }
-
- pub fn select_right_by_words(&mut self, value: &Value) {
- self.end = value.next_end_of_word(self.start);
- }
-
- pub fn select_all(&mut self, len: usize) {
- self.start = 0;
- self.end = len;
- }
- /* Selection section end */
-
- /* Double/Triple click section */
- // returns the amount of clicks on the same position in specific timeframe
- // (1=double click, 2=triple click)
- pub fn process_click(&mut self, position: Point) -> usize {
- if position
- == self.last_click_position.unwrap_or(Point { x: 0.0, y: 0.0 })
- && self.click_count < 2
- && SystemTime::now()
- .duration_since(
- self.last_click_timestamp
- .unwrap_or(SystemTime::UNIX_EPOCH),
- )
- .unwrap_or(Duration::from_secs(1))
- .as_millis()
- <= 500
- {
- self.click_count += 1;
- } else {
- self.click_count = 0;
- }
- self.last_click_position = Option::from(position);
- self.last_click_timestamp = Option::from(SystemTime::now());
- self.click_count
- }
- /* Double/Triple click section end */
-
- /* "get info about cursor/selection" section */
- pub fn is_selection(&self) -> bool {
- self.start != self.end
- }
-
- // get start position of selection (can be left OR right boundary of selection)
- pub(crate) fn start(&self) -> usize {
- self.start
- }
-
- // get end position of selection (can be left OR right boundary of selection)
- pub fn end(&self) -> usize {
- self.end
- }
-
- // get left boundary of selection
- pub fn left(&self) -> usize {
- self.start.min(self.end)
- }
-
- // get right boundary of selection
- pub fn right(&self) -> usize {
- self.start.max(self.end)
- }
-
- pub fn cursor_position(&self, value: &Value) -> (usize, usize) {
- (self.start.min(value.len()), self.end.min(value.len()))
- }
-
- pub fn cursor_position_left(&self, value: &Value) -> usize {
- let (a, b) = self.cursor_position(value);
- a.min(b)
- }
-
- pub fn cursor_position_right(&self, value: &Value) -> usize {
- let (a, b) = self.cursor_position(value);
- a.max(b)
- }
-
- pub fn draw_position(&self, value: &Value) -> usize {
- let (_, end) = self.cursor_position(value);
- end
- }
- /* "get info about cursor/selection" section end */
- }
-}
diff --git a/native/src/widget/text_input/cursor.rs b/native/src/widget/text_input/cursor.rs
new file mode 100644
index 00000000..bbf5448b
--- /dev/null
+++ b/native/src/widget/text_input/cursor.rs
@@ -0,0 +1,176 @@
+use crate::widget::text_input::Value;
+
+#[derive(Debug, Copy, Clone)]
+enum State {
+ Index(usize),
+ Selection { start: usize, end: usize },
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct Cursor {
+ state: State,
+}
+
+impl Default for Cursor {
+ fn default() -> Self {
+ Cursor {
+ state: State::Index(0),
+ }
+ }
+}
+
+impl Cursor {
+ /* index move methods */
+ pub fn move_to(&mut self, position: usize) {
+ self.state = State::Index(position);
+ }
+
+ pub fn move_right(&mut self, value: &Value) {
+ self.move_right_by_amount(value, 1)
+ }
+
+ pub fn move_right_by_words(&mut self, value: &Value) {
+ self.move_to(value.next_end_of_word(self.right()))
+ }
+
+ pub fn move_right_by_amount(&mut self, value: &Value, amount: usize) {
+ match self.state {
+ State::Index(index) => {
+ self.move_to(index.saturating_add(amount).min(value.len()))
+ }
+ State::Selection { .. } => self.move_to(self.right()),
+ }
+ }
+
+ pub fn move_left(&mut self) {
+ match self.state {
+ State::Index(index) if index > 0 => self.move_to(index - 1),
+ State::Selection { .. } => self.move_to(self.left()),
+ _ => self.move_to(0),
+ }
+ }
+
+ pub fn move_left_by_words(&mut self, value: &Value) {
+ self.move_to(value.previous_start_of_word(self.right()));
+ }
+ /* end of index move methods */
+
+ /* expand/shrink selection */
+ // TODO: (whole section): Return State::Cursor if start == end after operation
+ pub fn select_range(&mut self, start: usize, end: usize) {
+ self.state = State::Selection { start, end };
+ }
+
+ pub fn select_left(&mut self) {
+ match self.state {
+ State::Index(index) if index > 0 => {
+ self.select_range(index, index - 1)
+ }
+ State::Selection { start, end } if end > 0 => {
+ self.select_range(start, end - 1)
+ }
+ _ => (),
+ }
+ }
+
+ pub fn select_right(&mut self, value: &Value) {
+ match self.state {
+ State::Index(index) if index < value.len() => {
+ self.select_range(index, index + 1)
+ }
+ State::Selection { start, end } if end < value.len() => {
+ self.select_range(start, end + 1)
+ }
+ _ => (),
+ }
+ }
+
+ pub fn select_left_by_words(&mut self, value: &Value) {
+ match self.state {
+ State::Index(index) => {
+ self.select_range(index, value.previous_start_of_word(index))
+ }
+ State::Selection { start, end } => {
+ self.select_range(start, value.previous_start_of_word(end))
+ }
+ }
+ }
+
+ pub fn select_right_by_words(&mut self, value: &Value) {
+ match self.state {
+ State::Index(index) => {
+ self.select_range(index, value.next_end_of_word(index))
+ }
+ State::Selection { start, end } => {
+ self.select_range(start, value.next_end_of_word(end))
+ }
+ }
+ }
+
+ pub fn select_all(&mut self, value: &Value) {
+ self.select_range(0, value.len());
+ }
+ /* end of selection section */
+
+ /* helpers */
+ // get start position of selection (can be left OR right boundary of selection) or index
+ pub(crate) fn start(&self) -> usize {
+ match self.state {
+ State::Index(index) => index,
+ State::Selection { start, .. } => start,
+ }
+ }
+
+ // get end position of selection (can be left OR right boundary of selection) or index
+ pub fn end(&self) -> usize {
+ match self.state {
+ State::Index(index) => index,
+ State::Selection { end, .. } => end,
+ }
+ }
+
+ // get left boundary of selection or index
+ pub fn left(&self) -> usize {
+ match self.state {
+ State::Index(index) => index,
+ State::Selection { start, end } => start.min(end),
+ }
+ }
+
+ // get right boundary of selection or index
+ pub fn right(&self) -> usize {
+ match self.state {
+ State::Index(index) => index,
+ State::Selection { start, end } => start.max(end),
+ }
+ }
+
+ pub fn draw_position(&self, value: &Value) -> usize {
+ self.cursor_position(value)
+ }
+
+ pub fn cursor_position(&self, value: &Value) -> usize {
+ match self.state {
+ State::Index(index) => index.min(value.len()),
+ State::Selection { end, .. } => end.min(value.len()),
+ }
+ }
+
+ // returns Option of left and right border of selection
+ // a second method return start and end may be useful (see below)
+ pub fn selection_position(&self) -> Option<(usize, usize)> {
+ match self.state {
+ State::Selection { start, end } => {
+ Some((start.min(end), start.max(end)))
+ }
+ _ => None,
+ }
+ }
+
+ /* pub fn selection_position(&self) -> Option<(usize, usize)> {
+ match self.state {
+ State::Selection { start, end } => Some((start, end)),
+ _ => None,
+ }
+ } */
+}
diff --git a/wgpu/src/renderer/widget/text_input.rs b/wgpu/src/renderer/widget/text_input.rs
index 4e0274b8..fa108d68 100644
--- a/wgpu/src/renderer/widget/text_input.rs
+++ b/wgpu/src/renderer/widget/text_input.rs
@@ -46,7 +46,7 @@ impl text_input::Renderer for Renderer {
text_bounds,
value,
size,
- state.cursor.draw_position(value),
+ state.cursor().cursor_position(value),
font,
);
@@ -116,20 +116,20 @@ impl text_input::Renderer for Renderer {
text_bounds,
value,
size,
- state.cursor.draw_position(value),
+ state.cursor().cursor_position(value),
font,
);
- /*let selection = match cursor {
- text_input::Cursor::Index(_) => Primitive::None,
- text_input::Cursor::Selection { .. } => {
+ let selection = match state.cursor().selection_position() {
+ None => Primitive::None,
+ Some(_) => {
let (cursor_left_offset, _) =
measure_cursor_and_scroll_offset(
self,
text_bounds,
value,
size,
- state.cursor.left(),
+ state.cursor().left(),
font,
);
let (cursor_right_offset, _) =
@@ -138,7 +138,7 @@ impl text_input::Renderer for Renderer {
text_bounds,
value,
size,
- state.cursor.right(),
+ state.cursor().right(),
font,
);
let width = cursor_right_offset - cursor_left_offset;
@@ -157,42 +157,6 @@ impl text_input::Renderer for Renderer {
border_color: Color::TRANSPARENT,
}
}
- };*/
-
- let selection = if !state.cursor.is_selection() {
- Primitive::None
- } else {
- let (cursor_left_offset, _) = measure_cursor_and_scroll_offset(
- self,
- text_bounds,
- value,
- size,
- state.cursor.left(),
- font,
- );
- let (cursor_right_offset, _) = measure_cursor_and_scroll_offset(
- self,
- text_bounds,
- value,
- size,
- state.cursor.right(),
- font,
- );
- let width = cursor_right_offset - cursor_left_offset;
- Primitive::Quad {
- bounds: Rectangle {
- x: text_bounds.x + cursor_left_offset,
- y: text_bounds.y,
- width,
- height: text_bounds.height,
- },
- background: Background::Color(
- style_sheet.selection_color(),
- ),
- border_radius: 0,
- border_width: 0,
- border_color: Color::TRANSPARENT,
- }
};
let cursor = Primitive::Quad {