Make show details configurable and persistent, fixes #540 and #321

This commit is contained in:
Jeremy Soller 2024-10-02 15:26:02 -06:00
parent 60743ed251
commit 21eac9324f
No known key found for this signature in database
GPG key ID: D02FD439211AF56F
5 changed files with 58 additions and 105 deletions

View file

@ -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<Entity>),
ReplaceResult(ReplaceResult),
@ -308,6 +305,7 @@ pub enum Message {
TabRescan(Entity, Location, Vec<tab::Item>, Option<PathBuf>),
TabView(Option<Entity>, tab::View),
ToggleContextPage(ContextPage),
ToggleShowDetails,
ToggleFoldersFirst,
Undo(usize),
UndoTrash(widget::ToastId, Arc<[PathBuf]>),
@ -473,7 +471,6 @@ pub struct App {
pending_operations: BTreeMap<u64, (Operation, f32)>,
complete_operations: BTreeMap<u64, Operation>,
failed_operations: BTreeMap<u64, (Operation, String)>,
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<Self::Message>) {
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<Self::Message> {
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<Self::Message> {
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<Element<Self::Message>> {
vec![menu::menu_bar(self.tab_model.active_data::<Tab>(), &self.key_binds).into()]
vec![menu::menu_bar(
self.tab_model.active_data::<Tab>(),
&self.config,
&self.key_binds,
)
.into()]
}
fn header_end(&self) -> Vec<Element<Self::Message>> {

View file

@ -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<Favorite>,
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(),
}
}

View file

@ -315,7 +315,6 @@ enum Message {
NotifyEvents(Vec<DebouncedEvent>),
NotifyWatcher(WatcherWrapper),
Open,
Preview(PreviewKind, time::Duration),
Save(bool),
SearchActivate,
SearchClear,
@ -378,7 +377,6 @@ struct App {
mounters: Mounters,
mounter_items: HashMap<MounterKey, MounterItems>,
nav_model: segmented_button::SingleSelectModel,
preview_opt: Option<(PreviewKind, time::Instant)>,
result_opt: Option<DialogResult>,
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()));

View file

@ -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<KeyBind, Action>,
) -> 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,

View file

@ -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 };