fork widget text_input

This commit is contained in:
edouardparis 2025-02-10 13:25:25 +01:00
parent 27570b4de9
commit 64dc91b578
8 changed files with 1640 additions and 22 deletions

3
Cargo.lock generated
View File

@ -3118,6 +3118,9 @@ dependencies = [
"bitcoin",
"chrono",
"iced",
"iced_core",
"iced_runtime",
"unicode-segmentation",
]
[[package]]

View File

@ -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"

View File

@ -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::*};

View File

@ -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;

View 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),
}
}
}

View 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);
}
}
}
}
}

View 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>;

File diff suppressed because it is too large Load Diff