mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-01 10:17:47 +03:00
fix: inability to restore default cursor on Windows
This commit is contained in:
parent
43878fd92e
commit
3fcc2014bc
@ -1,10 +1,10 @@
|
||||
use std::time::SystemTime;
|
||||
use std::{mem, time::SystemTime};
|
||||
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_shared::{emit, fs::{File, FilesOp, Url}};
|
||||
use yazi_shared::fs::{File, FilesOp, Url};
|
||||
|
||||
use crate::{folder::Files, Step};
|
||||
use crate::{folder::Files, manager::Manager, Step};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Folder {
|
||||
@ -15,7 +15,8 @@ pub struct Folder {
|
||||
pub offset: usize,
|
||||
pub cursor: usize,
|
||||
|
||||
pub page: usize,
|
||||
pub page: usize,
|
||||
pub tracing: bool,
|
||||
}
|
||||
|
||||
impl From<Url> for Folder {
|
||||
@ -47,46 +48,57 @@ impl Folder {
|
||||
FilesOp::Upserting(_, files) => self.files.update_upserting(files),
|
||||
}
|
||||
|
||||
// TODO: use a better way to detect if the page is changed
|
||||
let old = self.page;
|
||||
self.prev(Default::default());
|
||||
|
||||
if self.page == old {
|
||||
self.set_page(true); // Force update
|
||||
self.arrow(0);
|
||||
if self.files.is_empty() {
|
||||
self.tracing = false;
|
||||
}
|
||||
|
||||
self.files.revision != revision
|
||||
}
|
||||
|
||||
pub fn set_page(&mut self, force: bool) {
|
||||
pub fn arrow(&mut self, step: impl Into<Step>) -> bool {
|
||||
let step = step.into() as Step;
|
||||
let b = if self.files.is_empty() {
|
||||
(self.cursor, self.offset, self.tracing) = (0, 0, false);
|
||||
false
|
||||
} else if step.is_positive() {
|
||||
self.next(step)
|
||||
} else {
|
||||
self.prev(step)
|
||||
};
|
||||
|
||||
self.sync_page(false);
|
||||
self.tracing |= b;
|
||||
b
|
||||
}
|
||||
|
||||
pub fn hover(&mut self, url: &Url) -> bool {
|
||||
if self.hovered().map(|h| &h.url) == Some(url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let new = self.files.position(url).unwrap_or(self.cursor) as isize;
|
||||
self.arrow(new - self.cursor as isize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn repos(&mut self, url: Option<impl AsRef<Url>>) -> bool {
|
||||
if let Some(u) = url { self.hover(u.as_ref()) } else { self.arrow(0) }
|
||||
}
|
||||
|
||||
pub fn sync_page(&mut self, force: bool) {
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
if limit == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let new = self.cursor / limit;
|
||||
if !force && self.page == new {
|
||||
return;
|
||||
if mem::replace(&mut self.page, new) != new || force {
|
||||
Manager::_update_pages_by(new, &self.cwd);
|
||||
}
|
||||
|
||||
// Current page
|
||||
emit!(Pages(new));
|
||||
|
||||
// Next page
|
||||
let max_page = (self.files.len() + limit - 1) / limit;
|
||||
if new < max_page && new + 1 != self.page {
|
||||
emit!(Pages(new + 1));
|
||||
}
|
||||
|
||||
// Previous page
|
||||
if new > 1 && new - 1 != self.page {
|
||||
emit!(Pages(new - 1));
|
||||
}
|
||||
|
||||
self.page = new;
|
||||
}
|
||||
|
||||
pub fn next(&mut self, step: Step) -> bool {
|
||||
fn next(&mut self, step: Step) -> bool {
|
||||
let old = (self.cursor, self.offset);
|
||||
let len = self.files.len();
|
||||
|
||||
@ -98,11 +110,10 @@ impl Folder {
|
||||
self.offset.min(len.saturating_sub(1))
|
||||
};
|
||||
|
||||
self.set_page(false);
|
||||
old != (self.cursor, self.offset)
|
||||
}
|
||||
|
||||
pub fn prev(&mut self, step: Step) -> bool {
|
||||
fn prev(&mut self, step: Step) -> bool {
|
||||
let old = (self.cursor, self.offset);
|
||||
let max = self.files.len().saturating_sub(1);
|
||||
|
||||
@ -113,27 +124,8 @@ impl Folder {
|
||||
self.offset.min(max)
|
||||
};
|
||||
|
||||
self.set_page(false);
|
||||
old != (self.cursor, self.offset)
|
||||
}
|
||||
|
||||
pub fn hover(&mut self, url: &Url) -> bool {
|
||||
if self.hovered().map(|h| &h.url) == Some(url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let new = self.files.position(url).unwrap_or(self.cursor);
|
||||
if new > self.cursor {
|
||||
self.next(Step::next(new - self.cursor))
|
||||
} else {
|
||||
self.prev(Step::prev(self.cursor - new))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn repos(&mut self, url: Option<impl AsRef<Url>>) -> bool {
|
||||
if let Some(u) = url { self.hover(u.as_ref()) } else { self.prev(Default::default()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Folder {
|
||||
@ -144,8 +136,8 @@ impl Folder {
|
||||
let len = self.files.len();
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
|
||||
let start = (page * limit).min(len.saturating_sub(1));
|
||||
let end = (start + limit).min(len);
|
||||
let start = (page.saturating_sub(1) * limit).min(len.saturating_sub(1));
|
||||
let end = ((page + 2) * limit).min(len);
|
||||
&self.files[start..end]
|
||||
}
|
||||
|
||||
|
@ -17,4 +17,5 @@ mod tab_swap;
|
||||
mod tab_switch;
|
||||
mod update_files;
|
||||
mod update_mimetype;
|
||||
mod update_pages;
|
||||
mod yank;
|
||||
|
@ -2,7 +2,7 @@ use std::env;
|
||||
|
||||
use yazi_shared::{emit, event::Exec, Layer};
|
||||
|
||||
use crate::manager::Manager;
|
||||
use crate::{manager::Manager, tasks::Tasks};
|
||||
|
||||
impl Manager {
|
||||
#[inline]
|
||||
@ -10,7 +10,7 @@ impl Manager {
|
||||
emit!(Call(Exec::call("refresh", vec![]).vec(), Layer::Manager));
|
||||
}
|
||||
|
||||
pub fn refresh(&mut self, _: &Exec) {
|
||||
pub fn refresh(&mut self, _: &Exec, tasks: &Tasks) {
|
||||
env::set_current_dir(self.cwd()).ok();
|
||||
env::set_var("PWD", self.cwd());
|
||||
|
||||
@ -23,5 +23,6 @@ impl Manager {
|
||||
}
|
||||
|
||||
self.hover(None);
|
||||
self.update_pages((), tasks);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ impl Manager {
|
||||
|
||||
if let Some(p) = self.active_mut().parent.as_mut() {
|
||||
render!(p.update(op));
|
||||
render!(p.files.catchup_revision());
|
||||
render!(p.hover(&cwd));
|
||||
}
|
||||
|
||||
@ -29,16 +28,15 @@ impl Manager {
|
||||
}
|
||||
|
||||
fn update_current(&mut self, op: FilesOp, tasks: &Tasks) {
|
||||
let hovered = self.hovered().map(|h| h.url());
|
||||
let hovered = self.hovered().filter(|_| self.current().tracing).map(|h| h.url());
|
||||
let calc = !matches!(op, FilesOp::Size(..) | FilesOp::Deleting(..));
|
||||
|
||||
render!(self.current_mut().update(op));
|
||||
render!(self.current_mut().files.catchup_revision());
|
||||
render!(hovered.as_ref().is_some_and(|h| self.current_mut().hover(h)));
|
||||
|
||||
if hovered.as_ref() != self.hovered().map(|h| &h.url) {
|
||||
self.hover(None);
|
||||
if self.current_mut().update(op) {
|
||||
self.current_mut().repos(hovered.as_ref());
|
||||
Self::_hover(None); // Re-hover in next loop
|
||||
Self::_update_pages(); // Update for paged files in next loop
|
||||
}
|
||||
|
||||
if calc {
|
||||
tasks.preload_sorted(&self.current().files);
|
||||
}
|
||||
@ -46,18 +44,9 @@ impl Manager {
|
||||
|
||||
fn update_hovered(&mut self, op: FilesOp) {
|
||||
let url = op.url();
|
||||
let folder = match self.active_mut().history.get_mut(url) {
|
||||
Some(f) => f,
|
||||
None => {
|
||||
let f = Folder::from(url);
|
||||
self.active_mut().history.insert(url.clone(), f);
|
||||
self.active_mut().apply_files_attrs();
|
||||
self.active_mut().history.get_mut(url).unwrap()
|
||||
}
|
||||
};
|
||||
let folder = self.active_mut().history.entry(url.clone()).or_insert_with(|| Folder::from(url));
|
||||
|
||||
if folder.update(op) {
|
||||
folder.files.catchup_revision();
|
||||
self.peek(true);
|
||||
}
|
||||
}
|
||||
@ -97,5 +86,7 @@ impl Manager {
|
||||
self.update_history(op);
|
||||
}
|
||||
}
|
||||
|
||||
self.active_mut().apply_files_attrs();
|
||||
}
|
||||
}
|
||||
|
50
yazi-core/src/manager/commands/update_pages.rs
Normal file
50
yazi-core/src/manager/commands/update_pages.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use yazi_shared::{emit, event::Exec, fs::Url, Layer};
|
||||
|
||||
use crate::{manager::Manager, tasks::Tasks};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Opt {
|
||||
page: Option<usize>,
|
||||
only_if: Option<Url>,
|
||||
}
|
||||
|
||||
impl From<&Exec> for Opt {
|
||||
fn from(e: &Exec) -> Self {
|
||||
Self {
|
||||
page: e.args.first().and_then(|s| s.parse().ok()),
|
||||
only_if: e.named.get("only-if").map(Url::from),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for Opt {
|
||||
fn from(_: ()) -> Self { Self::default() }
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
#[inline]
|
||||
pub fn _update_pages() {
|
||||
emit!(Call(Exec::call("update_pages", vec![]).vec(), Layer::Manager));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn _update_pages_by(page: usize, only_if: &Url) {
|
||||
emit!(Call(
|
||||
Exec::call("update_pages", vec![page.to_string()]).with("only-if", only_if.to_string()).vec(),
|
||||
Layer::Manager
|
||||
));
|
||||
}
|
||||
|
||||
pub fn update_pages(&mut self, opt: impl TryInto<Opt>, tasks: &Tasks) {
|
||||
let Ok(opt) = opt.try_into() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if opt.only_if.is_some_and(|u| u != self.current().cwd) {
|
||||
return;
|
||||
}
|
||||
|
||||
let targets = self.current().paginate(opt.page.unwrap_or(self.current().page));
|
||||
tasks.preload_paged(targets, &self.mimetype);
|
||||
}
|
||||
}
|
@ -22,12 +22,7 @@ where
|
||||
impl Tab {
|
||||
pub fn arrow(&mut self, opt: impl Into<Opt>) {
|
||||
let opt = opt.into() as Opt;
|
||||
let ok = if opt.step.is_positive() {
|
||||
self.current.next(opt.step)
|
||||
} else {
|
||||
self.current.prev(opt.step)
|
||||
};
|
||||
if !ok {
|
||||
if !self.current.arrow(opt.step) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ impl Tab {
|
||||
|
||||
pub fn apply_files_attrs(&mut self) {
|
||||
let apply = |f: &mut Folder| {
|
||||
let hovered = f.hovered().map(|h| h.url());
|
||||
let hovered = f.hovered().filter(|_| f.tracing).map(|h| h.url());
|
||||
|
||||
f.files.set_show_hidden(self.conf.show_hidden);
|
||||
f.files.set_sorter(self.conf.sorter());
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::{mem, sync::atomic::Ordering};
|
||||
|
||||
use anyhow::{Ok, Result};
|
||||
use crossterm::event::KeyEvent;
|
||||
@ -37,14 +37,12 @@ impl App {
|
||||
Event::Paste(str) => app.dispatch_paste(str),
|
||||
Event::Quit(no_cwd_file) => {
|
||||
app.quit(no_cwd_file)?;
|
||||
break;
|
||||
return Ok(());
|
||||
}
|
||||
event => app.dispatch_module(event),
|
||||
}
|
||||
}
|
||||
|
||||
if render_in_place {
|
||||
render_in_place = false;
|
||||
if mem::replace(&mut render_in_place, false) {
|
||||
app.render()?;
|
||||
}
|
||||
|
||||
@ -71,7 +69,7 @@ impl App {
|
||||
self.cx.manager.active_mut().preview.reset();
|
||||
self.render()?;
|
||||
|
||||
self.cx.manager.current_mut().set_page(true);
|
||||
self.cx.manager.current_mut().sync_page(true);
|
||||
self.cx.manager.peek(false);
|
||||
Ok(())
|
||||
}
|
||||
@ -80,15 +78,4 @@ impl App {
|
||||
fn dispatch_call(&mut self, exec: Vec<Exec>, layer: Layer) {
|
||||
Executor::new(self).dispatch(&exec, layer);
|
||||
}
|
||||
|
||||
fn dispatch_module(&mut self, event: Event) {
|
||||
let tasks = &mut self.cx.tasks;
|
||||
match event {
|
||||
Event::Pages(page) => {
|
||||
let targets = self.cx.manager.current().paginate(page);
|
||||
tasks.preload_paged(targets, &self.cx.manager.mimetype);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -108,10 +108,11 @@ impl<'a> Executor<'a> {
|
||||
|
||||
on!(MANAGER, update_files, &self.app.cx.tasks);
|
||||
on!(MANAGER, update_mimetype, &self.app.cx.tasks);
|
||||
on!(MANAGER, update_pages, &self.app.cx.tasks);
|
||||
on!(MANAGER, hover);
|
||||
on!(MANAGER, peek);
|
||||
on!(MANAGER, seek);
|
||||
on!(MANAGER, refresh);
|
||||
on!(MANAGER, refresh, &self.app.cx.tasks);
|
||||
on!(MANAGER, quit, &self.app.cx.tasks);
|
||||
on!(MANAGER, close, &self.app.cx.tasks);
|
||||
on!(MANAGER, suspend);
|
||||
|
@ -14,9 +14,6 @@ pub enum Event {
|
||||
Resize(u16, u16),
|
||||
Paste(String),
|
||||
Quit(bool), // no-cwd-file
|
||||
|
||||
// Manager
|
||||
Pages(usize),
|
||||
}
|
||||
|
||||
impl Event {
|
||||
@ -41,10 +38,6 @@ macro_rules! emit {
|
||||
$crate::event::Event::Call($exec, $layer).emit();
|
||||
};
|
||||
|
||||
(Pages($page:expr)) => {
|
||||
$crate::event::Event::Pages($page).emit();
|
||||
};
|
||||
|
||||
($event:ident) => {
|
||||
$crate::event::Event::$event.emit();
|
||||
};
|
||||
|
@ -49,9 +49,4 @@ impl Term {
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_bar() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBar)?) }
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_default() -> Result<()> {
|
||||
Ok(execute!(stdout(), SetCursorStyle::DefaultUserShape)?)
|
||||
}
|
||||
}
|
||||
|
@ -37,9 +37,14 @@ impl Term {
|
||||
execute!(stdout(), PopKeyboardEnhancementFlags)?;
|
||||
}
|
||||
|
||||
execute!(stdout(), DisableFocusChange, DisableBracketedPaste, LeaveAlternateScreen)?;
|
||||
execute!(
|
||||
stdout(),
|
||||
DisableFocusChange,
|
||||
DisableBracketedPaste,
|
||||
LeaveAlternateScreen,
|
||||
crossterm::cursor::SetCursorStyle::DefaultUserShape
|
||||
)?;
|
||||
|
||||
Self::set_cursor_default()?;
|
||||
self.show_cursor()?;
|
||||
Ok(disable_raw_mode()?)
|
||||
}
|
||||
@ -55,6 +60,7 @@ impl Term {
|
||||
crossterm::cursor::Show,
|
||||
)
|
||||
.ok();
|
||||
|
||||
disable_raw_mode().ok();
|
||||
std::process::exit(f() as i32);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user