feat: keep track of window size and position
This commit is contained in:
85
src/app.rs
85
src/app.rs
@@ -339,8 +339,7 @@ pub(crate) enum Message {
|
|||||||
ExportChatPicked(Option<PathBuf>, String),
|
ExportChatPicked(Option<PathBuf>, String),
|
||||||
ModelManagerOpened(window::Id),
|
ModelManagerOpened(window::Id),
|
||||||
WindowOpened(window::Id),
|
WindowOpened(window::Id),
|
||||||
WindowCloseRequested(window::Id),
|
WindowEvent(window::Id, window::Event),
|
||||||
WindowClosed(window::Id),
|
|
||||||
RequestQuit,
|
RequestQuit,
|
||||||
ConfirmQuit,
|
ConfirmQuit,
|
||||||
CancelQuit,
|
CancelQuit,
|
||||||
@@ -1046,33 +1045,50 @@ impl App {
|
|||||||
return focus_composer();
|
return focus_composer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::WindowCloseRequested(id) => {
|
Message::WindowEvent(id, event) => match event {
|
||||||
if id == self.main_window {
|
window::Event::Opened { position, size } if id == self.main_window => {
|
||||||
return self.update(Message::RequestQuit);
|
self.config.interface.window_size =
|
||||||
|
[size.width.round() as u32, size.height.round() as u32];
|
||||||
|
self.config.interface.window_position = position
|
||||||
|
.map(|position| [position.x.round() as i32, position.y.round() as i32]);
|
||||||
}
|
}
|
||||||
return window::close(id);
|
window::Event::Moved(position) if id == self.main_window => {
|
||||||
}
|
self.config.interface.window_position =
|
||||||
Message::WindowClosed(id) => {
|
Some([position.x.round() as i32, position.y.round() as i32]);
|
||||||
if self.model_manager_window == Some(id) {
|
|
||||||
self.model_manager_window = None;
|
|
||||||
self.pending_model_delete = None;
|
|
||||||
}
|
}
|
||||||
if self.preferences_window == Some(id) {
|
window::Event::Resized(size) if id == self.main_window => {
|
||||||
self.preferences_window = None;
|
self.config.interface.window_size =
|
||||||
self.preference_error = None;
|
[size.width.round() as u32, size.height.round() as u32];
|
||||||
self.restore_dev_brain_confirmation = false;
|
|
||||||
}
|
}
|
||||||
if self.help_window == Some(id) {
|
window::Event::CloseRequested => {
|
||||||
self.help_window = None;
|
if id == self.main_window {
|
||||||
|
return self.update(Message::RequestQuit);
|
||||||
|
}
|
||||||
|
return window::close(id);
|
||||||
}
|
}
|
||||||
}
|
window::Event::Closed => {
|
||||||
|
if self.model_manager_window == Some(id) {
|
||||||
|
self.model_manager_window = None;
|
||||||
|
self.pending_model_delete = None;
|
||||||
|
}
|
||||||
|
if self.preferences_window == Some(id) {
|
||||||
|
self.preferences_window = None;
|
||||||
|
self.preference_error = None;
|
||||||
|
self.restore_dev_brain_confirmation = false;
|
||||||
|
}
|
||||||
|
if self.help_window == Some(id) {
|
||||||
|
self.help_window = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
Message::RequestQuit => {
|
Message::RequestQuit => {
|
||||||
if self.active_chat_count() == 0 {
|
if self.active_chat_count() == 0 {
|
||||||
return iced::exit();
|
return self.quit();
|
||||||
}
|
}
|
||||||
self.quit_confirmation = true;
|
self.quit_confirmation = true;
|
||||||
}
|
}
|
||||||
Message::ConfirmQuit => return iced::exit(),
|
Message::ConfirmQuit => return self.quit(),
|
||||||
Message::CancelQuit => self.quit_confirmation = false,
|
Message::CancelQuit => self.quit_confirmation = false,
|
||||||
Message::Escape(id) => {
|
Message::Escape(id) => {
|
||||||
if self.preferences_window == Some(id) {
|
if self.preferences_window == Some(id) {
|
||||||
@@ -1888,7 +1904,7 @@ impl App {
|
|||||||
// focus, so a dialog would never see it through `on_key_press`.
|
// focus, so a dialog would never see it through `on_key_press`.
|
||||||
iced::event::listen_with(|event, _, id| {
|
iced::event::listen_with(|event, _, id| {
|
||||||
matches!(
|
matches!(
|
||||||
event,
|
&event,
|
||||||
iced::Event::Keyboard(keyboard::Event::KeyPressed {
|
iced::Event::Keyboard(keyboard::Event::KeyPressed {
|
||||||
key: keyboard::Key::Named(keyboard::key::Named::Escape),
|
key: keyboard::Key::Named(keyboard::key::Named::Escape),
|
||||||
..
|
..
|
||||||
@@ -1896,8 +1912,17 @@ impl App {
|
|||||||
)
|
)
|
||||||
.then_some(Message::Escape(id))
|
.then_some(Message::Escape(id))
|
||||||
}),
|
}),
|
||||||
window::close_requests().map(Message::WindowCloseRequested),
|
window::events().filter_map(|(id, event)| {
|
||||||
window::close_events().map(Message::WindowClosed),
|
matches!(
|
||||||
|
event,
|
||||||
|
window::Event::Opened { .. }
|
||||||
|
| window::Event::Moved(_)
|
||||||
|
| window::Event::Resized(_)
|
||||||
|
| window::Event::CloseRequested
|
||||||
|
| window::Event::Closed
|
||||||
|
)
|
||||||
|
.then_some(Message::WindowEvent(id, event))
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
subscriptions
|
subscriptions
|
||||||
.push(iced::time::every(METRICS_SAMPLE_INTERVAL).map(|_| Message::MetricsTick));
|
.push(iced::time::every(METRICS_SAMPLE_INTERVAL).map(|_| Message::MetricsTick));
|
||||||
@@ -2071,6 +2096,20 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn quit(&mut self) -> Task<Message> {
|
||||||
|
if self.database.is_none() {
|
||||||
|
return iced::exit();
|
||||||
|
}
|
||||||
|
match self.config.save(&config_path()) {
|
||||||
|
Ok(()) => iced::exit(),
|
||||||
|
Err(error) => {
|
||||||
|
self.quit_confirmation = false;
|
||||||
|
self.error = Some(error);
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn finish_cache_change(&mut self) {
|
fn finish_cache_change(&mut self) {
|
||||||
self.metrics.rescan_cache(&kv_cache_path());
|
self.metrics.rescan_cache(&kv_cache_path());
|
||||||
self.metrics_snapshot = self.metrics.snapshot();
|
self.metrics_snapshot = self.metrics.snapshot();
|
||||||
|
|||||||
@@ -229,6 +229,8 @@ impl Default for EndpointConfig {
|
|||||||
pub struct InterfaceConfig {
|
pub struct InterfaceConfig {
|
||||||
pub sidebar_collapsed: bool,
|
pub sidebar_collapsed: bool,
|
||||||
pub sidebar_width: i32,
|
pub sidebar_width: i32,
|
||||||
|
pub window_size: [u32; 2],
|
||||||
|
pub window_position: Option<[i32; 2]>,
|
||||||
/// Project the app reopens on. Cleared when that project goes away.
|
/// Project the app reopens on. Cleared when that project goes away.
|
||||||
pub last_project_id: Option<i32>,
|
pub last_project_id: Option<i32>,
|
||||||
}
|
}
|
||||||
@@ -238,6 +240,8 @@ impl Default for InterfaceConfig {
|
|||||||
Self {
|
Self {
|
||||||
sidebar_collapsed: false,
|
sidebar_collapsed: false,
|
||||||
sidebar_width: 276,
|
sidebar_width: 276,
|
||||||
|
window_size: [1120, 720],
|
||||||
|
window_position: None,
|
||||||
last_project_id: None,
|
last_project_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -348,6 +352,11 @@ mod tests {
|
|||||||
whitespace: GitDiffWhitespace::IgnoreEndOfLine,
|
whitespace: GitDiffWhitespace::IgnoreEndOfLine,
|
||||||
..GitConfig::default()
|
..GitConfig::default()
|
||||||
},
|
},
|
||||||
|
interface: InterfaceConfig {
|
||||||
|
window_size: [1280, 800],
|
||||||
|
window_position: Some([120, -40]),
|
||||||
|
..InterfaceConfig::default()
|
||||||
|
},
|
||||||
..Config::default()
|
..Config::default()
|
||||||
};
|
};
|
||||||
config.save(&path).unwrap();
|
config.save(&path).unwrap();
|
||||||
@@ -358,7 +367,8 @@ mod tests {
|
|||||||
"model: glm-5.2\ndefault_permission_mode: ai\na2ui_enabled: false\n\
|
"model: glm-5.2\ndefault_permission_mode: ai\na2ui_enabled: false\n\
|
||||||
generation:\n context_tokens: 65536\n reasoning_mode: none\n\
|
generation:\n context_tokens: 65536\n reasoning_mode: none\n\
|
||||||
runtime:\n ssd:\n enabled: true\n cache: 64GB\n\
|
runtime:\n ssd:\n enabled: true\n cache: 64GB\n\
|
||||||
git:\n diff_layout: split\n diff_algorithm: patience\n context_lines: 5\n whitespace: ignore-end-of-line\n"
|
git:\n diff_layout: split\n diff_algorithm: patience\n context_lines: 5\n whitespace: ignore-end-of-line\n\
|
||||||
|
interface:\n window_size:\n - 1280\n - 800\n window_position:\n - 120\n - -40\n"
|
||||||
);
|
);
|
||||||
assert_eq!(Config::load(&path).unwrap(), config);
|
assert_eq!(Config::load(&path).unwrap(), config);
|
||||||
fs::remove_dir_all(&directory).unwrap();
|
fs::remove_dir_all(&directory).unwrap();
|
||||||
|
|||||||
15
src/main.rs
15
src/main.rs
@@ -26,7 +26,7 @@ mod server;
|
|||||||
mod settings;
|
mod settings;
|
||||||
|
|
||||||
use app::{App, Message, app_icon, app_theme};
|
use app::{App, Message, app_icon, app_theme};
|
||||||
use iced::{Size, window};
|
use iced::{Point, Size, window};
|
||||||
|
|
||||||
fn main() -> iced::Result {
|
fn main() -> iced::Result {
|
||||||
if std::env::args().nth(1).as_deref() == Some("validate-a2ui") {
|
if std::env::args().nth(1).as_deref() == Some("validate-a2ui") {
|
||||||
@@ -42,8 +42,19 @@ fn main() -> iced::Result {
|
|||||||
}
|
}
|
||||||
iced::daemon(
|
iced::daemon(
|
||||||
|| {
|
|| {
|
||||||
|
let interface = config::Config::load(&app::config_path())
|
||||||
|
.unwrap_or_default()
|
||||||
|
.interface;
|
||||||
let (main_window, open) = window::open(window::Settings {
|
let (main_window, open) = window::open(window::Settings {
|
||||||
size: Size::new(1120.0, 720.0),
|
size: Size::new(
|
||||||
|
interface.window_size[0] as f32,
|
||||||
|
interface.window_size[1] as f32,
|
||||||
|
),
|
||||||
|
position: interface
|
||||||
|
.window_position
|
||||||
|
.map_or(window::Position::Default, |[x, y]| {
|
||||||
|
window::Position::Specific(Point::new(x as f32, y as f32))
|
||||||
|
}),
|
||||||
min_size: Some(Size::new(760.0, 480.0)),
|
min_size: Some(Size::new(760.0, 480.0)),
|
||||||
icon: Some(app_icon()),
|
icon: Some(app_icon()),
|
||||||
exit_on_close_request: false,
|
exit_on_close_request: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user