Persist more dialog options

This commit is contained in:
Jeremy Soller 2025-07-22 09:13:14 -06:00
parent 4f0c5c1ec1
commit 795f90ba95
No known key found for this signature in database
GPG key ID: 670FDFB5428E05CA
2 changed files with 95 additions and 67 deletions

View file

@ -200,6 +200,18 @@ impl Config {
CONFIG_VERSION, CONFIG_VERSION,
) )
} }
/// Construct tab config for dialog
pub fn dialog_tab(&self) -> TabConfig {
TabConfig {
folders_first: self.dialog.folders_first,
icon_sizes: self.dialog.icon_sizes,
military_time: self.tab.military_time,
show_hidden: self.dialog.show_hidden,
single_click: false,
view: self.dialog.view,
}
}
} }
impl Default for Config { impl Default for Config {
@ -251,15 +263,30 @@ impl DesktopConfig {
} }
} }
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, CosmicConfigEntry, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, Eq, PartialEq, CosmicConfigEntry, Deserialize, Serialize)]
#[serde(default)] #[serde(default)]
pub struct DialogConfig { pub struct DialogConfig {
/// Show folders before files
pub folders_first: bool,
/// Icon zoom
pub icon_sizes: IconSizes,
/// Show details sidebar
pub show_details: bool, pub show_details: bool,
/// Show hidden files and folders
pub show_hidden: bool,
/// Selected view, grid or list
pub view: View,
} }
impl Default for DialogConfig { impl Default for DialogConfig {
fn default() -> Self { fn default() -> Self {
Self { show_details: true } Self {
folders_first: false,
icon_sizes: IconSizes::default(),
show_details: true,
show_hidden: false,
view: View::List,
}
} }
} }
@ -271,30 +298,31 @@ impl Default for DialogConfig {
#[derive(Clone, Copy, Debug, Eq, PartialEq, CosmicConfigEntry, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, Eq, PartialEq, CosmicConfigEntry, Deserialize, Serialize)]
#[serde(default)] #[serde(default)]
pub struct TabConfig { pub struct TabConfig {
pub view: View,
/// Show folders before files /// Show folders before files
pub folders_first: bool, pub folders_first: bool,
/// Show hidden files and folders
pub show_hidden: bool,
/// Icon zoom /// Icon zoom
pub icon_sizes: IconSizes, pub icon_sizes: IconSizes,
#[serde(skip)] #[serde(skip)]
/// 24 hour clock; this is neither serialized nor deserialized because we use the user's global /// 24 hour clock; this is neither serialized nor deserialized because we use the user's global
/// preference rather than save it /// preference rather than save it
pub military_time: bool, pub military_time: bool,
/// Show hidden files and folders
pub show_hidden: bool,
/// Single click to open /// Single click to open
pub single_click: bool, pub single_click: bool,
/// Selected view, grid or list
pub view: View,
} }
impl Default for TabConfig { impl Default for TabConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
view: View::List,
folders_first: true, folders_first: true,
show_hidden: false,
icon_sizes: IconSizes::default(), icon_sizes: IconSizes::default(),
military_time: false, military_time: false,
show_hidden: false,
single_click: false, single_click: false,
view: View::List,
} }
} }
} }

View file

@ -36,7 +36,7 @@ use std::{
use crate::{ use crate::{
app::{Action, ContextPage, Message as AppMessage, PreviewItem, PreviewKind}, app::{Action, ContextPage, Message as AppMessage, PreviewItem, PreviewKind},
config::{Config, Favorite, TabConfig, TimeConfig, TIME_CONFIG_ID}, config::{Config, DialogConfig, Favorite, TimeConfig, TIME_CONFIG_ID},
fl, home_dir, fl, home_dir,
key_bind::key_binds, key_bind::key_binds,
localize::LANGUAGE_SORTER, localize::LANGUAGE_SORTER,
@ -436,6 +436,7 @@ enum Message {
TabView(tab::View), TabView(tab::View),
TimeConfigChange(TimeConfig), TimeConfigChange(TimeConfig),
ToggleFoldersFirst, ToggleFoldersFirst,
ToggleShowHidden,
ZoomDefault, ZoomDefault,
ZoomIn, ZoomIn,
ZoomOut, ZoomOut,
@ -451,6 +452,7 @@ impl From<AppMessage> for Message {
AppMessage::TabMessage(_entity_opt, tab_message) => Message::TabMessage(tab_message), AppMessage::TabMessage(_entity_opt, tab_message) => Message::TabMessage(tab_message),
AppMessage::TabView(_entity_opt, view) => Message::TabView(view), AppMessage::TabView(_entity_opt, view) => Message::TabView(view),
AppMessage::ToggleFoldersFirst => Message::ToggleFoldersFirst, AppMessage::ToggleFoldersFirst => Message::ToggleFoldersFirst,
AppMessage::ToggleShowHidden => Message::ToggleShowHidden,
AppMessage::ZoomDefault(_entity_opt) => Message::ZoomDefault, AppMessage::ZoomDefault(_entity_opt) => Message::ZoomDefault,
AppMessage::ZoomIn(_entity_opt) => Message::ZoomIn, AppMessage::ZoomIn(_entity_opt) => Message::ZoomIn,
AppMessage::ZoomOut(_entity_opt) => Message::ZoomOut, AppMessage::ZoomOut(_entity_opt) => Message::ZoomOut,
@ -735,12 +737,33 @@ impl App {
fn update_config(&mut self) -> Task<Message> { fn update_config(&mut self) -> Task<Message> {
self.core.window.show_context = self.flags.config.dialog.show_details; self.core.window.show_context = self.flags.config.dialog.show_details;
self.tab.config = self.flags.config.dialog_tab();
self.update_nav_model(); self.update_nav_model();
self.update(Message::TabMessage(tab::Message::Config(self.tab.config)))
}
self.update(Message::TabMessage(tab::Message::Config( fn with_dialog_config<F: Fn(&mut DialogConfig)>(&mut self, f: F) -> Task<Message> {
self.flags.config.tab, let mut dialog = self.flags.config.dialog;
))) f(&mut dialog);
if dialog != self.flags.config.dialog {
match &self.flags.config_handler {
Some(config_handler) => {
match self.flags.config.set_dialog(config_handler, dialog) {
Ok(_) => {}
Err(err) => {
log::warn!("failed to save config \"dialog\": {}", err);
}
}
}
None => {
self.flags.config.dialog = dialog;
log::warn!("failed to save config \"dialog\": no config handler",);
}
}
self.update_config()
} else {
Task::none()
}
} }
fn activate_nav_model_location(&mut self, location: &Location) { fn activate_nav_model_location(&mut self, location: &Location) {
@ -920,12 +943,7 @@ impl Application for App {
}, },
}); });
let tab_config = TabConfig { let mut tab = Tab::new(location, flags.config.dialog_tab(), None);
view: tab::View::List,
folders_first: false,
..Default::default()
};
let mut tab = Tab::new(location, tab_config, None);
tab.mode = tab::Mode::Dialog(flags.kind.clone()); tab.mode = tab::Mode::Dialog(flags.kind.clone());
tab.sort_name = tab::HeadingOptions::Modified; tab.sort_name = tab::HeadingOptions::Modified;
tab.sort_direction = false; tab.sort_direction = false;
@ -1242,33 +1260,6 @@ impl Application for App {
/// Handle application events here. /// Handle application events here.
fn update(&mut self, message: Message) -> Task<Message> { fn update(&mut self, message: Message) -> Task<Message> {
// Helper for updating config values efficiently
macro_rules! config_set {
($name: ident, $value: expr) => {
match &self.flags.config_handler {
Some(config_handler) => {
match paste::paste! { self.flags.config.[<set_ $name>](config_handler, $value) } {
Ok(_) => {}
Err(err) => {
log::warn!(
"failed to save config {:?}: {}",
stringify!($name),
err
);
}
}
}
None => {
self.flags.config.$name = $value;
log::warn!(
"failed to save config {:?}: no config handler",
stringify!($name)
);
}
}
};
}
match message { match message {
Message::None => {} Message::None => {}
Message::Cancel => { Message::Cancel => {
@ -1537,10 +1528,9 @@ impl Application for App {
} }
Message::Preview => { Message::Preview => {
self.context_page = ContextPage::Preview(None, PreviewKind::Selected); self.context_page = ContextPage::Preview(None, PreviewKind::Selected);
let mut new_dialog = self.flags.config.dialog; return self.with_dialog_config(|config| {
new_dialog.show_details = !new_dialog.show_details; config.show_details = !config.show_details;
config_set!(dialog, new_dialog); });
return self.update_config();
} }
Message::Save(replace) => { Message::Save(replace) => {
if let DialogKind::SaveFile { filename } = &self.flags.kind { if let DialogKind::SaveFile { filename } = &self.flags.kind {
@ -1627,10 +1617,9 @@ impl Application for App {
} }
tab::Command::Preview(kind) => { tab::Command::Preview(kind) => {
self.context_page = ContextPage::Preview(None, kind); self.context_page = ContextPage::Preview(None, kind);
let mut new_dialog = self.flags.config.dialog; commands.push(self.with_dialog_config(|config| {
new_dialog.show_details = true; config.show_details = true;
config_set!(dialog, new_dialog); }));
commands.push(self.update_config());
} }
tab::Command::WindowDrag => { tab::Command::WindowDrag => {
commands.push(window::drag(self.flags.window_id)); commands.push(window::drag(self.flags.window_id));
@ -1736,19 +1725,30 @@ impl Application for App {
} }
} }
Message::TabView(view) => { Message::TabView(view) => {
self.tab.config.view = view; return self.with_dialog_config(|config| {
config.view = view;
});
} }
Message::TimeConfigChange(time_config) => { Message::TimeConfigChange(time_config) => {
self.flags.config.tab.military_time = time_config.military_time; self.flags.config.tab.military_time = time_config.military_time;
return self.update_config(); return self.update_config();
} }
Message::ToggleFoldersFirst => { Message::ToggleFoldersFirst => {
self.tab.config.folders_first = !self.tab.config.folders_first; return self.with_dialog_config(|config| {
config.folders_first = !config.folders_first;
});
}
Message::ToggleShowHidden => {
return self.with_dialog_config(|config| {
config.show_hidden = !config.show_hidden;
});
}
Message::ZoomDefault => {
return self.with_dialog_config(|config| match config.view {
tab::View::List => config.icon_sizes.list = 100.try_into().unwrap(),
tab::View::Grid => config.icon_sizes.grid = 100.try_into().unwrap(),
});
} }
Message::ZoomDefault => match self.tab.config.view {
tab::View::List => self.tab.config.icon_sizes.list = 100.try_into().unwrap(),
tab::View::Grid => self.tab.config.icon_sizes.grid = 100.try_into().unwrap(),
},
Message::ZoomIn => { Message::ZoomIn => {
let zoom_in = |size: &mut NonZeroU16, min: u16, max: u16| { let zoom_in = |size: &mut NonZeroU16, min: u16, max: u16| {
let mut step = min; let mut step = min;
@ -1763,10 +1763,10 @@ impl Application for App {
*size = step.try_into().unwrap(); *size = step.try_into().unwrap();
} }
}; };
match self.tab.config.view { return self.with_dialog_config(|config| match config.view {
tab::View::List => zoom_in(&mut self.tab.config.icon_sizes.list, 50, 500), tab::View::List => zoom_in(&mut config.icon_sizes.list, 50, 500),
tab::View::Grid => zoom_in(&mut self.tab.config.icon_sizes.grid, 50, 500), tab::View::Grid => zoom_in(&mut config.icon_sizes.grid, 50, 500),
} });
} }
Message::ZoomOut => { Message::ZoomOut => {
let zoom_out = |size: &mut NonZeroU16, min: u16, max: u16| { let zoom_out = |size: &mut NonZeroU16, min: u16, max: u16| {
@ -1782,10 +1782,10 @@ impl Application for App {
*size = step.try_into().unwrap(); *size = step.try_into().unwrap();
} }
}; };
match self.tab.config.view { return self.with_dialog_config(|config| match config.view {
tab::View::List => zoom_out(&mut self.tab.config.icon_sizes.list, 50, 500), tab::View::List => zoom_out(&mut config.icon_sizes.list, 50, 500),
tab::View::Grid => zoom_out(&mut self.tab.config.icon_sizes.grid, 50, 500), tab::View::Grid => zoom_out(&mut config.icon_sizes.grid, 50, 500),
} });
} }
Message::Surface(a) => { Message::Surface(a) => {
return cosmic::task::message(cosmic::Action::Cosmic( return cosmic::task::message(cosmic::Action::Cosmic(