diff --git a/Cargo.lock b/Cargo.lock index 211aab3d..43d14dbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index ca3962ce..e5125f71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" ] } diff --git a/yazi-adapter/Cargo.toml b/yazi-adapter/Cargo.toml index f3a680ac..a6fcd1b9 100644 --- a/yazi-adapter/Cargo.toml +++ b/yazi-adapter/Cargo.toml @@ -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 } diff --git a/yazi-adapter/src/adapter.rs b/yazi-adapter/src/adapter.rs index 841effe8..7f281234 100644 --- a/yazi-adapter/src/adapter.rs +++ b/yazi-adapter/src/adapter.rs @@ -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 { SHOWN.load_full().map(|r| *r) } + pub fn shown_load(self) -> Option { 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); } diff --git a/yazi-adapter/src/lib.rs b/yazi-adapter/src/lib.rs index 8999cec5..a9879185 100644 --- a/yazi-adapter/src/lib.rs +++ b/yazi-adapter/src/lib.rs @@ -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 = RoCell::new(); // Tmux support @@ -17,7 +17,7 @@ static CLOSE: RoCell<&'static str> = RoCell::new(); pub static WSL: RoCell = RoCell::new(); // Image state -static SHOWN: RoCell> = RoCell::new(); +static SHOWN: SyncCell> = 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(); } diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 32fc3a9d..ed6efed7 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -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 } diff --git a/yazi-config/src/layout.rs b/yazi-config/src/layout.rs index 1f42a562..071c25b5 100644 --- a/yazi-config/src/layout.rs +++ b/yazi-config/src/layout.rs @@ -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 } + } } diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index e039b965..bd8a2d1b 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -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> = RoCell::new(); +use yazi_shared::{RoCell, SyncCell, Xdg}; pub static KEYMAP: RoCell = RoCell::new(); pub static LOG: RoCell = RoCell::new(); @@ -23,6 +21,8 @@ pub static CONFIRM: RoCell = RoCell::new(); pub static PICK: RoCell = RoCell::new(); pub static WHICH: RoCell = RoCell::new(); +pub static LAYOUT: SyncCell = 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); diff --git a/yazi-core/src/tab/tab.rs b/yazi-core/src/tab/tab.rs index 516925a8..3fe64ad7 100644 --- a/yazi-core/src/tab/tab.rs +++ b/yazi-core/src/tab/tab.rs @@ -69,7 +69,7 @@ impl Tab { pub fn hovered_rect(&self) -> Option { 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) diff --git a/yazi-core/src/tasks/progress.rs b/yazi-core/src/tasks/progress.rs index e62899a2..faf8a1e9 100644 --- a/yazi-core/src/tasks/progress.rs +++ b/yazi-core/src/tasks/progress.rs @@ -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, diff --git a/yazi-fm/src/app/commands/reflow.rs b/yazi-fm/src/app/commands/reflow.rs index bb18acef..20451fd6 100644 --- a/yazi-fm/src/app/commands/reflow.rs +++ b/yazi-fm/src/app/commands/reflow.rs @@ -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!(); } diff --git a/yazi-fm/src/app/commands/render.rs b/yazi-fm/src/app/commands/render.rs index 6fd73573..123e146a 100644 --- a/yazi-fm/src/app/commands/render.rs +++ b/yazi-fm/src/app/commands/render.rs @@ -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); diff --git a/yazi-fm/src/app/commands/update_notify.rs b/yazi-fm/src/app/commands/update_notify.rs index 4729ead2..08a5cc51 100644 --- a/yazi-fm/src/app/commands/update_notify.rs +++ b/yazi-fm/src/app/commands/update_notify.rs @@ -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(); } } } diff --git a/yazi-fm/src/app/commands/update_progress.rs b/yazi-fm/src/app/commands/update_progress.rs index 98d645b2..15d9633c 100644 --- a/yazi-fm/src/app/commands/update_progress.rs +++ b/yazi-fm/src/app/commands/update_progress.rs @@ -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 for Opt { impl App { pub(crate) fn update_progress(&mut self, opt: impl TryInto) { - 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(); + } } } diff --git a/yazi-fm/src/components/mod.rs b/yazi-fm/src/components/mod.rs deleted file mode 100644 index 2b7b0da6..00000000 --- a/yazi-fm/src/components/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![allow(clippy::module_inception)] - -yazi_macro::mod_flat!(preview progress); diff --git a/yazi-fm/src/components/progress.rs b/yazi-fm/src/components/progress.rs deleted file mode 100644 index 4091cc3f..00000000 --- a/yazi-fm/src/components/progress.rs +++ /dev/null @@ -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> { - let mut patches = vec![]; - let mut f = || { - let comp: Table = LUA.globals().raw_get("Progress")?; - for widget in comp.call_method::<_, Vec>("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 - } -} diff --git a/yazi-fm/src/help/layout.rs b/yazi-fm/src/help/help.rs similarity index 91% rename from yazi-fm/src/help/layout.rs rename to yazi-fm/src/help/help.rs index f0ba75da..6aec0937 100644 --- a/yazi-fm/src/help/layout.rs +++ b/yazi-fm/src/help/help.rs @@ -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); diff --git a/yazi-fm/src/help/mod.rs b/yazi-fm/src/help/mod.rs index 53f65c8a..3b22a7c9 100644 --- a/yazi-fm/src/help/mod.rs +++ b/yazi-fm/src/help/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(bindings layout); +yazi_macro::mod_flat!(bindings help); diff --git a/yazi-fm/src/lives/folder.rs b/yazi-fm/src/lives/folder.rs index e4fd4874..3a855ba5 100644 --- a/yazi-fm/src/lives/folder.rs +++ b/yazi-fm/src/lives/folder.rs @@ -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) } }; diff --git a/yazi-fm/src/lives/preview.rs b/yazi-fm/src/lives/preview.rs index 83018ed7..402ec170 100644 --- a/yazi-fm/src/lives/preview.rs +++ b/yazi-fm/src/lives/preview.rs @@ -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() diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs index ac33b1c9..026f66a9 100644 --- a/yazi-fm/src/main.rs +++ b/yazi-fm/src/main.rs @@ -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); diff --git a/yazi-fm/src/manager/mod.rs b/yazi-fm/src/manager/mod.rs new file mode 100644 index 00000000..b2788a42 --- /dev/null +++ b/yazi-fm/src/manager/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(preview); diff --git a/yazi-fm/src/components/preview.rs b/yazi-fm/src/manager/preview.rs similarity index 100% rename from yazi-fm/src/components/preview.rs rename to yazi-fm/src/manager/preview.rs diff --git a/yazi-fm/src/notify/mod.rs b/yazi-fm/src/notify/mod.rs index b6274db1..247b6e3b 100644 --- a/yazi-fm/src/notify/mod.rs +++ b/yazi-fm/src/notify/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(layout); +yazi_macro::mod_flat!(notify); diff --git a/yazi-fm/src/notify/layout.rs b/yazi-fm/src/notify/notify.rs similarity index 95% rename from yazi-fm/src/notify/layout.rs rename to yazi-fm/src/notify/notify.rs index ee4cbfad..7d3b061c 100644 --- a/yazi-fm/src/notify/layout.rs +++ b/yazi-fm/src/notify/notify.rs @@ -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; diff --git a/yazi-fm/src/root.rs b/yazi-fm/src/root.rs index d667cc3a..2f847f4f 100644 --- a/yazi-fm/src/root.rs +++ b/yazi-fm/src/root.rs @@ -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 { diff --git a/yazi-fm/src/tasks/mod.rs b/yazi-fm/src/tasks/mod.rs index b6274db1..52d57d37 100644 --- a/yazi-fm/src/tasks/mod.rs +++ b/yazi-fm/src/tasks/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(layout); +yazi_macro::mod_flat!(progress tasks); diff --git a/yazi-fm/src/tasks/progress.rs b/yazi-fm/src/tasks/progress.rs new file mode 100644 index 00000000..f9795dcc --- /dev/null +++ b/yazi-fm/src/tasks/progress.rs @@ -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}"); + } + } +} diff --git a/yazi-fm/src/tasks/layout.rs b/yazi-fm/src/tasks/tasks.rs similarity index 94% rename from yazi-fm/src/tasks/layout.rs rename to yazi-fm/src/tasks/tasks.rs index d8785f54..88ffcb00 100644 --- a/yazi-fm/src/tasks/layout.rs +++ b/yazi-fm/src/tasks/tasks.rs @@ -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); diff --git a/yazi-fm/src/which/mod.rs b/yazi-fm/src/which/mod.rs index 2d0635bd..d54bac3b 100644 --- a/yazi-fm/src/which/mod.rs +++ b/yazi-fm/src/which/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(cand layout); +yazi_macro::mod_flat!(cand which); diff --git a/yazi-fm/src/which/layout.rs b/yazi-fm/src/which/which.rs similarity index 100% rename from yazi-fm/src/which/layout.rs rename to yazi-fm/src/which/which.rs diff --git a/yazi-fs/src/folder.rs b/yazi-fs/src/folder.rs index c0f0dfe7..11e7205e 100644 --- a/yazi-fs/src/folder.rs +++ b/yazi-fs/src/folder.rs @@ -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); diff --git a/yazi-plugin/preset/components/progress.lua b/yazi-plugin/preset/components/progress.lua index 48064444..c9cf53dd 100644 --- a/yazi-plugin/preset/components/progress.lua +++ b/yazi-plugin/preset/components/progress.lua @@ -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) diff --git a/yazi-plugin/preset/components/status.lua b/yazi-plugin/preset/components/status.lua index e1b4cfa6..ddc6cbaf 100644 --- a/yazi-plugin/preset/components/status.lua +++ b/yazi-plugin/preset/components/status.lua @@ -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 diff --git a/yazi-plugin/preset/plugins/video.lua b/yazi-plugin/preset/plugins/video.lua index c4846ed5..74e462ff 100644 --- a/yazi-plugin/preset/plugins/video.lua +++ b/yazi-plugin/preset/plugins/video.lua @@ -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() diff --git a/yazi-plugin/src/isolate/fetch.rs b/yazi-plugin/src/isolate/fetch.rs index 1e92b36e..36863601 100644 --- a/yazi-plugin/src/isolate/fetch.rs +++ b/yazi-plugin/src/isolate/fetch.rs @@ -26,7 +26,7 @@ pub async fn fetch(name: &str, files: Vec) -> 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", ())) diff --git a/yazi-plugin/src/isolate/peek.rs b/yazi-plugin/src/isolate/peek.rs index 4c1ca3da..5d0866d2 100644 --- a/yazi-plugin/src/isolate/peek.rs +++ b/yazi-plugin/src/isolate/peek.rs @@ -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", ()) }); diff --git a/yazi-plugin/src/isolate/preload.rs b/yazi-plugin/src/isolate/preload.rs index 863a2b53..cf837367 100644 --- a/yazi-plugin/src/isolate/preload.rs +++ b/yazi-plugin/src/isolate/preload.rs @@ -18,7 +18,7 @@ pub async fn preload(name: &str, file: yazi_shared::fs::File) -> mlua::Result { - 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}"); diff --git a/yazi-scheduler/src/task.rs b/yazi-scheduler/src/task.rs index 27baceea..8041479b 100644 --- a/yazi-scheduler/src/task.rs +++ b/yazi-scheduler/src/task.rs @@ -31,7 +31,7 @@ pub enum TaskKind { Preload, } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct TaskSummary { pub name: String, diff --git a/yazi-shared/src/fs/file.rs b/yazi-shared/src/fs/file.rs index 84a480a2..41e1bf0a 100644 --- a/yazi-shared/src/fs/file.rs +++ b/yazi-shared/src/fs/file.rs @@ -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, - pub icon: Cell, + pub icon: SyncCell, } impl Deref for File { diff --git a/yazi-shared/src/lib.rs b/yazi-shared/src/lib.rs index 0b3ff28e..1dd01b9d 100644 --- a/yazi-shared/src/lib.rs +++ b/yazi-shared/src/lib.rs @@ -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)] diff --git a/yazi-shared/src/ro_cell.rs b/yazi-shared/src/ro_cell.rs index 5d7c95c8..e5c830fd 100644 --- a/yazi-shared/src/ro_cell.rs +++ b/yazi-shared/src/ro_cell.rs @@ -27,12 +27,6 @@ impl RoCell { 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()); diff --git a/yazi-shared/src/sync_cell.rs b/yazi-shared/src/sync_cell.rs new file mode 100644 index 00000000..7e15d095 --- /dev/null +++ b/yazi-shared/src/sync_cell.rs @@ -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(Cell); + +unsafe impl Sync for SyncCell {} + +impl SyncCell { + #[inline] + pub const fn new(value: T) -> Self { Self(Cell::new(value)) } +} + +impl Default for SyncCell { + fn default() -> Self { Self::new(T::default()) } +} + +impl Deref for SyncCell { + type Target = Cell; + + #[inline] + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl Clone for SyncCell { + #[inline] + fn clone(&self) -> SyncCell { SyncCell::new(self.get()) } +} + +impl Debug for SyncCell { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SyncCell").field("value", &self.get()).finish() + } +}