From 21eac9324f201e81c5c4105b240a899325961150 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 2 Oct 2024 15:26:02 -0600 Subject: [PATCH] Make show details configurable and persistent, fixes #540 and #321 --- src/app.rs | 79 +++++++++++++++++++++++++++------------------------ src/config.rs | 7 ++--- src/dialog.rs | 37 ++++-------------------- src/menu.rs | 4 ++- src/tab.rs | 36 +++-------------------- 5 files changed, 58 insertions(+), 105 deletions(-) diff --git a/src/app.rs b/src/app.rs index cfafbc5..3aab4ae 100644 --- a/src/app.rs +++ b/src/app.rs @@ -166,9 +166,7 @@ impl Action { Action::OpenTerminal => Message::OpenTerminal(entity_opt), Action::OpenWith => Message::ToggleContextPage(ContextPage::OpenWith), Action::Paste => Message::Paste(entity_opt), - Action::Preview => { - Message::ToggleContextPage(ContextPage::Preview(entity_opt, PreviewKind::Selected)) - } + Action::Preview => Message::ToggleShowDetails, Action::Rename => Message::Rename(entity_opt), Action::RestoreFromTrash => Message::RestoreFromTrash(entity_opt), Action::SearchActivate => Message::SearchActivate, @@ -288,7 +286,6 @@ pub enum Message { PendingComplete(u64), PendingError(u64, String), PendingProgress(u64, f32), - Preview(Entity, PreviewKind, time::Duration), RescanTrash, Rename(Option), ReplaceResult(ReplaceResult), @@ -308,6 +305,7 @@ pub enum Message { TabRescan(Entity, Location, Vec, Option), TabView(Option, tab::View), ToggleContextPage(ContextPage), + ToggleShowDetails, ToggleFoldersFirst, Undo(usize), UndoTrash(widget::ToastId, Arc<[PathBuf]>), @@ -473,7 +471,6 @@ pub struct App { pending_operations: BTreeMap, complete_operations: BTreeMap, failed_operations: BTreeMap, - preview_opt: Option<(Entity, PreviewKind, time::Instant)>, search_active: bool, search_id: widget::Id, search_input: String, @@ -1063,7 +1060,9 @@ impl Application for App { fn init(mut core: Core, flags: Self::Flags) -> (Self, Command) { core.window.context_is_overlay = false; match flags.mode { - Mode::App => {} + Mode::App => { + core.window.show_context = flags.config.show_details; + } Mode::Desktop => { core.window.content_container = false; core.window.show_window_menu = false; @@ -1086,7 +1085,7 @@ impl Application for App { config: flags.config, mode: flags.mode, app_themes, - context_page: ContextPage::Settings, + context_page: ContextPage::Preview(None, PreviewKind::Selected), dialog_pages: VecDeque::new(), dialog_text_input: widget::Id::unique(), key_binds: key_binds(), @@ -1101,7 +1100,6 @@ impl Application for App { pending_operations: BTreeMap::new(), complete_operations: BTreeMap::new(), failed_operations: BTreeMap::new(), - preview_opt: None, search_active: false, search_id: widget::Id::unique(), search_input: String::new(), @@ -1250,6 +1248,19 @@ impl Application for App { Some(Message::WindowClose) } + fn on_context_drawer(&mut self) -> Command { + match self.context_page { + ContextPage::Preview(_, _) => { + // Persist state of preview page + if self.core.window.show_context != self.config.show_details { + return self.update(Message::ToggleShowDetails); + } + } + _ => {} + } + Command::none() + } + fn on_escape(&mut self) -> Command { let entity = self.tab_model.active(); @@ -1360,8 +1371,10 @@ impl Application for App { Message::Config(config) => { if config != self.config { log::info!("update config"); - //TODO: update syntax theme by clearing tabs, only if needed + // Show details is preserved for existing instances + let show_details = self.config.show_details; self.config = config; + self.config.show_details = show_details; return self.update_config(); } } @@ -1933,17 +1946,6 @@ impl Application for App { } return self.update_notification(); } - Message::Preview(entity, kind, timeout) => { - if self - .preview_opt - .as_ref() - .is_some_and(|(e, k, i)| *e == entity && *k == kind && i.elapsed() > timeout) - { - self.context_page = ContextPage::Preview(Some(entity), kind); - self.set_show_context(true); - self.set_context_title(self.context_page.title()); - } - } Message::RescanTrash => { // Update trash icon if empty/full let maybe_entity = self.nav_model.iter().find(|&entity| { @@ -2152,6 +2154,16 @@ impl Application for App { return self.update_config(); } } + Message::ToggleShowDetails => { + let show_details = !self.config.show_details; + //TODO: move to update_config? + if show_details { + self.context_page = ContextPage::Preview(None, PreviewKind::Selected); + self.core.window.show_context = true; + } + config_set!(show_details, show_details); + return self.update_config(); + } Message::ToggleFoldersFirst => { let mut config = self.config.tab; config.folders_first = !config.folders_first; @@ -2273,22 +2285,10 @@ impl Application for App { log::error!("failed to get current executable path: {}", err); } }, - tab::Command::Preview(kind, mut timeout) => { - self.preview_opt = Some((entity, kind.clone(), 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(entity, kind, timeout)) - }, - |x| x, - )); - } - tab::Command::PreviewCancel => { - self.preview_opt = None; + tab::Command::Preview(kind) => { + self.context_page = ContextPage::Preview(Some(entity), kind); + self.set_show_context(true); + self.set_context_title(self.context_page.title()); } tab::Command::WindowDrag => { commands.push(window::drag(self.main_window_id())); @@ -3182,7 +3182,12 @@ impl Application for App { } fn header_start(&self) -> Vec> { - vec![menu::menu_bar(self.tab_model.active_data::(), &self.key_binds).into()] + vec![menu::menu_bar( + self.tab_model.active_data::(), + &self.config, + &self.key_binds, + ) + .into()] } fn header_end(&self) -> Vec> { diff --git a/src/config.rs b/src/config.rs index f37414b..b2da776 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,10 +9,7 @@ use cosmic::{ }; use serde::{Deserialize, Serialize}; -use crate::{ - app::App, - tab::{HeadingOptions, View}, -}; +use crate::{app::App, tab::View}; pub const CONFIG_VERSION: u64 = 1; @@ -96,6 +93,7 @@ impl Favorite { pub struct Config { pub app_theme: AppTheme, pub favorites: Vec, + pub show_details: bool, pub tab: TabConfig, } @@ -141,6 +139,7 @@ impl Default for Config { Favorite::Pictures, Favorite::Videos, ], + show_details: false, tab: TabConfig::default(), } } diff --git a/src/dialog.rs b/src/dialog.rs index bcad9ce..0a7c854 100644 --- a/src/dialog.rs +++ b/src/dialog.rs @@ -315,7 +315,6 @@ enum Message { NotifyEvents(Vec), NotifyWatcher(WatcherWrapper), Open, - Preview(PreviewKind, time::Duration), Save(bool), SearchActivate, SearchClear, @@ -378,7 +377,6 @@ 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, @@ -683,6 +681,7 @@ impl Application for App { core.window.show_close = false; core.window.show_maximize = false; core.window.show_minimize = false; + core.window.show_context = true; let title = flags.kind.title(); let accept_label = flags.kind.accept_label(); @@ -711,7 +710,7 @@ impl Application for App { title, accept_label, choices: Vec::new(), - context_page: ContextPage::Settings, + context_page: ContextPage::Preview(None, PreviewKind::Selected), dialog_pages: VecDeque::new(), dialog_text_input: widget::Id::unique(), filters: Vec::new(), @@ -721,7 +720,6 @@ 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(), @@ -1233,17 +1231,6 @@ 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() { @@ -1341,22 +1328,10 @@ 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; + tab::Command::Preview(kind) => { + self.context_page = ContextPage::Preview(None, kind); + self.set_show_context(true); + self.set_context_title(self.context_page.title()); } tab::Command::WindowDrag => { commands.push(window::drag(self.main_window_id())); diff --git a/src/menu.rs b/src/menu.rs index b1bdb51..2b35cef 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -15,6 +15,7 @@ use std::collections::HashMap; use crate::{ app::{Action, Message}, + config::Config, fl, tab::{self, HeadingOptions, Location, LocationMenuAction, Tab}, }; @@ -349,6 +350,7 @@ pub fn dialog_menu<'a>( pub fn menu_bar<'a>( tab_opt: Option<&Tab>, + config: &Config, key_binds: &HashMap, ) -> Element<'a, Message> { let sort_item = |label, sort, dir| { @@ -459,7 +461,7 @@ pub fn menu_bar<'a>( tab_opt.map_or(false, |tab| tab.config.folders_first), Action::ToggleFoldersFirst, ), - menu::Item::Button(fl!("show-details"), Action::Preview), + menu::Item::CheckBox(fl!("show-details"), config.show_details, Action::Preview), menu::Item::Divider, menu_button_optional(fl!("gallery-preview"), Action::Gallery, selected > 0), menu::Item::Divider, diff --git a/src/tab.rs b/src/tab.rs index d911c0d..e81ffef 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -821,8 +821,7 @@ pub enum Command { OpenFile(PathBuf), OpenInNewTab(PathBuf), OpenInNewWindow(PathBuf), - Preview(PreviewKind, Duration), - PreviewCancel, + Preview(PreviewKind), WindowDrag, WindowToggleMaximize, } @@ -1750,7 +1749,6 @@ impl Tab { let mut history_i_opt = None; let mod_ctrl = modifiers.contains(Modifiers::CTRL) && self.mode.multiple(); let mod_shift = modifiers.contains(Modifiers::SHIFT) && self.mode.multiple(); - let last_select_focus = self.select_focus; match message { Message::AddNetworkDrive => { commands.push(Command::AddNetworkDrive); @@ -1804,9 +1802,6 @@ impl Tab { } else { log::warn!("no item for click index {:?}", click_i_opt); } - - // Cancel any preview timers - commands.push(Command::PreviewCancel); } Message::Click(click_i_opt) => { self.selected_clicked = false; @@ -1988,11 +1983,9 @@ impl Tab { //TODO: blocking code, run in command match item_from_path(&path, IconSizes::default()) { Ok(item) => { - // Show preview instantly - commands.push(Command::Preview( - PreviewKind::Custom(PreviewItem(item)), - Duration::new(0, 0), - )); + commands.push(Command::Preview(PreviewKind::Custom( + PreviewItem(item), + ))); } Err(err) => { log::warn!("failed to get item from path {:?}: {}", path, err); @@ -2442,9 +2435,6 @@ impl Tab { |x| x, ))); } - - // Clear preview timer - commands.push(Command::PreviewCancel); } Message::DndLeave(loc) => { if Some(&loc) == self.dnd_hovered.as_ref().map(|(l, _)| l) { @@ -2459,24 +2449,6 @@ impl Tab { } } - // Update preview timer - //TODO: make this configurable - if last_select_focus != self.select_focus { - if let Some(index) = self.select_focus { - if let Some(ref items) = self.items_opt { - if let Some(item) = items.get(index) { - if let Some(location) = item.location_opt.clone() { - // Show preview after double click timeout - commands.push(Command::Preview( - PreviewKind::Location(location), - DOUBLE_CLICK_DURATION, - )); - } - } - } - } - } - // Scroll to top if needed if self.scroll_opt.is_none() { let offset = AbsoluteOffset { x: 0.0, y: 0.0 };