mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-25 01:31:36 +03:00
feat: when there are no files in the list, add a placeholder message (#900)
This commit is contained in:
parent
23c38ebae0
commit
3c67cae42d
@ -106,18 +106,18 @@ keymap = [
|
||||
{ on = [ "N" ], run = "find_arrow --previous", desc = "Go to previous found file" },
|
||||
|
||||
# Sorting
|
||||
{ on = [ ",", "m" ], run = "sort modified", desc = "Sort by modified time" },
|
||||
{ on = [ ",", "M" ], run = "sort modified --reverse", desc = "Sort by modified time (reverse)" },
|
||||
{ on = [ ",", "c" ], run = "sort created", desc = "Sort by created time" },
|
||||
{ on = [ ",", "C" ], run = "sort created --reverse", desc = "Sort by created time (reverse)" },
|
||||
{ on = [ ",", "e" ], run = "sort extension", desc = "Sort by extension" },
|
||||
{ on = [ ",", "E" ], run = "sort extension --reverse", desc = "Sort by extension (reverse)" },
|
||||
{ on = [ ",", "a" ], run = "sort alphabetical", desc = "Sort alphabetically" },
|
||||
{ on = [ ",", "A" ], run = "sort alphabetical --reverse", desc = "Sort alphabetically (reverse)" },
|
||||
{ on = [ ",", "n" ], run = "sort natural", desc = "Sort naturally" },
|
||||
{ on = [ ",", "N" ], run = "sort natural --reverse", desc = "Sort naturally (reverse)" },
|
||||
{ on = [ ",", "s" ], run = "sort size", desc = "Sort by size" },
|
||||
{ on = [ ",", "S" ], run = "sort size --reverse", desc = "Sort by size (reverse)" },
|
||||
{ on = [ ",", "m" ], run = "sort modified --dir-first", desc = "Sort by modified time" },
|
||||
{ on = [ ",", "M" ], run = "sort modified --reverse --dir-first", desc = "Sort by modified time (reverse)" },
|
||||
{ on = [ ",", "c" ], run = "sort created --dir-first", desc = "Sort by created time" },
|
||||
{ on = [ ",", "C" ], run = "sort created --reverse --dir-first", desc = "Sort by created time (reverse)" },
|
||||
{ on = [ ",", "e" ], run = "sort extension --dir-first", desc = "Sort by extension" },
|
||||
{ on = [ ",", "E" ], run = "sort extension --reverse --dir-first", desc = "Sort by extension (reverse)" },
|
||||
{ on = [ ",", "a" ], run = "sort alphabetical --dir-first", desc = "Sort alphabetically" },
|
||||
{ on = [ ",", "A" ], run = "sort alphabetical --reverse --dir-first", desc = "Sort alphabetically (reverse)" },
|
||||
{ on = [ ",", "n" ], run = "sort natural --dir-first", desc = "Sort naturally" },
|
||||
{ on = [ ",", "N" ], run = "sort natural --reverse --dir-first", desc = "Sort naturally (reverse)" },
|
||||
{ on = [ ",", "s" ], run = "sort size --dir-first", desc = "Sort by size" },
|
||||
{ on = [ ",", "S" ], run = "sort size --reverse --dir-first", desc = "Sort by size (reverse)" },
|
||||
|
||||
# Tabs
|
||||
{ on = [ "t" ], run = "tab_create --current", desc = "Create a new tab using the current path" },
|
||||
|
@ -11,20 +11,25 @@ pub struct Message {
|
||||
pub level: NotifyLevel,
|
||||
pub timeout: Duration,
|
||||
|
||||
pub instant: Instant,
|
||||
pub percent: u8,
|
||||
pub instant: Instant,
|
||||
pub percent: u8,
|
||||
pub max_width: usize,
|
||||
}
|
||||
|
||||
impl From<NotifyOpt> for Message {
|
||||
fn from(opt: NotifyOpt) -> Self {
|
||||
let title = opt.title.lines().next().unwrap_or_default();
|
||||
let max_width = opt.content.lines().map(|s| s.width()).max().unwrap_or(0).max(title.width());
|
||||
|
||||
Self {
|
||||
title: opt.title,
|
||||
title: title.to_owned(),
|
||||
content: opt.content,
|
||||
level: opt.level,
|
||||
timeout: opt.timeout,
|
||||
|
||||
instant: Instant::now(),
|
||||
percent: 0,
|
||||
instant: Instant::now(),
|
||||
percent: 0,
|
||||
max_width: max_width + NOTIFY_BORDER as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use std::ops::{Deref, Range};
|
||||
|
||||
use mlua::{AnyUserData, Lua, MetaMethod, UserDataMethods};
|
||||
use mlua::{AnyUserData, Lua, MetaMethod, UserDataFields, UserDataMethods};
|
||||
|
||||
use super::{File, SCOPE};
|
||||
use super::{File, Filter, SCOPE};
|
||||
|
||||
pub(super) struct Files {
|
||||
window: Range<usize>,
|
||||
@ -28,6 +28,8 @@ impl Files {
|
||||
|
||||
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.register_userdata_type::<Self>(|reg| {
|
||||
reg.add_field_method_get("filter", |_, me| me.filter().map(Filter::make).transpose());
|
||||
|
||||
reg.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.window.end - me.window.start));
|
||||
|
||||
reg.add_meta_method(MetaMethod::Index, |_, me, mut idx: usize| {
|
||||
|
28
yazi-fm/src/lives/filter.rs
Normal file
28
yazi-fm/src/lives/filter.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, Lua};
|
||||
|
||||
use super::SCOPE;
|
||||
|
||||
pub(super) struct Filter {
|
||||
inner: *const yazi_core::folder::Filter,
|
||||
}
|
||||
|
||||
impl Deref for Filter {
|
||||
type Target = yazi_core::folder::Filter;
|
||||
|
||||
fn deref(&self) -> &Self::Target { unsafe { &*self.inner } }
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
#[inline]
|
||||
pub(super) fn make(inner: &yazi_core::folder::Filter) -> mlua::Result<AnyUserData<'static>> {
|
||||
SCOPE.create_any_userdata(Self { inner })
|
||||
}
|
||||
|
||||
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.register_userdata_type::<Self>(|_| {})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ impl Lives {
|
||||
super::Config::register(&LUA)?;
|
||||
super::File::register(&LUA)?;
|
||||
super::Files::register(&LUA)?;
|
||||
super::Filter::register(&LUA)?;
|
||||
super::Folder::register(&LUA)?;
|
||||
super::Mode::register(&LUA)?;
|
||||
super::Preview::register(&LUA)?;
|
||||
|
@ -3,6 +3,7 @@
|
||||
mod config;
|
||||
mod file;
|
||||
mod files;
|
||||
mod filter;
|
||||
mod folder;
|
||||
mod iter;
|
||||
mod lives;
|
||||
@ -17,6 +18,7 @@ mod yanked;
|
||||
use config::*;
|
||||
use file::*;
|
||||
use files::*;
|
||||
use filter::*;
|
||||
use folder::*;
|
||||
use iter::*;
|
||||
pub(super) use lives::*;
|
||||
|
@ -1,5 +1,3 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use ratatui::{buffer::Buffer, layout::{self, Constraint, Offset, Rect}, widgets::{Block, BorderType, Paragraph, Widget, Wrap}};
|
||||
use yazi_config::THEME;
|
||||
use yazi_core::notify::Message;
|
||||
@ -15,9 +13,7 @@ impl<'a> Layout<'a> {
|
||||
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||
|
||||
pub(crate) fn available(area: Rect) -> Rect {
|
||||
let chunks =
|
||||
layout::Layout::horizontal([Constraint::Fill(1), Constraint::Length(80), Constraint::Max(1)])
|
||||
.split(area);
|
||||
let chunks = layout::Layout::horizontal([Constraint::Fill(1), Constraint::Min(80)]).split(area);
|
||||
|
||||
let chunks =
|
||||
layout::Layout::vertical([Constraint::Max(1), Constraint::Fill(1)]).split(chunks[1]);
|
||||
@ -25,12 +21,22 @@ impl<'a> Layout<'a> {
|
||||
chunks[1]
|
||||
}
|
||||
|
||||
fn tile(area: Rect, messages: &[Message]) -> Rc<[Rect]> {
|
||||
fn tile(area: Rect, messages: &[Message]) -> Vec<Rect> {
|
||||
layout::Layout::vertical(
|
||||
messages.iter().map(|m| Constraint::Length(m.height(area.width) as u16)),
|
||||
)
|
||||
.spacing(1)
|
||||
.split(area)
|
||||
.iter()
|
||||
.zip(messages)
|
||||
.map(|(&(mut r), m)| {
|
||||
if r.width > m.max_width as u16 {
|
||||
r.x = r.x.saturating_add(r.width - m.max_width as u16);
|
||||
r.width = m.max_width as u16;
|
||||
}
|
||||
r
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +57,7 @@ impl<'a> Widget for Layout<'a> {
|
||||
|
||||
let mut rect =
|
||||
tile[i].offset(Offset { x: (100 - m.percent) as i32 * tile[i].width as i32 / 100, y: 0 });
|
||||
rect.width = area.width.saturating_sub(rect.x);
|
||||
rect.width -= rect.x - tile[i].x;
|
||||
|
||||
yazi_plugin::elements::Clear::default().render(rect, buf);
|
||||
Paragraph::new(m.content.as_str())
|
||||
@ -59,7 +65,7 @@ impl<'a> Widget for Layout<'a> {
|
||||
.block(
|
||||
Block::bordered()
|
||||
.border_type(BorderType::Rounded)
|
||||
.title(format!("{} {}", icon, m.title))
|
||||
.title(format!("{icon} {}", m.title))
|
||||
.title_style(style)
|
||||
.border_style(style),
|
||||
)
|
||||
|
@ -2,12 +2,27 @@ Current = {
|
||||
area = ui.Rect.default,
|
||||
}
|
||||
|
||||
function Current:empty(area)
|
||||
local folder = Folder:by_kind(Folder.CURRENT)
|
||||
|
||||
local line
|
||||
if folder.files.filter then
|
||||
line = ui.Line("No filter results")
|
||||
else
|
||||
line = ui.Line(folder.stage == "loading" and "Loading..." or "No files")
|
||||
end
|
||||
|
||||
return {
|
||||
ui.Paragraph(area, { line }):align(ui.Paragraph.CENTER),
|
||||
}
|
||||
end
|
||||
|
||||
function Current:render(area)
|
||||
self.area = area
|
||||
|
||||
local files = Folder:by_kind(Folder.CURRENT).window
|
||||
if #files == 0 then
|
||||
return {}
|
||||
return self:empty(area)
|
||||
end
|
||||
|
||||
local items, markers = {}, {}
|
||||
|
@ -9,9 +9,7 @@ function M:peek()
|
||||
p = ui.Paragraph.parse(self.area, "----- File Type Classification -----\n\n" .. output.stdout)
|
||||
else
|
||||
p = ui.Paragraph(self.area, {
|
||||
ui.Line {
|
||||
ui.Span(string.format("Spawn `%s` command returns %s", cmd, code)),
|
||||
},
|
||||
ui.Line(string.format("Spawn `%s` command returns %s", cmd, code)),
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -3,12 +3,19 @@ local M = {}
|
||||
function M:peek()
|
||||
local folder = Folder:by_kind(Folder.PREVIEW)
|
||||
if not folder or folder.cwd ~= self.file.url then
|
||||
return {}
|
||||
return
|
||||
end
|
||||
|
||||
local bound = math.max(0, #folder.files - self.area.h)
|
||||
if self.skip > bound then
|
||||
ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true })
|
||||
return ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true })
|
||||
end
|
||||
|
||||
if #folder.files == 0 then
|
||||
return ya.preview_widgets(self, {
|
||||
ui.Paragraph(self.area, { ui.Line(folder.stage == "loading" and "Loading..." or "No files") })
|
||||
:align(ui.Paragraph.CENTER),
|
||||
})
|
||||
end
|
||||
|
||||
local items, markers = {}, {}
|
||||
|
@ -12,7 +12,7 @@ const RIGHT: u8 = 2;
|
||||
#[derive(Clone, FromLua)]
|
||||
pub struct Line(pub(super) ratatui::text::Line<'static>);
|
||||
|
||||
impl<'a> TryFrom<Table<'a>> for Line {
|
||||
impl TryFrom<Table<'_>> for Line {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(tb: Table) -> Result<Self, Self::Error> {
|
||||
@ -33,13 +33,20 @@ impl<'a> TryFrom<Table<'a>> for Line {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<mlua::String<'_>> for Line {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(s: mlua::String) -> Result<Self, Self::Error> {
|
||||
Ok(Self(ratatui::text::Line::from(s.to_string_lossy().into_owned())))
|
||||
}
|
||||
}
|
||||
|
||||
impl Line {
|
||||
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||
let new = lua.create_function(|_, (_, value): (Table, Value)| {
|
||||
if let Value::Table(tb) = value {
|
||||
return Line::try_from(tb);
|
||||
}
|
||||
Err("expected a table of Spans or Lines".into_lua_err())
|
||||
let new = lua.create_function(|_, (_, value): (Table, Value)| match value {
|
||||
Value::Table(tb) => Line::try_from(tb),
|
||||
Value::String(s) => Line::try_from(s),
|
||||
_ => Err("expected a String, or a table of Spans and Lines".into_lua_err()),
|
||||
})?;
|
||||
|
||||
let parse = lua.create_function(|_, code: mlua::String| {
|
||||
|
@ -110,7 +110,9 @@ impl Term {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn can_partial(&mut self) -> bool { self.last_area == self.inner.get_frame().size() }
|
||||
pub fn can_partial(&mut self) -> bool {
|
||||
self.inner.autoresize().is_ok() && self.last_area == self.inner.get_frame().size()
|
||||
}
|
||||
|
||||
pub fn size() -> WindowSize {
|
||||
let mut size = WindowSize { rows: 0, columns: 0, width: 0, height: 0 };
|
||||
|
Loading…
Reference in New Issue
Block a user