mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
a7e8567e81
commit
0fa8df100d
12 changed files with 184 additions and 149 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -1294,9 +1294,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.12"
|
version = "0.2.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05"
|
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "pin-utils"
|
||||||
|
|
@ -1527,18 +1527,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.186"
|
version = "1.0.188"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f5db24220c009de9bd45e69fb2938f4b6d2df856aa9304ce377b3180f83b7c1"
|
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.186"
|
version = "1.0.188"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670"
|
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,8 @@ impl App {
|
||||||
|
|
||||||
fn dispatch_resize(&mut self) {
|
fn dispatch_resize(&mut self) {
|
||||||
self.cx.manager.current_mut().set_page(true);
|
self.cx.manager.current_mut().set_page(true);
|
||||||
self.cx.manager.active_mut().preview_reset_image();
|
self.cx.manager.active_mut().preview_reset();
|
||||||
// TODO: use peek
|
self.cx.manager.peek(true, self.cx.image_layer());
|
||||||
self.cx.manager.preview(self.cx.image_layer());
|
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,8 +100,6 @@ impl App {
|
||||||
} else {
|
} else {
|
||||||
self.term = Some(Term::start().unwrap());
|
self.term = Some(Term::start().unwrap());
|
||||||
self.signals.stop_term(false);
|
self.signals.stop_term(false);
|
||||||
// TODO: use peek
|
|
||||||
self.cx.manager.preview(self.cx.image_layer());
|
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
emit!(Hover);
|
emit!(Hover);
|
||||||
}
|
}
|
||||||
|
|
@ -154,21 +151,24 @@ impl App {
|
||||||
Event::Mimetype(mimes) => {
|
Event::Mimetype(mimes) => {
|
||||||
if manager.update_mimetype(mimes, tasks) {
|
if manager.update_mimetype(mimes, tasks) {
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
self.cx.manager.preview(self.cx.image_layer());
|
emit!(Peek);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Hover(file) => {
|
Event::Hover(file) => {
|
||||||
if file.map(|f| manager.current_mut().hover_force(f)).unwrap_or(false) {
|
if manager.update_hover(file) {
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
self.cx.manager.preview(self.cx.image_layer());
|
emit!(Peek);
|
||||||
}
|
}
|
||||||
Event::Preview(path, mime, data) => {
|
Event::Peek(skip, sequent) => {
|
||||||
manager.update_preview(path, mime, data);
|
let b = sequent.is_some();
|
||||||
|
manager.active_mut().update_peek(skip as isize, sequent);
|
||||||
|
self.cx.manager.peek(b, self.cx.image_layer());
|
||||||
|
}
|
||||||
|
Event::Preview(lock) => {
|
||||||
|
if manager.active_mut().update_preview(lock) {
|
||||||
emit!(Render);
|
emit!(Render);
|
||||||
}
|
}
|
||||||
Event::Peek(path, skip) => {
|
|
||||||
manager.update_peek(path, skip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::Select(opt, tx) => {
|
Event::Select(opt, tx) => {
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,8 @@ impl Executor {
|
||||||
}
|
}
|
||||||
"peek" => {
|
"peek" => {
|
||||||
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0);
|
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0);
|
||||||
cx.manager.active_mut().peek(step)
|
cx.manager.active_mut().update_peek(step, None);
|
||||||
|
cx.manager.peek(true, cx.image_layer())
|
||||||
}
|
}
|
||||||
"leave" => cx.manager.active_mut().leave(),
|
"leave" => cx.manager.active_mut().leave(),
|
||||||
"enter" => cx.manager.active_mut().enter(),
|
"enter" => cx.manager.active_mut().enter(),
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,7 @@ impl<'a> Widget for Preview<'a> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match &preview.data {
|
match &preview.lock.as_ref().unwrap().data {
|
||||||
PreviewData::None => {}
|
|
||||||
PreviewData::Folder => {
|
PreviewData::Folder => {
|
||||||
if let Some(folder) = manager.active().history(hovered) {
|
if let Some(folder) = manager.active().history(hovered) {
|
||||||
Folder::new(self.cx, folder).with_preview(true).render(area, buf);
|
Folder::new(self.cx, folder).with_preview(true).render(area, buf);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ use crossterm::event::KeyEvent;
|
||||||
use shared::RoCell;
|
use shared::RoCell;
|
||||||
use tokio::sync::{mpsc::UnboundedSender, oneshot};
|
use tokio::sync::{mpsc::UnboundedSender, oneshot};
|
||||||
|
|
||||||
use super::{files::{File, FilesOp}, input::InputOpt, manager::PreviewData, select::SelectOpt};
|
use super::{files::{File, FilesOp}, input::InputOpt, select::SelectOpt};
|
||||||
|
use crate::manager::PreviewLock;
|
||||||
|
|
||||||
static TX: RoCell<UnboundedSender<Event>> = RoCell::new();
|
static TX: RoCell<UnboundedSender<Event>> = RoCell::new();
|
||||||
|
|
||||||
|
|
@ -26,8 +27,8 @@ pub enum Event {
|
||||||
Pages(usize),
|
Pages(usize),
|
||||||
Mimetype(BTreeMap<PathBuf, String>),
|
Mimetype(BTreeMap<PathBuf, String>),
|
||||||
Hover(Option<File>),
|
Hover(Option<File>),
|
||||||
Preview(PathBuf, String, PreviewData),
|
Peek(usize, Option<PathBuf>),
|
||||||
Peek(PathBuf, usize),
|
Preview(PreviewLock),
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
Select(SelectOpt, oneshot::Sender<Result<usize>>),
|
Select(SelectOpt, oneshot::Sender<Result<usize>>),
|
||||||
|
|
@ -88,11 +89,14 @@ macro_rules! emit {
|
||||||
(Hover($file:expr)) => {
|
(Hover($file:expr)) => {
|
||||||
$crate::Event::Hover(Some($file)).emit();
|
$crate::Event::Hover(Some($file)).emit();
|
||||||
};
|
};
|
||||||
(Preview($path:expr, $mime:expr, $data:expr)) => {
|
(Peek) => {
|
||||||
$crate::Event::Preview($path, $mime, $data).emit();
|
$crate::Event::Peek(0, None).emit();
|
||||||
};
|
};
|
||||||
(Peek($path:expr, $skip:expr)) => {
|
(Peek($skip:expr, $path:expr)) => {
|
||||||
$crate::Event::Peek($path, $skip).emit();
|
$crate::Event::Peek($skip, Some($path)).emit();
|
||||||
|
};
|
||||||
|
(Preview($lock:expr)) => {
|
||||||
|
$crate::Event::Preview($lock).emit();
|
||||||
};
|
};
|
||||||
|
|
||||||
(Select($opt:expr)) => {{
|
(Select($opt:expr)) => {{
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use config::{BOOT, OPEN};
|
||||||
use shared::{max_common_root, Defer, Term, MIME_DIR};
|
use shared::{max_common_root, Defer, Term, MIME_DIR};
|
||||||
use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}};
|
use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}};
|
||||||
|
|
||||||
use super::{PreviewData, Tab, Tabs, Watcher};
|
use super::{Tab, Tabs, Watcher};
|
||||||
use crate::{emit, external::{self, ShellOpt}, files::{File, FilesOp}, input::InputOpt, manager::Folder, select::SelectOpt, tasks::Tasks, Event, BLOCKER};
|
use crate::{emit, external::{self, ShellOpt}, files::{File, FilesOp}, input::InputOpt, manager::Folder, select::SelectOpt, tasks::Tasks, Event, BLOCKER};
|
||||||
|
|
||||||
pub struct Manager {
|
pub struct Manager {
|
||||||
|
|
@ -52,7 +52,7 @@ impl Manager {
|
||||||
self.watcher.watch(to_watch);
|
self.watcher.watch(to_watch);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preview(&mut self, show_image: bool) -> bool {
|
pub fn peek(&mut self, sequent: bool, show_image: bool) -> bool {
|
||||||
let Some(hovered) = self.hovered().cloned() else {
|
let Some(hovered) = self.hovered().cloned() else {
|
||||||
return self.active_mut().preview.reset();
|
return self.active_mut().preview.reset();
|
||||||
};
|
};
|
||||||
|
|
@ -61,19 +61,23 @@ impl Manager {
|
||||||
self.active_mut().preview_reset_image();
|
self.active_mut().preview_reset_image();
|
||||||
}
|
}
|
||||||
|
|
||||||
if hovered.meta.is_dir() {
|
let mime = if hovered.meta.is_dir() {
|
||||||
self.active_mut().preview.go(&hovered.path, MIME_DIR, show_image);
|
MIME_DIR.to_owned()
|
||||||
if let Some(offset) = self.active().history(&hovered.path).map(|f| f.offset()) {
|
} else if let Some(m) = self.mimetype.get(&hovered.path).cloned() {
|
||||||
emit!(Peek(hovered.path, offset));
|
m
|
||||||
}
|
|
||||||
} else if let Some(mime) = self.mimetype.get(&hovered.path).cloned() {
|
|
||||||
self.active_mut().preview.go(&hovered.path, &mime, show_image);
|
|
||||||
} else {
|
} else {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Ok(mimes) = external::file(&[hovered.path]).await {
|
if let Ok(mimes) = external::file(&[hovered.path]).await {
|
||||||
emit!(Mimetype(mimes));
|
emit!(Mimetype(mimes));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if sequent {
|
||||||
|
self.active_mut().preview.sequent(&hovered.path, &mime, show_image);
|
||||||
|
} else {
|
||||||
|
self.active_mut().preview.go(&hovered.path, &mime, show_image);
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
@ -393,39 +397,16 @@ impl Manager {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_preview(&mut self, path: PathBuf, mime: String, data: PreviewData) -> bool {
|
pub fn update_hover(&mut self, file: Option<File>) -> bool {
|
||||||
let Some(hovered) = self.hovered().map(|h| &h.path) else {
|
let b = file.map(|f| self.current_mut().hover_force(f)).unwrap_or(false);
|
||||||
return self.active_mut().preview.reset();
|
let Some(hovered) = self.hovered() else {
|
||||||
|
return b;
|
||||||
};
|
};
|
||||||
|
|
||||||
if path != *hovered {
|
if hovered.meta.is_dir() {
|
||||||
return false;
|
self.watcher.trigger_dirs(&[&hovered.path]);
|
||||||
}
|
}
|
||||||
|
b
|
||||||
let preview = &mut self.active_mut().preview;
|
|
||||||
preview.lock = Some((path, mime));
|
|
||||||
preview.data = data;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_peek(&mut self, path: PathBuf, skip: usize) -> bool {
|
|
||||||
if !matches!(self.hovered(), Some(f) if f.path == path) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.active().preview.lock.is_some() {
|
|
||||||
self.active_mut().preview.peek(skip);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.active().history(&path).is_some() {
|
|
||||||
let preview = &mut self.active_mut().preview;
|
|
||||||
preview.lock = Some((path, MIME_DIR.to_string()));
|
|
||||||
preview.data = PreviewData::Folder;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.active_mut().preview.peek(skip);
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,30 @@
|
||||||
use std::{mem, path::{Path, PathBuf}, sync::atomic::Ordering};
|
use std::{path::{Path, PathBuf}, sync::atomic::Ordering};
|
||||||
|
|
||||||
use adaptor::Adaptor;
|
use adaptor::Adaptor;
|
||||||
use config::MANAGER;
|
use config::MANAGER;
|
||||||
use shared::{MimeKind, PeekError};
|
use shared::{MimeKind, PeekError};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
use super::{provider::INCR, Provider};
|
use super::{Provider, INCR};
|
||||||
use crate::emit;
|
use crate::emit;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Preview {
|
pub struct Preview {
|
||||||
pub lock: Option<(PathBuf, String)>,
|
pub lock: Option<PreviewLock>,
|
||||||
pub data: PreviewData,
|
|
||||||
skip: usize,
|
skip: usize,
|
||||||
|
|
||||||
handle: Option<JoinHandle<()>>,
|
handle: Option<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq)]
|
pub struct PreviewLock {
|
||||||
|
pub path: PathBuf,
|
||||||
|
pub mime: String,
|
||||||
|
pub skip: usize,
|
||||||
|
pub data: PreviewData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum PreviewData {
|
pub enum PreviewData {
|
||||||
#[default]
|
|
||||||
None,
|
|
||||||
Folder,
|
Folder,
|
||||||
Text(String),
|
Text(String),
|
||||||
Image,
|
Image,
|
||||||
|
|
@ -29,50 +33,67 @@ pub enum PreviewData {
|
||||||
impl Preview {
|
impl Preview {
|
||||||
pub fn go(&mut self, path: &Path, mime: &str, show_image: bool) {
|
pub fn go(&mut self, path: &Path, mime: &str, show_image: bool) {
|
||||||
let kind = MimeKind::new(mime);
|
let kind = MimeKind::new(mime);
|
||||||
if !show_image && matches!(kind, MimeKind::Image | MimeKind::Video) {
|
if !show_image && kind.show_as_image() {
|
||||||
return;
|
return;
|
||||||
} else if self.same(path, mime) {
|
} else if self.same(path, mime) {
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
|
|
||||||
self.reset();
|
self.reset();
|
||||||
|
if !self.same_path(path) {
|
||||||
|
self.skip = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (path, mime, skip) = (path.to_path_buf(), mime.to_owned(), self.skip);
|
let (path, mime, skip) = (path.to_path_buf(), mime.to_owned(), self.skip);
|
||||||
self.handle = Some(tokio::spawn(async move {
|
self.handle = Some(tokio::spawn(async move {
|
||||||
let result = Provider::auto(kind, &path, skip).await;
|
match Provider::auto(kind, &path, skip).await {
|
||||||
emit!(Preview(path, mime, result.unwrap_or_default()));
|
Ok(data) => {
|
||||||
|
emit!(Preview(PreviewLock { path, mime, skip, data }));
|
||||||
|
}
|
||||||
|
Err(PeekError::Exceed(max)) => {
|
||||||
|
emit!(Peek(max, path));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek(&mut self, skip: usize) {
|
pub fn sequent(&mut self, path: &Path, mime: &str, show_image: bool) {
|
||||||
let Some((path, kind, mime)) = self.lock.clone().map(|(p, m)| (p, MimeKind::new(&m), m)) else {
|
let kind = MimeKind::new(mime);
|
||||||
|
if !show_image && kind.show_as_image() {
|
||||||
return;
|
return;
|
||||||
};
|
} else if self.same(path, mime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.handle.take().map(|h| h.abort());
|
self.handle.take().map(|h| h.abort());
|
||||||
INCR.fetch_add(1, Ordering::Relaxed);
|
INCR.fetch_add(1, Ordering::Relaxed);
|
||||||
|
|
||||||
self.skip = skip;
|
let (path, mime, skip) = (path.to_path_buf(), mime.to_owned(), self.skip);
|
||||||
self.handle = Some(tokio::spawn(async move {
|
self.handle = Some(tokio::spawn(async move {
|
||||||
match Provider::auto(kind, &path, skip).await {
|
match Provider::auto(kind, &path, skip).await {
|
||||||
Ok(result) => {
|
Ok(data) => {
|
||||||
emit!(Preview(path, mime, result));
|
emit!(Preview(PreviewLock { path, mime, skip, data }));
|
||||||
}
|
}
|
||||||
Err(PeekError::Exceed(max)) => {
|
Err(PeekError::Exceed(max)) => {
|
||||||
emit!(Peek(path, max));
|
emit!(Peek(max, path));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn peek_step(&mut self, step: isize) -> usize {
|
pub fn arrow(&mut self, step: isize, absolute: bool) -> bool {
|
||||||
let Some(kind) = self.lock.clone().map(|(_, m)| MimeKind::new(&m)) else {
|
let old = self.skip;
|
||||||
return 0;
|
if absolute {
|
||||||
};
|
self.skip = step.unsigned_abs();
|
||||||
|
} else if let Some(kind) = self.lock.as_ref().map(|l| MimeKind::new(&l.mime)) {
|
||||||
|
let size = Provider::step_size(kind, step.unsigned_abs());
|
||||||
|
self.skip = if step < 0 { old.saturating_sub(size) } else { old + size };
|
||||||
|
}
|
||||||
|
|
||||||
Provider::step_size(kind, step.unsigned_abs())
|
self.skip != old
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self) -> bool {
|
pub fn reset(&mut self) -> bool {
|
||||||
|
|
@ -80,12 +101,9 @@ impl Preview {
|
||||||
INCR.fetch_add(1, Ordering::Relaxed);
|
INCR.fetch_add(1, Ordering::Relaxed);
|
||||||
Adaptor::image_hide(MANAGER.layout.preview_rect()).ok();
|
Adaptor::image_hide(MANAGER.layout.preview_rect()).ok();
|
||||||
|
|
||||||
|
let b = matches!(&self.lock, Some(l) if !l.is_image());
|
||||||
self.lock = None;
|
self.lock = None;
|
||||||
self.skip = 0;
|
b
|
||||||
!matches!(
|
|
||||||
mem::replace(&mut self.data, PreviewData::None),
|
|
||||||
PreviewData::None | PreviewData::Image
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_image(&mut self) -> bool {
|
pub fn reset_image(&mut self) -> bool {
|
||||||
|
|
@ -93,26 +111,41 @@ impl Preview {
|
||||||
INCR.fetch_add(1, Ordering::Relaxed);
|
INCR.fetch_add(1, Ordering::Relaxed);
|
||||||
Adaptor::image_hide(MANAGER.layout.preview_rect()).ok();
|
Adaptor::image_hide(MANAGER.layout.preview_rect()).ok();
|
||||||
|
|
||||||
if self.data == PreviewData::Image {
|
if matches!(&self.lock, Some(l) if l.is_image()) {
|
||||||
self.lock = None;
|
self.lock = None;
|
||||||
self.data = PreviewData::None;
|
|
||||||
self.skip = 0;
|
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Preview {
|
impl Preview {
|
||||||
|
#[inline]
|
||||||
|
pub fn lock(&self) -> &Option<PreviewLock> { &self.lock }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn skip(&self) -> usize { self.skip }
|
pub fn skip(&self) -> usize { self.skip }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn same(&self, path: &Path, mime: &str) -> bool {
|
pub fn same(&self, path: &Path, mime: &str) -> bool {
|
||||||
self.lock.as_ref().map(|(p, m)| p == path && m == mime).unwrap_or(false)
|
if let Some(ref lock) = self.lock {
|
||||||
|
return lock.path == path && lock.mime == mime && lock.skip == self.skip;
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn same_path(&self, path: &Path) -> bool {
|
pub fn same_path(&self, path: &Path) -> bool {
|
||||||
self.lock.as_ref().map(|(p, _)| p == path).unwrap_or(false)
|
if let Some(ref lock) = self.lock {
|
||||||
|
return lock.path == path;
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PreviewLock {
|
||||||
|
#[inline]
|
||||||
|
pub fn is_image(&self) -> bool { matches!(self.data, PreviewData::Image) }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_folder(&self) -> bool { matches!(self.data, PreviewData::Folder) }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ impl Provider {
|
||||||
skip: usize,
|
skip: usize,
|
||||||
) -> Result<PreviewData, PeekError> {
|
) -> Result<PreviewData, PeekError> {
|
||||||
match kind {
|
match kind {
|
||||||
MimeKind::Empty => Ok(PreviewData::None),
|
MimeKind::Empty => Err("Empty file".into()),
|
||||||
MimeKind::Archive => Provider::archive(path, skip).await.map(PreviewData::Text),
|
MimeKind::Archive => Provider::archive(path, skip).await.map(PreviewData::Text),
|
||||||
MimeKind::Dir => Provider::folder(path).await,
|
MimeKind::Dir => Provider::folder(path).await,
|
||||||
MimeKind::Image => Provider::image(path).await,
|
MimeKind::Image => Provider::image(path).await,
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ use std::{collections::{BTreeMap, BTreeSet}, ffi::{OsStr, OsString}, mem, path::
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
use config::{open::Opener, MANAGER};
|
use config::{open::Opener, MANAGER};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use shared::{Defer, MIME_DIR};
|
use shared::Defer;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
use super::{Folder, Mode, Preview};
|
use super::{Folder, Mode, Preview, PreviewLock};
|
||||||
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, Files, FilesOp}, input::InputOpt, Event, BLOCKER};
|
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, Files, FilesOp}, input::InputOpt, Event, BLOCKER};
|
||||||
|
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
|
|
@ -76,32 +76,6 @@ impl Tab {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek(&mut self, step: isize) -> bool {
|
|
||||||
let size = self.preview.peek_step(step);
|
|
||||||
let old = self.preview.skip();
|
|
||||||
let new = if step < 0 { old.saturating_sub(size) } else { old + size };
|
|
||||||
if new == old {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !matches!(&self.preview.lock, Some((_, m)) if m == MIME_DIR) {
|
|
||||||
self.preview.peek(new);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
let path = self.preview.lock.as_ref().unwrap().0.clone();
|
|
||||||
if let Some(folder) = self.history(&path) {
|
|
||||||
let max = folder.files.len().saturating_sub(MANAGER.layout.preview_height());
|
|
||||||
if new > max {
|
|
||||||
emit!(Peek(path, max));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.preview.peek(new);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn cd(&mut self, mut target: PathBuf) -> bool {
|
pub async fn cd(&mut self, mut target: PathBuf) -> bool {
|
||||||
let Ok(file) = File::from(&target).await else {
|
let Ok(file) = File::from(&target).await else {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -335,6 +309,42 @@ impl Tab {
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_peek(&mut self, step: isize, path: Option<PathBuf>) {
|
||||||
|
let Some(ref hovered) = self.current.hovered else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if path.as_ref().map(|p| *p != hovered.path).unwrap_or(false) {
|
||||||
|
return;
|
||||||
|
} else if !self.preview.lock.as_ref().map(|l| l.path == hovered.path).unwrap_or(false) {
|
||||||
|
return;
|
||||||
|
} else if !self.preview.arrow(step, path.is_some()) {
|
||||||
|
return;
|
||||||
|
} else if !hovered.meta.is_dir() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(folder) = self.history(&hovered.path) {
|
||||||
|
let max = folder.files.len().saturating_sub(MANAGER.layout.preview_height());
|
||||||
|
if self.preview.skip() > max {
|
||||||
|
self.preview.arrow(max as isize, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_preview(&mut self, lock: PreviewLock) -> bool {
|
||||||
|
let Some(hovered) = self.current.hovered.as_ref().map(|h| &h.path) else {
|
||||||
|
return self.preview.reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
if lock.path != *hovered {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.preview.lock = Some(lock);
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
|
|
@ -388,6 +398,9 @@ impl Tab {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn preview(&self) -> &Preview { &self.preview }
|
pub fn preview(&self) -> &Preview { &self.preview }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn preview_reset(&mut self) -> bool { self.preview.reset() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn preview_reset_image(&mut self) -> bool { self.preview.reset_image() }
|
pub fn preview_reset_image(&mut self) -> bool { self.preview.reset_image() }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ impl Tasks {
|
||||||
|
|
||||||
pub fn toggle(&mut self) -> bool {
|
pub fn toggle(&mut self) -> bool {
|
||||||
self.visible = !self.visible;
|
self.visible = !self.visible;
|
||||||
emit!(Hover); // Show/hide preview for images
|
emit!(Peek); // Show/hide preview for images
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,6 @@ impl Which {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn switch(&mut self, state: bool) {
|
fn switch(&mut self, state: bool) {
|
||||||
self.visible = state;
|
self.visible = state;
|
||||||
emit!(Hover); // Show/hide preview for images
|
emit!(Peek); // Show/hide preview for images
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,21 +18,6 @@ pub enum MimeKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MimeKind {
|
impl MimeKind {
|
||||||
pub fn valid(s: &str) -> bool {
|
|
||||||
if s == "inode/x-empty" {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let parts = s.split('/').collect::<Vec<_>>();
|
|
||||||
if parts.len() != 2 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
let b = matches!(parts[0], "application" | "audio" | "example" | "font" | "image" | "message" | "model" | "multipart" | "text" | "video");
|
|
||||||
b && !parts[1].is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(s: &str) -> Self {
|
pub fn new(s: &str) -> Self {
|
||||||
if s == MIME_DIR {
|
if s == MIME_DIR {
|
||||||
Self::Dir
|
Self::Dir
|
||||||
|
|
@ -61,4 +46,23 @@ impl MimeKind {
|
||||||
Self::Others
|
Self::Others
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn valid(s: &str) -> bool {
|
||||||
|
if s == "inode/x-empty" {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parts = s.split('/').collect::<Vec<_>>();
|
||||||
|
if parts.len() != 2 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let b = matches!(parts[0], "application" | "audio" | "example" | "font" | "image" | "message" | "model" | "multipart" | "text" | "video");
|
||||||
|
b && !parts[1].is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_as_image(&self) -> bool {
|
||||||
|
matches!(self, MimeKind::Image | MimeKind::Video | MimeKind::PDF)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue