mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
ff369f4d15
commit
1270fd15f9
9 changed files with 108 additions and 52 deletions
|
|
@ -3,32 +3,33 @@ use yazi_shared::{Url, MIME_DIR};
|
|||
|
||||
use crate::{emit, manager::Manager};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Opt {
|
||||
step: isize,
|
||||
sequent: Option<Url>,
|
||||
upper_bound: Option<usize>,
|
||||
only_if: Option<Url>,
|
||||
upper_bound: bool,
|
||||
}
|
||||
|
||||
impl From<&Exec> for Opt {
|
||||
fn from(e: &Exec) -> Self {
|
||||
Self {
|
||||
step: e.args.first().and_then(|s| s.parse().ok()).unwrap_or(0),
|
||||
sequent: e.named.get("sequent").map(Url::from),
|
||||
upper_bound: e.named.get("upper-bound").and_then(|s| s.parse().ok()),
|
||||
only_if: e.named.get("only-if").map(Url::from),
|
||||
upper_bound: e.named.contains_key("upper-bound"),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<isize> for Opt {
|
||||
fn from(step: isize) -> Self { Self { step, sequent: None, upper_bound: None } }
|
||||
fn from(step: isize) -> Self { Self { step, only_if: None, upper_bound: false } }
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
#[inline]
|
||||
pub fn _peek_upper_bound(bound: usize, sequent: &Url) {
|
||||
pub fn _peek_upper_bound(bound: usize, only_if: &Url) {
|
||||
emit!(Call(
|
||||
Exec::call("peek", vec![])
|
||||
.with("sequent", sequent.to_string())
|
||||
.with("upper-bound", bound.to_string())
|
||||
Exec::call("peek", vec![bound.to_string()])
|
||||
.with("only-if", only_if.to_string())
|
||||
.with_bool("upper-bound", true)
|
||||
.vec(),
|
||||
KeymapLayer::Manager
|
||||
));
|
||||
|
|
@ -36,11 +37,11 @@ impl Manager {
|
|||
|
||||
pub fn peek(&mut self, opt: impl Into<Opt>) -> bool {
|
||||
let Some(hovered) = self.hovered() else {
|
||||
return self.active_mut().preview.reset(|_| true);
|
||||
return self.active_mut().preview.reset();
|
||||
};
|
||||
|
||||
let opt = opt.into() as Opt;
|
||||
if matches!(opt.sequent, Some(ref u) if *u != hovered.url) {
|
||||
if matches!(opt.only_if, Some(ref u) if *u != hovered.url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -49,35 +50,37 @@ impl Manager {
|
|||
}
|
||||
|
||||
let Some(mime) = self.mimetype.get(&hovered.url).cloned() else {
|
||||
return self.active_mut().preview.reset(|_| true);
|
||||
return self.active_mut().preview.reset();
|
||||
};
|
||||
|
||||
let url = hovered.url.clone();
|
||||
self.active_mut().preview.arrow(opt.step, &mime);
|
||||
if let Some(bound) = opt.upper_bound {
|
||||
self.active_mut().preview.apply_bound(bound);
|
||||
let (url, cha) = (hovered.url.clone(), hovered.cha);
|
||||
if self.active().preview.same_url(&url) {
|
||||
self.active_mut().preview.arrow(opt.step, &mime);
|
||||
} else if opt.upper_bound {
|
||||
self.active_mut().preview.apply_bound(opt.step as usize);
|
||||
} else {
|
||||
self.active_mut().preview.set_skip(0);
|
||||
}
|
||||
|
||||
self.active_mut().preview.go(&url, &mime);
|
||||
self.active_mut().preview.go(&url, cha, &mime);
|
||||
false
|
||||
}
|
||||
|
||||
fn peek_folder(&mut self, opt: Opt, url: Url) -> bool {
|
||||
let (skip, bound) = self
|
||||
.active()
|
||||
.history
|
||||
.get(&url)
|
||||
let folder = self.active().history.get(&url);
|
||||
let (skip, bound) = folder
|
||||
.map(|f| (f.offset, f.files.len().saturating_sub(MANAGER.layout.folder_height())))
|
||||
.unwrap_or_default();
|
||||
|
||||
if opt.sequent.is_some() {
|
||||
if self.active().preview.same_url(&url) {
|
||||
self.active_mut().preview.arrow(opt.step, MIME_DIR);
|
||||
} else {
|
||||
self.active_mut().preview.set_skip(skip);
|
||||
self.active_mut().preview.apply_bound(bound);
|
||||
return false;
|
||||
}
|
||||
|
||||
self.active_mut().preview.apply_bound(bound);
|
||||
self.active_mut().preview.go_folder(url, opt.sequent.is_none());
|
||||
let in_chunks = folder.is_none();
|
||||
self.active_mut().preview.set_skip(skip);
|
||||
self.active_mut().preview.go_folder(url, in_chunks);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ impl Tabs {
|
|||
#[inline]
|
||||
pub(super) fn set_idx(&mut self, idx: usize) {
|
||||
self.idx = idx;
|
||||
self.active_mut().preview.reset(|l| l.is_image());
|
||||
self.active_mut().preview.reset_image();
|
||||
Manager::_refresh();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,3 +3,5 @@ mod provider;
|
|||
|
||||
pub use preview::*;
|
||||
use provider::*;
|
||||
|
||||
pub static COLLISION: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use tokio::{pin, task::JoinHandle};
|
|||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
||||
use yazi_adaptor::ADAPTOR;
|
||||
use yazi_config::MANAGER;
|
||||
use yazi_shared::{MimeKind, PeekError, Url};
|
||||
use yazi_shared::{Cha, MimeKind, PeekError, Url};
|
||||
|
||||
use super::Provider;
|
||||
use crate::{emit, files::{Files, FilesOp}, manager::Manager, Highlighter};
|
||||
|
|
@ -19,6 +19,7 @@ pub struct Preview {
|
|||
|
||||
pub struct PreviewLock {
|
||||
pub url: Url,
|
||||
pub cha: Option<Cha>,
|
||||
pub skip: usize,
|
||||
pub data: PreviewData,
|
||||
}
|
||||
|
|
@ -31,14 +32,18 @@ pub enum PreviewData {
|
|||
}
|
||||
|
||||
impl Preview {
|
||||
pub fn go(&mut self, url: &Url, mime: &str) {
|
||||
self.reset(|_| true);
|
||||
let (url, skip, kind) = (url.clone(), self.skip, MimeKind::new(mime));
|
||||
pub fn go(&mut self, url: &Url, cha: Cha, mime: &str) {
|
||||
if self.content_unchanged(url, &cha) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.reset();
|
||||
let (url, kind, skip) = (url.clone(), MimeKind::new(mime), self.skip);
|
||||
|
||||
self.handle = Some(tokio::spawn(async move {
|
||||
match Provider::auto(kind, &url, skip).await {
|
||||
Ok(data) => {
|
||||
emit!(Preview(PreviewLock { url, skip, data }));
|
||||
emit!(Preview(PreviewLock { url, cha: Some(cha), skip, data }));
|
||||
}
|
||||
Err(PeekError::Exceed(max)) => {
|
||||
Manager::_peek_upper_bound(max, &url);
|
||||
|
|
@ -49,8 +54,13 @@ impl Preview {
|
|||
}
|
||||
|
||||
pub fn go_folder(&mut self, url: Url, in_chunks: bool) {
|
||||
self.reset(|_| true);
|
||||
emit!(Preview(PreviewLock { url: url.clone(), skip: self.skip, data: PreviewData::Folder }));
|
||||
self.reset();
|
||||
self.lock = Some(PreviewLock {
|
||||
url: url.clone(),
|
||||
cha: None,
|
||||
skip: self.skip,
|
||||
data: PreviewData::Folder,
|
||||
});
|
||||
|
||||
self.handle = Some(tokio::spawn(async move {
|
||||
let Ok(rx) = Files::from_dir(&url).await else {
|
||||
|
|
@ -74,7 +84,7 @@ impl Preview {
|
|||
}));
|
||||
}
|
||||
|
||||
pub fn reset<F: FnOnce(&PreviewLock) -> bool>(&mut self, f: F) -> bool {
|
||||
pub fn reset(&mut self) -> bool {
|
||||
self.handle.take().map(|h| h.abort());
|
||||
Highlighter::abort();
|
||||
ADAPTOR.image_hide(MANAGER.layout.image_rect()).ok();
|
||||
|
|
@ -83,14 +93,49 @@ impl Preview {
|
|||
return false;
|
||||
};
|
||||
|
||||
if !f(lock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let b = !lock.is_image();
|
||||
self.lock = None;
|
||||
b
|
||||
}
|
||||
|
||||
pub fn reset_image(&mut self) -> bool {
|
||||
if !matches!(self.lock, Some(ref lock) if lock.is_image()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.reset();
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn same_url(&self, url: &Url) -> bool {
|
||||
matches!(self.lock, Some(ref lock) if lock.url == *url)
|
||||
}
|
||||
|
||||
fn content_unchanged(&self, url: &Url, cha: &Cha) -> bool {
|
||||
let Some(lock) = &self.lock else {
|
||||
return false;
|
||||
};
|
||||
let Some(cha_) = &lock.cha else {
|
||||
return false;
|
||||
};
|
||||
|
||||
*url == lock.url
|
||||
&& self.skip == lock.skip
|
||||
&& cha.len == cha_.len
|
||||
&& cha.modified == cha_.modified
|
||||
&& cha.meta == cha_.meta
|
||||
&& {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
cha.permissions == cha_.permissions
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Preview {
|
||||
|
|
@ -106,12 +151,12 @@ impl Preview {
|
|||
pub fn set_skip(&mut self, skip: usize) -> bool { mem::replace(&mut self.skip, skip) != skip }
|
||||
|
||||
#[inline]
|
||||
pub fn apply_bound(&mut self, max: usize) -> bool {
|
||||
if self.skip <= max {
|
||||
pub fn apply_bound(&mut self, upper: usize) -> bool {
|
||||
if self.skip <= upper {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.skip = max;
|
||||
self.skip = upper;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ impl Tab {
|
|||
handle.abort();
|
||||
}
|
||||
if self.current.cwd.is_search() {
|
||||
self.preview.reset(|l| l.is_image());
|
||||
self.preview.reset_image();
|
||||
|
||||
let rep = self.history_new(&self.current.cwd.to_regular());
|
||||
drop(mem::replace(&mut self.current, rep));
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ impl From<&Url> for Tab {
|
|||
impl Tab {
|
||||
pub fn update_preview(&mut self, lock: PreviewLock) -> bool {
|
||||
let Some(hovered) = self.current.hovered().map(|h| &h.url) else {
|
||||
return self.preview.reset(|_| true);
|
||||
return self.preview.reset();
|
||||
};
|
||||
|
||||
if lock.url != *hovered {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use std::ffi::OsString;
|
||||
use std::{ffi::OsString, sync::atomic::Ordering};
|
||||
|
||||
use anyhow::{Ok, Result};
|
||||
use crossterm::event::KeyEvent;
|
||||
use ratatui::{backend::Backend, prelude::Rect};
|
||||
use tokio::sync::oneshot;
|
||||
use yazi_config::{keymap::{Exec, Key, KeymapLayer}, BOOT};
|
||||
use yazi_core::{emit, files::FilesOp, input::InputMode, manager::Manager, Ctx, Event};
|
||||
use yazi_core::{emit, files::FilesOp, input::InputMode, manager::Manager, preview::COLLISION, Ctx, Event};
|
||||
use yazi_shared::Term;
|
||||
|
||||
use crate::{Executor, Logs, Panic, Root, Signals};
|
||||
|
|
@ -81,14 +81,17 @@ impl App {
|
|||
return Ok(());
|
||||
};
|
||||
|
||||
COLLISION.store(false, Ordering::Relaxed);
|
||||
let frame = term.draw(|f| {
|
||||
yazi_plugin::scope(&self.cx, |_| {
|
||||
f.render_widget(Root::new(&self.cx), f.size());
|
||||
});
|
||||
})?;
|
||||
if !COLLISION.load(Ordering::Relaxed) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut patches = Vec::new();
|
||||
// TODO: find a more efficient way to do this
|
||||
for x in frame.area.left()..frame.area.right() {
|
||||
for y in frame.area.top()..frame.area.bottom() {
|
||||
let cell = frame.buffer.get(x, y);
|
||||
|
|
@ -114,14 +117,13 @@ impl App {
|
|||
}
|
||||
|
||||
self.cx.manager.current_mut().set_page(true);
|
||||
self.cx.manager.active_mut().preview.reset(|_| true);
|
||||
// TODO: Peek-trigger
|
||||
// self.cx.manager.peek(true);
|
||||
self.cx.manager.active_mut().preview.reset();
|
||||
self.cx.manager.peek(0);
|
||||
emit!(Render);
|
||||
}
|
||||
|
||||
fn dispatch_stop(&mut self, state: bool, tx: Option<oneshot::Sender<()>>) {
|
||||
self.cx.manager.active_mut().preview.reset(|l| l.is_image());
|
||||
self.cx.manager.active_mut().preview.reset_image();
|
||||
if state {
|
||||
self.signals.stop_term(true);
|
||||
self.term = None;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
use std::sync::atomic::Ordering;
|
||||
|
||||
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
||||
use yazi_adaptor::ADAPTOR;
|
||||
use yazi_config::MANAGER;
|
||||
use yazi_core::preview::COLLISION;
|
||||
|
||||
pub(crate) struct Clear;
|
||||
|
||||
|
|
@ -30,6 +33,7 @@ impl Widget for Clear {
|
|||
};
|
||||
|
||||
ADAPTOR.image_hide(r).ok();
|
||||
COLLISION.store(true, Ordering::Relaxed);
|
||||
for x in r.left()..r.right() {
|
||||
for y in r.top()..r.bottom() {
|
||||
buf.get_mut(x, y).set_skip(true);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{fs::Metadata, time::SystemTime};
|
|||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ChaMeta: u8 {
|
||||
const DIR = 0b00000001;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue