|
|
|
@ -4,11 +4,7 @@
|
|
|
|
|
|
|
|
|
|
use bitflags::bitflags;
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use tauri::{
|
|
|
|
|
plugin::{Builder as PluginBuilder, TauriPlugin},
|
|
|
|
|
LogicalSize, Manager, Monitor, PhysicalPosition, PhysicalSize, RunEvent, Runtime, Window,
|
|
|
|
|
WindowEvent,
|
|
|
|
|
};
|
|
|
|
|
use tauri::{plugin::{Builder as PluginBuilder, TauriPlugin}, LogicalSize, Manager, Monitor, PhysicalPosition, PhysicalSize, RunEvent, Runtime, Window, WindowEvent, State, AppHandle};
|
|
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
|
collections::{HashMap, HashSet},
|
|
|
|
@ -80,7 +76,8 @@ impl Default for WindowState {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct WindowStateCache(Arc<Mutex<HashMap<String, WindowState>>>);
|
|
|
|
|
struct WindowStateCache(Arc<Mutex< HashMap<String, WindowState>>>);
|
|
|
|
|
struct GroupStateCache(Arc<Mutex<Vec<Group>>>);
|
|
|
|
|
pub trait AppHandleExt {
|
|
|
|
|
/// Saves all open windows state to disk
|
|
|
|
|
fn save_window_state(&self, flags: StateFlags) -> Result<()>;
|
|
|
|
@ -92,10 +89,14 @@ impl<R: Runtime> AppHandleExt for tauri::AppHandle<R> {
|
|
|
|
|
let state_path = app_dir.join(STATE_FILENAME);
|
|
|
|
|
let cache = self.state::<WindowStateCache>();
|
|
|
|
|
let mut state = cache.0.lock().unwrap();
|
|
|
|
|
for (label, s) in state.iter_mut() {
|
|
|
|
|
if let Some(window) = self.get_window(label) {
|
|
|
|
|
window.update_state(s, flags)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let groups = self.state::<GroupStateCache>();
|
|
|
|
|
let binding = groups.0.clone();
|
|
|
|
|
let binding = binding.lock().unwrap();
|
|
|
|
|
let groups = binding.as_ref();
|
|
|
|
|
|
|
|
|
|
for window in self.windows().values() {
|
|
|
|
|
window.update_state(state.get(group_name(groups, window.label().into())), flags)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
create_dir_all(&app_dir)
|
|
|
|
@ -111,6 +112,13 @@ impl<R: Runtime> AppHandleExt for tauri::AppHandle<R> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn group_name_by_state(groups: State<GroupStateCache>, label: String) {
|
|
|
|
|
let binding = groups.0.clone();
|
|
|
|
|
let binding = binding.lock().unwrap();
|
|
|
|
|
let groups = binding.as_ref();
|
|
|
|
|
let key = group_name(groups, label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait WindowExt {
|
|
|
|
|
/// Restores this window state from disk
|
|
|
|
|
fn restore_state(&self, flags: StateFlags) -> tauri::Result<()>;
|
|
|
|
@ -121,9 +129,12 @@ impl<R: Runtime> WindowExt for Window<R> {
|
|
|
|
|
let cache = self.state::<WindowStateCache>();
|
|
|
|
|
let mut c = cache.0.lock().unwrap();
|
|
|
|
|
|
|
|
|
|
let groups = self.state::<GroupStateCache>();
|
|
|
|
|
let key = group_name_by_state(groups, self.label().into());
|
|
|
|
|
|
|
|
|
|
let mut should_show = true;
|
|
|
|
|
|
|
|
|
|
if let Some(state) = c.get(self.label()) {
|
|
|
|
|
if let Some(state) = c.get(key) {
|
|
|
|
|
// avoid restoring the default zeroed state
|
|
|
|
|
if *state == WindowState::default() {
|
|
|
|
|
return Ok(());
|
|
|
|
@ -197,7 +208,7 @@ impl<R: Runtime> WindowExt for Window<R> {
|
|
|
|
|
metadata.fullscreen = self.is_fullscreen()?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.insert(self.label().into(), metadata);
|
|
|
|
|
c.insert(key.into(), metadata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if flags.contains(StateFlags::VISIBLE) && should_show {
|
|
|
|
@ -265,11 +276,36 @@ impl<R: Runtime> WindowExtInternal for Window<R> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Group {
|
|
|
|
|
name: String,
|
|
|
|
|
match_rule: fn(&String) -> bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Group {
|
|
|
|
|
pub fn new(name: String, match_rule: fn(&String) -> bool) -> Group {
|
|
|
|
|
Group { name, match_rule }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn filter(&self, label: &String) -> bool {
|
|
|
|
|
(self.match_rule)(label)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// get group name which match first rule
|
|
|
|
|
pub fn group_name(groups: &Vec<Group>, label: String) -> String {
|
|
|
|
|
groups
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|g| g.filter(&label))
|
|
|
|
|
.nth(0)
|
|
|
|
|
.map_or(label, |g| g.name.clone())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
|
pub struct Builder {
|
|
|
|
|
denylist: HashSet<String>,
|
|
|
|
|
skip_initial_state: HashSet<String>,
|
|
|
|
|
state_flags: StateFlags,
|
|
|
|
|
groups: Vec<Group>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Builder {
|
|
|
|
@ -292,6 +328,12 @@ impl Builder {
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// add group
|
|
|
|
|
pub fn add_group(mut self, group: Group) -> Self {
|
|
|
|
|
self.groups.push(group);
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
|
|
|
|
|
let flags = self.state_flags;
|
|
|
|
|
PluginBuilder::new("window-state")
|
|
|
|
@ -318,6 +360,7 @@ impl Builder {
|
|
|
|
|
Default::default()
|
|
|
|
|
};
|
|
|
|
|
app.manage(WindowStateCache(cache));
|
|
|
|
|
app.manage(GroupStateCache(Arc::new(Mutex::new(self.groups))));
|
|
|
|
|
Ok(())
|
|
|
|
|
})
|
|
|
|
|
.on_webview_ready(move |window| {
|
|
|
|
@ -335,20 +378,23 @@ impl Builder {
|
|
|
|
|
let window_clone = window.clone();
|
|
|
|
|
let flags = self.state_flags;
|
|
|
|
|
|
|
|
|
|
let groups = window.state::<GroupStateCache>();
|
|
|
|
|
let key = group_name_by_state(groups, label);
|
|
|
|
|
|
|
|
|
|
// insert a default state if this window should be tracked and
|
|
|
|
|
// the disk cache doesn't have a state for it
|
|
|
|
|
{
|
|
|
|
|
cache
|
|
|
|
|
.lock()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.entry(label.clone())
|
|
|
|
|
.entry(key)
|
|
|
|
|
.or_insert_with(WindowState::default);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.on_window_event(move |e| {
|
|
|
|
|
if let WindowEvent::CloseRequested { .. } = e {
|
|
|
|
|
let mut c = cache.lock().unwrap();
|
|
|
|
|
if let Some(state) = c.get_mut(&label) {
|
|
|
|
|
if let Some(state) = c.get_mut(&key) {
|
|
|
|
|
let _ = window_clone.update_state(state, flags);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|