fix: use complete rendering instead of partial rendering for no task progress report (#1876)

This commit is contained in:
三咲雅 · Misaki Masa 2024-11-01 19:52:19 +08:00 committed by GitHub
parent a510673123
commit 0baccdc9aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 174 additions and 190 deletions

8
Cargo.lock generated
View file

@ -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",

View file

@ -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" ] }

View file

@ -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 }

View file

@ -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); }

View file

@ -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();
}

View file

@ -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 }

View file

@ -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 }
}
}

View file

@ -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);

View file

@ -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)

View file

@ -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,

View file

@ -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!();
}

View file

@ -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);

View file

@ -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();
}
}
}

View file

@ -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();
}
}
}

View file

@ -1,3 +0,0 @@
#![allow(clippy::module_inception)]
yazi_macro::mod_flat!(preview progress);

View file

@ -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
}
}

View file

@ -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);

View file

@ -1 +1 @@
yazi_macro::mod_flat!(bindings layout);
yazi_macro::mod_flat!(bindings help);

View file

@ -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)
}
};

View file

@ -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()

View file

@ -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);

View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(preview);

View file

@ -1 +1 @@
yazi_macro::mod_flat!(layout);
yazi_macro::mod_flat!(notify);

View file

@ -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;

View file

@ -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 {

View file

@ -1 +1 @@
yazi_macro::mod_flat!(layout);
yazi_macro::mod_flat!(progress tasks);

View 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}");
}
}
}

View file

@ -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);

View file

@ -1 +1 @@
yazi_macro::mod_flat!(cand layout);
yazi_macro::mod_flat!(cand which);

View file

@ -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);

View file

@ -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)

View file

@ -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

View file

@ -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()

View file

@ -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", ()))

View file

@ -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", ())
});

View file

@ -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", ()))

View file

@ -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)
});

View file

@ -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}");

View file

@ -31,7 +31,7 @@ pub enum TaskKind {
Preload,
}
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub struct TaskSummary {
pub name: String,

View file

@ -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 {

View 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)]

View file

@ -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());

View 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()
}
}