mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: confirm on deleting
This commit is contained in:
parent
9f5707ad74
commit
2279d95fab
6 changed files with 74 additions and 35 deletions
|
|
@ -24,7 +24,13 @@ pub struct Input {
|
|||
pub struct InputOpt {
|
||||
pub title: String,
|
||||
pub value: String,
|
||||
pub position: (u16, u16),
|
||||
pub position: InputPos,
|
||||
}
|
||||
|
||||
pub enum InputPos {
|
||||
Top,
|
||||
Hovered,
|
||||
Coords(u16, u16),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
|
|
@ -46,7 +52,10 @@ impl Input {
|
|||
pub fn show(&mut self, opt: InputOpt, tx: Sender<Result<String>>) {
|
||||
self.title = opt.title;
|
||||
self.value = opt.value;
|
||||
self.position = opt.position;
|
||||
self.position = match opt.position {
|
||||
InputPos::Coords(x, y) => (x, y),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
self.mode = InputMode::Insert;
|
||||
self.cursor = self.count();
|
||||
|
|
@ -276,7 +285,9 @@ impl Input {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn top_position() -> (u16, u16) { ((tty_size().ws_col / 2).saturating_sub(25), 2) }
|
||||
pub fn top_position() -> InputPos {
|
||||
InputPos::Coords((tty_size().ws_col / 2).saturating_sub(25), 2)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn count(&self) -> usize { self.value.chars().count() }
|
||||
|
|
|
|||
|
|
@ -127,6 +127,9 @@ impl Folder {
|
|||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_selected(&self) -> bool { self.files.iter().any(|(_, item)| item.is_selected) }
|
||||
|
||||
pub fn selected(&self) -> Option<Vec<PathBuf>> {
|
||||
let v = self
|
||||
.files
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{collections::{BTreeSet, HashMap, HashSet}, mem, path::PathBuf};
|
|||
use tokio::fs;
|
||||
|
||||
use super::{PreviewData, Tab, Tabs, Watcher};
|
||||
use crate::{core::{files::{File, FilesOp}, input::{Input, InputOpt}, manager::Folder, tasks::Precache}, emit};
|
||||
use crate::{core::{files::{File, FilesOp}, input::{Input, InputOpt, InputPos}, manager::Folder, tasks::Precache}, emit};
|
||||
|
||||
pub struct Manager {
|
||||
tabs: Tabs,
|
||||
|
|
@ -92,14 +92,12 @@ impl Manager {
|
|||
pub fn yanked(&self) -> &(bool, HashSet<PathBuf>) { &self.yanked }
|
||||
|
||||
pub fn create(&self) -> bool {
|
||||
let pos = Input::top_position();
|
||||
let cwd = self.current().cwd.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let result = emit!(Input(InputOpt {
|
||||
title: "Create:".to_string(),
|
||||
value: "".to_string(),
|
||||
position: pos,
|
||||
position: InputPos::Top,
|
||||
}))
|
||||
.await;
|
||||
|
||||
|
|
@ -113,35 +111,33 @@ impl Manager {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn rename(&self) -> bool {
|
||||
let selected = self.selected();
|
||||
if selected.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if selected.len() > 1 {
|
||||
if self.current().has_selected() {
|
||||
return self.bulk_rename();
|
||||
}
|
||||
|
||||
let rect = self.current().rect_current(&selected[0]).unwrap();
|
||||
let hovered = if let Some(h) = self.hovered() {
|
||||
h.path.clone()
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
let result = emit!(Input(InputOpt {
|
||||
title: "Rename:".to_string(),
|
||||
value: selected[0].file_name().unwrap().to_string_lossy().to_string(),
|
||||
position: (rect.x, rect.y),
|
||||
value: hovered.file_name().unwrap().to_string_lossy().to_string(),
|
||||
position: InputPos::Hovered,
|
||||
}))
|
||||
.await;
|
||||
|
||||
if let Ok(new) = result {
|
||||
let to = selected[0].parent().unwrap().join(new);
|
||||
fs::rename(&selected[0], to).await.ok();
|
||||
let to = hovered.parent().unwrap().join(new);
|
||||
fs::rename(&hovered, to).await.ok();
|
||||
}
|
||||
});
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use tokio::task::JoinHandle;
|
|||
use tracing::info;
|
||||
|
||||
use super::{Folder, Mode, Preview};
|
||||
use crate::{core::{external::{self, FzfOpt, ZoxideOpt}, files::{File, Files, FilesOp}, input::{Input, InputOpt}, Event, BLOCKER}, emit, misc::Defer};
|
||||
use crate::{core::{external::{self, FzfOpt, ZoxideOpt}, files::{File, Files, FilesOp}, input::{Input, InputOpt, InputPos}, Event, BLOCKER}, emit, misc::Defer};
|
||||
|
||||
pub struct Tab {
|
||||
pub(super) current: Folder,
|
||||
|
|
@ -184,12 +184,11 @@ impl Tab {
|
|||
let cwd = self.current.cwd.clone();
|
||||
let hidden = self.current.files.show_hidden;
|
||||
|
||||
let pos = Input::top_position();
|
||||
self.search = Some(tokio::spawn(async move {
|
||||
let subject = emit!(Input(InputOpt {
|
||||
title: "Search:".to_string(),
|
||||
value: "".to_string(),
|
||||
position: pos,
|
||||
position: InputPos::Top,
|
||||
}))
|
||||
.await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use std::{collections::{BTreeMap, HashSet}, path::PathBuf};
|
||||
use std::{collections::{BTreeMap, HashSet}, path::PathBuf, sync::Arc};
|
||||
|
||||
use tracing::trace;
|
||||
|
||||
use super::{Scheduler, TASKS_PADDING, TASKS_PERCENT};
|
||||
use crate::{config::OPEN, misc::tty_size};
|
||||
use crate::{config::OPEN, core::input::{Input, InputOpt, InputPos}, emit, misc::tty_size};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Task {
|
||||
|
|
@ -43,7 +43,7 @@ pub enum TaskStage {
|
|||
}
|
||||
|
||||
pub struct Tasks {
|
||||
scheduler: Scheduler,
|
||||
scheduler: Arc<Scheduler>,
|
||||
|
||||
pub visible: bool,
|
||||
pub cursor: usize,
|
||||
|
|
@ -52,7 +52,12 @@ pub struct Tasks {
|
|||
|
||||
impl Tasks {
|
||||
pub fn start() -> Self {
|
||||
Self { scheduler: Scheduler::start(), visible: false, cursor: 0, progress: (100, 0) }
|
||||
Self {
|
||||
scheduler: Arc::new(Scheduler::start()),
|
||||
visible: false,
|
||||
cursor: 0,
|
||||
progress: (100, 0),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -143,13 +148,28 @@ impl Tasks {
|
|||
}
|
||||
|
||||
pub fn file_remove(&self, targets: Vec<PathBuf>, permanently: bool) -> bool {
|
||||
for p in targets {
|
||||
if permanently {
|
||||
self.scheduler.file_delete(p);
|
||||
} else {
|
||||
self.scheduler.file_trash(p);
|
||||
let scheduler = self.scheduler.clone();
|
||||
tokio::spawn(async move {
|
||||
let result = emit!(Input(InputOpt {
|
||||
title: "Are you sure delete these files? (Y/n)".to_string(),
|
||||
value: "".to_string(),
|
||||
position: InputPos::Hovered,
|
||||
}))
|
||||
.await;
|
||||
|
||||
if let Ok(choice) = result {
|
||||
if choice.to_lowercase() != "y" {
|
||||
return;
|
||||
}
|
||||
for p in targets {
|
||||
if permanently {
|
||||
scheduler.file_delete(p);
|
||||
} else {
|
||||
scheduler.file_trash(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crossterm::event::KeyEvent;
|
|||
use tokio::sync::oneshot::{self};
|
||||
|
||||
use super::{root::Root, Ctx, Executor, Logs, Signals, Term};
|
||||
use crate::{config::keymap::Key, core::{files::FilesOp, Event}, emit};
|
||||
use crate::{config::keymap::Key, core::{files::FilesOp, input::{Input, InputPos}, Event}, emit};
|
||||
|
||||
pub struct App {
|
||||
cx: Ctx,
|
||||
|
|
@ -104,7 +104,17 @@ impl App {
|
|||
emit!(Render);
|
||||
}
|
||||
|
||||
Event::Input(opt, tx) => {
|
||||
Event::Input(mut opt, tx) => {
|
||||
opt.position = match opt.position {
|
||||
InputPos::Top => Input::top_position(),
|
||||
InputPos::Hovered => manager
|
||||
.hovered()
|
||||
.and_then(|h| manager.current().rect_current(&h.path))
|
||||
.map(|r| InputPos::Coords(r.x, r.y))
|
||||
.unwrap_or_else(|| Input::top_position()),
|
||||
p @ InputPos::Coords(..) => p,
|
||||
};
|
||||
|
||||
self.cx.input.show(opt, tx);
|
||||
emit!(Render);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue