mirror of
https://github.com/sxyazi/yazi.git
synced 2026-05-13 08:16:40 +00:00
fix: use complete rendering instead of partial rendering for no task progress report (#1876)
This commit is contained in:
parent
a510673123
commit
0baccdc9aa
45 changed files with 174 additions and 190 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
|
@ -133,12 +133,6 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
||||
|
||||
[[package]]
|
||||
name = "arg_enum_proc_macro"
|
||||
version = "0.3.4"
|
||||
|
|
@ -3177,7 +3171,6 @@ version = "0.3.3"
|
|||
dependencies = [
|
||||
"ansi-to-tui",
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"base64",
|
||||
"color_quant",
|
||||
"crossterm",
|
||||
|
|
@ -3244,7 +3237,6 @@ name = "yazi-config"
|
|||
version = "0.3.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"bitflags 2.6.0",
|
||||
"crossterm",
|
||||
"globset",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ strip = true
|
|||
[workspace.dependencies]
|
||||
ansi-to-tui = "7.0.0"
|
||||
anyhow = "1.0.91"
|
||||
arc-swap = "1.7.1"
|
||||
base64 = "0.22.1"
|
||||
bitflags = "2.6.0"
|
||||
clap = { version = "4.5.20", features = [ "derive" ] }
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ yazi-shared = { path = "../yazi-shared", version = "0.3.3" }
|
|||
# External dependencies
|
||||
ansi-to-tui = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
arc-swap = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
color_quant = "1.1.0"
|
||||
crossterm = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{env, fmt::Display, path::Path, sync::Arc};
|
||||
use std::{env, fmt::Display, path::Path};
|
||||
|
||||
use anyhow::Result;
|
||||
use ratatui::layout::Rect;
|
||||
|
|
@ -52,7 +52,7 @@ impl Adapter {
|
|||
}
|
||||
|
||||
pub fn image_hide(self) -> Result<()> {
|
||||
if let Some(area) = SHOWN.swap(None) { self.image_erase(*area) } else { Ok(()) }
|
||||
if let Some(area) = SHOWN.replace(None) { self.image_erase(area) } else { Ok(()) }
|
||||
}
|
||||
|
||||
pub fn image_erase(self, area: Rect) -> Result<()> {
|
||||
|
|
@ -67,10 +67,10 @@ impl Adapter {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn shown_load(self) -> Option<Rect> { SHOWN.load_full().map(|r| *r) }
|
||||
pub fn shown_load(self) -> Option<Rect> { SHOWN.get() }
|
||||
|
||||
#[inline]
|
||||
pub(super) fn shown_store(area: Rect) { SHOWN.store(Some(Arc::new(area))); }
|
||||
pub(super) fn shown_store(area: Rect) { SHOWN.set(Some(area)); }
|
||||
|
||||
pub(super) fn start(self) { Ueberzug::start(self); }
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ yazi_macro::mod_flat!(
|
|||
adapter chafa dimension emulator iip image kgp kgp_old mux sixel ueberzug
|
||||
);
|
||||
|
||||
use yazi_shared::{RoCell, env_exists, in_wsl};
|
||||
use yazi_shared::{RoCell, SyncCell, env_exists, in_wsl};
|
||||
pub static ADAPTOR: RoCell<Adapter> = RoCell::new();
|
||||
|
||||
// Tmux support
|
||||
|
|
@ -17,7 +17,7 @@ static CLOSE: RoCell<&'static str> = RoCell::new();
|
|||
pub static WSL: RoCell<bool> = RoCell::new();
|
||||
|
||||
// Image state
|
||||
static SHOWN: RoCell<arc_swap::ArcSwapOption<ratatui::layout::Rect>> = RoCell::new();
|
||||
static SHOWN: SyncCell<Option<ratatui::layout::Rect>> = SyncCell::new(None);
|
||||
|
||||
pub fn init() {
|
||||
// Tmux support
|
||||
|
|
@ -38,9 +38,6 @@ pub fn init() {
|
|||
// WSL support
|
||||
WSL.init(in_wsl());
|
||||
|
||||
// Image state
|
||||
SHOWN.with(<_>::default);
|
||||
|
||||
ADAPTOR.init(Adapter::matches());
|
||||
ADAPTOR.start();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ yazi-shared = { path = "../yazi-shared", version = "0.3.3" }
|
|||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
arc-swap = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
crossterm = { workspace = true }
|
||||
globset = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -2,6 +2,13 @@ use ratatui::layout::Rect;
|
|||
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct Layout {
|
||||
pub current: Rect,
|
||||
pub preview: Rect,
|
||||
pub current: Rect,
|
||||
pub preview: Rect,
|
||||
pub progress: Rect,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub const fn default() -> Self {
|
||||
Self { current: Rect::ZERO, preview: Rect::ZERO, progress: Rect::ZERO }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ yazi_macro::mod_flat!(layout pattern preset priority);
|
|||
|
||||
use std::str::FromStr;
|
||||
|
||||
use yazi_shared::{RoCell, Xdg};
|
||||
|
||||
pub static LAYOUT: RoCell<arc_swap::ArcSwap<Layout>> = RoCell::new();
|
||||
use yazi_shared::{RoCell, SyncCell, Xdg};
|
||||
|
||||
pub static KEYMAP: RoCell<keymap::Keymap> = RoCell::new();
|
||||
pub static LOG: RoCell<log::Log> = RoCell::new();
|
||||
|
|
@ -23,6 +21,8 @@ pub static CONFIRM: RoCell<popup::Confirm> = RoCell::new();
|
|||
pub static PICK: RoCell<popup::Pick> = RoCell::new();
|
||||
pub static WHICH: RoCell<which::Which> = RoCell::new();
|
||||
|
||||
pub static LAYOUT: SyncCell<Layout> = SyncCell::new(Layout::default());
|
||||
|
||||
pub fn init() -> anyhow::Result<()> {
|
||||
if let Err(e) = try_init(true) {
|
||||
eprintln!("{e}");
|
||||
|
|
@ -101,8 +101,6 @@ fn try_init(merge: bool) -> anyhow::Result<()> {
|
|||
let pick = <_>::from_str(&yazi_toml)?;
|
||||
let which = <_>::from_str(&yazi_toml)?;
|
||||
|
||||
LAYOUT.with(<_>::default);
|
||||
|
||||
KEYMAP.init(keymap);
|
||||
LOG.init(log);
|
||||
MANAGER.init(manager);
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ impl Tab {
|
|||
pub fn hovered_rect(&self) -> Option<Rect> {
|
||||
let y = self.current.files.position(self.hovered()?.urn())? - self.current.offset;
|
||||
|
||||
let mut rect = LAYOUT.load().current;
|
||||
let mut rect = LAYOUT.get().current;
|
||||
rect.y = rect.y.saturating_sub(1) + y as u16;
|
||||
rect.height = 1;
|
||||
Some(rect)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use serde::Serialize;
|
||||
use yazi_scheduler::Ongoing;
|
||||
|
||||
#[derive(Clone, Copy, Default, Eq, PartialEq, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
|
||||
pub struct TasksProgress {
|
||||
pub total: u32,
|
||||
pub succ: u32,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use mlua::Value;
|
||||
use ratatui::layout::Position;
|
||||
use tracing::error;
|
||||
|
|
@ -23,7 +21,7 @@ impl App {
|
|||
#[yazi_codegen::command]
|
||||
pub fn reflow(&mut self, _: Opt) {
|
||||
let Some(size) = self.term.as_ref().and_then(|t| t.size().ok()) else { return };
|
||||
let mut layout = *LAYOUT.load_full();
|
||||
let mut layout = LAYOUT.get();
|
||||
|
||||
let result = Lives::scope(&self.cx, |_| {
|
||||
let comps = Root::reflow((Position::ORIGIN, size).into())?;
|
||||
|
|
@ -38,14 +36,15 @@ impl App {
|
|||
match id.to_str()? {
|
||||
"current" => layout.current = *t.raw_get::<_, yazi_plugin::elements::Rect>("_area")?,
|
||||
"preview" => layout.preview = *t.raw_get::<_, yazi_plugin::elements::Rect>("_area")?,
|
||||
"progress" => layout.progress = *t.raw_get::<_, yazi_plugin::elements::Rect>("_area")?,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
|
||||
if layout != *LAYOUT.load_full() {
|
||||
LAYOUT.store(Arc::new(layout));
|
||||
if layout != LAYOUT.get() {
|
||||
LAYOUT.set(layout);
|
||||
render!();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ impl App {
|
|||
Self::patch(frame, self.cx.cursor());
|
||||
}
|
||||
if !self.cx.notify.messages.is_empty() {
|
||||
self.render_notify();
|
||||
self.render_partially();
|
||||
}
|
||||
|
||||
// Reload preview if collision is resolved
|
||||
|
|
@ -40,18 +40,19 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render_notify(&mut self) {
|
||||
let Some(term) = &mut self.term else {
|
||||
return;
|
||||
};
|
||||
|
||||
pub(crate) fn render_partially(&mut self) {
|
||||
let Some(term) = &mut self.term else { return };
|
||||
if !term.can_partial() {
|
||||
return self.render();
|
||||
}
|
||||
|
||||
let frame = term
|
||||
.draw_partial(|f| {
|
||||
f.render_widget(crate::notify::Layout::new(&self.cx), f.area());
|
||||
_ = Lives::scope(&self.cx, |_| {
|
||||
f.render_widget(crate::tasks::Progress, f.area());
|
||||
f.render_widget(crate::notify::Notify::new(&self.cx), f.area());
|
||||
Ok(())
|
||||
});
|
||||
|
||||
if let Some(pos) = self.cx.cursor() {
|
||||
f.set_cursor_position(pos);
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ impl App {
|
|||
pub(crate) fn update_notify(&mut self, cmd: Cmd) {
|
||||
let WindowSize { rows, columns, .. } = Dimension::available();
|
||||
let area =
|
||||
notify::Layout::available(Rect { x: 0, y: 0, width: columns, height: rows });
|
||||
notify::Notify::available(Rect { x: 0, y: 0, width: columns, height: rows });
|
||||
|
||||
self.cx.notify.tick(cmd, area);
|
||||
|
||||
if self.cx.notify.messages.is_empty() {
|
||||
self.render();
|
||||
} else {
|
||||
self.render_notify();
|
||||
self.render_partially();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
use ratatui::backend::Backend;
|
||||
use yazi_core::tasks::TasksProgress;
|
||||
use yazi_macro::render;
|
||||
use yazi_shared::event::Cmd;
|
||||
|
||||
use crate::{app::App, components::Progress, lives::Lives};
|
||||
use crate::app::App;
|
||||
|
||||
pub struct Opt {
|
||||
progress: TasksProgress,
|
||||
|
|
@ -19,41 +18,28 @@ impl TryFrom<Cmd> for Opt {
|
|||
|
||||
impl App {
|
||||
pub(crate) fn update_progress(&mut self, opt: impl TryInto<Opt>) {
|
||||
let Ok(opt) = opt.try_into() else {
|
||||
return;
|
||||
};
|
||||
let Ok(opt) = opt.try_into() else { return };
|
||||
|
||||
// Update the progress of all tasks.
|
||||
let tasks = &mut self.cx.tasks;
|
||||
let progressed = tasks.progress != opt.progress;
|
||||
tasks.progress = opt.progress;
|
||||
|
||||
// If the task manager is visible, update the summaries with a complete render.
|
||||
if tasks.visible {
|
||||
let new = tasks.paginate();
|
||||
if new.len() != tasks.summaries.len()
|
||||
|| new.iter().zip(&tasks.summaries).any(|(a, b)| a.name != b.name)
|
||||
{
|
||||
if tasks.summaries != new {
|
||||
tasks.summaries = new;
|
||||
tasks.arrow(0);
|
||||
return render!();
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, only partially update the progress.
|
||||
let Some(term) = &mut self.term else {
|
||||
return;
|
||||
};
|
||||
|
||||
_ = Lives::scope(&self.cx, |_| {
|
||||
for patch in Progress::partial_render(term.current_buffer_mut()) {
|
||||
term.backend_mut().draw(patch.iter().map(|(x, y, cell)| (*x, *y, cell)))?;
|
||||
if let Some(pos) = self.cx.cursor() {
|
||||
term.show_cursor()?;
|
||||
term.set_cursor_position(pos)?;
|
||||
}
|
||||
term.backend_mut().flush()?;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
if !progressed {
|
||||
} else if tasks.progress.total == 0 {
|
||||
render!();
|
||||
} else {
|
||||
self.render_partially();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
#![allow(clippy::module_inception)]
|
||||
|
||||
yazi_macro::mod_flat!(preview progress);
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
use std::mem;
|
||||
|
||||
use mlua::{AnyUserData, Table, TableExt};
|
||||
use tracing::error;
|
||||
use yazi_plugin::{LUA, cast_to_renderable};
|
||||
|
||||
pub(crate) struct Progress;
|
||||
|
||||
impl Progress {
|
||||
pub(crate) fn partial_render(
|
||||
buf: &mut ratatui::buffer::Buffer,
|
||||
) -> Vec<Vec<(u16, u16, ratatui::buffer::Cell)>> {
|
||||
let mut patches = vec![];
|
||||
let mut f = || {
|
||||
let comp: Table = LUA.globals().raw_get("Progress")?;
|
||||
for widget in comp.call_method::<_, Vec<AnyUserData>>("partial_redraw", ())? {
|
||||
let Some(w) = cast_to_renderable(&widget) else { continue };
|
||||
|
||||
let area = w.area();
|
||||
w.render(buf);
|
||||
|
||||
let mut patch = Vec::with_capacity(area.width as usize * area.height as usize);
|
||||
for y in area.top()..area.bottom() {
|
||||
for x in area.left()..area.right() {
|
||||
patch.push((x, y, mem::take(&mut buf[(x, y)])));
|
||||
}
|
||||
}
|
||||
|
||||
buf.reset();
|
||||
patches.push(patch);
|
||||
}
|
||||
|
||||
Ok::<_, anyhow::Error>(())
|
||||
};
|
||||
|
||||
if let Err(e) = f() {
|
||||
error!("{e}");
|
||||
}
|
||||
patches
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,11 @@ use yazi_config::{KEYMAP, THEME};
|
|||
use super::Bindings;
|
||||
use crate::Ctx;
|
||||
|
||||
pub(crate) struct Layout<'a> {
|
||||
pub(crate) struct Help<'a> {
|
||||
cx: &'a Ctx,
|
||||
}
|
||||
|
||||
impl<'a> Layout<'a> {
|
||||
impl<'a> Help<'a> {
|
||||
pub fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||
|
||||
fn tips() -> String {
|
||||
|
|
@ -19,7 +19,7 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Layout<'a> {
|
||||
impl<'a> Widget for Help<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let help = &self.cx.help;
|
||||
yazi_plugin::elements::Clear::default().render(area, buf);
|
||||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(bindings layout);
|
||||
yazi_macro::mod_flat!(bindings help);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ impl Folder {
|
|||
let window = match window {
|
||||
Some(w) => w,
|
||||
None => {
|
||||
let limit = LAYOUT.load().preview.height as usize;
|
||||
let limit = LAYOUT.get().preview.height as usize;
|
||||
inner.offset..inner.files.len().min(inner.offset + limit)
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ impl Preview {
|
|||
me.tab()
|
||||
.hovered_folder()
|
||||
.map(|f| {
|
||||
let limit = LAYOUT.load().preview.height as usize;
|
||||
let limit = LAYOUT.get().preview.height as usize;
|
||||
Folder::make(Some(me.skip..f.files.len().min(me.skip + limit)), f, me.tab())
|
||||
})
|
||||
.transpose()
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@
|
|||
#[global_allocator]
|
||||
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
yazi_macro::mod_pub!(
|
||||
app completion components confirm help input lives notify pick tasks which
|
||||
);
|
||||
yazi_macro::mod_pub!(app completion confirm help input lives manager notify pick tasks which);
|
||||
|
||||
yazi_macro::mod_flat!(context executor logs panic root router signals term);
|
||||
|
||||
|
|
|
|||
1
yazi-fm/src/manager/mod.rs
Normal file
1
yazi-fm/src/manager/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(preview);
|
||||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(layout);
|
||||
yazi_macro::mod_flat!(notify);
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ use yazi_core::notify::Message;
|
|||
|
||||
use crate::Ctx;
|
||||
|
||||
pub(crate) struct Layout<'a> {
|
||||
pub(crate) struct Notify<'a> {
|
||||
cx: &'a Ctx,
|
||||
}
|
||||
|
||||
impl<'a> Layout<'a> {
|
||||
impl<'a> Notify<'a> {
|
||||
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||
|
||||
pub(crate) fn available(area: Rect) -> Rect {
|
||||
|
|
@ -38,7 +38,7 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Layout<'a> {
|
||||
impl<'a> Widget for Notify<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let notify = &self.cx.notify;
|
||||
|
||||
|
|
@ -3,8 +3,8 @@ use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
|||
use tracing::error;
|
||||
use yazi_plugin::{LUA, elements::render_widgets};
|
||||
|
||||
use super::{completion, confirm, input, pick, tasks, which};
|
||||
use crate::{Ctx, components, help};
|
||||
use super::{completion, confirm, help, input, manager, pick, tasks, which};
|
||||
use crate::Ctx;
|
||||
|
||||
pub(super) struct Root<'a> {
|
||||
cx: &'a Ctx,
|
||||
|
|
@ -33,10 +33,10 @@ impl<'a> Widget for Root<'a> {
|
|||
error!("Failed to redraw the `Root` component:\n{e}");
|
||||
}
|
||||
|
||||
components::Preview::new(self.cx).render(area, buf);
|
||||
manager::Preview::new(self.cx).render(area, buf);
|
||||
|
||||
if self.cx.tasks.visible {
|
||||
tasks::Layout::new(self.cx).render(area, buf);
|
||||
tasks::Tasks::new(self.cx).render(area, buf);
|
||||
}
|
||||
|
||||
if self.cx.pick.visible {
|
||||
|
|
@ -52,7 +52,7 @@ impl<'a> Widget for Root<'a> {
|
|||
}
|
||||
|
||||
if self.cx.help.visible {
|
||||
help::Layout::new(self.cx).render(area, buf);
|
||||
help::Help::new(self.cx).render(area, buf);
|
||||
}
|
||||
|
||||
if self.cx.completion.visible {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(layout);
|
||||
yazi_macro::mod_flat!(progress tasks);
|
||||
|
|
|
|||
23
yazi-fm/src/tasks/progress.rs
Normal file
23
yazi-fm/src/tasks/progress.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use mlua::{Table, TableExt};
|
||||
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
||||
use tracing::error;
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_plugin::{LUA, elements::render_widgets};
|
||||
|
||||
pub(crate) struct Progress;
|
||||
|
||||
impl Widget for Progress {
|
||||
fn render(self, _: Rect, buf: &mut Buffer) {
|
||||
let mut f = || {
|
||||
let area = yazi_plugin::elements::Rect::from(LAYOUT.get().progress);
|
||||
let progress =
|
||||
LUA.globals().raw_get::<_, Table>("Progress")?.call_method::<_, Table>("use", area)?;
|
||||
|
||||
render_widgets(progress.call_method("redraw", ())?, buf);
|
||||
Ok::<_, mlua::Error>(())
|
||||
};
|
||||
if let Err(e) = f() {
|
||||
error!("Failed to redraw the `Progress` component:\n{e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,11 @@ use yazi_core::tasks::TASKS_PERCENT;
|
|||
|
||||
use crate::Ctx;
|
||||
|
||||
pub(crate) struct Layout<'a> {
|
||||
pub(crate) struct Tasks<'a> {
|
||||
cx: &'a Ctx,
|
||||
}
|
||||
|
||||
impl<'a> Layout<'a> {
|
||||
impl<'a> Tasks<'a> {
|
||||
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||
|
||||
pub(super) fn area(area: Rect) -> Rect {
|
||||
|
|
@ -28,7 +28,7 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Layout<'a> {
|
||||
impl<'a> Widget for Tasks<'a> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let area = Self::area(area);
|
||||
|
||||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(cand layout);
|
||||
yazi_macro::mod_flat!(cand which);
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ impl Folder {
|
|||
}
|
||||
|
||||
pub fn sync_page(&mut self, force: bool) {
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
let limit = LAYOUT.get().current.height as usize;
|
||||
if limit == 0 {
|
||||
return;
|
||||
}
|
||||
|
|
@ -109,7 +109,7 @@ impl Folder {
|
|||
let old = (self.cursor, self.offset);
|
||||
let len = self.files.len();
|
||||
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
let limit = LAYOUT.get().current.height as usize;
|
||||
let scrolloff = (limit / 2).min(MANAGER.scrolloff as usize);
|
||||
|
||||
self.cursor = step.add(self.cursor, limit).min(len.saturating_sub(1));
|
||||
|
|
@ -126,7 +126,7 @@ impl Folder {
|
|||
let old = (self.cursor, self.offset);
|
||||
let max = self.files.len().saturating_sub(1);
|
||||
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
let limit = LAYOUT.get().current.height as usize;
|
||||
let scrolloff = (limit / 2).min(MANAGER.scrolloff as usize);
|
||||
|
||||
self.cursor = step.add(self.cursor, limit).min(max);
|
||||
|
|
@ -143,7 +143,7 @@ impl Folder {
|
|||
let old = self.offset;
|
||||
let len = self.files.len();
|
||||
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
let limit = LAYOUT.get().current.height as usize;
|
||||
let scrolloff = (limit / 2).min(MANAGER.scrolloff as usize);
|
||||
|
||||
self.offset = if self.cursor < (self.offset + limit).min(len).saturating_sub(scrolloff) {
|
||||
|
|
@ -162,7 +162,7 @@ impl Folder {
|
|||
|
||||
pub fn paginate(&self, page: usize) -> &[File] {
|
||||
let len = self.files.len();
|
||||
let limit = LAYOUT.load().current.height as usize;
|
||||
let limit = LAYOUT.get().current.height as usize;
|
||||
|
||||
let start = (page.saturating_sub(1) * limit).min(len.saturating_sub(1));
|
||||
let end = ((page + 2) * limit).min(len);
|
||||
|
|
|
|||
|
|
@ -1,24 +1,30 @@
|
|||
Progress = {
|
||||
_area = ui.Rect.default, -- TODO: remove this
|
||||
_id = "progress",
|
||||
}
|
||||
|
||||
function Progress:redraw(area, offset)
|
||||
self._area = ui.Rect {
|
||||
x = math.max(0, area.w - offset - 21),
|
||||
y = area.y,
|
||||
w = ya.clamp(0, area.w - offset - 1, 20),
|
||||
h = math.min(1, area.h),
|
||||
}
|
||||
return self:partial_redraw()
|
||||
function Progress:new(area, offset)
|
||||
local me = setmetatable({ _area = area, _offset = offset }, { __index = self })
|
||||
me:layout()
|
||||
return me
|
||||
end
|
||||
|
||||
-- Progress bars usually need frequent updates to report the latest task progress.
|
||||
-- We use `partial_redraw()` to partially redraw it when there is progress change,
|
||||
-- which has almost no cost compared to a full redraw by `redraw()`.
|
||||
function Progress:partial_redraw()
|
||||
function Progress:use(area) return setmetatable({ _area = area }, { __index = self }) end
|
||||
|
||||
function Progress:layout()
|
||||
self._area = ui.Rect {
|
||||
x = math.max(0, self._area.w - self._offset - 21),
|
||||
y = self._area.y,
|
||||
w = ya.clamp(0, self._area.w - self._offset - 1, 20),
|
||||
h = math.min(1, self._area.h),
|
||||
}
|
||||
end
|
||||
|
||||
function Progress:reflow() return { self } end
|
||||
|
||||
function Progress:redraw()
|
||||
local progress = cx.tasks.progress
|
||||
if progress.total == 0 then
|
||||
return { ui.Text {} }
|
||||
return {}
|
||||
end
|
||||
|
||||
local gauge = ui.Gauge():area(self._area)
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ Status = {
|
|||
{ "name", id = 3, order = 3000 },
|
||||
},
|
||||
_right = {
|
||||
{ "permissions", id = 4, order = 1000 },
|
||||
{ "percentage", id = 5, order = 2000 },
|
||||
{ "perm", id = 4, order = 1000 },
|
||||
{ "percent", id = 5, order = 2000 },
|
||||
{ "position", id = 6, order = 3000 },
|
||||
},
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ function Status:name()
|
|||
return ui.Line(" " .. h.name)
|
||||
end
|
||||
|
||||
function Status:permissions()
|
||||
function Status:perm()
|
||||
local h = self._current.hovered
|
||||
if not h then
|
||||
return ui.Line {}
|
||||
|
|
@ -96,7 +96,7 @@ function Status:permissions()
|
|||
return ui.Line(spans)
|
||||
end
|
||||
|
||||
function Status:percentage()
|
||||
function Status:percent()
|
||||
local percent = 0
|
||||
local cursor = self._current.cursor
|
||||
local length = #self._current.files
|
||||
|
|
@ -142,7 +142,7 @@ function Status:redraw()
|
|||
return {
|
||||
ui.Text(left):area(self._area),
|
||||
ui.Text(right):area(self._area):align(ui.Text.RIGHT),
|
||||
table.unpack(Progress:redraw(self._area, right_width)),
|
||||
table.unpack(ya.redraw_with(Progress:new(self._area, right_width))),
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ function M:seek(units)
|
|||
end
|
||||
|
||||
function M:preload()
|
||||
local percentage = 5 + self.skip
|
||||
if percentage > 95 then
|
||||
local percent = 5 + self.skip
|
||||
if percent > 95 then
|
||||
ya.manager_emit("peek", { 90, only_if = self.file.url, upper_bound = true })
|
||||
return 2
|
||||
end
|
||||
|
|
@ -48,7 +48,7 @@ function M:preload()
|
|||
"-o",
|
||||
tostring(cache),
|
||||
"-t",
|
||||
tostring(percentage),
|
||||
tostring(percent),
|
||||
"-s",
|
||||
tostring(PREVIEW.max_width),
|
||||
}):spawn()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ pub async fn fetch(name: &str, files: Vec<yazi_shared::fs::File>) -> mlua::Resul
|
|||
}
|
||||
|
||||
plugin.raw_set("skip", 0)?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.load().preview))?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.get().preview))?;
|
||||
plugin.raw_set("files", files)?;
|
||||
|
||||
Handle::current().block_on(plugin.call_async_method("fetch", ()))
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ pub fn peek(
|
|||
plugin.raw_set("file", File::cast(&lua, file)?)?;
|
||||
plugin.raw_set("_mime", mime)?;
|
||||
plugin.raw_set("skip", skip)?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.load().preview))?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.get().preview))?;
|
||||
plugin.raw_set("window", Window::default())?;
|
||||
|
||||
if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("peek", ()).await }
|
||||
|
|
@ -69,7 +69,7 @@ pub fn peek_sync(cmd: &Cmd, file: yazi_shared::fs::File, mime: Cow<'static, str>
|
|||
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
||||
plugin.raw_set("_mime", mime)?;
|
||||
plugin.raw_set("skip", skip)?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.load().preview))?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.get().preview))?;
|
||||
plugin.raw_set("window", Window::default())?;
|
||||
plugin.call_method("peek", ())
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub async fn preload(name: &str, file: yazi_shared::fs::File) -> mlua::Result<u8
|
|||
};
|
||||
|
||||
plugin.raw_set("skip", 0)?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.load().preview))?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.get().preview))?;
|
||||
plugin.raw_set("file", File::cast(&lua, file)?)?;
|
||||
|
||||
Handle::current().block_on(plugin.call_async_method("preload", ()))
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::{LUA, Opt, OptCallback, bindings::Cast, elements::Rect, file::File};
|
|||
pub fn seek_sync(cmd: &Cmd, file: yazi_shared::fs::File, units: i16) {
|
||||
let cb: OptCallback = Box::new(move |_, plugin| {
|
||||
plugin.raw_set("file", File::cast(&LUA, file)?)?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.load().preview))?;
|
||||
plugin.raw_set("area", Rect::from(LAYOUT.get().preview))?;
|
||||
plugin.call_method("seek", units)
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mlua::{ExternalError, Lua, Table, TableExt, Value};
|
||||
use tracing::error;
|
||||
|
|
@ -42,22 +42,15 @@ impl Utils {
|
|||
let id: mlua::String = c.get("_id")?;
|
||||
let id = id.to_str()?;
|
||||
|
||||
let mut layout = LAYOUT.get();
|
||||
match id {
|
||||
"current" => {
|
||||
LAYOUT.store(Arc::new(yazi_config::Layout {
|
||||
current: *c.raw_get::<_, crate::elements::Rect>("_area")?,
|
||||
..*LAYOUT.load_full()
|
||||
}));
|
||||
}
|
||||
"preview" => {
|
||||
LAYOUT.store(Arc::new(yazi_config::Layout {
|
||||
preview: *c.raw_get::<_, crate::elements::Rect>("_area")?,
|
||||
..*LAYOUT.load_full()
|
||||
}));
|
||||
}
|
||||
"current" => layout.current = *c.raw_get::<_, crate::elements::Rect>("_area")?,
|
||||
"preview" => layout.preview = *c.raw_get::<_, crate::elements::Rect>("_area")?,
|
||||
"progress" => layout.progress = *c.raw_get::<_, crate::elements::Rect>("_area")?,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
LAYOUT.set(layout);
|
||||
match c.call_method::<_, Table>("redraw", ()) {
|
||||
Err(e) => {
|
||||
error!("Failed to `redraw()` the `{id}` component:\n{e}");
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ pub enum TaskKind {
|
|||
Preload,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct TaskSummary {
|
||||
pub name: String,
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
use std::{cell::Cell, ffi::OsStr, fs::{FileType, Metadata}, ops::Deref};
|
||||
use std::{ffi::OsStr, fs::{FileType, Metadata}, ops::Deref};
|
||||
|
||||
use anyhow::Result;
|
||||
use tokio::fs;
|
||||
|
||||
use super::{Urn, UrnBuf};
|
||||
use crate::{fs::{Cha, Url}, theme::IconCache};
|
||||
use crate::{SyncCell, fs::{Cha, Url}, theme::IconCache};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct File {
|
||||
pub url: Url,
|
||||
pub cha: Cha,
|
||||
pub link_to: Option<Url>,
|
||||
pub icon: Cell<IconCache>,
|
||||
pub icon: SyncCell<IconCache>,
|
||||
}
|
||||
|
||||
impl Deref for File {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
yazi_macro::mod_pub!(errors event fs shell theme translit);
|
||||
|
||||
yazi_macro::mod_flat!(chars condition debounce env id layer natsort number os rand ro_cell terminal throttle time xdg);
|
||||
yazi_macro::mod_flat!(chars condition debounce env id layer natsort number os rand ro_cell sync_cell terminal throttle time xdg);
|
||||
|
||||
pub fn init() {
|
||||
#[cfg(unix)]
|
||||
|
|
|
|||
|
|
@ -27,12 +27,6 @@ impl<T> RoCell<T> {
|
|||
self.init(f());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn replace(&self, value: T) -> T {
|
||||
debug_assert!(self.initialized());
|
||||
unsafe { mem::replace(&mut *self.0.get(), Some(value)).unwrap_unchecked() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drop(&self) -> T {
|
||||
debug_assert!(self.initialized());
|
||||
|
|
|
|||
36
yazi-shared/src/sync_cell.rs
Normal file
36
yazi-shared/src/sync_cell.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use std::{cell::Cell, fmt::{Debug, Formatter}, ops::Deref};
|
||||
|
||||
/// [`SyncCell`], but [`Sync`].
|
||||
///
|
||||
/// This is just an `Cell`, except it implements `Sync`
|
||||
/// if `T` implements `Sync`.
|
||||
pub struct SyncCell<T: ?Sized>(Cell<T>);
|
||||
|
||||
unsafe impl<T: ?Sized + Sync> Sync for SyncCell<T> {}
|
||||
|
||||
impl<T> SyncCell<T> {
|
||||
#[inline]
|
||||
pub const fn new(value: T) -> Self { Self(Cell::new(value)) }
|
||||
}
|
||||
|
||||
impl<T: Default> Default for SyncCell<T> {
|
||||
fn default() -> Self { Self::new(T::default()) }
|
||||
}
|
||||
|
||||
impl<T> Deref for SyncCell<T> {
|
||||
type Target = Cell<T>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl<T: Copy> Clone for SyncCell<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> SyncCell<T> { SyncCell::new(self.get()) }
|
||||
}
|
||||
|
||||
impl<T: Copy + Debug> Debug for SyncCell<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("SyncCell").field("value", &self.get()).finish()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue