fork widget text_input
This commit is contained in:
parent
27570b4de9
commit
64dc91b578
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -3118,6 +3118,9 @@ dependencies = [
|
||||
"bitcoin",
|
||||
"chrono",
|
||||
"iced",
|
||||
"iced_core",
|
||||
"iced_runtime",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@ -7,5 +7,8 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
iced = { version = "0.13.1", default-features = false, features = ["svg", "image", "lazy", "qr_code", "advanced"] }
|
||||
iced_core = { version = "0.13.2" }
|
||||
iced_runtime = { version = "0.13.2" }
|
||||
unicode-segmentation = "1.0"
|
||||
bitcoin = "0.32"
|
||||
chrono = "0.4"
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::widget::text_input;
|
||||
use bitcoin::Denomination;
|
||||
use iced::{widget::text_input, Length};
|
||||
use iced::Length;
|
||||
|
||||
use crate::{color, component::text, theme, widget::*};
|
||||
|
||||
|
||||
@ -4,24 +4,4 @@ pub mod font;
|
||||
pub mod icon;
|
||||
pub mod image;
|
||||
pub mod theme;
|
||||
|
||||
pub mod widget {
|
||||
#![allow(dead_code)]
|
||||
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> = iced::widget::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>;
|
||||
}
|
||||
pub mod widget;
|
||||
|
||||
183
liana-ui/src/widget/cursor.rs
Normal file
183
liana-ui/src/widget/cursor.rs
Normal file
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
71
liana-ui/src/widget/editor.rs
Normal file
71
liana-ui/src/widget/editor.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
liana-ui/src/widget/mod.rs
Normal file
21
liana-ui/src/widget/mod.rs
Normal file
@ -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>;
|
||||
1356
liana-ui/src/widget/text_input.rs
Normal file
1356
liana-ui/src/widget/text_input.rs
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user