mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: more complete hovered file cursor tracking (#2218)
This commit is contained in:
parent
e37d8d6bc1
commit
cd338d0d1b
7 changed files with 48 additions and 43 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashSet, path::PathBuf};
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use yazi_dds::Pubsub;
|
use yazi_dds::Pubsub;
|
||||||
use yazi_macro::render;
|
use yazi_macro::render;
|
||||||
|
|
@ -26,7 +26,7 @@ impl Manager {
|
||||||
if let Some(u) = opt.url {
|
if let Some(u) = opt.url {
|
||||||
self.hover_do(u, opt.tab);
|
self.hover_do(u, opt.tab);
|
||||||
} else {
|
} else {
|
||||||
self.current_or_mut(opt.tab).repos(None);
|
self.current_or_mut(opt.tab).arrow(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repeek
|
// Repeek
|
||||||
|
|
@ -51,15 +51,15 @@ impl Manager {
|
||||||
|
|
||||||
fn hover_do(&mut self, url: Url, tab: Option<Id>) {
|
fn hover_do(&mut self, url: Url, tab: Option<Id>) {
|
||||||
// Hover on the file
|
// Hover on the file
|
||||||
if let Ok(p) = url.strip_prefix(&self.current_or(tab).url).map(PathBuf::from) {
|
if let Ok(p) = url.strip_prefix(&self.current_or(tab).url) {
|
||||||
render!(self.current_or_mut(tab).repos(Some(Urn::new(&p))));
|
render!(self.current_or_mut(tab).hover(Urn::new(p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn on tracing
|
// Turn on tracing
|
||||||
if self.current_or(tab).hovered().is_some_and(|f| url == f.url) {
|
if self.current_or(tab).hovered().is_some_and(|h| h.url == url) {
|
||||||
// `hover(Some)` occurs after user actions, such as create, rename, reveal, etc.
|
// `hover(Some)` occurs after user actions, such as create, rename, reveal, etc.
|
||||||
// At this point, it's intuitive to track the location of this file regardless.
|
// At this point, it's intuitive to track the location of the file regardless.
|
||||||
self.current_or_mut(tab).tracing = true;
|
self.current_or_mut(tab).trace = Some(url.urn_owned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,16 +70,12 @@ impl Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_current(tab: &mut Tab, op: Cow<FilesOp>, tasks: &Tasks) {
|
fn update_current(tab: &mut Tab, op: Cow<FilesOp>, tasks: &Tasks) {
|
||||||
let hovered = tab.hovered().filter(|_| tab.current.tracing).map(|h| h.urn_owned());
|
|
||||||
let calc = !matches!(*op, FilesOp::Size(..) | FilesOp::Deleting(..));
|
let calc = !matches!(*op, FilesOp::Size(..) | FilesOp::Deleting(..));
|
||||||
|
|
||||||
let foreign = matches!(op, Cow::Borrowed(_));
|
let foreign = matches!(op, Cow::Borrowed(_));
|
||||||
|
|
||||||
if !tab.current.update_pub(tab.id, op.into_owned()) {
|
if !tab.current.update_pub(tab.id, op.into_owned()) {
|
||||||
return;
|
return;
|
||||||
}
|
} else if foreign {
|
||||||
|
|
||||||
tab.current.repos(hovered.as_ref().map(|u| u.as_urn()));
|
|
||||||
if foreign {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,11 +105,11 @@ impl Manager {
|
||||||
|(p, n)| matches!(*op, FilesOp::Deleting(ref parent, ref urns) if *parent == p && urns.contains(n)),
|
|(p, n)| matches!(*op, FilesOp::Deleting(ref parent, ref urns) if *parent == p && urns.contains(n)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let folder = tab.history.entry(op.cwd().clone()).or_insert_with(|| Folder::from(op.cwd()));
|
tab
|
||||||
let hovered = folder.hovered().filter(|_| folder.tracing).map(|h| h.urn_owned());
|
.history
|
||||||
if folder.update_pub(tab.id, op.into_owned()) {
|
.entry(op.cwd().clone())
|
||||||
folder.repos(hovered.as_ref().map(|u| u.as_urn()));
|
.or_insert_with(|| Folder::from(op.cwd()))
|
||||||
}
|
.update_pub(tab.id, op.into_owned());
|
||||||
|
|
||||||
if leave {
|
if leave {
|
||||||
tab.leave(());
|
tab.leave(());
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ impl Tab {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.current.repos(hovered.as_ref().map(|u| u.as_urn()));
|
self.current.repos(hovered.as_ref());
|
||||||
if self.hovered().map(|f| f.urn()) != hovered.as_ref().map(|u| u.as_urn()) {
|
if self.hovered().map(|f| f.urn()) != hovered.as_ref().map(|u| u.as_urn()) {
|
||||||
ManagerProxy::hover(None, self.id);
|
ManagerProxy::hover(None, self.id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use yazi_config::{LAYOUT, MANAGER};
|
||||||
use yazi_dds::Pubsub;
|
use yazi_dds::Pubsub;
|
||||||
use yazi_fs::{Cha, File, Files, FilesOp, FolderStage, Step};
|
use yazi_fs::{Cha, File, Files, FilesOp, FolderStage, Step};
|
||||||
use yazi_proxy::ManagerProxy;
|
use yazi_proxy::ManagerProxy;
|
||||||
use yazi_shared::{Id, url::{Url, Urn}};
|
use yazi_shared::{Id, url::{Url, Urn, UrnBuf}};
|
||||||
|
|
||||||
pub struct Folder {
|
pub struct Folder {
|
||||||
pub url: Url,
|
pub url: Url,
|
||||||
|
|
@ -15,21 +15,21 @@ pub struct Folder {
|
||||||
pub offset: usize,
|
pub offset: usize,
|
||||||
pub cursor: usize,
|
pub cursor: usize,
|
||||||
|
|
||||||
pub page: usize,
|
pub page: usize,
|
||||||
pub tracing: bool,
|
pub trace: Option<UrnBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Folder {
|
impl Default for Folder {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
url: Default::default(),
|
url: Default::default(),
|
||||||
cha: Default::default(),
|
cha: Default::default(),
|
||||||
files: Files::new(MANAGER.show_hidden),
|
files: Files::new(MANAGER.show_hidden),
|
||||||
stage: Default::default(),
|
stage: Default::default(),
|
||||||
offset: Default::default(),
|
offset: Default::default(),
|
||||||
cursor: Default::default(),
|
cursor: Default::default(),
|
||||||
page: Default::default(),
|
page: Default::default(),
|
||||||
tracing: Default::default(),
|
trace: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,6 +45,9 @@ impl Folder {
|
||||||
FilesOp::Full(_, _, cha) => {
|
FilesOp::Full(_, _, cha) => {
|
||||||
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
(self.cha, self.stage) = (cha, FolderStage::Loaded);
|
||||||
}
|
}
|
||||||
|
FilesOp::Part(_, ref files, _) if files.is_empty() => {
|
||||||
|
self.stage = FolderStage::Loading;
|
||||||
|
}
|
||||||
FilesOp::Part(_, _, ticket) if ticket == self.files.ticket() => {
|
FilesOp::Part(_, _, ticket) if ticket == self.files.ticket() => {
|
||||||
self.stage = FolderStage::Loading;
|
self.stage = FolderStage::Loading;
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +76,9 @@ impl Folder {
|
||||||
FilesOp::Upserting(_, files) => self.files.update_upserting(files),
|
FilesOp::Upserting(_, files) => self.files.update_upserting(files),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.arrow(0);
|
self.trace = self.trace.take_if(|_| !self.files.is_empty() || self.stage.is_loading());
|
||||||
|
self.repos(self.trace.clone());
|
||||||
|
|
||||||
(stage, revision) != (self.stage, self.files.revision)
|
(stage, revision) != (self.stage, self.files.revision)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,15 +95,14 @@ impl Folder {
|
||||||
pub fn arrow(&mut self, step: impl Into<Step>) -> bool {
|
pub fn arrow(&mut self, step: impl Into<Step>) -> bool {
|
||||||
let step = step.into() as Step;
|
let step = step.into() as Step;
|
||||||
let mut b = if self.files.is_empty() {
|
let mut b = if self.files.is_empty() {
|
||||||
(self.cursor, self.offset, self.tracing) = (0, 0, false);
|
(mem::take(&mut self.cursor), mem::take(&mut self.offset)) != (0, 0)
|
||||||
false
|
|
||||||
} else if step.is_positive() {
|
} else if step.is_positive() {
|
||||||
self.next(step)
|
self.next(step)
|
||||||
} else {
|
} else {
|
||||||
self.prev(step)
|
self.prev(step)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.tracing |= b;
|
self.trace = self.hovered().filter(|_| b).map(|h| h.urn_owned()).or(self.trace.take());
|
||||||
b |= self.squeeze_offset();
|
b |= self.squeeze_offset();
|
||||||
|
|
||||||
self.sync_page(false);
|
self.sync_page(false);
|
||||||
|
|
@ -107,7 +111,7 @@ impl Folder {
|
||||||
|
|
||||||
pub fn hover(&mut self, urn: &Urn) -> bool {
|
pub fn hover(&mut self, urn: &Urn) -> bool {
|
||||||
if self.hovered().map(|h| h.urn()) == Some(urn) {
|
if self.hovered().map(|h| h.urn()) == Some(urn) {
|
||||||
return false;
|
return self.arrow(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let new = self.files.position(urn).unwrap_or(self.cursor) as isize;
|
let new = self.files.position(urn).unwrap_or(self.cursor) as isize;
|
||||||
|
|
@ -115,8 +119,8 @@ impl Folder {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn repos(&mut self, url: Option<&Urn>) -> bool {
|
pub fn repos(&mut self, urn: Option<impl AsRef<Urn>>) -> bool {
|
||||||
if let Some(u) = url { self.hover(u) } else { self.arrow(0) }
|
if let Some(u) = urn { self.hover(u.as_ref()) } else { self.arrow(0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sync_page(&mut self, force: bool) {
|
pub fn sync_page(&mut self, force: bool) {
|
||||||
|
|
|
||||||
|
|
@ -123,22 +123,18 @@ impl Tab {
|
||||||
return render!();
|
return render!();
|
||||||
}
|
}
|
||||||
|
|
||||||
let hovered = f.hovered().filter(|_| f.tracing).map(|h| h.urn_owned());
|
|
||||||
f.files.set_show_hidden(self.pref.show_hidden);
|
f.files.set_show_hidden(self.pref.show_hidden);
|
||||||
f.files.set_sorter(<_>::from(&self.pref));
|
f.files.set_sorter(<_>::from(&self.pref));
|
||||||
|
|
||||||
render!(f.files.catchup_revision());
|
render!(f.files.catchup_revision());
|
||||||
render!(f.repos(hovered.as_ref().map(|u| u.as_urn())));
|
render!(f.repos(f.trace.clone()));
|
||||||
};
|
};
|
||||||
|
|
||||||
apply(&mut self.current);
|
apply(&mut self.current);
|
||||||
|
|
||||||
if let Some(parent) = &mut self.parent {
|
if let Some(parent) = &mut self.parent {
|
||||||
apply(parent);
|
apply(parent);
|
||||||
|
parent.hover(self.current.url.urn()); // The parent should always track the CWD
|
||||||
// The parent should always track the CWD
|
|
||||||
parent.hover(self.current.url.urn());
|
|
||||||
parent.tracing = parent.hovered().map(|h| &h.url) == Some(&self.current.url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,11 @@ pub enum FolderStage {
|
||||||
Failed(std::io::ErrorKind),
|
Failed(std::io::ErrorKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FolderStage {
|
||||||
|
#[inline]
|
||||||
|
pub fn is_loading(self) -> bool { self == Self::Loading }
|
||||||
|
}
|
||||||
|
|
||||||
impl Serialize for FolderStage {
|
impl Serialize for FolderStage {
|
||||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
let mut map = serializer.serialize_map(Some(2))?;
|
let mut map = serializer.serialize_map(Some(2))?;
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,10 @@ impl Borrow<Urn> for UrnBuf {
|
||||||
fn borrow(&self) -> &Urn { Urn::new(&self.0) }
|
fn borrow(&self) -> &Urn { Urn::new(&self.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<Urn> for UrnBuf {
|
||||||
|
fn as_ref(&self) -> &Urn { self.borrow() }
|
||||||
|
}
|
||||||
|
|
||||||
impl AsRef<Path> for UrnBuf {
|
impl AsRef<Path> for UrnBuf {
|
||||||
fn as_ref(&self) -> &Path { &self.0 }
|
fn as_ref(&self) -> &Path { &self.0 }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue