mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-23 15:51:03 +00:00
feat: add show_icons and truncate_paths options to [confirm]
The trash/delete/overwrite confirmation dialogs (icons added in #4096) had no way to disable icons, and long file paths would hard word-wrap onto a new line right after the icon since a path has no internal spaces to break on cleanly. Add two options under [confirm], both independently toggleable from the existing [input] show_icons: - show_icons (default true): skip the icon lookup for confirm dialogs. - truncate_paths (default false): RTL-truncate each path with a leading "…" to fit the popup's configured width, instead of word-wrapping. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
b53a1b2ea7
commit
4a128fc1f5
6 changed files with 64 additions and 9 deletions
|
|
@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
|||
- H/M/L Vim-like motion for moving cursor relative to viewport ([#3970])
|
||||
- Context-aware icons for inputs ([#4080])
|
||||
- New `show_icons` option under `[input]` to toggle icons on input popups ([#4100])
|
||||
- New `show_icons` and `truncate_paths` options under `[confirm]` to toggle icons and RTL-ellipsis-truncate long paths on trash/delete/overwrite confirmations ([#4100])
|
||||
- Show file icons in trash/delete/overwrite confirmations ([#4096])
|
||||
- Dynamic keymap Lua API ([#4031])
|
||||
- New `ui.Input` element ([#4040])
|
||||
|
|
|
|||
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -4882,6 +4882,7 @@ dependencies = [
|
|||
"serde_with",
|
||||
"toml",
|
||||
"tracing",
|
||||
"unicode-width",
|
||||
"yazi-binding",
|
||||
"yazi-codegen",
|
||||
"yazi-fs",
|
||||
|
|
|
|||
|
|
@ -39,3 +39,4 @@ serde = { workspace = true }
|
|||
serde_with = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
unicode-width = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -214,6 +214,9 @@ shell_origin = "top-center"
|
|||
shell_offset = [ 0, 2, 50, 3 ]
|
||||
|
||||
[confirm]
|
||||
truncate_paths = false
|
||||
show_icons = true
|
||||
|
||||
# trash
|
||||
trash_title = "Trash {n} selected file{s}?"
|
||||
trash_origin = "center"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ use yazi_codegen::{DeserializeOver, DeserializeOver2};
|
|||
|
||||
#[derive(Deserialize, DeserializeOver, DeserializeOver2)]
|
||||
pub struct Confirm {
|
||||
pub truncate_paths: bool,
|
||||
pub show_icons: bool,
|
||||
|
||||
// trash
|
||||
pub trash_title: String,
|
||||
pub trash_origin: Origin,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use std::slice;
|
|||
|
||||
use ratatui_core::text::{Line, Span, Text};
|
||||
use ratatui_widgets::paragraph::{Paragraph, Wrap};
|
||||
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||
use yazi_binding::position::Position;
|
||||
use yazi_fs::file::File;
|
||||
use yazi_macro::impl_data_any;
|
||||
|
|
@ -9,6 +10,11 @@ use yazi_shared::strand::ToStrand;
|
|||
|
||||
use crate::{THEME, YAZI};
|
||||
|
||||
// Mirrors the horizontal `Margin::new(2, 0)` the confirm list widget applies
|
||||
// around its content (yazi-fm/src/confirm/list.rs), so a path truncated here
|
||||
// to the popup's configured width doesn't still overflow once rendered.
|
||||
const LIST_MARGIN: u16 = 4;
|
||||
|
||||
// --- PickCfg
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PickCfg {
|
||||
|
|
@ -46,29 +52,32 @@ impl ConfirmCfg {
|
|||
}
|
||||
|
||||
pub fn trash(files: &[File]) -> Self {
|
||||
let position = YAZI.confirm.trash_position();
|
||||
Self::new(
|
||||
Self::replace_number(&YAZI.confirm.trash_title, files.len()),
|
||||
YAZI.confirm.trash_position(),
|
||||
position,
|
||||
None,
|
||||
Self::truncate_files(files, 100),
|
||||
Self::truncate_files(files, 100, position.width),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn delete(files: &[File]) -> Self {
|
||||
let position = YAZI.confirm.delete_position();
|
||||
Self::new(
|
||||
Self::replace_number(&YAZI.confirm.delete_title, files.len()),
|
||||
YAZI.confirm.delete_position(),
|
||||
position,
|
||||
None,
|
||||
Self::truncate_files(files, 100),
|
||||
Self::truncate_files(files, 100, position.width),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn overwrite(file: &File) -> Self {
|
||||
let position = YAZI.confirm.overwrite_position();
|
||||
Self::new(
|
||||
YAZI.confirm.overwrite_title.clone(),
|
||||
YAZI.confirm.overwrite_position(),
|
||||
position,
|
||||
Some(Text::raw(&YAZI.confirm.overwrite_body)),
|
||||
Self::truncate_files(slice::from_ref(file), 1),
|
||||
Self::truncate_files(slice::from_ref(file), 1, position.width),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +110,9 @@ impl ConfirmCfg {
|
|||
Some(Text::from_iter(lines))
|
||||
}
|
||||
|
||||
fn truncate_files(files: &[File], max: usize) -> Option<Text<'static>> {
|
||||
fn truncate_files(files: &[File], max: usize, width: u16) -> Option<Text<'static>> {
|
||||
let budget = (width as usize).saturating_sub(LIST_MARGIN as usize);
|
||||
|
||||
let mut lines = Vec::with_capacity(files.len().min(max + 1));
|
||||
for (i, f) in files.iter().enumerate() {
|
||||
if i >= max {
|
||||
|
|
@ -110,12 +121,47 @@ impl ConfirmCfg {
|
|||
}
|
||||
|
||||
lines.push(Line::default());
|
||||
if let Some(icon) = THEME.icon.matches(f, false) {
|
||||
let mut prefix = 0;
|
||||
if YAZI.confirm.show_icons
|
||||
&& let Some(icon) = THEME.icon.matches(f, false)
|
||||
{
|
||||
prefix = icon.text.width() + 1;
|
||||
lines[i].push_span(Span::styled(icon.text, icon.style));
|
||||
lines[i].push_span(" ");
|
||||
}
|
||||
lines[i].push_span(f.url.to_strand().into_string_lossy());
|
||||
|
||||
let path = f.url.to_strand().into_string_lossy();
|
||||
let path = if YAZI.confirm.truncate_paths {
|
||||
Self::truncate_rtl(&path, budget.saturating_sub(prefix))
|
||||
} else {
|
||||
path
|
||||
};
|
||||
lines[i].push_span(path);
|
||||
}
|
||||
Some(lines.into())
|
||||
}
|
||||
|
||||
// Truncates `s` from the left with a leading `…`, keeping its tail, since
|
||||
// the end of a path is usually more identifying than its start.
|
||||
fn truncate_rtl(s: &str, max: usize) -> String {
|
||||
if s.width() <= max {
|
||||
return s.to_owned();
|
||||
}
|
||||
if max == 0 {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
let mut adv = 0;
|
||||
let mut idx = s.len();
|
||||
for (i, c) in s.char_indices().rev() {
|
||||
let w = c.width().unwrap_or(0);
|
||||
if adv + w > max.saturating_sub(1) {
|
||||
break;
|
||||
}
|
||||
adv += w;
|
||||
idx = i;
|
||||
}
|
||||
|
||||
format!("…{}", &s[idx..])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue