From e4df96c890bd627e9bd5d8a54977befac4d67849 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 16 Jan 2025 22:02:43 +0100 Subject: [PATCH] Save position of the main window to be used after restart --- dev_data/state/.gitignore | 3 ++- rust/client/src/ui/mod.rs | 46 ++++++++++++++++++++++++++-------- rust/common/src/dirs.rs | 3 +++ rust/common/src/model.rs | 3 ++- rust/server/src/plugins/mod.rs | 2 ++ 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/dev_data/state/.gitignore b/dev_data/state/.gitignore index f1a26a4..555232b 100644 --- a/dev_data/state/.gitignore +++ b/dev_data/state/.gitignore @@ -1,2 +1,3 @@ logs -local_storage \ No newline at end of file +local_storage +window_position \ No newline at end of file diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 0dec384..faab7e7 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -12,10 +12,10 @@ use iced::widget::text::Shaping; use iced::widget::text_input::focus; use iced::widget::{button, column, container, horizontal_rule, horizontal_space, row, scrollable, text, text_input, Space}; use iced::window::{Level, Mode, Position, Screenshot}; -use iced::{event, executor, font, futures, keyboard, stream, window, Alignment, Event, Font, Length, Padding, Pixels, Renderer, Settings, Size, Subscription, Task}; +use iced::{event, executor, font, futures, keyboard, stream, window, Alignment, Event, Font, Length, Padding, Pixels, Point, Renderer, Settings, Size, Subscription, Task}; use std::collections::HashMap; use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::rc::Rc; use std::sync::{Arc, Mutex as StdMutex, Mutex, RwLock as StdRwLock}; use iced::alignment::{Horizontal, Vertical}; @@ -74,6 +74,7 @@ pub struct AppModel { theme: GauntletComplexTheme, window_position_mode: WindowPositionMode, close_on_unfocus: bool, + window_position_file: PathBuf, // ephemeral state prompt: String, @@ -245,10 +246,10 @@ const WINDOW_WIDTH: f32 = 750.0; const WINDOW_HEIGHT: f32 = 450.0; #[cfg(not(target_os = "macos"))] -fn window_settings(visible: bool) -> window::Settings { +fn window_settings(visible: bool, position: Position) -> window::Settings { window::Settings { size: Size::new(WINDOW_WIDTH, WINDOW_HEIGHT), - position: Position::Centered, + position, resizable: false, decorations: false, visible, @@ -260,10 +261,10 @@ fn window_settings(visible: bool) -> window::Settings { } #[cfg(target_os = "macos")] -fn window_settings(visible: bool) -> window::Settings { +fn window_settings(visible: bool, position: Position) -> window::Settings { window::Settings { size: Size::new(WINDOW_WIDTH, WINDOW_HEIGHT), - position: Position::Centered, + position, resizable: false, decorations: true, visible, @@ -296,8 +297,22 @@ fn layer_shell_settings() -> iced_layershell::reexport::NewLayerShellSettings { } } -fn open_main_window_non_wayland(minimized: bool) -> (window::Id, Task) { - let (main_window_id, open_task) = window::open(window_settings(!minimized)); +fn open_main_window_non_wayland(minimized: bool, window_position_file: &PathBuf) -> (window::Id, Task) { + let position = fs::read_to_string(window_position_file) + .map(|data| { + if let Some((x, y)) = data.split_once(":") { + match (x.parse(), y.parse()) { + (Ok(x), Ok(y)) => Some(Position::Specific(Point::new(x, y))), + _ => None + } + } else { + None + } + }) + .unwrap_or(None) + .unwrap_or(Position::Centered); + + let (main_window_id, open_task) = window::open(window_settings(!minimized, position)); (main_window_id, Task::batch([ open_task.map(|_| AppMsg::Noop), @@ -423,11 +438,11 @@ fn new( open_main_window_wayland(id) } } else { - open_main_window_non_wayland(minimized) + open_main_window_non_wayland(minimized, &setup_data.window_position_file) }; #[cfg(not(target_os = "linux"))] - let (main_window_id, open_task) = open_main_window_non_wayland(minimized); + let (main_window_id, open_task) = open_main_window_non_wayland(minimized, &setup_data.window_position_file); tasks.push(open_task); @@ -531,6 +546,7 @@ fn new( theme, window_position_mode: setup_data.window_position_mode, close_on_unfocus: setup_data.close_on_unfocus, + window_position_file: setup_data.window_position_file, // ephemeral state prompt: "".to_string(), @@ -1004,6 +1020,16 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { state.on_unfocused() } } + AppMsg::IcedEvent(window_id, Event::Window(window::Event::Moved(point))) => { + if window_id != state.main_window_id { + return Task::none() + } + + let _ = fs::create_dir_all(state.window_position_file.parent().unwrap()); + let _ = fs::write(&state.window_position_file, format!("{}:{}", point.x, point.y)); + + Task::none() + } AppMsg::IcedEvent(_, _) => Task::none(), AppMsg::WidgetEvent { widget_event: ComponentWidgetEvent::Noop, .. } => Task::none(), AppMsg::WidgetEvent { widget_event: ComponentWidgetEvent::PreviousView, .. } => state.global_state.back(&state.client_context), diff --git a/rust/common/src/dirs.rs b/rust/common/src/dirs.rs index f0562ef..f11cee3 100644 --- a/rust/common/src/dirs.rs +++ b/rust/common/src/dirs.rs @@ -124,4 +124,7 @@ impl Dirs { state_dir.join(format!("project-gauntlet-{}.sock", plugin_uuid)) } + pub fn window_position(&self) -> PathBuf { + self.state_dir().join("window_position") + } } \ No newline at end of file diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index a4ae32f..8235abc 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use std::fmt::{Display, Formatter}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use anyhow::anyhow; @@ -218,6 +218,7 @@ pub struct UiTheme { #[derive(Debug)] pub struct UiSetupData { + pub window_position_file: PathBuf, pub theme: UiTheme, pub global_shortcut: Option, pub close_on_unfocus: bool, diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index b0dd6a3..3912751 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -88,12 +88,14 @@ impl ApplicationManager { } pub async fn setup_data(&self) -> anyhow::Result { + let window_position_file = self.dirs.window_position(); let theme = self.settings.effective_theme().await?; let global_shortcut = self.settings.effective_global_shortcut().await?; let window_position_mode = self.settings.window_position_mode_setting().await?; let close_on_unfocus = self.config_reader.close_on_unfocus(); Ok(UiSetupData { + window_position_file, theme, global_shortcut, close_on_unfocus,