diff --git a/yazi-core/src/cmp/cmp.rs b/yazi-core/src/cmp/cmp.rs index f6673400..46b88b41 100644 --- a/yazi-core/src/cmp/cmp.rs +++ b/yazi-core/src/cmp/cmp.rs @@ -2,8 +2,7 @@ use std::{collections::HashMap, path::PathBuf}; use yazi_proxy::options::CmpItem; use yazi_shared::Id; - -use crate::Scrollable; +use yazi_widgets::Scrollable; #[derive(Default)] pub struct Cmp { diff --git a/yazi-core/src/cmp/commands/arrow.rs b/yazi-core/src/cmp/commands/arrow.rs index 147ef199..6e00842e 100644 --- a/yazi-core/src/cmp/commands/arrow.rs +++ b/yazi-core/src/cmp/commands/arrow.rs @@ -1,8 +1,8 @@ -use yazi_fs::Step; use yazi_macro::render; use yazi_shared::event::CmdCow; +use yazi_widgets::{Scrollable, Step}; -use crate::{Scrollable, cmp::Cmp}; +use crate::cmp::Cmp; struct Opt { step: Step, @@ -23,7 +23,7 @@ impl Cmp { impl Scrollable for Cmp { #[inline] - fn len(&self) -> usize { self.cands.len() } + fn total(&self) -> usize { self.cands.len() } #[inline] fn limit(&self) -> usize { self.cands.len().min(10) } diff --git a/yazi-core/src/confirm/commands/arrow.rs b/yazi-core/src/confirm/commands/arrow.rs index 103aa4ed..64dbb5dc 100644 --- a/yazi-core/src/confirm/commands/arrow.rs +++ b/yazi-core/src/confirm/commands/arrow.rs @@ -1,6 +1,6 @@ -use yazi_fs::Step; use yazi_macro::render; use yazi_shared::event::CmdCow; +use yazi_widgets::Step; use crate::{confirm::Confirm, mgr::Mgr}; diff --git a/yazi-core/src/help/commands/arrow.rs b/yazi-core/src/help/commands/arrow.rs index b2503a85..903bdf2b 100644 --- a/yazi-core/src/help/commands/arrow.rs +++ b/yazi-core/src/help/commands/arrow.rs @@ -1,9 +1,9 @@ use yazi_adapter::Dimension; -use yazi_fs::Step; use yazi_macro::render; use yazi_shared::event::CmdCow; +use yazi_widgets::{Scrollable, Step}; -use crate::{Scrollable, help::{HELP_MARGIN, Help}}; +use crate::help::{HELP_MARGIN, Help}; struct Opt { step: Step, @@ -28,7 +28,7 @@ impl Help { impl Scrollable for Help { #[inline] - fn len(&self) -> usize { self.bindings.len() } + fn total(&self) -> usize { self.bindings.len() } #[inline] fn limit(&self) -> usize { Dimension::available().rows.saturating_sub(HELP_MARGIN) as usize } diff --git a/yazi-core/src/help/help.rs b/yazi-core/src/help/help.rs index f1e6345d..e5990cc8 100644 --- a/yazi-core/src/help/help.rs +++ b/yazi-core/src/help/help.rs @@ -4,8 +4,7 @@ use yazi_adapter::Dimension; use yazi_config::{KEYMAP, YAZI, keymap::{Chord, Key}}; use yazi_macro::{render, render_and}; use yazi_shared::Layer; - -use crate::Scrollable; +use yazi_widgets::Scrollable; #[derive(Default)] pub struct Help { diff --git a/yazi-core/src/lib.rs b/yazi-core/src/lib.rs index f2709d7a..e921f8be 100644 --- a/yazi-core/src/lib.rs +++ b/yazi-core/src/lib.rs @@ -6,8 +6,6 @@ clippy::unit_arg )] -yazi_macro::mod_flat!(scrollable); - yazi_macro::mod_pub!(cmp confirm help input mgr notify pick spot tab tasks which); pub fn init() { diff --git a/yazi-core/src/pick/commands/arrow.rs b/yazi-core/src/pick/commands/arrow.rs index c0243241..a97e9325 100644 --- a/yazi-core/src/pick/commands/arrow.rs +++ b/yazi-core/src/pick/commands/arrow.rs @@ -1,9 +1,9 @@ use yazi_config::YAZI; -use yazi_fs::Step; use yazi_macro::render; use yazi_shared::event::CmdCow; +use yazi_widgets::{Scrollable, Step}; -use crate::{Scrollable, pick::Pick}; +use crate::pick::Pick; struct Opt { step: Step, @@ -24,7 +24,7 @@ impl Pick { impl Scrollable for Pick { #[inline] - fn len(&self) -> usize { self.items.len() } + fn total(&self) -> usize { self.items.len() } #[inline] fn limit(&self) -> usize { diff --git a/yazi-core/src/pick/pick.rs b/yazi-core/src/pick/pick.rs index 29c0dc6f..8309c27d 100644 --- a/yazi-core/src/pick/pick.rs +++ b/yazi-core/src/pick/pick.rs @@ -1,8 +1,7 @@ use anyhow::Result; use tokio::sync::oneshot::Sender; use yazi_config::popup::Position; - -use crate::Scrollable; +use yazi_widgets::Scrollable; #[derive(Default)] pub struct Pick { diff --git a/yazi-core/src/spot/commands/arrow.rs b/yazi-core/src/spot/commands/arrow.rs index 7a9e5203..87764962 100644 --- a/yazi-core/src/spot/commands/arrow.rs +++ b/yazi-core/src/spot/commands/arrow.rs @@ -1,7 +1,7 @@ -use yazi_fs::Step; use yazi_macro::render; use yazi_proxy::MgrProxy; use yazi_shared::event::CmdCow; +use yazi_widgets::Step; use crate::spot::Spot; diff --git a/yazi-core/src/tab/commands/arrow.rs b/yazi-core/src/tab/commands/arrow.rs index f02a0ae3..3b9372ef 100644 --- a/yazi-core/src/tab/commands/arrow.rs +++ b/yazi-core/src/tab/commands/arrow.rs @@ -1,7 +1,7 @@ -use yazi_fs::Step; use yazi_macro::render; use yazi_proxy::MgrProxy; use yazi_shared::event::CmdCow; +use yazi_widgets::Step; use crate::tab::Tab; diff --git a/yazi-core/src/tab/folder.rs b/yazi-core/src/tab/folder.rs index 7c2c2a6a..89218798 100644 --- a/yazi-core/src/tab/folder.rs +++ b/yazi-core/src/tab/folder.rs @@ -2,12 +2,11 @@ use std::mem; use yazi_config::{LAYOUT, YAZI}; use yazi_dds::Pubsub; -use yazi_fs::{File, Files, FilesOp, FolderStage, Step, cha::Cha}; +use yazi_fs::{File, Files, FilesOp, FolderStage, cha::Cha}; use yazi_macro::err; use yazi_proxy::MgrProxy; use yazi_shared::{Id, url::{Url, Urn, UrnBuf}}; - -use crate::Scrollable; +use yazi_widgets::{Scrollable, Step}; pub struct Folder { pub url: Url, @@ -100,7 +99,7 @@ impl Folder { let mut b = if self.files.is_empty() { (mem::take(&mut self.cursor), mem::take(&mut self.offset)) != (0, 0) } else { - self.scroll(step.into()) + self.scroll(step) }; self.trace = self.hovered().filter(|_| b).map(|h| h.urn_owned()).or(self.trace.take()); @@ -169,7 +168,7 @@ impl Folder { impl Scrollable for Folder { #[inline] - fn len(&self) -> usize { self.files.len() } + fn total(&self) -> usize { self.files.len() } #[inline] fn limit(&self) -> usize { LAYOUT.get().limit() } diff --git a/yazi-core/src/tasks/commands/arrow.rs b/yazi-core/src/tasks/commands/arrow.rs index 40bd5569..f77d7115 100644 --- a/yazi-core/src/tasks/commands/arrow.rs +++ b/yazi-core/src/tasks/commands/arrow.rs @@ -1,6 +1,6 @@ -use yazi_fs::Step; use yazi_macro::render; use yazi_shared::event::CmdCow; +use yazi_widgets::Step; use crate::tasks::Tasks; diff --git a/yazi-fm/src/pick/list.rs b/yazi-fm/src/pick/list.rs index 532bc2ab..486c77ad 100644 --- a/yazi-fm/src/pick/list.rs +++ b/yazi-fm/src/pick/list.rs @@ -1,6 +1,6 @@ use ratatui::{buffer::Buffer, layout::{Margin, Rect}, widgets::{ListItem, Scrollbar, ScrollbarOrientation, ScrollbarState, StatefulWidget, Widget}}; use yazi_config::THEME; -use yazi_core::Scrollable; +use yazi_widgets::Scrollable; use crate::Ctx; @@ -17,11 +17,11 @@ impl Widget for List<'_> { let pick = &self.cx.pick; // Vertical scrollbar - if pick.len() > pick.limit() { + if pick.total() > pick.limit() { Scrollbar::new(ScrollbarOrientation::VerticalRight).render( area, buf, - &mut ScrollbarState::new(pick.len()).position(pick.cursor), + &mut ScrollbarState::new(pick.total()).position(pick.cursor), ); } diff --git a/yazi-fs/src/lib.rs b/yazi-fs/src/lib.rs index 7df8a8ae..9bfaf558 100644 --- a/yazi-fs/src/lib.rs +++ b/yazi-fs/src/lib.rs @@ -2,7 +2,7 @@ yazi_macro::mod_pub!(cha mounts); -yazi_macro::mod_flat!(calculator cwd file files filter fns op path sorter sorting stage step xdg); +yazi_macro::mod_flat!(calculator cwd file files filter fns op path sorter sorting stage xdg); pub fn init() { CWD.init(<_>::default()); diff --git a/yazi-widgets/src/input/commands/move.rs b/yazi-widgets/src/input/commands/move.rs index 728398fa..4ca3a965 100644 --- a/yazi-widgets/src/input/commands/move.rs +++ b/yazi-widgets/src/input/commands/move.rs @@ -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 + } + }; } } diff --git a/yazi-widgets/src/input/commands/redo.rs b/yazi-widgets/src/input/commands/redo.rs index bab835bd..ed058e65 100644 --- a/yazi-widgets/src/input/commands/redo.rs +++ b/yazi-widgets/src/input/commands/redo.rs @@ -6,5 +6,7 @@ use crate::input::Input; impl Input { pub fn redo(&mut self, _: CmdCow) { render!(self.snaps.redo()); + + self.r#move(0); } } diff --git a/yazi-widgets/src/input/commands/undo.rs b/yazi-widgets/src/input/commands/undo.rs index 54a66f3a..07a8bb5c 100644 --- a/yazi-widgets/src/input/commands/undo.rs +++ b/yazi-widgets/src/input/commands/undo.rs @@ -8,9 +8,12 @@ impl Input { if !self.snaps.undo() { return; } + + self.r#move(0); if self.snap().mode == InputMode::Insert { self.escape(()); } + render!(); } } diff --git a/yazi-widgets/src/lib.rs b/yazi-widgets/src/lib.rs index 6ce2b0ad..dc1f4256 100644 --- a/yazi-widgets/src/lib.rs +++ b/yazi-widgets/src/lib.rs @@ -1 +1,3 @@ yazi_macro::mod_pub!(input); + +yazi_macro::mod_flat!(scrollable step); diff --git a/yazi-core/src/scrollable.rs b/yazi-widgets/src/scrollable.rs similarity index 62% rename from yazi-core/src/scrollable.rs rename to yazi-widgets/src/scrollable.rs index 650edbfe..4fde11c5 100644 --- a/yazi-core/src/scrollable.rs +++ b/yazi-widgets/src/scrollable.rs @@ -1,25 +1,25 @@ -use yazi_fs::Step; +use crate::Step; pub trait Scrollable { - fn len(&self) -> usize; + 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: Step) -> bool { - let new = step.add(*self.cursor_mut(), self.len(), self.limit()); + fn scroll(&mut self, step: impl Into) -> 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 (len, limit, scrolloff) = (self.len(), self.limit(), self.scrolloff()); + let (total, limit, scrolloff) = (self.total(), self.limit(), self.scrolloff()); - let n_off = if n_cur < len.min(o_off + limit).saturating_sub(scrolloff) { - o_off.min(len.saturating_sub(1)) + let n_off = if n_cur < total.min(o_off + limit).saturating_sub(scrolloff) { + o_off.min(total.saturating_sub(1)) } else { - len.saturating_sub(limit).min(o_off + n_cur - o_cur) + total.saturating_sub(limit).min(o_off + n_cur - o_cur) }; *self.cursor_mut() = n_cur; @@ -33,7 +33,7 @@ pub trait Scrollable { let n_off = if n_cur < o_off + self.scrolloff() { o_off.saturating_sub(o_cur - n_cur) } else { - self.len().saturating_sub(1).min(o_off) + self.total().saturating_sub(1).min(o_off) }; *self.cursor_mut() = n_cur; diff --git a/yazi-fs/src/step.rs b/yazi-widgets/src/step.rs similarity index 76% rename from yazi-fs/src/step.rs rename to yazi-widgets/src/step.rs index ae5277dc..ee522d48 100644 --- a/yazi-fs/src/step.rs +++ b/yazi-widgets/src/step.rs @@ -8,16 +8,16 @@ pub enum Step { Bot, Prev, Next, - Fixed(isize), + Offset(isize), Percent(i8), } impl Default for Step { - fn default() -> Self { Self::Fixed(0) } + fn default() -> Self { Self::Offset(0) } } impl From for Step { - fn from(n: isize) -> Self { Self::Fixed(n) } + fn from(n: isize) -> Self { Self::Offset(n) } } impl FromStr for Step { @@ -30,7 +30,7 @@ impl FromStr for Step { "prev" => Self::Prev, "next" => Self::Next, s if s.ends_with('%') => Self::Percent(s[..s.len() - 1].parse()?), - s => Self::Fixed(s.parse()?), + s => Self::Offset(s.parse()?), }) } } @@ -53,22 +53,22 @@ impl Step { return 0; } - let fixed = match self { + let off = match self { Self::Top => return 0, Self::Bot => return len - 1, Self::Prev => -1, Self::Next => 1, - Self::Fixed(n) => n, + 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) { - fixed.saturating_add_unsigned(pos).rem_euclid(len as _) as _ - } else if fixed >= 0 { - pos.saturating_add_signed(fixed) + off.saturating_add_unsigned(pos).rem_euclid(len as _) as _ + } else if off >= 0 { + pos.saturating_add_signed(off) } else { - pos.saturating_sub(fixed.unsigned_abs()) + pos.saturating_sub(off.unsigned_abs()) } .min(len - 1) }