diff options
| author | 2020-02-24 18:03:42 +0100 | |
|---|---|---|
| committer | 2020-02-24 18:03:42 +0100 | |
| commit | c6c8cabdaf03f90b1739be828cf140d8ddb92e65 (patch) | |
| tree | 07986c2132fd6f66904170e7f745a9d680fd17a7 /native/src | |
| parent | 190dcef1553efa181b68caa91b109f83fe289412 (diff) | |
| download | iced-c6c8cabdaf03f90b1739be828cf140d8ddb92e65.tar.gz iced-c6c8cabdaf03f90b1739be828cf140d8ddb92e65.tar.bz2 iced-c6c8cabdaf03f90b1739be828cf140d8ddb92e65.zip | |
moved cursor into own file
moved click tracking as a new State struct to input::mouse
made cursor field of text_input state private
brought back cursor type(Index, Selection) representation with a state enum
cleaned out some stuff (but not enough/all)
TODO: Documentation (sigh)
TODO: Editor struct
TODO: some (hopefully) small improvements here and there
Diffstat (limited to '')
| -rw-r--r-- | native/src/input/mouse.rs | 64 | ||||
| -rw-r--r-- | native/src/widget/text_input.rs | 309 | ||||
| -rw-r--r-- | native/src/widget/text_input/cursor.rs | 176 | 
3 files changed, 308 insertions, 241 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, +        } +    } */ +} | 
