feat(window-management): enhance child window state management and persistence (#177)
- Introduced a new `ChildWindowStateKey` type to manage the state of various child windows. - Updated `openChildWindow` function to restore dimensions and maximized state from saved states based on the window label. - Implemented state saving for child windows during application shutdown and on close events, improving user experience by preserving window states. - Refactored window state handling in `window_state.rs` to support loading and saving of child window states, ensuring consistent behavior across the application.
This commit is contained in:
1 parent
ce246203cc
commit
fd7fb1e5ab
4 files changed
+422
-41
No files matched your search
@@ -259,6 +259,19 @@ pub fn prepare_app_shutdown(app: &tauri::AppHandle) {
|
||||
}
|
||||
}
|
||||
|
||||
for window in app.webview_windows().into_values() {
|
||||
if crate::window_state::is_main_window_label(window.label()) {
|
||||
continue;
|
||||
}
|
||||
if let Err(error) = crate::window_state::save_child_webview_window_state(&window) {
|
||||
tracing::warn!(
|
||||
window_label = window.label(),
|
||||
"Failed to save child window state before shutdown: {}",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let session_manager = app.state::<Arc<SessionManager>>();
|
||||
session_manager.flush_history_before_shutdown();
|
||||
|
||||
@@ -315,5 +328,26 @@ pub fn on_window_event(window: &tauri::Window, event: &tauri::WindowEvent) {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if crate::window_state::child_window_state_key_for_label(window.label()).is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
match event {
|
||||
tauri::WindowEvent::Resized(_) | tauri::WindowEvent::ScaleFactorChanged { .. } => {
|
||||
crate::window_state::schedule_window_state_save(window.app_handle(), window.label());
|
||||
}
|
||||
tauri::WindowEvent::CloseRequested { .. } => {
|
||||
if let Err(error) = crate::window_state::save_child_window_state(window) {
|
||||
tracing::warn!(
|
||||
window_label = window.label(),
|
||||
"Failed to save child window state on close: {}",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ pub struct ChildWindowOptions {
|
||||
height: Option<f64>,
|
||||
resizable: Option<bool>,
|
||||
always_on_top: Option<bool>,
|
||||
state_key: Option<crate::window_state::ChildWindowStateKey>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Deserialize, PartialEq, Eq)]
|
||||
@@ -83,8 +84,26 @@ pub async fn open_child_window(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let width = options.width.unwrap_or(720.0);
|
||||
let height = options.height.unwrap_or(560.0);
|
||||
let requested_width = options.width.unwrap_or(720.0);
|
||||
let requested_height = options.height.unwrap_or(560.0);
|
||||
let resizable = options.resizable.unwrap_or(true);
|
||||
let state_key = if resizable {
|
||||
options
|
||||
.state_key
|
||||
.or_else(|| crate::window_state::child_window_state_key_for_label(&options.label))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let restored_state = state_key.map(|key| {
|
||||
crate::window_state::load_child_window_state(key, requested_width, requested_height)
|
||||
});
|
||||
let width = restored_state
|
||||
.as_ref()
|
||||
.map_or(requested_width, |state| state.width);
|
||||
let height = restored_state
|
||||
.as_ref()
|
||||
.map_or(requested_height, |state| state.height);
|
||||
let maximized = restored_state.as_ref().is_some_and(|state| state.maximized);
|
||||
let kind = options.kind.unwrap_or(ChildWindowKind::Modal);
|
||||
let parent_label = options
|
||||
.parent_label
|
||||
@@ -100,9 +119,10 @@ pub async fn open_child_window(
|
||||
)
|
||||
.title(options.title)
|
||||
.inner_size(width, height)
|
||||
.maximized(maximized)
|
||||
.visible(false)
|
||||
.decorations(cfg!(target_os = "macos"))
|
||||
.resizable(options.resizable.unwrap_or(true))
|
||||
.resizable(resizable)
|
||||
.always_on_top(options.always_on_top.unwrap_or(false));
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
|
||||
+357
-38
@@ -1,4 +1,5 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
use std::time::{Duration, Instant};
|
||||
use tauri::{Manager, PhysicalPosition, PhysicalRect, PhysicalSize, Position};
|
||||
@@ -10,8 +11,15 @@ const DEFAULT_MAIN_WIDTH: f64 = 1280.0;
|
||||
const DEFAULT_MAIN_HEIGHT: f64 = 800.0;
|
||||
const MIN_MAIN_WIDTH: f64 = 720.0;
|
||||
const MIN_MAIN_HEIGHT: f64 = 480.0;
|
||||
const MIN_CHILD_WIDTH: f64 = 360.0;
|
||||
const MIN_CHILD_HEIGHT: f64 = 240.0;
|
||||
pub const MAIN_WINDOW_LABEL: &str = "main";
|
||||
pub const MAIN_WINDOW_PREFIX: &str = "main-";
|
||||
const SETTINGS_WINDOW_KEY: &str = "settings";
|
||||
const NEW_SESSION_WINDOW_KEY: &str = "new-session";
|
||||
const QUICK_COMMAND_WINDOW_KEY: &str = "quick-command";
|
||||
const FILE_EDITOR_WINDOW_KEY: &str = "file-editor";
|
||||
const AUTO_UPLOAD_WINDOW_PREFIX: &str = "auto-upload-";
|
||||
const SAVE_DEBOUNCE: Duration = Duration::from_millis(500);
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -42,6 +50,87 @@ impl MainWindowState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
struct WindowStateDoc {
|
||||
#[serde(flatten)]
|
||||
main: MainWindowState,
|
||||
#[serde(default)]
|
||||
children: BTreeMap<String, ChildWindowState>,
|
||||
}
|
||||
|
||||
impl WindowStateDoc {
|
||||
fn normalized(mut self) -> Self {
|
||||
self.main = self.main.normalized();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ChildWindowState {
|
||||
#[serde(default)]
|
||||
pub width: f64,
|
||||
#[serde(default)]
|
||||
pub height: f64,
|
||||
#[serde(default)]
|
||||
pub maximized: bool,
|
||||
}
|
||||
|
||||
impl Default for ChildWindowState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
width: 0.0,
|
||||
height: 0.0,
|
||||
maximized: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildWindowState {
|
||||
fn normalized(mut self, fallback_width: f64, fallback_height: f64) -> Self {
|
||||
self.width = normalize_child_dimension(self.width, MIN_CHILD_WIDTH, fallback_width);
|
||||
self.height = normalize_child_dimension(self.height, MIN_CHILD_HEIGHT, fallback_height);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum ChildWindowStateKey {
|
||||
Settings,
|
||||
NewSession,
|
||||
QuickCommand,
|
||||
FileEditor,
|
||||
}
|
||||
|
||||
impl ChildWindowStateKey {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Settings => SETTINGS_WINDOW_KEY,
|
||||
Self::NewSession => NEW_SESSION_WINDOW_KEY,
|
||||
Self::QuickCommand => QUICK_COMMAND_WINDOW_KEY,
|
||||
Self::FileEditor => FILE_EDITOR_WINDOW_KEY,
|
||||
}
|
||||
}
|
||||
|
||||
fn default_size(self) -> (f64, f64) {
|
||||
match self {
|
||||
Self::Settings => (800.0, 560.0),
|
||||
Self::NewSession => (520.0, 620.0),
|
||||
Self::QuickCommand => (540.0, 640.0),
|
||||
Self::FileEditor => (980.0, 720.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn default_state(self) -> ChildWindowState {
|
||||
let (width, height) = self.default_size();
|
||||
ChildWindowState {
|
||||
width,
|
||||
height,
|
||||
maximized: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ChildWindowPlacement {
|
||||
pub x: i32,
|
||||
@@ -51,11 +140,10 @@ pub struct ChildWindowPlacement {
|
||||
#[derive(Debug)]
|
||||
struct PendingSave {
|
||||
app: tauri::AppHandle,
|
||||
window_label: String,
|
||||
scheduled_at: Instant,
|
||||
}
|
||||
|
||||
static PENDING_SAVE: OnceLock<Mutex<Option<PendingSave>>> = OnceLock::new();
|
||||
static PENDING_SAVES: OnceLock<Mutex<BTreeMap<String, PendingSave>>> = OnceLock::new();
|
||||
|
||||
fn default_main_width() -> f64 {
|
||||
DEFAULT_MAIN_WIDTH
|
||||
@@ -73,20 +161,87 @@ fn normalize_dimension(value: f64, min: f64, fallback: f64) -> f64 {
|
||||
}
|
||||
}
|
||||
|
||||
fn pending_save() -> &'static Mutex<Option<PendingSave>> {
|
||||
PENDING_SAVE.get_or_init(|| Mutex::new(None))
|
||||
fn normalize_child_dimension(value: f64, min: f64, fallback: f64) -> f64 {
|
||||
let fallback = if fallback.is_finite() && fallback > 0.0 {
|
||||
fallback.max(min)
|
||||
} else {
|
||||
min
|
||||
};
|
||||
|
||||
if value.is_finite() && value > 0.0 {
|
||||
value.max(min)
|
||||
} else {
|
||||
fallback
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_main_window_state() -> MainWindowState {
|
||||
storage::load_settings_doc::<MainWindowState>(SettingsDocKey::WindowState)
|
||||
fn pending_saves() -> &'static Mutex<BTreeMap<String, PendingSave>> {
|
||||
PENDING_SAVES.get_or_init(|| Mutex::new(BTreeMap::new()))
|
||||
}
|
||||
|
||||
fn load_window_state_doc() -> WindowStateDoc {
|
||||
storage::load_settings_doc::<WindowStateDoc>(SettingsDocKey::WindowState)
|
||||
.unwrap_or_default()
|
||||
.normalized()
|
||||
}
|
||||
|
||||
pub fn load_main_window_state() -> MainWindowState {
|
||||
load_window_state_doc().main
|
||||
}
|
||||
|
||||
pub fn is_main_window_label(label: &str) -> bool {
|
||||
label == MAIN_WINDOW_LABEL || label.starts_with(MAIN_WINDOW_PREFIX)
|
||||
}
|
||||
|
||||
fn is_scoped_child_label(label: &str, base: &str) -> bool {
|
||||
if label == base {
|
||||
return true;
|
||||
}
|
||||
|
||||
label
|
||||
.strip_prefix(base)
|
||||
.and_then(|rest| rest.strip_prefix('-'))
|
||||
.is_some_and(is_main_window_label)
|
||||
}
|
||||
|
||||
pub fn child_window_state_key_for_label(label: &str) -> Option<ChildWindowStateKey> {
|
||||
if label.starts_with(AUTO_UPLOAD_WINDOW_PREFIX) || is_main_window_label(label) {
|
||||
return None;
|
||||
}
|
||||
|
||||
if is_scoped_child_label(label, SETTINGS_WINDOW_KEY) {
|
||||
return Some(ChildWindowStateKey::Settings);
|
||||
}
|
||||
if is_scoped_child_label(label, NEW_SESSION_WINDOW_KEY) {
|
||||
return Some(ChildWindowStateKey::NewSession);
|
||||
}
|
||||
if is_scoped_child_label(label, QUICK_COMMAND_WINDOW_KEY) {
|
||||
return Some(ChildWindowStateKey::QuickCommand);
|
||||
}
|
||||
if label.starts_with(&format!("{FILE_EDITOR_WINDOW_KEY}-")) {
|
||||
return Some(ChildWindowStateKey::FileEditor);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn load_child_window_state(
|
||||
key: ChildWindowStateKey,
|
||||
fallback_width: f64,
|
||||
fallback_height: f64,
|
||||
) -> ChildWindowState {
|
||||
load_window_state_doc()
|
||||
.children
|
||||
.get(key.as_str())
|
||||
.cloned()
|
||||
.unwrap_or(ChildWindowState {
|
||||
width: fallback_width,
|
||||
height: fallback_height,
|
||||
maximized: false,
|
||||
})
|
||||
.normalized(fallback_width, fallback_height)
|
||||
}
|
||||
|
||||
pub fn save_main_window_state(window: &tauri::Window) -> AppResult<()> {
|
||||
if !is_main_window_label(window.label()) {
|
||||
return Ok(());
|
||||
@@ -122,65 +277,153 @@ fn save_main_window_state_values(
|
||||
inner_size: PhysicalSize<u32>,
|
||||
maximized: bool,
|
||||
) -> AppResult<()> {
|
||||
let mut state = load_main_window_state();
|
||||
state.maximized = maximized;
|
||||
storage::update_settings_doc::<WindowStateDoc, _, _>(SettingsDocKey::WindowState, |doc| {
|
||||
let mut state = doc.main.clone().normalized();
|
||||
state.maximized = maximized;
|
||||
|
||||
if !maximized {
|
||||
let scale = if scale_factor.is_finite() && scale_factor > 0.0 {
|
||||
scale_factor
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
state.width =
|
||||
normalize_dimension(inner_size.width as f64 / scale, MIN_MAIN_WIDTH, state.width);
|
||||
state.height = normalize_dimension(
|
||||
inner_size.height as f64 / scale,
|
||||
MIN_MAIN_HEIGHT,
|
||||
state.height,
|
||||
);
|
||||
}
|
||||
if !maximized {
|
||||
let scale = if scale_factor.is_finite() && scale_factor > 0.0 {
|
||||
scale_factor
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
state.width =
|
||||
normalize_dimension(inner_size.width as f64 / scale, MIN_MAIN_WIDTH, state.width);
|
||||
state.height = normalize_dimension(
|
||||
inner_size.height as f64 / scale,
|
||||
MIN_MAIN_HEIGHT,
|
||||
state.height,
|
||||
);
|
||||
}
|
||||
|
||||
storage::save_settings_doc(SettingsDocKey::WindowState, &state.normalized())
|
||||
doc.main = state.normalized();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn schedule_main_window_state_save(app: &tauri::AppHandle, window_label: &str) {
|
||||
pub fn save_child_window_state(window: &tauri::Window) -> AppResult<()> {
|
||||
let Some(key) = child_window_state_key_for_label(window.label()) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let maximized = window.is_maximized().unwrap_or(false);
|
||||
let scale_factor = window
|
||||
.scale_factor()
|
||||
.map_err(|error| AppError::Config(error.to_string()))?;
|
||||
let inner_size = window
|
||||
.inner_size()
|
||||
.map_err(|error| AppError::Config(error.to_string()))?;
|
||||
save_child_window_state_values(key, scale_factor, inner_size, maximized)
|
||||
}
|
||||
|
||||
pub fn save_child_webview_window_state(window: &tauri::WebviewWindow) -> AppResult<()> {
|
||||
let Some(key) = child_window_state_key_for_label(window.label()) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let maximized = window.is_maximized().unwrap_or(false);
|
||||
let scale_factor = window
|
||||
.scale_factor()
|
||||
.map_err(|error| AppError::Config(error.to_string()))?;
|
||||
let inner_size = window
|
||||
.inner_size()
|
||||
.map_err(|error| AppError::Config(error.to_string()))?;
|
||||
save_child_window_state_values(key, scale_factor, inner_size, maximized)
|
||||
}
|
||||
|
||||
fn save_child_window_state_values(
|
||||
key: ChildWindowStateKey,
|
||||
scale_factor: f64,
|
||||
inner_size: PhysicalSize<u32>,
|
||||
maximized: bool,
|
||||
) -> AppResult<()> {
|
||||
storage::update_settings_doc::<WindowStateDoc, _, _>(SettingsDocKey::WindowState, |doc| {
|
||||
let (fallback_width, fallback_height) = key.default_size();
|
||||
let mut state = doc
|
||||
.children
|
||||
.get(key.as_str())
|
||||
.cloned()
|
||||
.unwrap_or_else(|| key.default_state())
|
||||
.normalized(fallback_width, fallback_height);
|
||||
state.maximized = maximized;
|
||||
|
||||
if !maximized {
|
||||
let scale = if scale_factor.is_finite() && scale_factor > 0.0 {
|
||||
scale_factor
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
state.width = normalize_child_dimension(
|
||||
inner_size.width as f64 / scale,
|
||||
MIN_CHILD_WIDTH,
|
||||
state.width,
|
||||
);
|
||||
state.height = normalize_child_dimension(
|
||||
inner_size.height as f64 / scale,
|
||||
MIN_CHILD_HEIGHT,
|
||||
state.height,
|
||||
);
|
||||
}
|
||||
|
||||
doc.children.insert(key.as_str().to_string(), state);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn save_webview_window_state(window: &tauri::WebviewWindow) -> AppResult<()> {
|
||||
if is_main_window_label(window.label()) {
|
||||
return save_main_webview_window_state(window);
|
||||
}
|
||||
save_child_webview_window_state(window)
|
||||
}
|
||||
|
||||
pub fn schedule_window_state_save(app: &tauri::AppHandle, window_label: &str) {
|
||||
let scheduled_at = Instant::now();
|
||||
let window_label = window_label.to_string();
|
||||
{
|
||||
let mut pending = pending_save()
|
||||
let mut pending = pending_saves()
|
||||
.lock()
|
||||
.unwrap_or_else(|poisoned| poisoned.into_inner());
|
||||
*pending = Some(PendingSave {
|
||||
app: app.clone(),
|
||||
window_label: window_label.to_string(),
|
||||
scheduled_at,
|
||||
});
|
||||
pending.insert(
|
||||
window_label.clone(),
|
||||
PendingSave {
|
||||
app: app.clone(),
|
||||
scheduled_at,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
tokio::time::sleep(SAVE_DEBOUNCE).await;
|
||||
let (app, window_label) = {
|
||||
let mut pending = pending_save()
|
||||
let mut pending = pending_saves()
|
||||
.lock()
|
||||
.unwrap_or_else(|poisoned| poisoned.into_inner());
|
||||
match pending.as_ref() {
|
||||
match pending.get(&window_label) {
|
||||
Some(save) if save.scheduled_at == scheduled_at => {
|
||||
let app = save.app.clone();
|
||||
let window_label = save.window_label.clone();
|
||||
*pending = None;
|
||||
(app, window_label)
|
||||
let save = pending.remove(&window_label).expect("pending save exists");
|
||||
(save.app, window_label.clone())
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(window) = app.get_webview_window(&window_label) {
|
||||
if let Err(error) = save_main_webview_window_state(&window) {
|
||||
tracing::warn!("Failed to save main window state: {}", error);
|
||||
if let Err(error) = save_webview_window_state(&window) {
|
||||
tracing::warn!(
|
||||
window_label = window.label(),
|
||||
"Failed to save window state: {}",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn schedule_main_window_state_save(app: &tauri::AppHandle, window_label: &str) {
|
||||
schedule_window_state_save(app, window_label);
|
||||
}
|
||||
|
||||
pub fn center_child_in_parent_monitor(
|
||||
app: &tauri::AppHandle,
|
||||
parent_label: Option<&str>,
|
||||
@@ -268,6 +511,82 @@ mod tests {
|
||||
assert!(state.maximized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserializes_legacy_main_window_state_doc() {
|
||||
let doc: WindowStateDoc =
|
||||
serde_json::from_str(r#"{ "width": 1000, "height": 700, "maximized": true }"#).unwrap();
|
||||
|
||||
assert_eq!(doc.main.width, 1000.0);
|
||||
assert_eq!(doc.main.height, 700.0);
|
||||
assert!(doc.main.maximized);
|
||||
assert!(doc.children.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserializes_child_window_state_doc() {
|
||||
let doc: WindowStateDoc = serde_json::from_str(
|
||||
r#"{
|
||||
"width": 1280,
|
||||
"height": 800,
|
||||
"maximized": false,
|
||||
"children": {
|
||||
"file-editor": { "width": 1100, "height": 760, "maximized": true }
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
let state = doc
|
||||
.children
|
||||
.get(ChildWindowStateKey::FileEditor.as_str())
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.normalized(980.0, 720.0);
|
||||
|
||||
assert_eq!(state.width, 1100.0);
|
||||
assert_eq!(state.height, 760.0);
|
||||
assert!(state.maximized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalizes_invalid_child_window_state_values() {
|
||||
let state = ChildWindowState {
|
||||
width: f64::NAN,
|
||||
height: 100.0,
|
||||
maximized: false,
|
||||
}
|
||||
.normalized(520.0, 620.0);
|
||||
|
||||
assert_eq!(state.width, 520.0);
|
||||
assert_eq!(state.height, MIN_CHILD_HEIGHT);
|
||||
assert!(!state.maximized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recognizes_child_window_state_keys() {
|
||||
assert_eq!(
|
||||
child_window_state_key_for_label("settings"),
|
||||
Some(ChildWindowStateKey::Settings)
|
||||
);
|
||||
assert_eq!(
|
||||
child_window_state_key_for_label("settings-main-abc"),
|
||||
Some(ChildWindowStateKey::Settings)
|
||||
);
|
||||
assert_eq!(
|
||||
child_window_state_key_for_label("new-session-main-abc"),
|
||||
Some(ChildWindowStateKey::NewSession)
|
||||
);
|
||||
assert_eq!(
|
||||
child_window_state_key_for_label("quick-command-main-abc"),
|
||||
Some(ChildWindowStateKey::QuickCommand)
|
||||
);
|
||||
assert_eq!(
|
||||
child_window_state_key_for_label("file-editor-abc"),
|
||||
Some(ChildWindowStateKey::FileEditor)
|
||||
);
|
||||
assert_eq!(child_window_state_key_for_label("auto-upload-abc"), None);
|
||||
assert_eq!(child_window_state_key_for_label("main"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recognizes_main_window_label_family() {
|
||||
assert!(is_main_window_label("main"));
|
||||
|
||||
@@ -9,6 +9,8 @@ import i18n from "../i18n";
|
||||
import { invoke } from "./invoke";
|
||||
import { isMacOS } from "./platform";
|
||||
|
||||
type ChildWindowStateKey = "settings" | "new-session" | "quick-command" | "file-editor";
|
||||
|
||||
interface ChildWindowOptions {
|
||||
label: string;
|
||||
title: string;
|
||||
@@ -18,6 +20,7 @@ interface ChildWindowOptions {
|
||||
width?: number;
|
||||
height?: number;
|
||||
resizable?: boolean;
|
||||
stateKey?: ChildWindowStateKey;
|
||||
}
|
||||
|
||||
const MAIN_WINDOW_LABEL = "main";
|
||||
@@ -292,6 +295,7 @@ export async function openChildWindow(opts: ChildWindowOptions) {
|
||||
height: opts.height ?? 560,
|
||||
resizable: opts.resizable ?? true,
|
||||
alwaysOnTop: needsAlwaysOnTop(opts.label),
|
||||
stateKey: opts.stateKey,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -322,6 +326,7 @@ export async function openSettings(tab?: string) {
|
||||
parentLabel: ownerMainWindowLabel,
|
||||
width: 800,
|
||||
height: 560,
|
||||
stateKey: "settings",
|
||||
});
|
||||
if (tab) {
|
||||
const payload = { tab, targetWindowLabel: ownerMainWindowLabel };
|
||||
@@ -378,6 +383,7 @@ export function openNewSessionWithTarget(
|
||||
parentLabel: ownerMainWindowLabel,
|
||||
width: 520,
|
||||
height: 620,
|
||||
stateKey: "new-session",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -392,6 +398,7 @@ export function openQuickCommand(editJson?: string) {
|
||||
parentLabel: ownerMainWindowLabel,
|
||||
width: 540,
|
||||
height: 640,
|
||||
stateKey: "quick-command",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -431,6 +438,7 @@ export function openRemoteFileEditor(data: RemoteFileEditorWindowData) {
|
||||
parentLabel: ownerMainWindowLabel,
|
||||
width: 980,
|
||||
height: 720,
|
||||
stateKey: "file-editor",
|
||||
}).then((win) => {
|
||||
const payload = { targetLabel: label, data };
|
||||
emit("remote-file-editor-open", payload);
|
||||
|
||||
Reference in new issue
Block a user