From 9ff74599eba4edd821578b599d975767c90e7edb Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Mon, 25 Sep 2023 07:43:13 +0700 Subject: [PATCH] customize traffic light on macOS --- src-tauri/Cargo.lock | 2 ++ src-tauri/Cargo.toml | 5 ++- src-tauri/src/main.rs | 24 ++++++++++++-- src-tauri/src/traffic_light.rs | 60 ++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 src-tauri/src/traffic_light.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c6427455..79d61671 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2623,6 +2623,8 @@ dependencies = [ name = "lume" version = "1.2.5" dependencies = [ + "cocoa 0.25.0", + "objc", "rust-argon2", "serde", "serde_json", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 5fb1f6d0..95d84a51 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -16,7 +16,8 @@ tauri-build = { version = "1.4", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.4", features = [ "macos-private-api", +tauri = { version = "1.4", features = [ + "macos-private-api", "window-close", "window-print", "window-create", @@ -53,6 +54,8 @@ sqlx-cli = { version = "0.7.0", default-features = false, features = [ ] } rust-argon2 = "1.0" webpage = { version = "1.6.0", features = ["serde"] } +cocoa = "0.25.0" +objc = "0.2.7" [features] # by default Tauri runs in production mode diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 08dca932..9526311c 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,15 +3,25 @@ windows_subsystem = "windows" )] -use tauri::Manager; +#[cfg(target_os = "macos")] +#[macro_use] +extern crate objc; + +use std::time::Duration; +use tauri::{Manager, WindowEvent}; use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_sql::{Migration, MigrationKind}; use webpage::{Webpage, WebpageOptions}; -use std::time::Duration; #[cfg(target_os = "macos")] use window_vibrancy::{apply_vibrancy, NSVisualEffectMaterial}; +#[cfg(target_os = "macos")] +use traffic_light::TrafficLight; + +#[cfg(target_os = "macos")] +mod traffic_light; + #[derive(Clone, serde::Serialize)] struct Payload { args: Vec, @@ -102,8 +112,18 @@ fn main() { apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, None) .expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS"); + #[cfg(target_os = "macos")] + window.set_transparent_titlebar(true); + window.position_traffic_lights(16.0, 25.0); + Ok(()) }) + .on_window_event(|e| { + if let WindowEvent::Resized(..) = e.event() { + let window = e.window(); + window.position_traffic_lights(16., 25.); + } + }) .plugin( tauri_plugin_sql::Builder::default() .add_migrations( diff --git a/src-tauri/src/traffic_light.rs b/src-tauri/src/traffic_light.rs new file mode 100644 index 00000000..fddee2d4 --- /dev/null +++ b/src-tauri/src/traffic_light.rs @@ -0,0 +1,60 @@ +use tauri::{Runtime, Window}; + +pub trait TrafficLight { + #[cfg(target_os = "macos")] + fn set_transparent_titlebar(&self, transparent: bool); + fn position_traffic_lights(&self, x: f64, y: f64); +} + +impl TrafficLight for Window { + #[cfg(target_os = "macos")] + fn set_transparent_titlebar(&self, transparent: bool) { + use cocoa::appkit::{NSWindow, NSWindowTitleVisibility}; + + let window = self.ns_window().unwrap() as cocoa::base::id; + + unsafe { + window.setTitleVisibility_(NSWindowTitleVisibility::NSWindowTitleHidden); + + if transparent { + window.setTitlebarAppearsTransparent_(cocoa::base::YES); + } else { + window.setTitlebarAppearsTransparent_(cocoa::base::NO); + } + } + } + + #[cfg(target_os = "macos")] + fn position_traffic_lights(&self, x: f64, y: f64) { + use cocoa::appkit::{NSView, NSWindow, NSWindowButton}; + use cocoa::foundation::NSRect; + + let window = self.ns_window().unwrap() as cocoa::base::id; + + unsafe { + let close = window.standardWindowButton_(NSWindowButton::NSWindowCloseButton); + let miniaturize = window.standardWindowButton_(NSWindowButton::NSWindowMiniaturizeButton); + let zoom = window.standardWindowButton_(NSWindowButton::NSWindowZoomButton); + + let title_bar_container_view = close.superview().superview(); + + let close_rect: NSRect = msg_send![close, frame]; + let button_height = close_rect.size.height; + + let title_bar_frame_height = button_height + y; + let mut title_bar_rect = NSView::frame(title_bar_container_view); + title_bar_rect.size.height = title_bar_frame_height; + title_bar_rect.origin.y = NSView::frame(window).size.height - title_bar_frame_height; + let _: () = msg_send![title_bar_container_view, setFrame: title_bar_rect]; + + let window_buttons = vec![close, miniaturize, zoom]; + let space_between = NSView::frame(miniaturize).origin.x - NSView::frame(close).origin.x; + + for (i, button) in window_buttons.into_iter().enumerate() { + let mut rect: NSRect = NSView::frame(button); + rect.origin.x = x + (i as f64 * space_between); + button.setFrameOrigin(rect.origin); + } + } + } +}