Skip to content

Commit 040d7d4

Browse files
committed
fix: auto-save on change, settings reopen, version 1.0.7, update check
1 parent 5b6b4e4 commit 040d7d4

8 files changed

Lines changed: 152 additions & 85 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "HideDesktopApps"
3-
version = "1.0.0"
3+
version = "1.0.7"
44
edition = "2021"
55
description = "System tray app to hide/show desktop icons, taskbar, and windows via hotkeys"
66
repository = "https://github.com/Londopy/HideDesktopApps"
@@ -13,7 +13,7 @@ path = "src/main.rs"
1313
[dependencies]
1414
tray-icon = "0.17"
1515
global-hotkey = "0.6"
16-
eframe = { version = "0.29", default-features = false, features = ["default_fonts", "wgpu"] }
16+
eframe = { version = "0.29", default-features = false, features = ["default_fonts", "glow"] }
1717
egui = "0.29"
1818
winit = "0.30"
1919
serde = { version = "1", features = ["derive"] }

src/ui/discord_tab.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ impl SettingsApp {
66
ui.heading("Discord Rich Presence");
77
ui.add_space(8.0);
88

9-
ui.checkbox(
10-
&mut self.config.discord.enabled,
11-
"Show current hide state in Discord Rich Presence",
12-
);
9+
if ui
10+
.checkbox(
11+
&mut self.config.discord.enabled,
12+
"Show current hide state in Discord Rich Presence",
13+
)
14+
.changed()
15+
{
16+
self.dirty = true;
17+
}
1318

1419
ui.add_space(8.0);
1520
ui.group(|ui| {

src/ui/hotkeys_tab.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,22 @@ impl SettingsApp {
9393
&self.config.hotkeys.windows.clone(),
9494
);
9595

96-
self.config.hotkeys.icons = new_icons;
97-
self.config.hotkeys.taskbar = new_taskbar;
98-
self.config.hotkeys.windows = new_windows;
96+
if new_icons != self.config.hotkeys.icons
97+
|| new_taskbar != self.config.hotkeys.taskbar
98+
|| new_windows != self.config.hotkeys.windows
99+
{
100+
self.config.hotkeys.icons = new_icons;
101+
self.config.hotkeys.taskbar = new_taskbar;
102+
self.config.hotkeys.windows = new_windows;
103+
self.dirty = true;
104+
}
99105

100-
// Inline duplicate warning
106+
// Inline duplicate warning — save is blocked until this clears.
101107
let h = &self.config.hotkeys;
102-
let icons = h.icons.clone();
103-
let taskbar = h.taskbar.clone();
104-
let windows = h.windows.clone();
105-
if icons == taskbar || icons == windows || taskbar == windows {
108+
if h.icons == h.taskbar || h.icons == h.windows || h.taskbar == h.windows {
106109
ui.colored_label(
107110
egui::Color32::YELLOW,
108-
"Warning: duplicate hotkeys detected. All three must be unique.",
111+
"Warning: duplicate hotkeys — all three must be unique.",
109112
);
110113
}
111114
}

src/ui/mod.rs

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ mod updater_tab;
88

99
use crate::config::AppConfig;
1010
use crate::Cmd;
11+
use std::sync::atomic::{AtomicBool, Ordering};
1112
use std::sync::{Arc, Mutex};
1213

14+
// Prevents opening a second settings window while one is already showing.
15+
static SETTINGS_OPEN: AtomicBool = AtomicBool::new(false);
16+
1317
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1418
pub enum Tab {
1519
Hotkeys,
@@ -52,9 +56,10 @@ pub struct SettingsApp {
5256
pub config_shared: Arc<Mutex<AppConfig>>,
5357
pub cmd_tx: std::sync::mpsc::Sender<Cmd>,
5458
pub current_tab: Tab,
55-
pub hotkey_error: Option<String>,
5659
pub update_status: Option<String>,
5760
pub startup_registered: bool,
61+
/// Set to true by any tab that modifies config; triggers a save at end of frame.
62+
pub dirty: bool,
5863
}
5964

6065
impl SettingsApp {
@@ -66,33 +71,22 @@ impl SettingsApp {
6671
config_shared,
6772
cmd_tx,
6873
current_tab: Tab::Hotkeys,
69-
hotkey_error: None,
7074
update_status: None,
7175
startup_registered,
76+
dirty: false,
7277
}
7378
}
7479

75-
fn apply(&mut self) {
76-
// Validate hotkeys are unique
77-
let h = &self.config.hotkeys;
78-
if h.icons == h.taskbar || h.icons == h.windows || h.taskbar == h.windows {
79-
self.hotkey_error = Some("All three hotkeys must be unique.".to_string());
80-
return;
81-
}
82-
self.hotkey_error = None;
83-
84-
// Persist config to disk
80+
/// Persist the current config to disk and notify the main loop.
81+
/// Does not validate hotkeys — the hotkeys tab handles that inline.
82+
pub fn save_now(&mut self) {
8583
if let Err(e) = crate::config::save_config(&self.config) {
8684
eprintln!("Failed to save config: {e}");
8785
}
88-
89-
// Update the shared config so other threads can see it
9086
{
9187
let mut shared = self.config_shared.lock().unwrap();
9288
*shared = self.config.clone();
9389
}
94-
95-
// Notify main loop
9690
let _ = self.cmd_tx.send(Cmd::ConfigUpdated(self.config.clone()));
9791
}
9892
}
@@ -116,31 +110,28 @@ impl eframe::App for SettingsApp {
116110
Tab::Discord => self.discord_tab(ui),
117111
Tab::About => self.about_tab(ui),
118112
});
113+
});
119114

120-
ui.separator();
121-
122-
// Hotkey validation error only applies on the Hotkeys tab
123-
if self.current_tab == Tab::Hotkeys {
124-
if let Some(ref err) = self.hotkey_error.clone() {
125-
ui.colored_label(egui::Color32::RED, err);
126-
}
115+
// Auto-save at end of frame if anything changed.
116+
// Skip if hotkeys are currently invalid (duplicate) — the tab shows a warning.
117+
if self.dirty {
118+
self.dirty = false;
119+
let h = &self.config.hotkeys;
120+
let hotkeys_ok = h.icons != h.taskbar && h.icons != h.windows && h.taskbar != h.windows;
121+
if hotkeys_ok {
122+
self.save_now();
127123
}
128-
129-
ui.horizontal(|ui| {
130-
// Apply & Save doesn't make sense on the About tab
131-
if self.current_tab != Tab::About && ui.button("Apply & Save").clicked() {
132-
self.apply();
133-
}
134-
if ui.button("Close").clicked() {
135-
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
136-
}
137-
});
138-
});
124+
}
139125
}
140126
}
141127

142-
/// Open the Settings window on a background thread so the main loop keeps running.
128+
/// Open the settings window on a background thread so the main loop keeps running.
129+
/// If a settings window is already open, this is a no-op.
143130
pub fn open_settings(config_shared: Arc<Mutex<AppConfig>>, cmd_tx: std::sync::mpsc::Sender<Cmd>) {
131+
if SETTINGS_OPEN.swap(true, Ordering::SeqCst) {
132+
return;
133+
}
134+
144135
std::thread::spawn(move || {
145136
let native_options = eframe::NativeOptions {
146137
viewport: egui::ViewportBuilder::default()
@@ -164,5 +155,8 @@ pub fn open_settings(config_shared: Arc<Mutex<AppConfig>>, cmd_tx: std::sync::mp
164155
crate::dlog!("Settings window error: {}", e);
165156
eprintln!("Settings window error: {e}");
166157
}
158+
159+
// Allow the settings window to be opened again.
160+
SETTINGS_OPEN.store(false, Ordering::SeqCst);
167161
});
168162
}

src/ui/notifications_tab.rs

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,47 @@ impl SettingsApp {
66
ui.heading("Notifications");
77
ui.add_space(8.0);
88

9-
ui.checkbox(
10-
&mut self.config.notifications.enabled,
11-
"Enable Windows toast notifications",
12-
);
9+
if ui
10+
.checkbox(
11+
&mut self.config.notifications.enabled,
12+
"Enable Windows toast notifications",
13+
)
14+
.changed()
15+
{
16+
self.dirty = true;
17+
}
1318

1419
ui.add_space(8.0);
1520
ui.add_enabled_ui(self.config.notifications.enabled, |ui| {
1621
ui.group(|ui| {
1722
ui.label("Show notifications for:");
18-
ui.checkbox(
19-
&mut self.config.notifications.on_update,
20-
"Available updates",
21-
);
22-
ui.checkbox(
23-
&mut self.config.notifications.on_hotkey_fail,
24-
"Hotkey registration failures",
25-
);
26-
ui.checkbox(
27-
&mut self.config.notifications.on_profile_switch,
28-
"Profile switches",
29-
);
23+
if ui
24+
.checkbox(
25+
&mut self.config.notifications.on_update,
26+
"Available updates",
27+
)
28+
.changed()
29+
{
30+
self.dirty = true;
31+
}
32+
if ui
33+
.checkbox(
34+
&mut self.config.notifications.on_hotkey_fail,
35+
"Hotkey registration failures",
36+
)
37+
.changed()
38+
{
39+
self.dirty = true;
40+
}
41+
if ui
42+
.checkbox(
43+
&mut self.config.notifications.on_profile_switch,
44+
"Profile switches",
45+
)
46+
.changed()
47+
{
48+
self.dirty = true;
49+
}
3050
});
3151
});
3252

src/ui/profiles_tab.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ impl SettingsApp {
1414
ui.group(|ui| {
1515
ui.horizontal(|ui| {
1616
ui.label("Name:");
17-
ui.text_edit_singleline(&mut profile.name);
17+
if ui.text_edit_singleline(&mut profile.name).changed() {
18+
self.dirty = true;
19+
}
1820
if ui
1921
.add(egui::Button::new("Remove").fill(egui::Color32::DARK_RED))
2022
.clicked()
@@ -25,25 +27,35 @@ impl SettingsApp {
2527

2628
ui.horizontal(|ui| {
2729
ui.label("Hotkey:");
28-
ui.text_edit_singleline(&mut profile.hotkey);
30+
if ui.text_edit_singleline(&mut profile.hotkey).changed() {
31+
self.dirty = true;
32+
}
2933
ui.label("(empty = none, e.g. ctrl+alt+1)");
3034
});
3135

3236
ui.horizontal(|ui| {
33-
ui.checkbox(&mut profile.icons, "Hide Icons");
34-
ui.checkbox(&mut profile.taskbar, "Hide Taskbar");
35-
ui.checkbox(&mut profile.windows, "Hide Windows");
37+
if ui.checkbox(&mut profile.icons, "Hide Icons").changed() {
38+
self.dirty = true;
39+
}
40+
if ui.checkbox(&mut profile.taskbar, "Hide Taskbar").changed() {
41+
self.dirty = true;
42+
}
43+
if ui.checkbox(&mut profile.windows, "Hide Windows").changed() {
44+
self.dirty = true;
45+
}
3646
});
3747
});
3848
ui.add_space(4.0);
3949
}
4050

4151
if let Some(idx) = to_delete {
4252
self.config.profiles.remove(idx);
53+
self.dirty = true;
4354
}
4455

4556
if ui.button("+ Add Profile").clicked() {
4657
self.config.profiles.push(ProfileConfig::default());
58+
self.dirty = true;
4759
}
4860

4961
ui.add_space(8.0);
@@ -58,14 +70,28 @@ impl SettingsApp {
5870
self.config.defaults.profile.clone()
5971
};
6072

61-
egui::ComboBox::from_label("Default profile")
73+
let combo = egui::ComboBox::from_label("Default profile")
6274
.selected_text(&current)
6375
.show_ui(ui, |ui| {
76+
let mut changed = false;
6477
for name in &profile_names {
6578
let is_none = name == "(none)";
6679
let value = if is_none { "" } else { name.as_str() };
67-
ui.selectable_value(&mut self.config.defaults.profile, value.to_string(), name);
80+
if ui
81+
.selectable_value(
82+
&mut self.config.defaults.profile,
83+
value.to_string(),
84+
name,
85+
)
86+
.changed()
87+
{
88+
changed = true;
89+
}
6890
}
91+
changed
6992
});
93+
if combo.inner == Some(true) {
94+
self.dirty = true;
95+
}
7096
}
7197
}

src/ui/startup_tab.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ impl SettingsApp {
66
ui.heading("Startup");
77
ui.add_space(8.0);
88

9-
ui.checkbox(
10-
&mut self.config.startup.enabled,
11-
"Start HideDesktopApps at Windows logon",
12-
);
9+
if ui
10+
.checkbox(
11+
&mut self.config.startup.enabled,
12+
"Start HideDesktopApps at Windows logon",
13+
)
14+
.changed()
15+
{
16+
self.dirty = true;
17+
}
1318
ui.label("Places a launcher in your Windows Startup folder.");
1419

1520
ui.add_space(8.0);

0 commit comments

Comments
 (0)