mirror of
https://github.com/sxyazi/yazi.git
synced 2026-05-13 08:16:40 +00:00
feat: improve the UX of the input component (#2935)
This commit is contained in:
parent
8657e6b6f5
commit
5a66559d1c
20 changed files with 76 additions and 61 deletions
|
|
@ -30,21 +30,35 @@ impl Input {
|
|||
return;
|
||||
}
|
||||
|
||||
render!(self.handle_op(opt.step.cursor(snap), false));
|
||||
let (o_cur, n_cur) = (snap.cursor, opt.step.cursor(snap));
|
||||
render!(self.handle_op(n_cur, false));
|
||||
|
||||
let (limit, snap) = (self.limit, self.snap_mut());
|
||||
if snap.offset > snap.cursor {
|
||||
snap.offset = snap.cursor;
|
||||
} else if snap.value.is_empty() {
|
||||
snap.offset = 0;
|
||||
} else {
|
||||
let delta = snap.mode.delta();
|
||||
let range = snap.offset..snap.cursor + delta;
|
||||
if snap.width(range.clone()) >= limit as u16 {
|
||||
let it = snap.slice(range).chars().rev().map(|c| if snap.obscure { '•' } else { c });
|
||||
snap.offset = snap.cursor - InputSnap::find_window(it, 0, limit).end.saturating_sub(delta);
|
||||
}
|
||||
if snap.value.is_empty() {
|
||||
return snap.offset = 0;
|
||||
}
|
||||
|
||||
let (o_off, scrolloff) = (snap.offset, 5.min(limit / 2));
|
||||
snap.offset = if n_cur <= o_cur {
|
||||
let it = snap.slice(0..n_cur).chars().rev().map(|c| if snap.obscure { '•' } else { c });
|
||||
let pad = InputSnap::find_window(it, 0, scrolloff).end;
|
||||
|
||||
if n_cur >= o_off { snap.offset.min(n_cur - pad) } else { n_cur - pad }
|
||||
} else {
|
||||
let count = snap.count();
|
||||
|
||||
let it = snap.slice(n_cur..count).chars().map(|c| if snap.obscure { '•' } else { c });
|
||||
let pad = InputSnap::find_window(it, 0, scrolloff + snap.mode.delta()).end;
|
||||
|
||||
let it = snap.slice(0..n_cur + pad).chars().rev().map(|c| if snap.obscure { '•' } else { c });
|
||||
let max = InputSnap::find_window(it, 0, limit).end;
|
||||
|
||||
if snap.width(o_off..n_cur) < limit as u16 {
|
||||
snap.offset.max(n_cur + pad - max)
|
||||
} else {
|
||||
n_cur + pad - max
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,5 +6,7 @@ use crate::input::Input;
|
|||
impl Input {
|
||||
pub fn redo(&mut self, _: CmdCow) {
|
||||
render!(self.snaps.redo());
|
||||
|
||||
self.r#move(0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,12 @@ impl Input {
|
|||
if !self.snaps.undo() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.r#move(0);
|
||||
if self.snap().mode == InputMode::Insert {
|
||||
self.escape(());
|
||||
}
|
||||
|
||||
render!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,3 @@
|
|||
yazi_macro::mod_pub!(input);
|
||||
|
||||
yazi_macro::mod_flat!(scrollable step);
|
||||
|
|
|
|||
43
yazi-widgets/src/scrollable.rs
Normal file
43
yazi-widgets/src/scrollable.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
use crate::Step;
|
||||
|
||||
pub trait Scrollable {
|
||||
fn total(&self) -> usize;
|
||||
fn limit(&self) -> usize;
|
||||
fn scrolloff(&self) -> usize { self.limit() / 2 }
|
||||
fn cursor_mut(&mut self) -> &mut usize;
|
||||
fn offset_mut(&mut self) -> &mut usize;
|
||||
|
||||
fn scroll(&mut self, step: impl Into<Step>) -> bool {
|
||||
let new = step.into().add(*self.cursor_mut(), self.total(), self.limit());
|
||||
if new > *self.cursor_mut() { self.next(new) } else { self.prev(new) }
|
||||
}
|
||||
|
||||
fn next(&mut self, n_cur: usize) -> bool {
|
||||
let (o_cur, o_off) = (*self.cursor_mut(), *self.offset_mut());
|
||||
let (total, limit, scrolloff) = (self.total(), self.limit(), self.scrolloff());
|
||||
|
||||
let n_off = if n_cur < total.min(o_off + limit).saturating_sub(scrolloff) {
|
||||
o_off.min(total.saturating_sub(1))
|
||||
} else {
|
||||
total.saturating_sub(limit).min(o_off + n_cur - o_cur)
|
||||
};
|
||||
|
||||
*self.cursor_mut() = n_cur;
|
||||
*self.offset_mut() = n_off;
|
||||
(n_cur, n_off) != (o_cur, o_off)
|
||||
}
|
||||
|
||||
fn prev(&mut self, n_cur: usize) -> bool {
|
||||
let (o_cur, o_off) = (*self.cursor_mut(), *self.offset_mut());
|
||||
|
||||
let n_off = if n_cur < o_off + self.scrolloff() {
|
||||
o_off.saturating_sub(o_cur - n_cur)
|
||||
} else {
|
||||
self.total().saturating_sub(1).min(o_off)
|
||||
};
|
||||
|
||||
*self.cursor_mut() = n_cur;
|
||||
*self.offset_mut() = n_off;
|
||||
(n_cur, n_off) != (o_cur, o_off)
|
||||
}
|
||||
}
|
||||
75
yazi-widgets/src/step.rs
Normal file
75
yazi-widgets/src/step.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
use std::{num::ParseIntError, str::FromStr};
|
||||
|
||||
use yazi_shared::event::Data;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Step {
|
||||
Top,
|
||||
Bot,
|
||||
Prev,
|
||||
Next,
|
||||
Offset(isize),
|
||||
Percent(i8),
|
||||
}
|
||||
|
||||
impl Default for Step {
|
||||
fn default() -> Self { Self::Offset(0) }
|
||||
}
|
||||
|
||||
impl From<isize> for Step {
|
||||
fn from(n: isize) -> Self { Self::Offset(n) }
|
||||
}
|
||||
|
||||
impl FromStr for Step {
|
||||
type Err = ParseIntError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s {
|
||||
"top" => Self::Top,
|
||||
"bot" => Self::Bot,
|
||||
"prev" => Self::Prev,
|
||||
"next" => Self::Next,
|
||||
s if s.ends_with('%') => Self::Percent(s[..s.len() - 1].parse()?),
|
||||
s => Self::Offset(s.parse()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Data> for Step {
|
||||
type Error = ParseIntError;
|
||||
|
||||
fn try_from(value: &Data) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
Data::Integer(i) => Self::from(*i as isize),
|
||||
Data::String(s) => s.parse()?,
|
||||
_ => "".parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Step {
|
||||
pub fn add(self, pos: usize, len: usize, limit: usize) -> usize {
|
||||
if len == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let off = match self {
|
||||
Self::Top => return 0,
|
||||
Self::Bot => return len - 1,
|
||||
Self::Prev => -1,
|
||||
Self::Next => 1,
|
||||
Self::Offset(n) => n,
|
||||
Self::Percent(0) => 0,
|
||||
Self::Percent(n) => n as isize * limit as isize / 100,
|
||||
};
|
||||
|
||||
if matches!(self, Self::Prev | Self::Next) {
|
||||
off.saturating_add_unsigned(pos).rem_euclid(len as _) as _
|
||||
} else if off >= 0 {
|
||||
pos.saturating_add_signed(off)
|
||||
} else {
|
||||
pos.saturating_sub(off.unsigned_abs())
|
||||
}
|
||||
.min(len - 1)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue