feat: long text scrolling

This commit is contained in:
sxyazi 2023-07-21 23:41:22 +08:00
parent 820ddf2d4c
commit b6ea235b5e
No known key found for this signature in database
4 changed files with 104 additions and 59 deletions

46
Cargo.lock generated
View File

@ -71,13 +71,13 @@ dependencies = [
[[package]]
name = "async-trait"
version = "0.1.71"
version = "0.1.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.27",
]
[[package]]
@ -538,7 +538,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.27",
]
[[package]]
@ -1098,9 +1098,9 @@ dependencies = [
[[package]]
name = "num-traits"
version = "0.2.15"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [
"autocfg",
]
@ -1219,7 +1219,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.27",
]
[[package]]
@ -1511,22 +1511,22 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.173"
version = "1.0.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91f70896d6720bc714a4a57d22fc91f1db634680e65c8efe13323f1fa38d53f"
checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.173"
version = "1.0.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6250dde8342e0232232be9ca3db7aa40aceb5a3e5dd9bddbc00d99a007cde49"
checksum = "6e5c3a298c7f978e53536f95a63bdc4c4a64550582f31a0359a9afda6aede62e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.27",
]
[[package]]
@ -1653,9 +1653,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.26"
version = "2.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0"
dependencies = [
"proc-macro2",
"quote",
@ -1693,22 +1693,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.43"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42"
checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.43"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.27",
]
[[package]]
@ -1813,7 +1813,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.27",
]
[[package]]
@ -1967,7 +1967,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.27",
]
[[package]]
@ -2127,7 +2127,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.27",
"wasm-bindgen-shared",
]
@ -2149,7 +2149,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.26",
"syn 2.0.27",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]

View File

@ -1,23 +1,24 @@
use anyhow::{anyhow, Result};
use ratatui::layout::Rect;
use tokio::sync::oneshot::Sender;
use unicode_width::UnicodeWidthStr;
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
use super::InputSnap;
use crate::{core::{external, Position}, misc::CharKind};
#[derive(Default)]
pub struct Input {
title: String,
value: String,
position: (u16, u16),
title: String,
pub(crate) value: String,
position: (u16, u16),
op: InputOp,
start: Option<usize>,
pub(crate) op: InputOp,
pub(crate) start: Option<usize>,
mode: InputMode,
offset: usize,
cursor: usize,
callback: Option<Sender<Result<String>>>,
pub(crate) mode: InputMode,
pub(crate) offset: usize,
pub(crate) cursor: usize,
callback: Option<Sender<Result<String>>>,
pub visible: bool,
}
@ -60,7 +61,7 @@ impl Input {
};
self.cursor = self.count();
self.offset = self.value.width().saturating_sub(50);
self.offset = self.cursor.saturating_sub(50 - 2 /* Border width */);
self.callback = Some(tx);
self.visible = true;
}
@ -129,8 +130,8 @@ impl Input {
if self.cursor < self.offset {
self.offset = self.cursor;
} else if self.cursor > self.offset + 50 {
self.offset = self.cursor.saturating_sub(50);
} else if self.cursor >= self.offset + 50 - 2 {
self.offset = self.cursor.saturating_sub(50 - 2 - self.mode.delta());
}
b
}
@ -271,24 +272,20 @@ impl Input {
}
fn handle_op(&mut self, cursor: usize, include: bool) -> bool {
let old = self.cursor;
let snap = self.snap();
let range = if self.op == InputOp::None { None } else { self.range(cursor, include) };
let b = match self.op {
match self.op {
InputOp::None => {
self.cursor = cursor;
false
}
InputOp::Delete(insert) => {
let range = range.unwrap();
let old_mode = self.mode;
let (start, end) = (self.idx(range.0), self.idx(range.1));
self.value.drain(start.unwrap()..end.unwrap());
self.mode = if insert { InputMode::Insert } else { InputMode::Normal };
self.cursor = range.0;
start != end || self.mode != old_mode
}
InputOp::Yank => {
let range = range.unwrap();
@ -298,13 +295,12 @@ impl Input {
futures::executor::block_on(async {
external::clipboard_set(yanked).await.ok();
});
start != end
}
};
self.op = InputOp::None;
self.cursor = self.count().saturating_sub(self.mode.delta()).min(self.cursor);
b || self.cursor != old
snap != self.snap()
}
}
@ -313,28 +309,24 @@ impl Input {
pub fn title(&self) -> String { self.title.clone() }
#[inline]
pub fn value(&self) -> String { self.value.clone() }
pub fn value(&self) -> &str {
let win = self.window();
&self.value[self.idx(win.0).unwrap()..self.idx(win.1).unwrap()]
}
#[inline]
pub fn mode(&self) -> InputMode { self.mode }
#[inline]
pub fn area(&self) -> Rect {
Rect { x: self.position.0, y: self.position.1 + 2, width: 50, height: 3 }
}
#[inline]
pub fn mode(&self) -> InputMode { self.mode }
#[inline]
pub fn cursor(&self) -> (u16, u16) {
let width = self
.value
.chars()
.enumerate()
.take_while(|(i, _)| *i < self.cursor)
.map(|(_, c)| c)
.collect::<String>()
.width() as u16;
let area = self.area();
let width = self.value[self.offset..self.idx(self.cursor).unwrap()].width() as u16;
(area.x + width + 1, area.y + 1)
}
@ -346,10 +338,14 @@ impl Input {
let start = self.start.unwrap();
let (start, end) =
if start < self.cursor { (start, self.cursor) } else { (self.cursor + 1, start + 1) };
let win = self.window();
let (start, end) = (start.max(win.0), end.min(win.1));
let (start, end) = (self.idx(start).unwrap(), self.idx(end).unwrap());
let offset = self.idx(self.offset).unwrap();
Some(Rect {
x: self.position.0 + 1 + self.value[..start].width() as u16,
x: self.position.0 + 1 + self.value[offset..start].width() as u16,
y: self.position.1 + 3,
width: self.value[start..end].width() as u16,
height: 1,
@ -377,4 +373,23 @@ impl Input {
.map(|s| if s <= cursor { (s, cursor) } else { (cursor, s) })
.map(|(s, e)| (s, e + include as usize))
}
#[inline]
fn window(&self) -> (usize, usize) {
let mut len = 0;
let v = self
.value
.chars()
.enumerate()
.skip(self.offset)
.map_while(|(i, c)| {
len += c.width().unwrap_or(0);
if len > 50 - 2/*Border width*/ { None } else { Some(i) }
})
.collect::<Vec<_>>();
(v.first().copied().unwrap_or(0), v.last().map(|l| l + 1).unwrap_or(0))
}
#[inline]
fn snap(&self) -> InputSnap { InputSnap::from(self) }
}

View File

@ -1,3 +1,5 @@
mod input;
mod snap;
pub use input::*;
pub use snap::*;

28
src/core/input/snap.rs Normal file
View File

@ -0,0 +1,28 @@
use super::{Input, InputMode, InputOp};
#[derive(Default, PartialEq, Eq)]
pub struct InputSnap {
value: String,
op: InputOp,
start: Option<usize>,
mode: InputMode,
offset: usize,
cursor: usize,
}
impl From<&Input> for InputSnap {
fn from(input: &Input) -> Self {
Self {
value: input.value.clone(),
op: input.op,
start: input.start,
mode: input.mode,
offset: input.offset,
cursor: input.cursor,
}
}
}