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