use tauri::{WebviewWindow, WebviewWindowBuilder, Wry};
pub fn style<'a>(
b: WebviewWindowBuilder<'a, Wry, tauri::AppHandle<Wry>>,
frameless: bool,
) -> WebviewWindowBuilder<'a, Wry, tauri::AppHandle<Wry>> {
if !frameless {
return b;
}
#[cfg(target_os = "macos")]
{
b.title_bar_style(tauri::TitleBarStyle::Overlay)
.hidden_title(true)
}
#[cfg(not(target_os = "macos"))]
{
b.decorations(false)
}
}
#[derive(Default, Clone)]
pub struct TrafficLights {
#[cfg(target_os = "macos")]
inner: std::sync::Arc<macos::Inner>,
}
impl std::fmt::Debug for TrafficLights {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TrafficLights").finish_non_exhaustive()
}
}
impl TrafficLights {
pub fn new() -> Self {
Self::default()
}
pub fn set_offset(&self, window: &WebviewWindow<Wry>, dx: f64, dy: f64) {
#[cfg(target_os = "macos")]
{
let inner = std::sync::Arc::clone(&self.inner);
macos::dispatch(window, move |window| macos::apply(window, dx, dy, &inner));
}
#[cfg(not(target_os = "macos"))]
let _ = (window, dx, dy);
}
pub fn reapply(&self, window: &WebviewWindow<Wry>) {
#[cfg(target_os = "macos")]
{
let inner = std::sync::Arc::clone(&self.inner);
macos::dispatch(window, move |window| macos::reapply(window, &inner));
}
#[cfg(not(target_os = "macos"))]
let _ = window;
}
pub fn handle_window_event(&self, window: &WebviewWindow<Wry>, event: &tauri::WindowEvent) {
if matches!(
event,
tauri::WindowEvent::Resized(_)
| tauri::WindowEvent::Focused(true)
| tauri::WindowEvent::ScaleFactorChanged { .. }
) {
self.reapply(window);
}
}
}
#[cfg(target_os = "macos")]
mod macos {
use std::sync::Mutex;
use objc2_app_kit::{NSWindow, NSWindowButton};
use objc2_foundation::NSPoint;
use tauri::{WebviewWindow, Wry};
const KINDS: [NSWindowButton; 3] = [
NSWindowButton::CloseButton,
NSWindowButton::MiniaturizeButton,
NSWindowButton::ZoomButton,
];
#[derive(Default)]
pub struct Inner {
defaults: Mutex<Option<[NSPoint; 3]>>,
offset: Mutex<(f64, f64)>,
}
extern "C" {
fn pthread_main_np() -> std::os::raw::c_int;
}
pub fn dispatch<F>(window: &WebviewWindow<Wry>, f: F)
where
F: FnOnce(&WebviewWindow<Wry>) + Send + 'static,
{
use tauri::Manager;
if window
.app_handle()
.try_state::<crate::ShellState>()
.is_some()
{
let _ = crate::windows::dispatch_native(window, move |w| {
f(w);
Ok(())
});
} else if unsafe { pthread_main_np() } == 1 {
f(window);
} else {
let w = window.clone();
let _ = window.run_on_main_thread(move || f(&w));
}
}
fn ns_window(window: &WebviewWindow<Wry>) -> Option<&NSWindow> {
let ptr = window.ns_window().ok()?;
if ptr.is_null() {
return None;
}
Some(unsafe { &*(ptr as *const NSWindow) })
}
pub fn apply(window: &WebviewWindow<Wry>, dx: f64, dy: f64, inner: &Inner) {
let Some(win) = ns_window(window) else { return };
let mut buttons = Vec::with_capacity(3);
for k in KINDS {
match win.standardWindowButton(k) {
Some(b) => buttons.push(b),
None => return,
}
}
let base = {
let mut d = inner.defaults.lock().unwrap_or_else(|e| e.into_inner());
*d.get_or_insert_with(|| {
let mut o = [NSPoint { x: 0.0, y: 0.0 }; 3];
for (i, b) in buttons.iter().enumerate() {
o[i] = b.frame().origin;
}
o
})
};
for (i, b) in buttons.iter().enumerate() {
b.setFrameOrigin(NSPoint {
x: base[i].x + dx,
y: base[i].y - dy,
});
}
*inner.offset.lock().unwrap_or_else(|e| e.into_inner()) = (dx, dy);
}
pub fn reapply(window: &WebviewWindow<Wry>, inner: &Inner) {
if inner
.defaults
.lock()
.unwrap_or_else(|e| e.into_inner())
.is_some()
{
let (dx, dy) = *inner.offset.lock().unwrap_or_else(|e| e.into_inner());
apply(window, dx, dy, inner);
}
}
}