дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
fix(window): cover the whole screen with a full-screen project window group on notched Macs (#1608)
A project tab group in full screen uses simple fullscreen and keeps its title bar for the window buttons. AppKit's constrainFrameRect:toScreen: keeps a titled window below the camera housing whenever it is placed, shown or resized, so on a notched display the group sat 32 pt low with the status bar off-screen. Windows in simple fullscreen now keep the frame they ask for; every other window still goes through AppKit's constraint. Co-authored-by: KJ-AIML <[email protected]>
Цей коміт міститься в:
1 джерело
1dda103bf0
коміт
7ad02da722
1 file changed
+44
-9
@@ -8,7 +8,7 @@ use objc2::{
|
||||
runtime::{AnyClass, AnyObject, Imp, ProtocolObject, Sel},
|
||||
};
|
||||
use objc2_app_kit::{NSWindow, NSWindowUserTabbingPreference};
|
||||
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
|
||||
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol, NSRect};
|
||||
use tauri::{AppHandle, Manager, WebviewWindow};
|
||||
|
||||
fn tabbing_preference(app: &AppHandle) -> IdeResult<TabbingPreference> {
|
||||
@@ -136,30 +136,35 @@ pub fn set_simple_fullscreen(window: &WebviewWindow, on: bool) -> tauri::Result<
|
||||
})
|
||||
}
|
||||
|
||||
/// A window in simple fullscreen: its `NSWindow` and the minimum size restored when it leaves.
|
||||
struct Covering {
|
||||
native: usize,
|
||||
min_size: objc2_foundation::NSSize,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
/// Minimum sizes of windows in simple fullscreen, restored when they leave it.
|
||||
static MIN_SIZES: std::cell::RefCell<std::collections::HashMap<String, objc2_foundation::NSSize>> =
|
||||
std::cell::RefCell::default();
|
||||
static COVERING: std::cell::RefCell<std::collections::HashMap<String, Covering>> = std::cell::RefCell::default();
|
||||
}
|
||||
|
||||
/// Simple fullscreen drops the title bar and resizing; ours is transparent over the web toolbar,
|
||||
/// so keep it and its buttons, the green one (enabled only for a resizable window) leading back
|
||||
/// out of fullscreen. A minimum size of the whole screen keeps the edges from being dragged.
|
||||
/// A titled window is kept below the menu bar or camera housing; see `constrain_frame_rect`.
|
||||
fn keep_window_buttons(window: &WebviewWindow, on: bool) {
|
||||
use objc2_app_kit::NSWindowStyleMask;
|
||||
let Ok(pointer) = window.ns_window() else { return };
|
||||
let native = unsafe { &*pointer.cast::<NSWindow>() };
|
||||
let label = window.label().to_owned();
|
||||
if !on {
|
||||
if let Some(size) = MIN_SIZES.with_borrow_mut(|sizes| sizes.remove(&label)) {
|
||||
native.setMinSize(size);
|
||||
if let Some(covering) = COVERING.with_borrow_mut(|windows| windows.remove(&label)) {
|
||||
native.setMinSize(covering.min_size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let Some(screen) = native.screen() else { return };
|
||||
let frame = screen.frame();
|
||||
MIN_SIZES.with_borrow_mut(|sizes| {
|
||||
sizes.entry(label).or_insert_with(|| native.minSize());
|
||||
COVERING.with_borrow_mut(|windows| {
|
||||
windows.entry(label).or_insert_with(|| Covering { native: pointer as usize, min_size: native.minSize() });
|
||||
});
|
||||
native.setMinSize(frame.size);
|
||||
native.setStyleMask(native.styleMask() | NSWindowStyleMask::Titled | NSWindowStyleMask::Resizable);
|
||||
@@ -177,7 +182,7 @@ pub fn forget_simple_fullscreen(app: &AppHandle, label: &str) -> bool {
|
||||
if was {
|
||||
let label = label.to_owned();
|
||||
let _ = app.run_on_main_thread(move || {
|
||||
MIN_SIZES.with_borrow_mut(|sizes| sizes.remove(&label));
|
||||
COVERING.with_borrow_mut(|windows| windows.remove(&label));
|
||||
present(any);
|
||||
});
|
||||
}
|
||||
@@ -218,9 +223,39 @@ pub fn intercept_toggle_fullscreen(window: &WebviewWindow) {
|
||||
let _ = NATIVE_TOGGLE_FULLSCREEN.set(native);
|
||||
let replacement: Imp = std::mem::transmute(toggle_fullscreen as ToggleFullScreen);
|
||||
objc2::ffi::class_replaceMethod(class, selector, replacement, types);
|
||||
|
||||
let selector = objc2::sel!(constrainFrameRect:toScreen:);
|
||||
let Some(method) = (*class).instance_method(selector) else { return };
|
||||
let native: ConstrainFrameRect = std::mem::transmute(method.implementation());
|
||||
let types = objc2::ffi::method_getTypeEncoding(method);
|
||||
let _ = NATIVE_CONSTRAIN_FRAME_RECT.set(native);
|
||||
let replacement: Imp = std::mem::transmute(constrain_frame_rect as ConstrainFrameRect);
|
||||
objc2::ffi::class_replaceMethod(class, selector, replacement, types);
|
||||
});
|
||||
}
|
||||
|
||||
type ConstrainFrameRect = unsafe extern "C-unwind" fn(*mut AnyObject, Sel, NSRect, *mut AnyObject) -> NSRect;
|
||||
|
||||
static NATIVE_CONSTRAIN_FRAME_RECT: std::sync::OnceLock<ConstrainFrameRect> = std::sync::OnceLock::new();
|
||||
|
||||
/// AppKit keeps a titled window's top edge below the menu bar, and on a notched display below the
|
||||
/// camera housing, whenever the window is placed, shown or resized. A window in simple fullscreen
|
||||
/// is titled for its buttons but must still cover the whole screen.
|
||||
unsafe extern "C-unwind" fn constrain_frame_rect(
|
||||
this: *mut AnyObject,
|
||||
selector: Sel,
|
||||
frame: NSRect,
|
||||
screen: *mut AnyObject,
|
||||
) -> NSRect {
|
||||
if COVERING.with_borrow(|windows| windows.values().any(|covering| covering.native == this as usize)) {
|
||||
return frame;
|
||||
}
|
||||
match NATIVE_CONSTRAIN_FRAME_RECT.get() {
|
||||
Some(native) => unsafe { native(this, selector, frame, screen) },
|
||||
None => frame,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C-unwind" fn toggle_fullscreen(this: *mut AnyObject, selector: Sel, sender: *mut AnyObject) {
|
||||
if let Some(window) = APP.get().and_then(|app| simple_fullscreen_window(app, this)) {
|
||||
if let Err(error) = crate::window_groups::toggle_fullscreen(&window) {
|
||||
|
||||
Reference in new issue
Заблокувати користувача