mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
666adc7c2f
commit
854c142d2a
24 changed files with 227 additions and 132 deletions
|
|
@ -24,7 +24,7 @@ libc = "0.2.158"
|
||||||
md-5 = "0.10.6"
|
md-5 = "0.10.6"
|
||||||
mlua = { version = "0.9.9", features = [ "lua54", "serialize", "macros", "async" ] }
|
mlua = { version = "0.9.9", features = [ "lua54", "serialize", "macros", "async" ] }
|
||||||
parking_lot = "0.12.3"
|
parking_lot = "0.12.3"
|
||||||
ratatui = "0.27.0"
|
ratatui = { version = "0.27.0", features = [ "unstable-rendered-line-info" ] }
|
||||||
regex = "1.10.6"
|
regex = "1.10.6"
|
||||||
scopeguard = "1.2.0"
|
scopeguard = "1.2.0"
|
||||||
serde = { version = "1.0.208", features = [ "derive" ] }
|
serde = { version = "1.0.208", features = [ "derive" ] }
|
||||||
|
|
|
||||||
|
|
@ -170,25 +170,25 @@ shell_offset = [ 0, 2, 50, 3 ]
|
||||||
[confirm]
|
[confirm]
|
||||||
# trash
|
# trash
|
||||||
trash_title = "Trash {n} selected file{s}?"
|
trash_title = "Trash {n} selected file{s}?"
|
||||||
trash_origin = "top-center"
|
trash_origin = "center"
|
||||||
trash_offset = [ 0, 5, 70, 20 ]
|
trash_offset = [ 0, 0, 70, 20 ]
|
||||||
|
|
||||||
# delete
|
# delete
|
||||||
delete_title = "Permanently delete {n} selected file{s}?"
|
delete_title = "Permanently delete {n} selected file{s}?"
|
||||||
delete_origin = "top-center"
|
delete_origin = "center"
|
||||||
delete_offset = [ 0, 5, 70, 20 ]
|
delete_offset = [ 0, 0, 70, 20 ]
|
||||||
|
|
||||||
# overwrite
|
# overwrite
|
||||||
overwrite_title = "Overwrite file?"
|
overwrite_title = "Overwrite file?"
|
||||||
overwrite_content = "Will overwrite the following file:\n{url}"
|
overwrite_content = "Will overwrite the following file:"
|
||||||
overwrite_origin = "top-center"
|
overwrite_origin = "center"
|
||||||
overwrite_offset = [ 0, 2, 50, 20 ]
|
overwrite_offset = [ 0, 0, 50, 20 ]
|
||||||
|
|
||||||
# quit
|
# quit
|
||||||
quit_title = "Quit?"
|
quit_title = "Quit?"
|
||||||
quit_content = "The following task is still running, are you sure you want to quit?\n{tasks}"
|
quit_content = "The following task is still running, are you sure you want to quit?"
|
||||||
quit_origin = "top-center"
|
quit_origin = "center"
|
||||||
quit_offset = [ 0, 2, 50, 20 ]
|
quit_offset = [ 0, 0, 50, 20 ]
|
||||||
|
|
||||||
[select]
|
[select]
|
||||||
open_title = "Open with:"
|
open_title = "Open with:"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Deserialize)]
|
#[derive(Clone, Copy, Debug, Default, Deserialize)]
|
||||||
#[serde(try_from = "Vec<i16>")]
|
#[serde(try_from = "Vec<i16>")]
|
||||||
pub struct Offset {
|
pub struct Offset {
|
||||||
pub x: i16,
|
pub x: i16,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
use ratatui::{text::Text, widgets::{Paragraph, Wrap}};
|
||||||
use yazi_shared::fs::Url;
|
use yazi_shared::fs::Url;
|
||||||
|
|
||||||
use super::{Offset, Position};
|
use super::{Offset, Origin, Position};
|
||||||
use crate::{CONFIRM, INPUT, SELECT};
|
use crate::{CONFIRM, INPUT, SELECT};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -24,8 +25,9 @@ pub struct SelectCfg {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ConfirmCfg {
|
pub struct ConfirmCfg {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub content: String,
|
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
|
pub content: Paragraph<'static>,
|
||||||
|
pub list: Paragraph<'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputCfg {
|
impl InputCfg {
|
||||||
|
|
@ -103,36 +105,54 @@ impl InputCfg {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfirmCfg {
|
impl ConfirmCfg {
|
||||||
pub fn trash(urls: &[yazi_shared::fs::Url]) -> Self {
|
fn new(
|
||||||
|
title: String,
|
||||||
|
(origin, offset): (Origin, Offset),
|
||||||
|
content: Option<Text<'static>>,
|
||||||
|
list: Option<Text<'static>>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
title: Self::replace_number(&CONFIRM.trash_title, urls.len(), usize::MAX),
|
title,
|
||||||
position: Position::new(CONFIRM.trash_origin, CONFIRM.trash_offset),
|
position: Position::new(origin, offset),
|
||||||
content: urls.iter().map(ToString::to_string).collect::<Vec<_>>().join("\n"),
|
content: content.map(|c| Paragraph::new(c).wrap(Wrap { trim: false })).unwrap_or_default(),
|
||||||
|
list: list.map(|l| Paragraph::new(l).wrap(Wrap { trim: false })).unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn trash(urls: &[yazi_shared::fs::Url]) -> Self {
|
||||||
|
Self::new(
|
||||||
|
Self::replace_number(&CONFIRM.trash_title, urls.len(), usize::MAX),
|
||||||
|
(CONFIRM.trash_origin, CONFIRM.trash_offset),
|
||||||
|
None,
|
||||||
|
Some(urls.iter().map(ToString::to_string).collect()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn delete(urls: &[yazi_shared::fs::Url]) -> Self {
|
pub fn delete(urls: &[yazi_shared::fs::Url]) -> Self {
|
||||||
Self {
|
Self::new(
|
||||||
title: Self::replace_number(&CONFIRM.delete_title, urls.len(), usize::MAX),
|
Self::replace_number(&CONFIRM.delete_title, urls.len(), usize::MAX),
|
||||||
position: Position::new(CONFIRM.delete_origin, CONFIRM.delete_offset),
|
(CONFIRM.delete_origin, CONFIRM.delete_offset),
|
||||||
content: urls.iter().map(ToString::to_string).collect::<Vec<_>>().join("\n"),
|
None,
|
||||||
}
|
Some(urls.iter().map(ToString::to_string).collect()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn overwrite(url: &Url) -> Self {
|
pub fn overwrite(url: &Url) -> Self {
|
||||||
Self {
|
Self::new(
|
||||||
title: CONFIRM.overwrite_title.to_owned(),
|
CONFIRM.overwrite_title.to_owned(),
|
||||||
content: CONFIRM.overwrite_content.replace("{url}", &url.to_string()),
|
(CONFIRM.overwrite_origin, CONFIRM.overwrite_offset),
|
||||||
position: Position::new(CONFIRM.overwrite_origin, CONFIRM.overwrite_offset),
|
Some(Text::raw(&CONFIRM.overwrite_content)),
|
||||||
}
|
Some(url.to_string().into()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn quit(tasks: Vec<String>) -> Self {
|
pub fn quit(left: Vec<String>) -> Self {
|
||||||
Self {
|
Self::new(
|
||||||
title: Self::replace_number(&CONFIRM.quit_title, tasks.len(), 10),
|
Self::replace_number(&CONFIRM.quit_title, left.len(), 10),
|
||||||
content: CONFIRM.quit_content.replace("{tasks}", &tasks.join("\n")),
|
(CONFIRM.quit_origin, CONFIRM.quit_offset),
|
||||||
position: Position::new(CONFIRM.quit_origin, CONFIRM.quit_offset),
|
Some(Text::raw(&CONFIRM.quit_content)),
|
||||||
}
|
Some(left.into_iter().collect()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_number(tpl: &str, n: usize, max: usize) -> String {
|
fn replace_number(tpl: &str, n: usize, max: usize) -> String {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{fmt::Display, str::FromStr};
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
|
||||||
#[serde(try_from = "String")]
|
#[serde(try_from = "String")]
|
||||||
pub enum Origin {
|
pub enum Origin {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use ratatui::layout::Rect;
|
||||||
|
|
||||||
use super::{Offset, Origin};
|
use super::{Offset, Origin};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct Position {
|
pub struct Position {
|
||||||
pub origin: Origin,
|
pub origin: Origin,
|
||||||
pub offset: Offset,
|
pub offset: Offset,
|
||||||
|
|
@ -30,7 +30,7 @@ impl Position {
|
||||||
let max_y = rows.saturating_sub(height);
|
let max_y = rows.saturating_sub(height);
|
||||||
let new_y = match self.origin {
|
let new_y = match self.origin {
|
||||||
TopLeft | TopCenter | TopRight => y.clamp(0, max_y as i16) as u16,
|
TopLeft | TopCenter | TopRight => y.clamp(0, max_y as i16) as u16,
|
||||||
Center => (max_y / 2).saturating_sub(height / 2).saturating_add_signed(y).clamp(0, max_y),
|
Center => (rows / 2).saturating_sub(height / 2).saturating_add_signed(y).clamp(0, max_y),
|
||||||
BottomLeft | BottomCenter | BottomRight => max_y.saturating_add_signed(y).clamp(0, max_y),
|
BottomLeft | BottomCenter | BottomRight => max_y.saturating_add_signed(y).clamp(0, max_y),
|
||||||
Hovered => unreachable!(),
|
Hovered => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use yazi_shared::{event::{Cmd, Data}, render};
|
use yazi_shared::{event::{Cmd, Data}, render};
|
||||||
|
|
||||||
use crate::confirm::Confirm;
|
use crate::{confirm::Confirm, manager::Manager};
|
||||||
|
|
||||||
pub struct Opt {
|
pub struct Opt {
|
||||||
step: isize,
|
step: isize,
|
||||||
|
|
@ -11,13 +11,14 @@ impl From<Cmd> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Confirm {
|
impl Confirm {
|
||||||
fn next(&mut self, step: usize) {
|
fn next(&mut self, step: usize, width: u16) {
|
||||||
if self.lines == 0 {
|
let height = self.list.line_count(width);
|
||||||
|
if height == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let old = self.offset;
|
let old = self.offset;
|
||||||
self.offset = (self.offset + step).min(self.lines - 1);
|
self.offset = (self.offset + step).min(height - 1);
|
||||||
|
|
||||||
render!(old != self.offset);
|
render!(old != self.offset);
|
||||||
}
|
}
|
||||||
|
|
@ -29,8 +30,12 @@ impl Confirm {
|
||||||
render!(old != self.offset);
|
render!(old != self.offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arrow(&mut self, opt: impl Into<Opt>) {
|
pub fn arrow(&mut self, opt: impl Into<Opt>, manager: &Manager) {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if opt.step > 0 { self.next(opt.step as usize) } else { self.prev(opt.step.unsigned_abs()) }
|
if opt.step > 0 {
|
||||||
|
self.next(opt.step as usize, manager.area(self.position).width)
|
||||||
|
} else {
|
||||||
|
self.prev(opt.step.unsigned_abs())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ impl Confirm {
|
||||||
self.close(false);
|
self.close(false);
|
||||||
self.title = opt.cfg.title;
|
self.title = opt.cfg.title;
|
||||||
self.content = opt.cfg.content;
|
self.content = opt.cfg.content;
|
||||||
self.lines = self.content.lines().count();
|
self.list = opt.cfg.list;
|
||||||
|
|
||||||
self.offset = 0;
|
self.offset = 0;
|
||||||
self.position = opt.cfg.position;
|
self.position = opt.cfg.position;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
|
use ratatui::widgets::Paragraph;
|
||||||
use tokio::sync::oneshot::Sender;
|
use tokio::sync::oneshot::Sender;
|
||||||
use yazi_config::popup::Position;
|
use yazi_config::popup::Position;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Confirm {
|
pub struct Confirm {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub content: String,
|
pub content: Paragraph<'static>,
|
||||||
pub lines: usize,
|
pub list: Paragraph<'static>,
|
||||||
|
|
||||||
pub offset: usize,
|
pub offset: usize,
|
||||||
pub position: Position,
|
pub position: Position,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use ratatui::layout::Rect;
|
||||||
|
use yazi_adapter::Dimension;
|
||||||
|
use yazi_config::popup::{Origin, Position};
|
||||||
use yazi_fs::Folder;
|
use yazi_fs::Folder;
|
||||||
use yazi_shared::fs::{File, Url};
|
use yazi_shared::fs::{File, Url};
|
||||||
|
|
||||||
|
|
@ -25,6 +28,14 @@ impl Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn area(&self, pos: Position) -> Rect {
|
||||||
|
if pos.origin == Origin::Hovered {
|
||||||
|
self.active().hovered_rect_based(pos)
|
||||||
|
} else {
|
||||||
|
pos.rect(Dimension::available())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn shutdown(&mut self) { self.tabs.iter_mut().for_each(|t| t.shutdown()); }
|
pub fn shutdown(&mut self) { self.tabs.iter_mut().for_each(|t| t.shutdown()); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
use std::{collections::HashMap, iter};
|
use std::{collections::HashMap, iter};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use ratatui::layout::Rect;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
use yazi_adapter::Dimension;
|
||||||
|
use yazi_config::{popup::{Origin, Position}, LAYOUT};
|
||||||
use yazi_fs::{Folder, FolderStage};
|
use yazi_fs::{Folder, FolderStage};
|
||||||
use yazi_shared::{fs::Url, render};
|
use yazi_shared::{fs::Url, render};
|
||||||
|
|
||||||
|
|
@ -35,6 +38,24 @@ impl Tab {
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
// --- Current
|
// --- Current
|
||||||
|
pub fn hovered_rect(&self) -> Option<Rect> {
|
||||||
|
let y = self.current.files.position(&self.current.hovered()?.url)? - self.current.offset;
|
||||||
|
|
||||||
|
let mut rect = LAYOUT.load().current;
|
||||||
|
rect.y = rect.y.saturating_sub(1) + y as u16;
|
||||||
|
rect.height = 1;
|
||||||
|
Some(rect)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hovered_rect_based(&self, pos: Position) -> Rect {
|
||||||
|
let ws = Dimension::available();
|
||||||
|
if let Some(r) = self.hovered_rect() {
|
||||||
|
Position::sticky(ws, r, pos.offset)
|
||||||
|
} else {
|
||||||
|
Position::new(Origin::TopCenter, pos.offset).rect(ws)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn selected_or_hovered(&self, reorder: bool) -> Box<dyn Iterator<Item = &Url> + '_> {
|
pub fn selected_or_hovered(&self, reorder: bool) -> Box<dyn Iterator<Item = &Url> + '_> {
|
||||||
if self.selected.is_empty() {
|
if self.selected.is_empty() {
|
||||||
Box::new(self.current.hovered().map(|h| vec![&h.url]).unwrap_or_default().into_iter())
|
Box::new(self.current.hovered().map(|h| vec![&h.url]).unwrap_or_default().into_iter())
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ impl<'a> Widget for Completion<'a> {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let input_area = self.cx.area(&self.cx.input.position);
|
let input_area = self.cx.manager.area(self.cx.input.position);
|
||||||
let mut area = Position::sticky(Dimension::available(), input_area, Offset {
|
let mut area = Position::sticky(Dimension::available(), input_area, Offset {
|
||||||
x: 1,
|
x: 1,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
|
|
||||||
13
yazi-fm/src/confirm/buttons.rs
Normal file
13
yazi-fm/src/confirm/buttons.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
use ratatui::{buffer::Buffer, layout::{Constraint, Rect}, style::Stylize, text::Span, widgets::{Paragraph, Widget}};
|
||||||
|
|
||||||
|
pub(crate) struct Buttons;
|
||||||
|
|
||||||
|
impl Widget for Buttons {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
let chunks =
|
||||||
|
ratatui::layout::Layout::horizontal([Constraint::Fill(1), Constraint::Fill(1)]).split(area);
|
||||||
|
|
||||||
|
Paragraph::new(Span::raw(" [Y]es ").reversed()).centered().render(chunks[0], buf);
|
||||||
|
Paragraph::new(Span::raw(" (N)o ")).centered().render(chunks[1], buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use ratatui::{buffer::Buffer, layout::{Constraint, Layout, Margin, Rect}, text::Line, widgets::{Block, BorderType, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState, StatefulWidget, Widget, Wrap}};
|
use ratatui::{buffer::Buffer, layout::{Alignment, Constraint, Layout, Margin, Rect}, style::{Style, Stylize}, text::Line, widgets::{Block, BorderType, Widget}};
|
||||||
use yazi_config::THEME;
|
|
||||||
|
|
||||||
use crate::Ctx;
|
use crate::Ctx;
|
||||||
|
|
||||||
|
|
@ -10,55 +9,33 @@ pub(crate) struct Confirm<'a> {
|
||||||
impl<'a> Confirm<'a> {
|
impl<'a> Confirm<'a> {
|
||||||
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Widget for Confirm<'a> {
|
impl<'a> Widget for Confirm<'a> {
|
||||||
fn render(self, _win: Rect, buf: &mut Buffer) {
|
fn render(self, _win: Rect, buf: &mut Buffer) {
|
||||||
let confirm = &self.cx.confirm;
|
let confirm = &self.cx.confirm;
|
||||||
let area = self.cx.area(&confirm.position);
|
let area = self.cx.manager.area(confirm.position);
|
||||||
|
|
||||||
yazi_plugin::elements::Clear::default().render(area, buf);
|
yazi_plugin::elements::Clear::default().render(area, buf);
|
||||||
|
|
||||||
Block::bordered()
|
Block::bordered()
|
||||||
.border_type(BorderType::Rounded)
|
.border_type(BorderType::Rounded)
|
||||||
.border_style(THEME.input.border)
|
.border_style(Style::new().blue())
|
||||||
.title(Line::styled(&confirm.title, THEME.input.title))
|
.title(Line::styled(&confirm.title, Style::new().blue()))
|
||||||
|
.title_alignment(Alignment::Center)
|
||||||
.render(area, buf);
|
.render(area, buf);
|
||||||
|
|
||||||
let popup_layout =
|
let content = confirm.content.clone();
|
||||||
Layout::vertical(vec![Constraint::Percentage(70), Constraint::Percentage(30)])
|
let content_height = content.line_count(area.width).saturating_add(1) as u16;
|
||||||
.vertical_margin(1)
|
|
||||||
.horizontal_margin(2)
|
|
||||||
.split(area);
|
|
||||||
|
|
||||||
let button_layout = Layout::horizontal(vec![
|
let chunks = Layout::vertical([
|
||||||
Constraint::Percentage(10),
|
Constraint::Length(if content_height == 1 { 0 } else { content_height }),
|
||||||
Constraint::Percentage(30),
|
Constraint::Fill(1),
|
||||||
Constraint::Percentage(20),
|
Constraint::Length(1),
|
||||||
Constraint::Percentage(30),
|
|
||||||
Constraint::Percentage(10),
|
|
||||||
])
|
])
|
||||||
.vertical_margin(1)
|
.split(area.inner(Margin::new(0, 1)));
|
||||||
.split(popup_layout[1]);
|
|
||||||
|
|
||||||
Paragraph::new(confirm.content.lines().map(Line::from).collect::<Vec<Line>>())
|
super::Content::new(content).render(chunks[0], buf);
|
||||||
.block(Block::bordered().border_type(BorderType::Rounded).border_style(THEME.input.border))
|
super::List::new(self.cx).render(chunks[1], buf);
|
||||||
.scroll((confirm.offset as u16, 0))
|
super::Buttons.render(chunks[2], buf);
|
||||||
.wrap(Wrap { trim: false })
|
|
||||||
.render(popup_layout[0], buf);
|
|
||||||
|
|
||||||
const BORDER_SIZE: usize = 2;
|
|
||||||
if confirm.lines > popup_layout[0].as_size().height as usize - BORDER_SIZE {
|
|
||||||
let mut scrollbar_state =
|
|
||||||
ScrollbarState::new(confirm.content.lines().collect::<Vec<&str>>().len())
|
|
||||||
.position(confirm.offset);
|
|
||||||
|
|
||||||
Scrollbar::new(ScrollbarOrientation::VerticalRight).render(
|
|
||||||
popup_layout[0].inner(Margin { vertical: 1, horizontal: 0 }),
|
|
||||||
buf,
|
|
||||||
&mut scrollbar_state,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Paragraph::new("[Y]es").block(Block::bordered()).centered().render(button_layout[1], buf);
|
|
||||||
Paragraph::new("(N)o").block(Block::bordered()).centered().render(button_layout[3], buf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
22
yazi-fm/src/confirm/content.rs
Normal file
22
yazi-fm/src/confirm/content.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
use ratatui::{buffer::Buffer, layout::{Margin, Rect}, style::{Style, Stylize}, widgets::{Block, Borders, Paragraph, Widget}};
|
||||||
|
|
||||||
|
pub(crate) struct Content<'a> {
|
||||||
|
p: Paragraph<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Content<'a> {
|
||||||
|
pub(crate) fn new(p: Paragraph<'a>) -> Self { Self { p } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Widget for Content<'a> {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
// Content area
|
||||||
|
let inner = area.inner(Margin::new(1, 0));
|
||||||
|
|
||||||
|
// Bottom border
|
||||||
|
let block = Block::new().borders(Borders::BOTTOM).border_style(Style::new().blue());
|
||||||
|
block.clone().render(area.inner(Margin::new(1, 0)), buf);
|
||||||
|
|
||||||
|
self.p.alignment(ratatui::layout::Alignment::Center).block(block).render(inner, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
44
yazi-fm/src/confirm/list.rs
Normal file
44
yazi-fm/src/confirm/list.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
use ratatui::{buffer::Buffer, layout::{Margin, Rect}, style::{Style, Stylize}, widgets::{Block, Borders, Scrollbar, ScrollbarOrientation, ScrollbarState, StatefulWidget, Widget, Wrap}};
|
||||||
|
|
||||||
|
use crate::Ctx;
|
||||||
|
|
||||||
|
pub(crate) struct List<'a> {
|
||||||
|
cx: &'a Ctx,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> List<'a> {
|
||||||
|
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Widget for List<'a> {
|
||||||
|
fn render(self, mut area: Rect, buf: &mut Buffer) {
|
||||||
|
// List content area
|
||||||
|
let inner = area.inner(Margin::new(2, 0));
|
||||||
|
|
||||||
|
// Bottom border
|
||||||
|
let block = Block::new().borders(Borders::BOTTOM).border_style(Style::new().blue());
|
||||||
|
block.clone().render(area.inner(Margin::new(1, 0)), buf);
|
||||||
|
|
||||||
|
let list = self
|
||||||
|
.cx
|
||||||
|
.confirm
|
||||||
|
.list
|
||||||
|
.clone()
|
||||||
|
.scroll((self.cx.confirm.offset as u16, 0))
|
||||||
|
.block(block)
|
||||||
|
.wrap(Wrap { trim: false });
|
||||||
|
|
||||||
|
// Vertical scrollbar
|
||||||
|
let lines = list.line_count(inner.width);
|
||||||
|
if lines >= inner.height as usize {
|
||||||
|
area.height = area.height.saturating_sub(1);
|
||||||
|
Scrollbar::new(ScrollbarOrientation::VerticalRight).render(
|
||||||
|
area,
|
||||||
|
buf,
|
||||||
|
&mut ScrollbarState::new(lines).position(self.cx.confirm.offset),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
list.render(inner, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,9 @@
|
||||||
|
mod buttons;
|
||||||
mod confirm;
|
mod confirm;
|
||||||
|
mod content;
|
||||||
|
mod list;
|
||||||
|
|
||||||
|
use buttons::*;
|
||||||
pub(super) use confirm::*;
|
pub(super) use confirm::*;
|
||||||
|
use content::*;
|
||||||
|
use list::*;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
use ratatui::layout::Rect;
|
use ratatui::layout::Rect;
|
||||||
use yazi_adapter::Dimension;
|
|
||||||
use yazi_config::popup::{Origin, Position};
|
|
||||||
use yazi_core::{completion::Completion, confirm::Confirm, help::Help, input::Input, manager::Manager, notify::Notify, select::Select, tasks::Tasks, which::Which};
|
use yazi_core::{completion::Completion, confirm::Confirm, help::Help, input::Input, manager::Manager, notify::Notify, select::Select, tasks::Tasks, which::Which};
|
||||||
|
|
||||||
pub struct Ctx {
|
pub struct Ctx {
|
||||||
|
|
@ -30,25 +28,10 @@ impl Ctx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn area(&self, position: &Position) -> Rect {
|
|
||||||
let ws = Dimension::available();
|
|
||||||
if position.origin != Origin::Hovered {
|
|
||||||
return position.rect(ws);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(r) =
|
|
||||||
self.manager.hovered().and_then(|h| self.manager.current().rect_current(&h.url))
|
|
||||||
{
|
|
||||||
Position::sticky(ws, r, position.offset)
|
|
||||||
} else {
|
|
||||||
Position::new(Origin::TopCenter, position.offset).rect(ws)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cursor(&self) -> Option<(u16, u16)> {
|
pub fn cursor(&self) -> Option<(u16, u16)> {
|
||||||
if self.input.visible {
|
if self.input.visible {
|
||||||
let Rect { x, y, .. } = self.area(&self.input.position);
|
let Rect { x, y, .. } = self.manager.area(self.input.position);
|
||||||
return Some((x + 1 + self.input.cursor(), y + 1));
|
return Some((x + 1 + self.input.cursor(), y + 1));
|
||||||
}
|
}
|
||||||
if let Some((x, y)) = self.help.cursor() {
|
if let Some((x, y)) = self.help.cursor() {
|
||||||
|
|
|
||||||
|
|
@ -252,14 +252,14 @@ impl<'a> Executor<'a> {
|
||||||
|
|
||||||
fn confirm(&mut self, cmd: Cmd) {
|
fn confirm(&mut self, cmd: Cmd) {
|
||||||
macro_rules! on {
|
macro_rules! on {
|
||||||
($name:ident) => {
|
($name:ident $(,$args:expr)*) => {
|
||||||
if cmd.name == stringify!($name) {
|
if cmd.name == stringify!($name) {
|
||||||
return self.app.cx.confirm.$name(cmd);
|
return self.app.cx.confirm.$name(cmd, $($args),*);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
on!(arrow);
|
on!(arrow, &self.app.cx.manager);
|
||||||
on!(show);
|
on!(show);
|
||||||
on!(close);
|
on!(close);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ impl<'a> Input<'a> {
|
||||||
impl<'a> Widget for Input<'a> {
|
impl<'a> Widget for Input<'a> {
|
||||||
fn render(self, win: Rect, buf: &mut Buffer) {
|
fn render(self, win: Rect, buf: &mut Buffer) {
|
||||||
let input = &self.cx.input;
|
let input = &self.cx.input;
|
||||||
let area = self.cx.area(&input.position);
|
let area = self.cx.manager.area(input.position);
|
||||||
|
|
||||||
yazi_plugin::elements::Clear::default().render(area, buf);
|
yazi_plugin::elements::Clear::default().render(area, buf);
|
||||||
Paragraph::new(self.highlighted_value().unwrap_or_else(|_| Line::from(input.value())))
|
Paragraph::new(self.highlighted_value().unwrap_or_else(|_| Line::from(input.value())))
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ impl<'a> Select<'a> {
|
||||||
impl<'a> Widget for Select<'a> {
|
impl<'a> Widget for Select<'a> {
|
||||||
fn render(self, _: Rect, buf: &mut Buffer) {
|
fn render(self, _: Rect, buf: &mut Buffer) {
|
||||||
let select = &self.cx.select;
|
let select = &self.cx.select;
|
||||||
let area = self.cx.area(&select.position);
|
let area = self.cx.manager.area(select.position);
|
||||||
|
|
||||||
let items: Vec<_> = select
|
let items: Vec<_> = select
|
||||||
.window()
|
.window()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use ratatui::layout::Rect;
|
|
||||||
use yazi_config::{LAYOUT, MANAGER};
|
use yazi_config::{LAYOUT, MANAGER};
|
||||||
use yazi_proxy::ManagerProxy;
|
use yazi_proxy::ManagerProxy;
|
||||||
use yazi_shared::fs::{Cha, File, FilesOp, Url};
|
use yazi_shared::fs::{Cha, File, FilesOp, Url};
|
||||||
|
|
@ -151,13 +150,4 @@ impl Folder {
|
||||||
let end = ((page + 2) * limit).min(len);
|
let end = ((page + 2) * limit).min(len);
|
||||||
&self.files[start..end]
|
&self.files[start..end]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rect_current(&self, url: &Url) -> Option<Rect> {
|
|
||||||
let y = self.files.position(url)? - self.offset;
|
|
||||||
|
|
||||||
let mut rect = LAYOUT.load().current;
|
|
||||||
rect.y = rect.y.saturating_sub(1) + y as u16;
|
|
||||||
rect.height = 1;
|
|
||||||
Some(rect)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ use std::{str::FromStr, time::Duration};
|
||||||
use mlua::{ExternalError, ExternalResult, IntoLuaMulti, Lua, Table, Value};
|
use mlua::{ExternalError, ExternalResult, IntoLuaMulti, Lua, Table, Value};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||||
use yazi_config::{keymap::{Control, Key}, popup::{ConfirmCfg, InputCfg}};
|
use yazi_config::{keymap::{Control, Key}, popup::InputCfg};
|
||||||
use yazi_proxy::{AppProxy, ConfirmProxy, InputProxy};
|
use yazi_proxy::{AppProxy, InputProxy};
|
||||||
use yazi_shared::{emit, event::Cmd, Debounce, Layer};
|
use yazi_shared::{emit, event::Cmd, Debounce, Layer};
|
||||||
|
|
||||||
use super::Utils;
|
use super::Utils;
|
||||||
|
|
@ -86,17 +86,18 @@ impl Utils {
|
||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
ya.raw_set(
|
// TODO: redesign the confirm API
|
||||||
"confirm",
|
// ya.raw_set(
|
||||||
lua.create_async_function(|_, t: Table| async move {
|
// "confirm",
|
||||||
let result = ConfirmProxy::show(ConfirmCfg {
|
// lua.create_async_function(|_, t: Table| async move {
|
||||||
title: t.raw_get("title")?,
|
// let result = ConfirmProxy::show(ConfirmCfg {
|
||||||
content: t.raw_get("content")?,
|
// title: t.raw_get("title")?,
|
||||||
position: Position::try_from(t.raw_get::<_, Table>("position")?)?.into(),
|
// content: t.raw_get("content")?,
|
||||||
});
|
// position: Position::try_from(t.raw_get::<_, Table>("position")?)?.into(),
|
||||||
Ok(result.await)
|
// });
|
||||||
})?,
|
// Ok(result.await)
|
||||||
)?;
|
// })?,
|
||||||
|
// )?;
|
||||||
|
|
||||||
ya.raw_set(
|
ya.raw_set(
|
||||||
"notify",
|
"notify",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ impl ConfirmProxy {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn show(cfg: ConfirmCfg) -> bool { Self::show_rx(cfg).await.unwrap_or(false) }
|
pub async fn show(cfg: ConfirmCfg) -> bool { Self::show_rx(cfg).await.unwrap_or(false) }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn show_rx(cfg: ConfirmCfg) -> oneshot::Receiver<bool> {
|
pub fn show_rx(cfg: ConfirmCfg) -> oneshot::Receiver<bool> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
emit!(Call(Cmd::new("show").with_any("tx", tx).with_any("cfg", cfg), Layer::Confirm));
|
emit!(Call(Cmd::new("show").with_any("tx", tx).with_any("cfg", cfg), Layer::Confirm));
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue