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 struct InputOpt {
pub title: String, pub title: String,
pub value: 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)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
@ -46,7 +52,10 @@ impl Input {
pub fn show(&mut self, opt: InputOpt, tx: Sender<Result<String>>) { pub fn show(&mut self, opt: InputOpt, tx: Sender<Result<String>>) {
self.title = opt.title; self.title = opt.title;
self.value = opt.value; 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.mode = InputMode::Insert;
self.cursor = self.count(); self.cursor = self.count();
@ -276,7 +285,9 @@ impl Input {
} }
#[inline] #[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] #[inline]
fn count(&self) -> usize { self.value.chars().count() } fn count(&self) -> usize { self.value.chars().count() }

View file

@ -127,6 +127,9 @@ impl Folder {
false false
} }
#[inline]
pub fn has_selected(&self) -> bool { self.files.iter().any(|(_, item)| item.is_selected) }
pub fn selected(&self) -> Option<Vec<PathBuf>> { pub fn selected(&self) -> Option<Vec<PathBuf>> {
let v = self let v = self
.files .files

View file

@ -3,7 +3,7 @@ use std::{collections::{BTreeSet, HashMap, HashSet}, mem, path::PathBuf};
use tokio::fs; use tokio::fs;
use super::{PreviewData, Tab, Tabs, Watcher}; 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 { pub struct Manager {
tabs: Tabs, tabs: Tabs,
@ -92,14 +92,12 @@ impl Manager {
pub fn yanked(&self) -> &(bool, HashSet<PathBuf>) { &self.yanked } pub fn yanked(&self) -> &(bool, HashSet<PathBuf>) { &self.yanked }
pub fn create(&self) -> bool { pub fn create(&self) -> bool {
let pos = Input::top_position();
let cwd = self.current().cwd.clone(); let cwd = self.current().cwd.clone();
tokio::spawn(async move { tokio::spawn(async move {
let result = emit!(Input(InputOpt { let result = emit!(Input(InputOpt {
title: "Create:".to_string(), title: "Create:".to_string(),
value: "".to_string(), value: "".to_string(),
position: pos, position: InputPos::Top,
})) }))
.await; .await;
@ -113,35 +111,33 @@ impl Manager {
} }
} }
}); });
false false
} }
pub fn rename(&self) -> bool { pub fn rename(&self) -> bool {
let selected = self.selected(); if self.current().has_selected() {
if selected.is_empty() {
return false;
}
if selected.len() > 1 {
return self.bulk_rename(); 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 { tokio::spawn(async move {
let result = emit!(Input(InputOpt { let result = emit!(Input(InputOpt {
title: "Rename:".to_string(), title: "Rename:".to_string(),
value: selected[0].file_name().unwrap().to_string_lossy().to_string(), value: hovered.file_name().unwrap().to_string_lossy().to_string(),
position: (rect.x, rect.y), position: InputPos::Hovered,
})) }))
.await; .await;
if let Ok(new) = result { if let Ok(new) = result {
let to = selected[0].parent().unwrap().join(new); let to = hovered.parent().unwrap().join(new);
fs::rename(&selected[0], to).await.ok(); fs::rename(&hovered, to).await.ok();
} }
}); });
false false
} }

View file

@ -5,7 +5,7 @@ use tokio::task::JoinHandle;
use tracing::info; use tracing::info;
use super::{Folder, Mode, Preview}; 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 struct Tab {
pub(super) current: Folder, pub(super) current: Folder,
@ -184,12 +184,11 @@ impl Tab {
let cwd = self.current.cwd.clone(); let cwd = self.current.cwd.clone();
let hidden = self.current.files.show_hidden; let hidden = self.current.files.show_hidden;
let pos = Input::top_position();
self.search = Some(tokio::spawn(async move { self.search = Some(tokio::spawn(async move {
let subject = emit!(Input(InputOpt { let subject = emit!(Input(InputOpt {
title: "Search:".to_string(), title: "Search:".to_string(),
value: "".to_string(), value: "".to_string(),
position: pos, position: InputPos::Top,
})) }))
.await?; .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 tracing::trace;
use super::{Scheduler, TASKS_PADDING, TASKS_PERCENT}; 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)] #[derive(Clone, Debug)]
pub struct Task { pub struct Task {
@ -43,7 +43,7 @@ pub enum TaskStage {
} }
pub struct Tasks { pub struct Tasks {
scheduler: Scheduler, scheduler: Arc<Scheduler>,
pub visible: bool, pub visible: bool,
pub cursor: usize, pub cursor: usize,
@ -52,7 +52,12 @@ pub struct Tasks {
impl Tasks { impl Tasks {
pub fn start() -> Self { 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] #[inline]
@ -143,13 +148,28 @@ impl Tasks {
} }
pub fn file_remove(&self, targets: Vec<PathBuf>, permanently: bool) -> bool { pub fn file_remove(&self, targets: Vec<PathBuf>, permanently: bool) -> bool {
for p in targets { let scheduler = self.scheduler.clone();
if permanently { tokio::spawn(async move {
self.scheduler.file_delete(p); let result = emit!(Input(InputOpt {
} else { title: "Are you sure delete these files? (Y/n)".to_string(),
self.scheduler.file_trash(p); 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 false
} }

View file

@ -3,7 +3,7 @@ use crossterm::event::KeyEvent;
use tokio::sync::oneshot::{self}; use tokio::sync::oneshot::{self};
use super::{root::Root, Ctx, Executor, Logs, Signals, Term}; 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 { pub struct App {
cx: Ctx, cx: Ctx,
@ -104,7 +104,17 @@ impl App {
emit!(Render); 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); self.cx.input.show(opt, tx);
emit!(Render); emit!(Render);
} }