tauri_plugin_positioner/
lib.rs

1// Copyright 2021 Jonas Kruckenberg
2// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
3// SPDX-License-Identifier: Apache-2.0
4// SPDX-License-Identifier: MIT
5
6//! A plugin for Tauri that helps position your windows at well-known locations.
7//!
8//! # Cargo features
9//!
10//! - **tray-icon**: Enables tray-icon-relative positions.
11//!
12//!   Note: This requires attaching the Tauri plugin, *even* when using the trait extension only.
13
14#![doc(
15    html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png",
16    html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
17)]
18#![cfg(not(any(target_os = "android", target_os = "ios")))]
19
20mod ext;
21
22pub use ext::*;
23use tauri::{
24    plugin::{self, TauriPlugin},
25    Result, Runtime,
26};
27
28#[cfg(feature = "tray-icon")]
29use tauri::{tray::TrayIconEvent, AppHandle, Manager, PhysicalPosition, PhysicalSize};
30
31#[cfg(feature = "tray-icon")]
32struct Tray(std::sync::Mutex<Option<(PhysicalPosition<f64>, PhysicalSize<f64>)>>);
33
34#[cfg(feature = "tray-icon")]
35pub fn on_tray_event<R: Runtime>(app: &AppHandle<R>, event: &TrayIconEvent) {
36    let (position, size) = {
37        match event {
38            TrayIconEvent::Click { rect, .. }
39            | TrayIconEvent::Enter { rect, .. }
40            | TrayIconEvent::Leave { rect, .. }
41            | TrayIconEvent::Move { rect, .. } => {
42                // tray-icon emits PhysicalSize so the scale factor should not matter.
43                let size = rect.size.to_physical(1.0);
44                let position = rect.position.to_physical(1.0);
45                (position, size)
46            }
47
48            _ => return,
49        }
50    };
51
52    app.state::<Tray>()
53        .0
54        .lock()
55        .unwrap()
56        .replace((position, size));
57}
58
59#[tauri::command]
60async fn move_window<R: Runtime>(window: tauri::Window<R>, position: Position) -> Result<()> {
61    window.move_window(position)
62}
63
64#[cfg(feature = "tray-icon")]
65#[tauri::command]
66async fn move_window_constrained<R: Runtime>(
67    window: tauri::Window<R>,
68    position: Position,
69) -> Result<()> {
70    window.move_window_constrained(position)
71}
72
73#[cfg(feature = "tray-icon")]
74#[tauri::command]
75fn set_tray_icon_state<R: Runtime>(
76    app: AppHandle<R>,
77    position: PhysicalPosition<f64>,
78    size: PhysicalSize<f64>,
79) {
80    app.state::<Tray>()
81        .0
82        .lock()
83        .unwrap()
84        .replace((position, size));
85}
86
87/// The Tauri plugin that exposes [`WindowExt::move_window`] to the webview.
88pub fn init<R: Runtime>() -> TauriPlugin<R> {
89    let plugin = plugin::Builder::new("positioner").invoke_handler(tauri::generate_handler![
90        move_window,
91        #[cfg(feature = "tray-icon")]
92        move_window_constrained,
93        #[cfg(feature = "tray-icon")]
94        set_tray_icon_state
95    ]);
96
97    #[cfg(feature = "tray-icon")]
98    let plugin = plugin.setup(|app_handle, _api| {
99        app_handle.manage(Tray(std::sync::Mutex::new(None)));
100        Ok(())
101    });
102
103    plugin.build()
104}