hide app icon in macos dock without real windows
This commit is contained in:
@@ -47,7 +47,11 @@ pub fn run() {
|
|||||||
)
|
)
|
||||||
.expect("Failed to export TypeScript bindings");
|
.expect("Failed to export TypeScript bindings");
|
||||||
|
|
||||||
tauri::Builder::default()
|
let builder = tauri::Builder::default();
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
let builder = builder.plugin(ui::activation_policy::ActivationPolicy::default());
|
||||||
|
|
||||||
|
builder
|
||||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||||
.plugin(tauri_plugin_autostart::init(
|
.plugin(tauri_plugin_autostart::init(
|
||||||
tauri_plugin_autostart::MacosLauncher::LaunchAgent,
|
tauri_plugin_autostart::MacosLauncher::LaunchAgent,
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use tauri::{AppHandle, Manager, RunEvent, Window, WindowEvent, Wry, plugin::Plugin};
|
||||||
|
|
||||||
|
/// Normal windows live from opening until dismissal. Minimized windows still count.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ActivationPolicy {
|
||||||
|
windows: HashSet<String>,
|
||||||
|
applied_regular: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActivationPolicy {
|
||||||
|
fn record_window(&mut self, label: &str, created: bool) {
|
||||||
|
if matches!(label, "scene" | "splashscreen") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if created {
|
||||||
|
self.windows.insert(label.to_owned());
|
||||||
|
} else {
|
||||||
|
self.windows.remove(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reconcile(&mut self, app: &AppHandle) -> tauri::Result<()> {
|
||||||
|
let regular = !self.windows.is_empty();
|
||||||
|
if self.applied_regular != Some(regular) {
|
||||||
|
app.set_activation_policy(if regular {
|
||||||
|
tauri::ActivationPolicy::Regular
|
||||||
|
} else {
|
||||||
|
tauri::ActivationPolicy::Accessory
|
||||||
|
})?;
|
||||||
|
self.applied_regular = Some(regular);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin<Wry> for ActivationPolicy {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"activation-policy"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialize(
|
||||||
|
&mut self,
|
||||||
|
app: &AppHandle,
|
||||||
|
_config: serde_json::Value,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
self.reconcile(app)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_created(&mut self, window: Window) {
|
||||||
|
self.record_window(window.label(), true);
|
||||||
|
if let Err(error) = self.reconcile(window.app_handle()) {
|
||||||
|
eprintln!("failed to update macOS activation policy: {error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_event(&mut self, app: &AppHandle, event: &RunEvent) {
|
||||||
|
if let RunEvent::WindowEvent {
|
||||||
|
label,
|
||||||
|
event: WindowEvent::Destroyed,
|
||||||
|
..
|
||||||
|
} = event
|
||||||
|
{
|
||||||
|
self.record_window(label, false);
|
||||||
|
if let Err(error) = self.reconcile(app) {
|
||||||
|
eprintln!("failed to update macOS activation policy: {error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::ActivationPolicy;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decoration_windows_do_not_keep_the_app_in_the_dock() {
|
||||||
|
let mut policy = ActivationPolicy::default();
|
||||||
|
policy.record_window("scene", true);
|
||||||
|
policy.record_window("splashscreen", true);
|
||||||
|
policy.record_window("onboarding", true);
|
||||||
|
policy.record_window("splashscreen", false);
|
||||||
|
assert_eq!(policy.windows.len(), 1);
|
||||||
|
policy.record_window("onboarding", false);
|
||||||
|
assert!(policy.windows.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn action_windows_keep_the_app_in_the_dock_after_the_panel_closes() {
|
||||||
|
let mut policy = ActivationPolicy::default();
|
||||||
|
policy.record_window("control-panel", true);
|
||||||
|
policy.record_window("control-panel", true);
|
||||||
|
policy.record_window("control-panel-action-add-friend", true);
|
||||||
|
policy.record_window("control-panel", false);
|
||||||
|
policy.record_window("control-panel", false);
|
||||||
|
assert_eq!(policy.windows.len(), 1);
|
||||||
|
policy.record_window("control-panel-action-add-friend", false);
|
||||||
|
assert!(policy.windows.is_empty());
|
||||||
|
policy.record_window("control-panel", true);
|
||||||
|
assert_eq!(policy.windows.len(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,40 +3,25 @@ use tauri::{AppHandle, Manager, WebviewUrl};
|
|||||||
const WINDOW_LABEL: &str = "control-panel";
|
const WINDOW_LABEL: &str = "control-panel";
|
||||||
const ACTION_WINDOW_PREFIX: &str = "control-panel-action-";
|
const ACTION_WINDOW_PREFIX: &str = "control-panel-action-";
|
||||||
|
|
||||||
pub fn init(handle: &AppHandle) {
|
|
||||||
if let Some(window) = handle.get_webview_window(WINDOW_LABEL) {
|
|
||||||
window.hide().unwrap();
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let builder = tauri::WebviewWindowBuilder::new(
|
|
||||||
handle,
|
|
||||||
WINDOW_LABEL,
|
|
||||||
WebviewUrl::App("/control-panel".into()),
|
|
||||||
)
|
|
||||||
.title("Friendolls")
|
|
||||||
.inner_size(400.0, 600.0)
|
|
||||||
.min_inner_size(400.0, 600.0)
|
|
||||||
.resizable(false)
|
|
||||||
.maximizable(false)
|
|
||||||
.center()
|
|
||||||
.visible(false);
|
|
||||||
|
|
||||||
let window = builder.build().unwrap();
|
|
||||||
window.hide().unwrap();
|
|
||||||
let window_c = window.clone();
|
|
||||||
window.on_window_event(move |event: &tauri::WindowEvent| {
|
|
||||||
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
|
|
||||||
api.prevent_close();
|
|
||||||
window_c.hide().unwrap();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn show(handle: &AppHandle) -> Result<(), String> {
|
pub fn show(handle: &AppHandle) -> Result<(), String> {
|
||||||
let window = handle
|
let window = if let Some(window) = handle.get_webview_window(WINDOW_LABEL) {
|
||||||
.get_webview_window(WINDOW_LABEL)
|
window
|
||||||
.ok_or_else(|| "Control panel window was not created during startup.".to_string())?;
|
} else {
|
||||||
|
tauri::WebviewWindowBuilder::new(
|
||||||
|
handle,
|
||||||
|
WINDOW_LABEL,
|
||||||
|
WebviewUrl::App("/control-panel".into()),
|
||||||
|
)
|
||||||
|
.title("Friendolls")
|
||||||
|
.inner_size(400.0, 600.0)
|
||||||
|
.min_inner_size(400.0, 600.0)
|
||||||
|
.resizable(false)
|
||||||
|
.maximizable(false)
|
||||||
|
.transparent(true)
|
||||||
|
.center()
|
||||||
|
.build()
|
||||||
|
.map_err(|error| error.to_string())?
|
||||||
|
};
|
||||||
|
|
||||||
window.unminimize().map_err(|error| error.to_string())?;
|
window.unminimize().map_err(|error| error.to_string())?;
|
||||||
window.show().map_err(|error| error.to_string())?;
|
window.show().map_err(|error| error.to_string())?;
|
||||||
@@ -84,6 +69,7 @@ pub async fn open_action_window(
|
|||||||
.resizable(false)
|
.resizable(false)
|
||||||
.minimizable(false)
|
.minimizable(false)
|
||||||
.maximizable(false)
|
.maximizable(false)
|
||||||
|
.transparent(true)
|
||||||
.skip_taskbar(true)
|
.skip_taskbar(true)
|
||||||
.center()
|
.center()
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -18,14 +18,8 @@ pub fn init(handle: &AppHandle) {
|
|||||||
.title("Friendolls Debug")
|
.title("Friendolls Debug")
|
||||||
.inner_size(800.0, 600.0)
|
.inner_size(800.0, 600.0)
|
||||||
.min_inner_size(500.0, 400.0)
|
.min_inner_size(500.0, 400.0)
|
||||||
|
.transparent(true)
|
||||||
.visible(true);
|
.visible(true);
|
||||||
|
|
||||||
let window = builder.build().unwrap();
|
builder.build().unwrap();
|
||||||
let window_c = window.clone();
|
|
||||||
window.on_window_event(move |event: &tauri::WindowEvent| {
|
|
||||||
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
|
|
||||||
api.prevent_close();
|
|
||||||
window_c.hide().unwrap();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use tauri::AppHandle;
|
use tauri::AppHandle;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub mod activation_policy;
|
||||||
pub mod control_panel;
|
pub mod control_panel;
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
mod debug;
|
mod debug;
|
||||||
@@ -12,7 +14,6 @@ pub fn init(app_handle: &AppHandle) {
|
|||||||
// #[cfg(debug_assertions)]
|
// #[cfg(debug_assertions)]
|
||||||
// debug::init(app_handle);
|
// debug::init(app_handle);
|
||||||
scene::init(app_handle);
|
scene::init(app_handle);
|
||||||
control_panel::init(app_handle);
|
|
||||||
tray::init(app_handle);
|
tray::init(app_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ async fn show(handle: &AppHandle, accessibility_only: bool) -> Result<(), String
|
|||||||
))
|
))
|
||||||
.inner_size(680.0, 520.0)
|
.inner_size(680.0, 520.0)
|
||||||
.min_inner_size(680.0, 520.0)
|
.min_inner_size(680.0, 520.0)
|
||||||
|
.transparent(true)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.maximizable(false)
|
.maximizable(false)
|
||||||
.center()
|
.center()
|
||||||
|
|||||||
@@ -68,21 +68,21 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hideWindow() {
|
async function closeWindow() {
|
||||||
try {
|
try {
|
||||||
await getCurrentWindow().hide();
|
await getCurrentWindow().close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("failed to hide control panel", error);
|
console.error("failed to close control panel", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function confirm() {
|
async function confirm() {
|
||||||
if (await applyAll()) await hideWindow();
|
if (await applyAll()) await closeWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
tabs.forEach((tab) => actions[tab.id]?.reset());
|
tabs.forEach((tab) => actions[tab.id]?.reset());
|
||||||
void hideWindow();
|
void closeWindow();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user