Skip to content

Commit e787ad3

Browse files
committed
Encrypt keyfile path and improve window state
Add config_crypto.rs to encrypt/decrypt the global keyfile path using AES-256-GCM and store the encryption key in a file under the config directory. Settings now keep an encrypted keyfile_path_encrypted (persisted) and a transient global_keyfile_path (skipped from serialization); load/save call helper functions to decrypt/encrypt the field and preserve existing encrypted data on errors. Add aes-gcm to Cargo.toml and update the UI to clear the encrypted field when the user clears the global keyfile. Refactor window handling: replace startup_frame_count with first_frame/start_maximized/is_maximized, send a first-frame ViewportCommand to reliably start maximized on Windows, read the OS maximized state each frame, and only persist geometry when values actually change to avoid unnecessary disk writes.
1 parent 948102d commit e787ad3

7 files changed

Lines changed: 353 additions & 53 deletions

File tree

Cargo.lock

Lines changed: 103 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ tiny-skia = "0.11"
1818

1919
# Cryptography
2020
orion = "0.17"
21+
aes-gcm = "0.10"
2122

2223
# File dialogs
2324
rfd = "0.15"

src/app.rs

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,12 @@ pub struct EditorApp {
120120
pub(crate) batch_output_dir: Option<PathBuf>,
121121

122122
// Window state tracking
123-
// Window state tracking
124-
pub(crate) last_known_maximized: bool,
125-
pub(crate) startup_frame_count: usize,
123+
/// True only for the very first update() call — used for the first-frame maximize trick
124+
pub(crate) first_frame: bool,
125+
/// Whether the window should start maximized (from saved settings)
126+
pub(crate) start_maximized: bool,
127+
/// Current maximized state, updated every frame from the OS viewport
128+
pub(crate) is_maximized: bool,
126129
}
127130

128131
impl EditorApp {
@@ -200,8 +203,9 @@ impl EditorApp {
200203
batch_files: Vec::new(),
201204
batch_keyfile: None,
202205
batch_output_dir: None,
203-
last_known_maximized: settings.start_maximized,
204-
startup_frame_count: 0,
206+
first_frame: true,
207+
start_maximized: settings.start_maximized,
208+
is_maximized: false,
205209
last_autosave_time: None,
206210
last_copy_time: None,
207211

@@ -232,56 +236,64 @@ impl eframe::App for EditorApp {
232236
// Update window title dynamically
233237
self.update_window_title(ctx);
234238

235-
// Track window state (maximized, position, size)
236-
// Wait for a few frames to let the OS apply the initial window state
237-
if self.startup_frame_count <= 30 {
238-
self.startup_frame_count += 1;
239+
// ── Window state tracking ──────────────────────────────────────────
240+
// Read the OS-reported maximized state every frame.
241+
// This correctly tracks when the user clicks the Windows maximize/restore button.
242+
self.is_maximized = ctx.input(|i| i.viewport().maximized.unwrap_or(false));
243+
244+
// First-frame maximize trick: with_maximized(true) in ViewportBuilder has a
245+
// race condition on Windows 10/11. Instead we send a ViewportCommand on the
246+
// very first frame, which fires after the window is fully created.
247+
if self.first_frame {
248+
self.first_frame = false;
249+
if self.start_maximized {
250+
ctx.send_viewport_cmd(egui::ViewportCommand::Maximized(true));
251+
}
239252
}
240-
241-
if self.startup_frame_count > 30 {
242-
ctx.input(|i| {
243-
let is_maximized = i.viewport().maximized.unwrap_or(false);
244-
245-
// Only save changes to avoid disk spam
253+
254+
// Persist window geometry changes — only save when something actually changed
255+
{
246256
let mut changed = false;
247257

248-
if is_maximized != self.last_known_maximized {
249-
self.settings.start_maximized = is_maximized;
250-
self.last_known_maximized = is_maximized;
258+
// Track maximize state changes
259+
if self.is_maximized != self.settings.start_maximized {
260+
self.settings.start_maximized = self.is_maximized;
251261
changed = true;
252262
}
253263

254-
// If not maximized, save position and size
255-
if !is_maximized {
256-
if let Some(rect) = i.viewport().outer_rect {
257-
// Save position if valid (not negative coordinates that might be off-screen/minimized)
258-
if rect.min.x >= 0.0 && rect.min.y >= 0.0 {
259-
if (self.settings.window_pos_x - rect.min.x).abs() > 1.0 {
260-
self.settings.window_pos_x = rect.min.x;
264+
// When NOT maximized, save position and size so we preserve the last
265+
// known non-maximized geometry. Never overwrite these while maximized,
266+
// because the maximized rect covers the whole screen and would clobber
267+
// the user's preferred restored-window position/size.
268+
if !self.is_maximized {
269+
ctx.input(|i| {
270+
if let Some(rect) = i.viewport().outer_rect {
271+
// Only save valid positions (negative coords = off-screen/minimized)
272+
if rect.min.x >= 0.0 && rect.min.y >= 0.0 {
273+
if (self.settings.window_pos_x - rect.min.x).abs() > 1.0 {
274+
self.settings.window_pos_x = rect.min.x;
275+
changed = true;
276+
}
277+
if (self.settings.window_pos_y - rect.min.y).abs() > 1.0 {
278+
self.settings.window_pos_y = rect.min.y;
279+
changed = true;
280+
}
281+
}
282+
if (self.settings.window_width - rect.width()).abs() > 1.0 {
283+
self.settings.window_width = rect.width();
261284
changed = true;
262-
}
263-
if (self.settings.window_pos_y - rect.min.y).abs() > 1.0 {
264-
self.settings.window_pos_y = rect.min.y;
285+
}
286+
if (self.settings.window_height - rect.height()).abs() > 1.0 {
287+
self.settings.window_height = rect.height();
265288
changed = true;
266-
}
267-
}
268-
269-
// Save size
270-
if (self.settings.window_width - rect.width()).abs() > 1.0 {
271-
self.settings.window_width = rect.width();
272-
changed = true;
273-
}
274-
if (self.settings.window_height - rect.height()).abs() > 1.0 {
275-
self.settings.window_height = rect.height();
276-
changed = true;
289+
}
277290
}
278-
}
291+
});
279292
}
280293

281294
if changed {
282295
let _ = self.settings.save();
283296
}
284-
});
285297
}
286298

287299
// Perform auto-save check

0 commit comments

Comments
 (0)