feat: confirm on deleting

This commit is contained in:
sxyazi 2023-07-13 21:25:03 +08:00
parent 9f5707ad74
commit 2279d95fab
No known key found for this signature in database
6 changed files with 74 additions and 35 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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