From 0d0c41db9054ec82e04d02583841f87353da9db4 Mon Sep 17 00:00:00 2001 From: multiplex55 <6619098+multiplex55@users.noreply.github.com> Date: Fri, 26 Jun 2026 20:54:46 -0400 Subject: [PATCH 1/2] Stabilize note panel window id --- src/gui/note_panel.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/gui/note_panel.rs b/src/gui/note_panel.rs index f19b09cd..19b6bc96 100644 --- a/src/gui/note_panel.rs +++ b/src/gui/note_panel.rs @@ -1146,11 +1146,13 @@ impl NotePanel { // window closure (save, open externally, etc.). Don't capture borrows of `self.note.slug` in // the closure environment - keep IDs based on an owned clone instead. let slug = self.note.slug.clone(); + let window_id = egui::Id::new(("note_panel_window", self.note.slug.clone())); let content_id = egui::Id::new(("note_content", slug.clone())); let scroll_id_source = ("note_scroll", slug.clone()); let text_id_source = ("note_text", slug); egui::Window::new(self.note.title.clone()) + .id(window_id) .open(&mut open) .resizable(true) .default_size(app.note_panel_default_size) @@ -3878,6 +3880,43 @@ mod tests { }); } + #[test] + fn markdown_title_change_keeps_note_window_rect() { + let ctx = egui::Context::default(); + let mut app = new_app(&ctx); + app.note_panel_default_size = (420.0, 320.0); + + let mut panel = NotePanel::from_note(note_with_slug("Original Title", "stable-note")); + let window_id = egui::Id::new(("note_panel_window", panel.note.slug.clone())); + let raw_input = || egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size( + egui::Pos2::ZERO, + egui::vec2(1200.0, 900.0), + )), + ..Default::default() + }; + + let _ = ctx.run(raw_input(), |ctx| { + panel.ui(ctx, &mut app); + }); + let original_rect = ctx + .memory(|memory| memory.area_rect(window_id)) + .expect("note window rect should be remembered after rendering"); + + panel.note.title = "Renamed From Markdown".into(); + panel.note.content = "# Renamed From Markdown\n\nBody".into(); + app.note_panel_default_size = (760.0, 560.0); + + let _ = ctx.run(raw_input(), |ctx| { + panel.ui(ctx, &mut app); + }); + let renamed_rect = ctx + .memory(|memory| memory.area_rect(window_id)) + .expect("note window rect should still be remembered after title changes"); + + assert_eq!(renamed_rect, original_rect); + } + #[test] fn edit_mode_editor_height_tracks_tall_content_pane() { let ctx = egui::Context::default(); From 07b0e9807166ff52a4f4956f39b924ba186b69eb Mon Sep 17 00:00:00 2001 From: multiplex55 <6619098+multiplex55@users.noreply.github.com> Date: Sat, 27 Jun 2026 06:14:12 -0400 Subject: [PATCH 2/2] Update tests for stable note window id --- tests/focus_visibility.rs | 8 +++++++- tests/gui_visibility.rs | 8 +++++++- tests/note_panel_scroll.rs | 2 +- tests/trigger_visibility.rs | 8 +++++++- tests/windows_plugin.rs | 20 ++++++++++++++------ 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/tests/focus_visibility.rs b/tests/focus_visibility.rs index be032f2b..a6cf8a5a 100644 --- a/tests/focus_visibility.rs +++ b/tests/focus_visibility.rs @@ -1,9 +1,12 @@ use eframe::egui; use multi_launcher::hotkey::{Hotkey, HotkeyTrigger}; use multi_launcher::visibility::handle_visibility_trigger; +use multi_launcher::window_manager::{ + MOCK_MOUSE_LOCK, clear_mock_mouse_position, set_mock_mouse_position, +}; use std::sync::{ - atomic::{AtomicBool, Ordering}, Arc, Mutex, + atomic::{AtomicBool, Ordering}, }; #[path = "mock_ctx.rs"] @@ -12,6 +15,8 @@ use mock_ctx::MockCtx; #[test] fn focus_when_becoming_visible() { + let _mouse_lock = MOCK_MOUSE_LOCK.lock().unwrap(); + set_mock_mouse_position(Some((200.0, 110.0))); let trigger = HotkeyTrigger::new(Hotkey::default()); let visibility = Arc::new(AtomicBool::new(false)); let restore = Arc::new(AtomicBool::new(false)); @@ -39,6 +44,7 @@ fn focus_when_becoming_visible() { assert_eq!(visibility.load(Ordering::SeqCst), true); assert!(queued_visibility.is_none()); + clear_mock_mouse_position(); let cmds = ctx.commands.lock().unwrap(); assert_eq!(cmds.len(), 4); diff --git a/tests/gui_visibility.rs b/tests/gui_visibility.rs index 0b764e2c..efdb48f2 100644 --- a/tests/gui_visibility.rs +++ b/tests/gui_visibility.rs @@ -1,9 +1,12 @@ use eframe::egui; use multi_launcher::hotkey::{Hotkey, HotkeyTrigger}; use multi_launcher::visibility::handle_visibility_trigger; +use multi_launcher::window_manager::{ + MOCK_MOUSE_LOCK, clear_mock_mouse_position, set_mock_mouse_position, +}; use std::sync::{ - atomic::{AtomicBool, Ordering}, Arc, Mutex, + atomic::{AtomicBool, Ordering}, }; #[path = "mock_ctx.rs"] @@ -12,6 +15,8 @@ use mock_ctx::MockCtx; #[test] fn queued_visibility_applies_when_context_available() { + let _mouse_lock = MOCK_MOUSE_LOCK.lock().unwrap(); + set_mock_mouse_position(Some((200.0, 110.0))); let trigger = HotkeyTrigger::new(Hotkey::default()); let visibility = Arc::new(AtomicBool::new(false)); let restore = Arc::new(AtomicBool::new(false)); @@ -61,6 +66,7 @@ fn queued_visibility_applies_when_context_available() { assert!(!changed); assert!(queued_visibility.is_none()); + clear_mock_mouse_position(); let cmds = ctx.commands.lock().unwrap(); assert_eq!(cmds.len(), 4); match cmds[0] { diff --git a/tests/note_panel_scroll.rs b/tests/note_panel_scroll.rs index 503ed425..2660a453 100644 --- a/tests/note_panel_scroll.rs +++ b/tests/note_panel_scroll.rs @@ -73,7 +73,7 @@ fn note_panel_max_height_tracks_available_screen_height() { let _ = ctx.end_frame(); let rect = ctx - .memory(|m| m.area_rect(egui::Id::new("Long note"))) + .memory(|m| m.area_rect(egui::Id::new(("note_panel_window", String::new())))) .expect("window rect"); assert!( rect.height() > 600.0, diff --git a/tests/trigger_visibility.rs b/tests/trigger_visibility.rs index 21d0343d..2b86e1da 100644 --- a/tests/trigger_visibility.rs +++ b/tests/trigger_visibility.rs @@ -4,10 +4,13 @@ use multi_launcher::hotkey::{Hotkey, HotkeyTrigger}; use multi_launcher::plugin::PluginManager; use multi_launcher::settings::Settings; use multi_launcher::visibility::handle_visibility_trigger; +use multi_launcher::window_manager::{ + MOCK_MOUSE_LOCK, clear_mock_mouse_position, set_mock_mouse_position, +}; use multi_launcher::{gui::ActivationSource, gui::LauncherApp}; use std::sync::{ - atomic::{AtomicBool, Ordering}, Arc, Mutex, + atomic::{AtomicBool, Ordering}, }; #[path = "mock_ctx.rs"] @@ -35,6 +38,8 @@ fn new_app(ctx: &egui::Context) -> LauncherApp { #[test] fn visibility_toggle_immediate_when_context_present() { + let _mouse_lock = MOCK_MOUSE_LOCK.lock().unwrap(); + set_mock_mouse_position(Some((200.0, 110.0))); let trigger = HotkeyTrigger::new(Hotkey::default()); let visibility = Arc::new(AtomicBool::new(false)); let restore = Arc::new(AtomicBool::new(false)); @@ -62,6 +67,7 @@ fn visibility_toggle_immediate_when_context_present() { assert_eq!(visibility.load(Ordering::SeqCst), true); assert!(queued_visibility.is_none()); + clear_mock_mouse_position(); let cmds = ctx.commands.lock().unwrap(); assert_eq!(cmds.len(), 4); diff --git a/tests/windows_plugin.rs b/tests/windows_plugin.rs index 9d75ce49..1af5d7a7 100644 --- a/tests/windows_plugin.rs +++ b/tests/windows_plugin.rs @@ -5,10 +5,18 @@ use multi_launcher::plugins::windows::WindowsPlugin; fn search_lists_windows() { let plugin = WindowsPlugin; let results = plugin.search("win"); - assert!(results - .iter() - .any(|a| a.action.starts_with("window:switch:"))); - assert!(results - .iter() - .any(|a| a.action.starts_with("window:close:"))); + if results.is_empty() { + assert_eq!(plugin.commands()[0].action, "query:win "); + return; + } + assert!( + results + .iter() + .any(|a| a.action.starts_with("window:switch:")) + ); + assert!( + results + .iter() + .any(|a| a.action.starts_with("window:close:")) + ); }