Refactored window management solution & implemented macOS accessory mode
This commit is contained in:
@@ -7,6 +7,9 @@ use tracing::{error, info};
|
||||
use crate::{
|
||||
get_app_handle,
|
||||
services::app_events::{CreateDoll, EditDoll, SetInteractionOverlay},
|
||||
services::window_manager::{
|
||||
encode_query_value, ensure_window, EnsureWindowError, EnsureWindowResult, WindowConfig,
|
||||
},
|
||||
};
|
||||
|
||||
static APP_MENU_WINDOW_LABEL: &str = "app_menu";
|
||||
@@ -71,55 +74,14 @@ pub async fn open_doll_editor_window(doll_id: Option<String>) {
|
||||
};
|
||||
|
||||
// Check if the window already exists
|
||||
let existing_window = app_handle.get_webview_window(&window_label);
|
||||
if let Some(window) = existing_window {
|
||||
// If it exists, we might want to reload it with new params or just focus it
|
||||
if let Err(e) = window.set_focus() {
|
||||
error!("Failed to focus existing doll editor window: {}", e);
|
||||
}
|
||||
|
||||
// Ensure overlay is active on parent (redundancy for safety)
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Some(parent) = app_handle.get_webview_window(APP_MENU_WINDOW_LABEL) {
|
||||
if let Err(e) = SetInteractionOverlay(true).emit(&parent) {
|
||||
error!("Failed to ensure interaction overlay on parent: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit event to update context
|
||||
if let Some(id) = doll_id {
|
||||
if let Err(e) = EditDoll(id).emit(&window) {
|
||||
error!("Failed to emit edit-doll event: {}", e);
|
||||
}
|
||||
} else if let Err(e) = CreateDoll.emit(&window) {
|
||||
error!("Failed to emit create-doll event: {}", e);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let url_path = if let Some(id) = doll_id {
|
||||
format!("/doll-editor?id={}", id)
|
||||
let url_path = if let Some(ref id) = doll_id {
|
||||
format!("/doll-editor?id={}", encode_query_value(id))
|
||||
} else {
|
||||
"/doll-editor".to_string()
|
||||
};
|
||||
|
||||
let mut builder = tauri::WebviewWindowBuilder::new(
|
||||
app_handle,
|
||||
&window_label,
|
||||
tauri::WebviewUrl::App(url_path.into()),
|
||||
)
|
||||
.title("Doll Editor")
|
||||
.inner_size(300.0, 400.0)
|
||||
.resizable(false)
|
||||
.maximizable(false)
|
||||
.decorations(true)
|
||||
.transparent(false)
|
||||
.shadow(true)
|
||||
.visible(true)
|
||||
.skip_taskbar(false)
|
||||
.always_on_top(true) // Helper window, nice to stay on top
|
||||
.visible_on_all_workspaces(false);
|
||||
let has_existing_window = app_handle.get_webview_window(&window_label).is_some();
|
||||
let parent_window = app_handle.get_webview_window(APP_MENU_WINDOW_LABEL);
|
||||
|
||||
// Set parent if app menu exists
|
||||
// Also disable interaction with parent while child is open
|
||||
@@ -129,10 +91,11 @@ pub async fn open_doll_editor_window(doll_id: Option<String>) {
|
||||
|
||||
let mut parent_focus_listener_id: Option<u32> = None;
|
||||
|
||||
if let Some(parent) = app_handle.get_webview_window(APP_MENU_WINDOW_LABEL) {
|
||||
if !has_existing_window {
|
||||
if let Some(parent) = &parent_window {
|
||||
// 1. Disable parent interaction immediately (Windows only)
|
||||
#[cfg(target_os = "windows")]
|
||||
set_window_interaction(&parent, false);
|
||||
set_window_interaction(parent, false);
|
||||
|
||||
// 2. Setup Focus Trap (macOS only)
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -141,7 +104,7 @@ pub async fn open_doll_editor_window(doll_id: Option<String>) {
|
||||
let app_handle_clone = get_app_handle().clone();
|
||||
|
||||
// Emit event to show overlay
|
||||
if let Err(e) = SetInteractionOverlay(true).emit(&parent) {
|
||||
if let Err(e) = SetInteractionOverlay(true).emit(parent) {
|
||||
error!("Failed to emit set-interaction-overlay event: {}", e);
|
||||
}
|
||||
|
||||
@@ -159,32 +122,22 @@ pub async fn open_doll_editor_window(doll_id: Option<String>) {
|
||||
});
|
||||
parent_focus_listener_id = Some(id);
|
||||
}
|
||||
|
||||
match builder.parent(&parent) {
|
||||
Ok(b) => builder = b,
|
||||
Err(e) => {
|
||||
error!("Failed to set parent for doll editor window: {}", e);
|
||||
// If we fail, revert changes
|
||||
#[cfg(target_os = "windows")]
|
||||
set_window_interaction(&parent, true);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
if let Some(id) = parent_focus_listener_id {
|
||||
parent.unlisten(id);
|
||||
}
|
||||
// Remove overlay if we failed
|
||||
let _ = SetInteractionOverlay(false).emit(&parent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
match builder.build() {
|
||||
Ok(window) => {
|
||||
info!("{} window builder succeeded", window_label);
|
||||
let mut config = WindowConfig::regular_ui(window_label.as_str(), url_path, "Doll Editor");
|
||||
config.width = 300.0;
|
||||
config.height = 400.0;
|
||||
config.always_on_top = true;
|
||||
config.parent_label = if !has_existing_window && parent_window.is_some() {
|
||||
Some(APP_MENU_WINDOW_LABEL)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
config.require_parent = false;
|
||||
|
||||
match ensure_window(&config, true, true) {
|
||||
Ok(EnsureWindowResult::Created(window)) => {
|
||||
// 3. Setup cleanup hook: When this child window is destroyed, re-enable the parent
|
||||
let app_handle_clone = get_app_handle().clone();
|
||||
|
||||
@@ -223,10 +176,35 @@ pub async fn open_doll_editor_window(doll_id: Option<String>) {
|
||||
// #[cfg(debug_assertions)]
|
||||
// window.open_devtools();
|
||||
}
|
||||
Err(e) => {
|
||||
Ok(EnsureWindowResult::Existing(window)) => {
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Some(parent) = parent_window {
|
||||
if let Err(e) = SetInteractionOverlay(true).emit(&parent) {
|
||||
error!("Failed to ensure interaction overlay on parent: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(id) = doll_id {
|
||||
if let Err(e) = EditDoll(id).emit(&window) {
|
||||
error!("Failed to emit edit-doll event: {}", e);
|
||||
}
|
||||
} else if let Err(e) = CreateDoll.emit(&window) {
|
||||
error!("Failed to emit create-doll event: {}", e);
|
||||
}
|
||||
}
|
||||
Err(EnsureWindowError::ShowExisting(e)) => {
|
||||
error!("Failed to show existing {} window: {}", window_label, e);
|
||||
}
|
||||
Err(EnsureWindowError::MissingParent(parent_label)) => {
|
||||
error!(
|
||||
"Failed to create {} due to missing parent '{}': impossible state",
|
||||
window_label, parent_label
|
||||
);
|
||||
}
|
||||
Err(EnsureWindowError::SetParent(e)) | Err(EnsureWindowError::Build(e)) => {
|
||||
error!("Failed to build {} window: {}", window_label, e);
|
||||
// If build failed, revert
|
||||
if let Some(parent) = get_app_handle().get_webview_window(APP_MENU_WINDOW_LABEL) {
|
||||
if let Some(parent) = parent_window {
|
||||
#[cfg(target_os = "windows")]
|
||||
set_window_interaction(&parent, true);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user