feat: when there are no files in the list, add a placeholder message (#900)

This commit is contained in:
三咲雅 · Misaki Masa 2024-04-12 10:09:29 +08:00 committed by GitHub
parent 23c38ebae0
commit 3c67cae42d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 113 additions and 40 deletions

View File

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

View File

@ -13,18 +13,23 @@ pub struct Message {
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,
max_width: max_width + NOTIFY_BORDER as usize,
}
}
}

View File

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

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

View File

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

View File

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

View File

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

View File

@ -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 = {}, {}

View File

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

View File

@ -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 = {}, {}

View File

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

View File

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