-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b1354be
commit 11dcced
Showing
8 changed files
with
1,639 additions
and
22 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
//! Track the cursor of a text input. | ||
use iced::widget::text_input::Value; | ||
|
||
/// The cursor of a text input. | ||
#[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
pub struct Cursor { | ||
state: State, | ||
} | ||
|
||
/// The state of a [`Cursor`]. | ||
#[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
pub enum State { | ||
/// Cursor without a selection | ||
Index(usize), | ||
|
||
/// Cursor selecting a range of text | ||
Selection { | ||
/// The start of the selection | ||
start: usize, | ||
/// The end of the selection | ||
end: usize, | ||
}, | ||
} | ||
|
||
impl Default for Cursor { | ||
fn default() -> Self { | ||
Cursor { | ||
state: State::Index(0), | ||
} | ||
} | ||
} | ||
|
||
impl Cursor { | ||
/// Returns the [`State`] of the [`Cursor`]. | ||
pub fn state(&self, value: &Value) -> State { | ||
match self.state { | ||
State::Index(index) => State::Index(index.min(value.len())), | ||
State::Selection { start, end } => { | ||
let start = start.min(value.len()); | ||
let end = end.min(value.len()); | ||
|
||
if start == end { | ||
State::Index(start) | ||
} else { | ||
State::Selection { start, end } | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Returns the current selection of the [`Cursor`] for the given [`Value`]. | ||
/// | ||
/// `start` is guaranteed to be <= than `end`. | ||
pub fn selection(&self, value: &Value) -> Option<(usize, usize)> { | ||
match self.state(value) { | ||
State::Selection { start, end } => Some((start.min(end), start.max(end))), | ||
State::Index(_) => None, | ||
} | ||
} | ||
|
||
pub(crate) fn move_to(&mut self, position: usize) { | ||
self.state = State::Index(position); | ||
} | ||
|
||
pub(crate) fn move_right(&mut self, value: &Value) { | ||
self.move_right_by_amount(value, 1); | ||
} | ||
|
||
pub(crate) fn move_right_by_words(&mut self, value: &Value) { | ||
self.move_to(value.next_end_of_word(self.right(value))); | ||
} | ||
|
||
pub(crate) fn move_right_by_amount(&mut self, value: &Value, amount: usize) { | ||
match self.state(value) { | ||
State::Index(index) => { | ||
self.move_to(index.saturating_add(amount).min(value.len())); | ||
} | ||
State::Selection { start, end } => self.move_to(end.max(start)), | ||
} | ||
} | ||
|
||
pub(crate) fn move_left(&mut self, value: &Value) { | ||
match self.state(value) { | ||
State::Index(index) if index > 0 => self.move_to(index - 1), | ||
State::Selection { start, end } => self.move_to(start.min(end)), | ||
State::Index(_) => self.move_to(0), | ||
} | ||
} | ||
|
||
pub(crate) fn move_left_by_words(&mut self, value: &Value) { | ||
self.move_to(value.previous_start_of_word(self.left(value))); | ||
} | ||
|
||
pub(crate) fn select_range(&mut self, start: usize, end: usize) { | ||
if start == end { | ||
self.state = State::Index(start); | ||
} else { | ||
self.state = State::Selection { start, end }; | ||
} | ||
} | ||
|
||
pub(crate) fn select_left(&mut self, value: &Value) { | ||
match self.state(value) { | ||
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(crate) fn select_right(&mut self, value: &Value) { | ||
match self.state(value) { | ||
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(crate) fn select_left_by_words(&mut self, value: &Value) { | ||
match self.state(value) { | ||
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(crate) fn select_right_by_words(&mut self, value: &Value) { | ||
match self.state(value) { | ||
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(crate) fn select_all(&mut self, value: &Value) { | ||
self.select_range(0, value.len()); | ||
} | ||
|
||
pub(crate) fn start(&self, value: &Value) -> usize { | ||
let start = match self.state { | ||
State::Index(index) => index, | ||
State::Selection { start, .. } => start, | ||
}; | ||
|
||
start.min(value.len()) | ||
} | ||
|
||
pub(crate) fn end(&self, value: &Value) -> usize { | ||
let end = match self.state { | ||
State::Index(index) => index, | ||
State::Selection { end, .. } => end, | ||
}; | ||
|
||
end.min(value.len()) | ||
} | ||
|
||
fn left(&self, value: &Value) -> usize { | ||
match self.state(value) { | ||
State::Index(index) => index, | ||
State::Selection { start, end } => start.min(end), | ||
} | ||
} | ||
|
||
fn right(&self, value: &Value) -> usize { | ||
match self.state(value) { | ||
State::Index(index) => index, | ||
State::Selection { start, end } => start.max(end), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
use super::cursor::Cursor; | ||
use iced::widget::text_input::Value; | ||
|
||
pub struct Editor<'a> { | ||
value: &'a mut Value, | ||
cursor: &'a mut Cursor, | ||
} | ||
|
||
impl<'a> Editor<'a> { | ||
pub fn new(value: &'a mut Value, cursor: &'a mut Cursor) -> Editor<'a> { | ||
Editor { value, cursor } | ||
} | ||
|
||
pub fn contents(&self) -> String { | ||
self.value.to_string() | ||
} | ||
|
||
pub fn insert(&mut self, character: char) { | ||
if let Some((left, right)) = self.cursor.selection(self.value) { | ||
self.cursor.move_left(self.value); | ||
self.value.remove_many(left, right); | ||
} | ||
|
||
self.value.insert(self.cursor.end(self.value), character); | ||
self.cursor.move_right(self.value); | ||
} | ||
|
||
pub fn paste(&mut self, content: Value) { | ||
let length = content.len(); | ||
if let Some((left, right)) = self.cursor.selection(self.value) { | ||
self.cursor.move_left(self.value); | ||
self.value.remove_many(left, right); | ||
} | ||
|
||
self.value.insert_many(self.cursor.end(self.value), content); | ||
|
||
self.cursor.move_right_by_amount(self.value, length); | ||
} | ||
|
||
pub fn backspace(&mut self) { | ||
match self.cursor.selection(self.value) { | ||
Some((start, end)) => { | ||
self.cursor.move_left(self.value); | ||
self.value.remove_many(start, end); | ||
} | ||
None => { | ||
let start = self.cursor.start(self.value); | ||
|
||
if start > 0 { | ||
self.cursor.move_left(self.value); | ||
self.value.remove(start - 1); | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn delete(&mut self) { | ||
match self.cursor.selection(self.value) { | ||
Some(_) => { | ||
self.backspace(); | ||
} | ||
None => { | ||
let end = self.cursor.end(self.value); | ||
|
||
if end < self.value.len() { | ||
self.value.remove(end); | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
mod cursor; | ||
mod editor; | ||
pub mod text_input; | ||
|
||
use crate::theme::Theme; | ||
|
||
pub type Renderer = iced::Renderer; | ||
pub type Element<'a, Message> = iced::Element<'a, Message, Theme, Renderer>; | ||
pub type Container<'a, Message> = iced::widget::Container<'a, Message, Theme, Renderer>; | ||
pub type Column<'a, Message> = iced::widget::Column<'a, Message, Theme, Renderer>; | ||
pub type Row<'a, Message> = iced::widget::Row<'a, Message, Theme, Renderer>; | ||
pub type Button<'a, Message> = iced::widget::Button<'a, Message, Theme, Renderer>; | ||
pub type CheckBox<'a, Message> = iced::widget::Checkbox<'a, Message, Theme, Renderer>; | ||
pub type Text<'a> = iced::widget::Text<'a, Theme, Renderer>; | ||
pub type TextInput<'a, Message> = text_input::TextInput<'a, Message, Theme, Renderer>; | ||
pub type Tooltip<'a> = iced::widget::Tooltip<'a, Theme, Renderer>; | ||
pub type ProgressBar<'a> = iced::widget::ProgressBar<'a, Theme>; | ||
pub type PickList<'a, T, L, V, Message> = | ||
iced::widget::PickList<'a, T, L, V, Message, Theme, Renderer>; | ||
pub type Scrollable<'a, Message> = iced::widget::Scrollable<'a, Message, Theme, Renderer>; | ||
pub type Svg<'a> = iced::widget::Svg<'a, Theme>; |
Oops, something went wrong.