diff --git a/yazi-config/src/popup/options.rs b/yazi-config/src/popup/options.rs index 7e3e752c..177788c9 100644 --- a/yazi-config/src/popup/options.rs +++ b/yazi-config/src/popup/options.rs @@ -2,7 +2,7 @@ use super::{Offset, Position}; use crate::{INPUT, SELECT}; #[derive(Default)] -pub struct InputOpt { +pub struct InputCfg { pub title: String, pub value: String, pub position: Position, @@ -18,7 +18,7 @@ pub struct SelectCfg { pub position: Position, } -impl InputOpt { +impl InputCfg { #[inline] pub fn cd() -> Self { Self { diff --git a/yazi-core/src/event.rs b/yazi-core/src/event.rs index 1f232a0a..d5eb6b61 100644 --- a/yazi-core/src/event.rs +++ b/yazi-core/src/event.rs @@ -1,10 +1,9 @@ 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}; -use yazi_shared::{fs::Url, term::Term, Exec, InputError, Layer, RoCell}; +use tokio::sync::{mpsc::UnboundedSender, oneshot}; +use yazi_config::open::Opener; +use yazi_shared::{fs::Url, term::Term, Exec, Layer, RoCell}; use super::files::FilesOp; use crate::{preview::PreviewLock, tasks::TasksProgress}; @@ -26,8 +25,7 @@ pub enum Event { Mimetype(BTreeMap), Preview(PreviewLock), - // Input - Input(InputOpt, mpsc::UnboundedSender>), + // Input(InputOpt, mpsc::UnboundedSender>), // Tasks Open(Vec<(OsString, String)>, Option), @@ -82,12 +80,6 @@ macro_rules! emit { $crate::Event::Preview($lock).emit(); }; - (Input($opt:expr)) => {{ - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - $crate::Event::Input($opt, tx).emit(); - rx - }}; - (Open($targets:expr, $opener:expr)) => { $crate::Event::Open($targets, $opener).emit(); }; diff --git a/yazi-core/src/input/commands/mod.rs b/yazi-core/src/input/commands/mod.rs index 9ad96381..8759bf85 100644 --- a/yazi-core/src/input/commands/mod.rs +++ b/yazi-core/src/input/commands/mod.rs @@ -10,6 +10,7 @@ mod kill; mod move_; mod paste; mod redo; +mod show; mod type_; mod undo; mod visual; diff --git a/yazi-core/src/input/commands/show.rs b/yazi-core/src/input/commands/show.rs new file mode 100644 index 00000000..796763ec --- /dev/null +++ b/yazi-core/src/input/commands/show.rs @@ -0,0 +1,56 @@ +use anyhow::bail; +use tokio::sync::mpsc; +use yazi_config::popup::InputCfg; +use yazi_shared::{Exec, InputError, Layer}; + +use crate::{emit, input::Input}; + +pub struct Opt { + cfg: InputCfg, + tx: mpsc::UnboundedSender>, +} + +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 Input { + pub fn _show(cfg: InputCfg) -> mpsc::UnboundedReceiver> { + let (tx, rx) = mpsc::unbounded_channel(); + emit!(Call(Exec::call("show", vec![]).with_data(Opt { cfg, tx }).vec(), Layer::Input)); + rx + } + + pub fn show(&mut self, opt: impl TryInto) -> bool { + let Ok(opt) = opt.try_into() else { + return false; + }; + + self.close(false); + self.visible = true; + self.title = opt.cfg.title; + self.position = opt.cfg.position; + + // Typing + self.callback = Some(opt.tx); + self.realtime = opt.cfg.realtime; + self.completion = opt.cfg.completion; + + // Shell + self.highlight = opt.cfg.highlight; + + // Reset snaps + self.snaps.reset(opt.cfg.value, self.limit()); + true + } +} diff --git a/yazi-core/src/input/input.rs b/yazi-core/src/input/input.rs index 0709f4e1..07fae677 100644 --- a/yazi-core/src/input/input.rs +++ b/yazi-core/src/input/input.rs @@ -2,7 +2,7 @@ use std::ops::Range; use tokio::sync::mpsc::UnboundedSender; use unicode_width::UnicodeWidthStr; -use yazi_config::{popup::{InputOpt, Position}, INPUT}; +use yazi_config::{popup::Position, INPUT}; use yazi_shared::InputError; use super::{mode::InputMode, op::InputOp, InputSnap, InputSnaps}; @@ -19,7 +19,7 @@ pub struct Input { // Typing pub(super) callback: Option>>, - realtime: bool, + pub(super) realtime: bool, pub(super) completion: bool, // Shell @@ -27,24 +27,6 @@ pub struct Input { } impl Input { - pub fn show(&mut self, opt: InputOpt, tx: UnboundedSender>) { - self.close(false); - self.visible = true; - self.title = opt.title; - self.position = opt.position; - - // Typing - self.callback = Some(tx); - self.realtime = opt.realtime; - self.completion = opt.completion; - - // Shell - self.highlight = opt.highlight; - - // Reset snaps - self.snaps.reset(opt.value, self.limit()); - } - #[inline] pub(super) fn limit(&self) -> usize { self.position.offset.width.saturating_sub(INPUT.border()) as usize diff --git a/yazi-core/src/manager/commands/create.rs b/yazi-core/src/manager/commands/create.rs index 664a5b15..cd3fb5b2 100644 --- a/yazi-core/src/manager/commands/create.rs +++ b/yazi-core/src/manager/commands/create.rs @@ -1,10 +1,10 @@ use std::path::{PathBuf, MAIN_SEPARATOR}; use tokio::fs; -use yazi_config::popup::InputOpt; +use yazi_config::popup::InputCfg; use yazi_shared::{fs::Url, Exec}; -use crate::{emit, files::{File, FilesOp}, manager::Manager}; +use crate::{emit, files::{File, FilesOp}, input::Input, manager::Manager}; pub struct Opt { force: bool, @@ -19,14 +19,14 @@ impl Manager { let opt = opt.into() as Opt; let cwd = self.cwd().to_owned(); tokio::spawn(async move { - let mut result = emit!(Input(InputOpt::create())); + let mut result = Input::_show(InputCfg::create()); let Some(Ok(name)) = result.recv().await else { return Ok(()); }; let path = cwd.join(&name); if !opt.force && fs::symlink_metadata(&path).await.is_ok() { - match emit!(Input(InputOpt::overwrite())).recv().await { + match Input::_show(InputCfg::overwrite()).recv().await { Some(Ok(c)) if c == "y" || c == "Y" => (), _ => return Ok(()), } diff --git a/yazi-core/src/manager/commands/quit.rs b/yazi-core/src/manager/commands/quit.rs index 4745953b..1c630c45 100644 --- a/yazi-core/src/manager/commands/quit.rs +++ b/yazi-core/src/manager/commands/quit.rs @@ -1,7 +1,7 @@ -use yazi_config::popup::InputOpt; +use yazi_config::popup::InputCfg; use yazi_shared::Exec; -use crate::{emit, manager::Manager, tasks::Tasks}; +use crate::{emit, input::Input, manager::Manager, tasks::Tasks}; #[derive(Default)] pub struct Opt { @@ -25,7 +25,7 @@ impl Manager { } tokio::spawn(async move { - let mut result = emit!(Input(InputOpt::quit(tasks))); + let mut result = Input::_show(InputCfg::quit(tasks)); if let Some(Ok(choice)) = result.recv().await { if choice == "y" || choice == "Y" { emit!(Quit(opt.no_cwd_file)); diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index dcc0faa6..4b09caac 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -2,10 +2,10 @@ use std::{collections::BTreeMap, ffi::OsStr, io::{stdout, BufWriter, Write}, pat use anyhow::{anyhow, bail, Result}; use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; -use yazi_config::{popup::InputOpt, OPEN, PREVIEW}; +use yazi_config::{popup::InputCfg, OPEN, PREVIEW}; use yazi_shared::{fs::{max_common_root, Url}, term::Term, Defer, Exec}; -use crate::{emit, external::{self, ShellOpt}, files::{File, FilesOp}, manager::Manager, Event, BLOCKER}; +use crate::{emit, external::{self, ShellOpt}, files::{File, FilesOp}, input::Input, manager::Manager, Event, BLOCKER}; pub struct Opt { force: bool, @@ -39,7 +39,7 @@ impl Manager { let opt = opt.into() as Opt; tokio::spawn(async move { let mut result = - emit!(Input(InputOpt::rename().with_value(hovered.file_name().unwrap().to_string_lossy()))); + Input::_show(InputCfg::rename().with_value(hovered.file_name().unwrap().to_string_lossy())); let Some(Ok(name)) = result.recv().await else { return; @@ -51,7 +51,7 @@ impl Manager { return; } - let mut result = emit!(Input(InputOpt::overwrite())); + let mut result = Input::_show(InputCfg::overwrite()); if let Some(Ok(choice)) = result.recv().await { if choice == "y" || choice == "Y" { Self::rename_and_hover(hovered, Url::from(new)).await.ok(); diff --git a/yazi-core/src/tab/commands/cd.rs b/yazi-core/src/tab/commands/cd.rs index 7592aa8c..c2d7fdc1 100644 --- a/yazi-core/src/tab/commands/cd.rs +++ b/yazi-core/src/tab/commands/cd.rs @@ -2,10 +2,10 @@ use std::{mem, time::Duration}; use tokio::{fs, pin}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; -use yazi_config::popup::InputOpt; +use yazi_config::popup::InputCfg; use yazi_shared::{fs::{expand_path, Url}, Debounce, Exec, InputError, Layer}; -use crate::{completion::Completion, emit, manager::Manager, tab::Tab}; +use crate::{completion::Completion, emit, input::Input, manager::Manager, tab::Tab}; pub struct Opt { target: Url, @@ -72,7 +72,7 @@ impl Tab { let opt = opt.into() as Opt; tokio::spawn(async move { - let rx = emit!(Input(InputOpt::cd().with_value(opt.target.to_string_lossy()))); + let rx = Input::_show(InputCfg::cd().with_value(opt.target.to_string_lossy())); let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); pin!(rx); diff --git a/yazi-core/src/tab/commands/find.rs b/yazi-core/src/tab/commands/find.rs index 9e425c24..a8cccae5 100644 --- a/yazi-core/src/tab/commands/find.rs +++ b/yazi-core/src/tab/commands/find.rs @@ -2,10 +2,10 @@ use std::time::Duration; use tokio::pin; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; -use yazi_config::popup::InputOpt; +use yazi_config::popup::InputCfg; use yazi_shared::{Debounce, Exec, InputError, Layer}; -use crate::{emit, tab::{Finder, FinderCase, Tab}}; +use crate::{emit, input::Input, tab::{Finder, FinderCase, Tab}}; pub struct Opt<'a> { query: Option<&'a str>, @@ -39,7 +39,7 @@ impl Tab { pub fn find<'a>(&mut self, opt: impl Into>) -> bool { let opt = opt.into() as Opt; tokio::spawn(async move { - let rx = emit!(Input(InputOpt::find(opt.prev))); + let rx = Input::_show(InputCfg::find(opt.prev)); let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); pin!(rx); diff --git a/yazi-core/src/tab/commands/search.rs b/yazi-core/src/tab/commands/search.rs index c6f70298..3756f5fa 100644 --- a/yazi-core/src/tab/commands/search.rs +++ b/yazi-core/src/tab/commands/search.rs @@ -3,10 +3,10 @@ use std::{mem, time::Duration}; use anyhow::bail; use tokio::pin; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; -use yazi_config::popup::InputOpt; +use yazi_config::popup::InputCfg; use yazi_shared::Exec; -use crate::{emit, external, files::FilesOp, manager::Manager, tab::Tab}; +use crate::{emit, external, files::FilesOp, input::Input, manager::Manager, tab::Tab}; pub struct Opt { pub type_: OptType, @@ -46,7 +46,7 @@ impl Tab { let hidden = self.conf.show_hidden; self.search = Some(tokio::spawn(async move { - let Some(Ok(subject)) = emit!(Input(InputOpt::search())).recv().await else { bail!("") }; + let Some(Ok(subject)) = Input::_show(InputCfg::search()).recv().await else { bail!("") }; cwd = cwd.into_search(subject.clone()); let rx = if opt.type_ == OptType::Rg { diff --git a/yazi-core/src/tab/commands/shell.rs b/yazi-core/src/tab/commands/shell.rs index 6cf21b04..945446ee 100644 --- a/yazi-core/src/tab/commands/shell.rs +++ b/yazi-core/src/tab/commands/shell.rs @@ -1,7 +1,7 @@ -use yazi_config::{open::Opener, popup::InputOpt}; +use yazi_config::{open::Opener, popup::InputCfg}; use yazi_shared::Exec; -use crate::{emit, tab::Tab}; +use crate::{emit, input::Input, tab::Tab}; pub struct Opt { cmd: String, @@ -30,7 +30,7 @@ impl Tab { let mut opt = opt.into() as Opt; tokio::spawn(async move { if !opt.confirm || opt.cmd.is_empty() { - let mut result = emit!(Input(InputOpt::shell(opt.block).with_value(opt.cmd))); + let mut result = Input::_show(InputCfg::shell(opt.block).with_value(opt.cmd)); match result.recv().await { Some(Ok(e)) => opt.cmd = e, _ => return, diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index f94ed465..b70b3e87 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -2,11 +2,11 @@ use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, path::Path, syn use serde::Serialize; use tracing::debug; -use yazi_config::{manager::SortBy, open::Opener, popup::InputOpt, OPEN}; +use yazi_config::{manager::SortBy, open::Opener, popup::InputCfg, OPEN}; use yazi_shared::{fs::Url, term::Term, MimeKind}; use super::{running::Running, task::TaskSummary, Scheduler, TASKS_PADDING, TASKS_PERCENT}; -use crate::{emit, files::{File, Files}}; +use crate::{files::{File, Files}, input::Input}; pub struct Tasks { pub(super) scheduler: Arc, @@ -110,11 +110,11 @@ impl Tasks { let scheduler = self.scheduler.clone(); tokio::spawn(async move { - let mut result = emit!(Input(if permanently { - InputOpt::delete(targets.len()) + let mut result = Input::_show(if permanently { + InputCfg::delete(targets.len()) } else { - InputOpt::trash(targets.len()) - })); + InputCfg::trash(targets.len()) + }); if let Some(Ok(choice)) = result.recv().await { if choice != "y" && choice != "Y" { diff --git a/yazi-fm/src/app.rs b/yazi-fm/src/app.rs index a9a161c5..a6b206e5 100644 --- a/yazi-fm/src/app.rs +++ b/yazi-fm/src/app.rs @@ -177,11 +177,6 @@ impl App { } } - Event::Input(opt, tx) => { - self.cx.input.show(opt, tx); - emit!(Render); - } - Event::Open(targets, opener) => { if let Some(p) = &BOOT.chooser_file { let paths = targets.into_iter().fold(OsString::new(), |mut s, (p, _)| { diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index bec2bdac..51c6beb8 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -207,6 +207,7 @@ impl<'a> Executor<'a> { }; } + on!(show); on!(close); on!(escape); on!(move_, "move");