From 73ed4cfa5a5a472e5e61f1b6cfa8301db5f5ea2b Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 20 Sep 2024 19:36:50 -0600 Subject: [PATCH] Enable preview in dialog --- src/app.rs | 2 +- src/dialog.rs | 115 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 97 insertions(+), 20 deletions(-) diff --git a/src/app.rs b/src/app.rs index 5d1049e..7b59d81 100644 --- a/src/app.rs +++ b/src/app.rs @@ -342,7 +342,7 @@ pub enum ContextPage { } impl ContextPage { - fn title(&self) -> String { + pub fn title(&self) -> String { match self { Self::About => String::new(), Self::EditHistory => fl!("edit-history"), diff --git a/src/dialog.rs b/src/dialog.rs index a98a182..bd46e42 100644 --- a/src/dialog.rs +++ b/src/dialog.rs @@ -39,8 +39,8 @@ use std::{ }; use crate::{ - app::{Action, Message as AppMessage}, - config::{Config, Favorite, TabConfig}, + app::{Action, ContextPage, Message as AppMessage, PreviewItem, PreviewKind}, + config::{Config, Favorite, IconSizes, TabConfig}, fl, home_dir, localize::LANGUAGE_SORTER, menu, @@ -315,6 +315,7 @@ enum Message { NotifyEvents(Vec), NotifyWatcher(WatcherWrapper), Open, + Preview(PreviewKind, time::Duration), Save(bool), SearchActivate, SearchClear, @@ -324,6 +325,18 @@ enum Message { TabRescan(Vec), } +impl From for Message { + fn from(app_message: AppMessage) -> Message { + match app_message { + AppMessage::TabMessage(_entity_opt, tab_message) => Message::TabMessage(tab_message), + unsupported => { + log::warn!("{unsupported:?} not supported in dialog mode"); + Message::None + } + } + } +} + pub struct MounterData(MounterKey, MounterItem); struct WatcherWrapper { @@ -355,6 +368,7 @@ struct App { title: String, accept_label: String, choices: Vec, + context_page: ContextPage, dialog_pages: VecDeque, dialog_text_input: widget::Id, filters: Vec, @@ -364,6 +378,7 @@ struct App { mounters: Mounters, mounter_items: HashMap, nav_model: segmented_button::SingleSelectModel, + preview_opt: Option<(PreviewKind, time::Instant)>, result_opt: Option, search_active: bool, search_id: widget::Id, @@ -374,6 +389,40 @@ struct App { } impl App { + fn preview(&self, kind: &PreviewKind) -> Element { + let mut children = Vec::with_capacity(1); + match kind { + PreviewKind::Custom(PreviewItem(item)) => { + children.push(item.property_view(IconSizes::default())); + } + PreviewKind::Location(location) => { + if let Some(items) = self.tab.items_opt() { + for item in items.iter() { + if item.location_opt.as_ref() == Some(location) { + children.push(item.property_view(self.tab.config.icon_sizes)); + // Only show one property view to avoid issues like hangs when generating + // preview images on thousands of files + break; + } + } + } + } + PreviewKind::Selected => { + if let Some(items) = self.tab.items_opt() { + for item in items.iter() { + if item.selected { + children.push(item.property_view(self.tab.config.icon_sizes)); + // Only show one property view to avoid issues like hangs when generating + // preview images on thousands of files + break; + } + } + } + } + } + widget::settings::view_column(children).into() + } + fn rescan_tab(&self) -> Command { let location = self.tab.location.clone(); let mounters = self.mounters.clone(); @@ -606,6 +655,7 @@ impl Application for App { title, accept_label, choices: Vec::new(), + context_page: ContextPage::Settings, dialog_pages: VecDeque::new(), dialog_text_input: widget::Id::unique(), filters: Vec::new(), @@ -615,6 +665,7 @@ impl Application for App { mounters: mounters(), mounter_items: HashMap::new(), nav_model: segmented_button::ModelBuilder::default().build(), + preview_opt: None, result_opt: None, search_active: false, search_id: widget::Id::unique(), @@ -638,6 +689,17 @@ impl Application for App { self.flags.window_id } + fn context_drawer(&self) -> Option> { + if !self.core.window.show_context { + return None; + } + + match &self.context_page { + ContextPage::Preview(_, kind) => Some(self.preview(kind).map(Message::from)), + _ => None, + } + } + fn dialog(&self) -> Option> { let dialog_page = match self.dialog_pages.front() { Some(some) => some, @@ -752,15 +814,7 @@ impl Application for App { elements.push( menu::dialog_menu(&self.tab, &self.key_binds) - .map(|message| match message { - AppMessage::TabMessage(_entity_opt, tab_message) => { - Message::TabMessage(tab_message) - } - unsupported => { - log::warn!("{unsupported:?} not supported in dialog mode"); - Message::None - } - }) + .map(Message::from) .into(), ); @@ -1100,6 +1154,17 @@ impl Application for App { } } } + Message::Preview(kind, timeout) => { + if self + .preview_opt + .as_ref() + .is_some_and(|(k, i)| *k == kind && i.elapsed() > timeout) + { + self.context_page = ContextPage::Preview(None, kind); + self.set_show_context(true); + self.set_context_title(self.context_page.title()); + } + } Message::Save(replace) => { if let DialogKind::SaveFile { filename } = &self.flags.kind { if !filename.is_empty() { @@ -1176,14 +1241,9 @@ impl Application for App { let mut commands = Vec::new(); for tab_command in tab_commands { match tab_command { - tab::Command::Action(action) => match action.message() { - AppMessage::TabMessage(_entity_opt, tab_message) => { - commands.push(self.update(Message::TabMessage(tab_message))); - } - unsupported => { - log::warn!("{unsupported:?} not supported in dialog mode"); - } - }, + tab::Command::Action(action) => { + commands.push(self.update(Message::from(action.message()))); + } tab::Command::ChangeLocation(_tab_title, _tab_path, _selection_path) => { commands .push(Command::batch([self.update_watcher(), self.rescan_tab()])); @@ -1202,6 +1262,23 @@ impl Application for App { commands.push(self.update(Message::Open)); } } + tab::Command::Preview(kind, mut timeout) => { + self.preview_opt = Some((kind.clone(), time::Instant::now())); + if self.core.window.show_context { + // If the context window is already open, immediately show the preview + timeout = time::Duration::new(0, 0) + }; + commands.push(Command::perform( + async move { + tokio::time::sleep(timeout).await; + message::app(Message::Preview(kind, timeout)) + }, + |x| x, + )); + } + tab::Command::PreviewCancel => { + self.preview_opt = None; + } unsupported => { log::warn!("{unsupported:?} not supported in dialog mode"); }