diff --git a/yazi-config/src/keymap/control.rs b/yazi-config/src/keymap/control.rs index dad676514..6544654a6 100644 --- a/yazi-config/src/keymap/control.rs +++ b/yazi-config/src/keymap/control.rs @@ -19,10 +19,10 @@ impl Control { .exec .iter() .map(|e| Exec { - cmd: e.cmd.clone(), - args: e.args.clone(), + cmd: e.cmd.clone(), + args: e.args.clone(), named: e.named.clone(), - data: None, + ..Default::default() }) .collect() } diff --git a/yazi-config/src/popup/options.rs b/yazi-config/src/popup/options.rs index 472d45b56..7e3e752ce 100644 --- a/yazi-config/src/popup/options.rs +++ b/yazi-config/src/popup/options.rs @@ -12,7 +12,7 @@ pub struct InputOpt { } #[derive(Default)] -pub struct SelectOpt { +pub struct SelectCfg { pub title: String, pub items: Vec, pub position: Position, @@ -122,7 +122,7 @@ impl InputOpt { } } -impl SelectOpt { +impl SelectCfg { #[inline] fn max_height(len: usize) -> u16 { SELECT.open_offset.height.min(SELECT.border().saturating_add(len as u16)) diff --git a/yazi-core/src/event.rs b/yazi-core/src/event.rs index e8b88e5cf..1f232a0ab 100644 --- a/yazi-core/src/event.rs +++ b/yazi-core/src/event.rs @@ -3,8 +3,8 @@ use std::{collections::BTreeMap, ffi::OsString}; use anyhow::Result; use crossterm::event::KeyEvent; use tokio::sync::{mpsc::{self, UnboundedSender}, oneshot}; -use yazi_config::{open::Opener, popup::{InputOpt, SelectOpt}}; -use yazi_shared::{fs::Url, Exec, InputError, Layer, RoCell}; +use yazi_config::{open::Opener, popup::InputOpt}; +use yazi_shared::{fs::Url, term::Term, Exec, InputError, Layer, RoCell}; use super::files::FilesOp; use crate::{preview::PreviewLock, tasks::TasksProgress}; @@ -27,7 +27,6 @@ pub enum Event { Preview(PreviewLock), // Input - Select(SelectOpt, oneshot::Sender>), Input(InputOpt, mpsc::UnboundedSender>), // Tasks @@ -44,7 +43,7 @@ impl Event { pub async fn wait(self, rx: oneshot::Receiver) -> T { TX.send(self).ok(); - rx.await.unwrap_or_else(|_| std::process::exit(0)) + rx.await.unwrap_or_else(|_| Term::goodbye(|| false)) } } @@ -83,10 +82,6 @@ macro_rules! emit { $crate::Event::Preview($lock).emit(); }; - (Select($opt:expr)) => {{ - let (tx, rx) = tokio::sync::oneshot::channel(); - $crate::Event::Select($opt, tx).wait(rx) - }}; (Input($opt:expr)) => {{ let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); $crate::Event::Input($opt, tx).emit(); diff --git a/yazi-core/src/manager/commands/open.rs b/yazi-core/src/manager/commands/open.rs index b2e601d89..92a788c05 100644 --- a/yazi-core/src/manager/commands/open.rs +++ b/yazi-core/src/manager/commands/open.rs @@ -1,9 +1,9 @@ use std::ffi::OsString; -use yazi_config::{popup::SelectOpt, OPEN}; +use yazi_config::{popup::SelectCfg, OPEN}; use yazi_shared::{Exec, MIME_DIR}; -use crate::{emit, external, manager::Manager}; +use crate::{emit, external, manager::Manager, select::Select}; pub struct Opt { interactive: bool, @@ -20,7 +20,7 @@ impl Manager { return; } - let result = emit!(Select(SelectOpt::open(openers.iter().map(|o| o.desc.clone()).collect()))); + let result = Select::_show(SelectCfg::open(openers.iter().map(|o| o.desc.clone()).collect())); if let Ok(choice) = result.await { emit!(Open(files, Some(openers[choice].clone()))); } diff --git a/yazi-core/src/select/commands/mod.rs b/yazi-core/src/select/commands/mod.rs index 9225a7a8a..d36c6ae84 100644 --- a/yazi-core/src/select/commands/mod.rs +++ b/yazi-core/src/select/commands/mod.rs @@ -1,2 +1,3 @@ mod arrow; mod close; +mod show; diff --git a/yazi-core/src/select/commands/show.rs b/yazi-core/src/select/commands/show.rs new file mode 100644 index 000000000..ca635b334 --- /dev/null +++ b/yazi-core/src/select/commands/show.rs @@ -0,0 +1,48 @@ +use anyhow::{bail, Result}; +use tokio::sync::oneshot; +use yazi_config::popup::SelectCfg; +use yazi_shared::{term::Term, Exec, Layer}; + +use crate::{emit, select::Select}; + +pub struct Opt { + cfg: SelectCfg, + tx: oneshot::Sender>, +} + +impl TryFrom<&Exec> for Opt { + type Error = anyhow::Error; + + fn try_from(e: &Exec) -> Result { + let Some(data) = e.data.borrow_mut().take() else { + bail!("missing data"); + }; + let Ok(opt) = data.downcast::() else { + bail!("invalid data"); + }; + Ok(*opt) + } +} + +impl Select { + pub async fn _show(cfg: SelectCfg) -> Result { + let (tx, rx) = oneshot::channel(); + emit!(Call(Exec::call("show", vec![]).with_data(Opt { cfg, tx }).vec(), Layer::Select)); + rx.await.unwrap_or_else(|_| Term::goodbye(|| false)) + } + + pub fn show(&mut self, opt: impl TryInto) -> bool { + let Ok(opt) = opt.try_into() else { + return false; + }; + + self.close(false); + self.title = opt.cfg.title; + self.items = opt.cfg.items; + self.position = opt.cfg.position; + + self.callback = Some(opt.tx); + self.visible = true; + true + } +} diff --git a/yazi-core/src/select/select.rs b/yazi-core/src/select/select.rs index 5946c42b8..26e4f4fa9 100644 --- a/yazi-core/src/select/select.rs +++ b/yazi-core/src/select/select.rs @@ -1,10 +1,10 @@ use anyhow::Result; use tokio::sync::oneshot::Sender; -use yazi_config::{popup::{Position, SelectOpt}, SELECT}; +use yazi_config::{popup::Position, SELECT}; #[derive(Default)] pub struct Select { - title: String, + pub(super) title: String, pub(super) items: Vec, pub position: Position, @@ -16,17 +16,6 @@ pub struct Select { } impl Select { - pub fn show(&mut self, opt: SelectOpt, tx: Sender>) { - self.close(false); - - self.title = opt.title; - self.items = opt.items; - self.position = opt.position; - - self.callback = Some(tx); - self.visible = true; - } - #[inline] pub fn window(&self) -> &[String] { let end = (self.offset + self.limit()).min(self.items.len()); diff --git a/yazi-fm/src/app.rs b/yazi-fm/src/app.rs index 1239e2c98..a9a161c53 100644 --- a/yazi-fm/src/app.rs +++ b/yazi-fm/src/app.rs @@ -48,7 +48,7 @@ impl App { let cwd = self.cx.manager.cwd().as_os_str(); std::fs::write(p, cwd.as_encoded_bytes()).ok(); } - Term::goodbye(|| false).unwrap(); + Term::goodbye(|| false); } fn dispatch_key(&mut self, key: KeyEvent) { @@ -177,10 +177,6 @@ impl App { } } - Event::Select(opt, tx) => { - self.cx.select.show(opt, tx); - emit!(Render); - } Event::Input(opt, tx) => { self.cx.input.show(opt, tx); emit!(Render); diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 17b57d139..bec2bdac8 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -183,6 +183,7 @@ impl<'a> Executor<'a> { }; } + on!(show); on!(close); on!(arrow); diff --git a/yazi-shared/src/event.rs b/yazi-shared/src/event.rs index 1706e0ff7..8d804e659 100644 --- a/yazi-shared/src/event.rs +++ b/yazi-shared/src/event.rs @@ -1,11 +1,11 @@ -use std::{any::Any, collections::BTreeMap, fmt::{self, Display}}; +use std::{any::Any, cell::RefCell, collections::BTreeMap, fmt::{self, Display}}; #[derive(Debug, Default)] pub struct Exec { pub cmd: String, pub args: Vec, pub named: BTreeMap, - pub data: Option>, + pub data: RefCell>>, } impl Exec { @@ -35,6 +35,12 @@ impl Exec { } self } + + #[inline] + pub fn with_data(mut self, data: impl Any + Send) -> Self { + self.data = RefCell::new(Some(Box::new(data))); + self + } } impl Display for Exec { diff --git a/yazi-shared/src/term/term.rs b/yazi-shared/src/term/term.rs index 1098c4b1d..86d6f6831 100644 --- a/yazi-shared/src/term/term.rs +++ b/yazi-shared/src/term/term.rs @@ -44,7 +44,7 @@ impl Term { Ok(disable_raw_mode()?) } - pub fn goodbye(f: impl FnOnce() -> bool) -> Result<()> { + pub fn goodbye(f: impl FnOnce() -> bool) -> ! { execute!( stdout(), PopKeyboardEnhancementFlags, @@ -53,8 +53,9 @@ impl Term { LeaveAlternateScreen, crossterm::cursor::SetCursorStyle::DefaultUserShape, crossterm::cursor::Show, - )?; - disable_raw_mode()?; + ) + .ok(); + disable_raw_mode().ok(); std::process::exit(f() as i32); }