feat(ui): adapt macOS header/vav bar height for legacy versions & fix traffic lights wobble
This commit is contained in:
@@ -85,13 +85,6 @@ use tokio::{
|
|||||||
use tower_http::cors::CorsLayer;
|
use tower_http::cors::CorsLayer;
|
||||||
use window_state::{load_window_state, persist_window_state, WindowStateManager};
|
use window_state::{load_window_state, persist_window_state, WindowStateManager};
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
static NEEDS_TRAFFIC_LIGHT_FIX: AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
const PROXY_BODY_LIMIT: usize = 50 * 1024 * 1024; // 50MB
|
const PROXY_BODY_LIMIT: usize = 50 * 1024 * 1024; // 50MB
|
||||||
const CLIENT_RELOAD_DELAY_MS: u64 = 800;
|
const CLIENT_RELOAD_DELAY_MS: u64 = 800;
|
||||||
@@ -316,84 +309,6 @@ async fn desktop_open_devtools(window: WebviewWindow) -> Result<(), String> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
fn get_macos_major_version() -> isize {
|
|
||||||
use objc2_foundation::NSProcessInfo;
|
|
||||||
let process_info = NSProcessInfo::processInfo();
|
|
||||||
let version = process_info.operatingSystemVersion();
|
|
||||||
version.majorVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
fn get_traffic_light_y(macos_version: isize) -> f64 {
|
|
||||||
if macos_version <= 15 {
|
|
||||||
4.0
|
|
||||||
} else {
|
|
||||||
16.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
fn adjust_traffic_lights_position<R: tauri::Runtime>(
|
|
||||||
window: &tauri::WebviewWindow<R>,
|
|
||||||
x: f64,
|
|
||||||
y: f64,
|
|
||||||
) {
|
|
||||||
use objc2::msg_send;
|
|
||||||
use objc2::runtime::AnyObject;
|
|
||||||
use objc2_foundation::{NSPoint, NSRect};
|
|
||||||
|
|
||||||
if let Ok(ns_window) = window.ns_window() {
|
|
||||||
unsafe {
|
|
||||||
let ns_window: *mut AnyObject = ns_window.cast();
|
|
||||||
let close_button: *mut AnyObject = msg_send![ns_window, standardWindowButton: 0usize];
|
|
||||||
|
|
||||||
if !close_button.is_null() {
|
|
||||||
let superview: *mut AnyObject = msg_send![close_button, superview];
|
|
||||||
if !superview.is_null() {
|
|
||||||
let frame: NSRect = msg_send![superview, frame];
|
|
||||||
let new_frame = NSRect::new(NSPoint::new(x, y), frame.size);
|
|
||||||
let _: () = msg_send![superview, setFrame: new_frame];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
fn adjust_traffic_lights_centered<R: tauri::Runtime>(
|
|
||||||
window: &tauri::WebviewWindow<R>,
|
|
||||||
fallback_x: f64,
|
|
||||||
y: f64,
|
|
||||||
) {
|
|
||||||
use objc2::msg_send;
|
|
||||||
use objc2::runtime::AnyObject;
|
|
||||||
use objc2_foundation::{NSPoint, NSRect};
|
|
||||||
|
|
||||||
if let Ok(ns_window) = window.ns_window() {
|
|
||||||
unsafe {
|
|
||||||
let ns_window: *mut AnyObject = ns_window.cast();
|
|
||||||
let close_button: *mut AnyObject = msg_send![ns_window, standardWindowButton: 0usize];
|
|
||||||
|
|
||||||
if !close_button.is_null() {
|
|
||||||
let superview: *mut AnyObject = msg_send![close_button, superview];
|
|
||||||
if !superview.is_null() {
|
|
||||||
let frame: NSRect = msg_send![superview, frame];
|
|
||||||
let parent: *mut AnyObject = msg_send![superview, superview];
|
|
||||||
if !parent.is_null() {
|
|
||||||
let parent_frame: NSRect = msg_send![parent, frame];
|
|
||||||
let new_x = (parent_frame.size.width - frame.size.width) / 2.0;
|
|
||||||
let new_frame = NSRect::new(NSPoint::new(new_x, y), frame.size);
|
|
||||||
let _: () = msg_send![superview, setFrame: new_frame];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
adjust_traffic_lights_position(window, fallback_x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
fn optimize_webview_layer<R: tauri::Runtime>(window: &tauri::WebviewWindow<R>) {
|
fn optimize_webview_layer<R: tauri::Runtime>(window: &tauri::WebviewWindow<R>) {
|
||||||
@@ -756,15 +671,6 @@ fn main() {
|
|||||||
if let Some(window) = app.get_webview_window("main") {
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
let macos_version = get_macos_major_version();
|
|
||||||
info!("[macos] Detected macOS version: {}", macos_version);
|
|
||||||
|
|
||||||
if macos_version <= 15 {
|
|
||||||
NEEDS_TRAFFIC_LIGHT_FIX.store(true, Ordering::SeqCst);
|
|
||||||
let traffic_light_y = get_traffic_light_y(macos_version);
|
|
||||||
adjust_traffic_lights_centered(&window, 17.0, traffic_light_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply layer optimizations for smoother scrolling
|
// Apply layer optimizations for smoother scrolling
|
||||||
optimize_webview_layer(&window);
|
optimize_webview_layer(&window);
|
||||||
}
|
}
|
||||||
@@ -1129,14 +1035,6 @@ fn main() {
|
|||||||
size.height as f64,
|
size.height as f64,
|
||||||
is_maximized,
|
is_maximized,
|
||||||
);
|
);
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
if NEEDS_TRAFFIC_LIGHT_FIX.load(Ordering::SeqCst) {
|
|
||||||
if let Some(webview) = window.app_handle().get_webview_window("main") {
|
|
||||||
let macos_version = get_macos_major_version();
|
|
||||||
let traffic_light_y = get_traffic_light_y(macos_version);
|
|
||||||
adjust_traffic_lights_centered(&webview, 17.0, traffic_light_y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tauri::WindowEvent::CloseRequested { api, .. } => {
|
tauri::WindowEvent::CloseRequested { api, .. } => {
|
||||||
api.prevent_close();
|
api.prevent_close();
|
||||||
|
|||||||
@@ -107,6 +107,18 @@ export const Header: React.FC = () => {
|
|||||||
return /Macintosh|Mac OS X/.test(navigator.userAgent || '');
|
return /Macintosh|Mac OS X/.test(navigator.userAgent || '');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const macosMajorVersion = React.useMemo(() => {
|
||||||
|
if (typeof navigator === 'undefined') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const match = (navigator.userAgent || '').match(/Mac OS X (\d+)[._]/);
|
||||||
|
if (!match) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const parsed = Number.parseInt(match[1], 10);
|
||||||
|
return Number.isNaN(parsed) ? null : parsed;
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return;
|
return;
|
||||||
@@ -289,6 +301,16 @@ export const Header: React.FC = () => {
|
|||||||
return 'pl-3';
|
return 'pl-3';
|
||||||
}, [isDesktopApp, isMacPlatform]);
|
}, [isDesktopApp, isMacPlatform]);
|
||||||
|
|
||||||
|
const macosHeaderSizeClass = React.useMemo(() => {
|
||||||
|
if (!isDesktopApp || !isMacPlatform) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (macosMajorVersion === null || macosMajorVersion > 15) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return 'h-14';
|
||||||
|
}, [isDesktopApp, isMacPlatform, macosMajorVersion]);
|
||||||
|
|
||||||
const updateHeaderHeight = React.useCallback(() => {
|
const updateHeaderHeight = React.useCallback(() => {
|
||||||
if (typeof document === 'undefined') {
|
if (typeof document === 'undefined') {
|
||||||
return;
|
return;
|
||||||
@@ -463,7 +485,8 @@ export const Header: React.FC = () => {
|
|||||||
onMouseDown={handleDragStart}
|
onMouseDown={handleDragStart}
|
||||||
className={cn(
|
className={cn(
|
||||||
'app-region-drag relative flex h-12 select-none items-center',
|
'app-region-drag relative flex h-12 select-none items-center',
|
||||||
desktopPaddingClass
|
desktopPaddingClass,
|
||||||
|
macosHeaderSizeClass
|
||||||
)}
|
)}
|
||||||
role="tablist"
|
role="tablist"
|
||||||
aria-label="Main navigation"
|
aria-label="Main navigation"
|
||||||
|
|||||||
Reference in New Issue
Block a user