Files
MyGoNavi/internal/app/window_style_darwin.go

236 lines
7.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//go:build darwin
package app
/*
#cgo CFLAGS: -x objective-c -fblocks
#cgo LDFLAGS: -framework Cocoa
#include <stdlib.h>
#import <Cocoa/Cocoa.h>
#import <dispatch/dispatch.h>
static inline BOOL gonaviBoolYES() { return YES; }
static inline BOOL gonaviBoolNO() { return NO; }
static char *gonaviNativeLogPath = NULL;
static BOOL gonaviNativeObserverInstalled = NO;
static void gonaviWriteNativeWindowLogLine(NSString *line) {
if (line == nil || gonaviNativeLogPath == NULL) {
return;
}
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:[NSString stringWithUTF8String:gonaviNativeLogPath]];
if (handle == nil) {
return;
}
@try {
[handle seekToEndOfFile];
NSData *data = [line dataUsingEncoding:NSUTF8StringEncoding];
[handle writeData:data];
} @catch (__unused NSException *exception) {
} @finally {
[handle closeFile];
}
}
static NSString *gonaviWindowDiagnosticLine(NSString *eventName, NSWindow *window) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy/MM/dd HH:mm:ss.SSSSSS"];
NSString *timestamp = [formatter stringFromDate:[NSDate date]];
[formatter release];
if (window == nil) {
return [NSString stringWithFormat:@"%@ [WARN] 原生窗口诊断event=%@ window=nil\n", timestamp, eventName ?: @"unknown"];
}
NSUInteger occlusionState = 0;
NSInteger windowNumber = [window windowNumber];
NSInteger level = [window level];
NSUInteger collectionBehavior = [window collectionBehavior];
NSString *className = NSStringFromClass([window class]);
NSString *delegateClassName = [window delegate] ? NSStringFromClass([[window delegate] class]) : @"nil";
if (@available(macOS 10.9, *)) {
occlusionState = [window occlusionState];
}
return [NSString stringWithFormat:@"%@ [WARN] 原生窗口诊断event=%@ ptr=%p number=%ld class=%@ delegate=%@ visible=%@ miniaturized=%@ key=%@ main=%@ canHide=%@ level=%ld occlusion=%lu styleMask=%lu collectionBehavior=%lu frame=%@ screen=%@ title=%@\n",
timestamp,
eventName ?: @"unknown",
window,
(long)windowNumber,
className ?: @"nil",
delegateClassName,
[window isVisible] ? @"true" : @"false",
[window isMiniaturized] ? @"true" : @"false",
[window isKeyWindow] ? @"true" : @"false",
[window isMainWindow] ? @"true" : @"false",
[window canHide] ? @"true" : @"false",
(long)level,
(unsigned long)occlusionState,
(unsigned long)[window styleMask],
(unsigned long)collectionBehavior,
NSStringFromRect([window frame]),
[window screen] ? NSStringFromRect([[window screen] frame]) : @"nil",
[window title] ?: @""];
}
@interface GoNaviNativeWindowObserver : NSObject
@end
@implementation GoNaviNativeWindowObserver
- (void)logNotification:(NSNotification *)notification {
NSString *name = [notification name] ?: @"unknown";
NSWindow *window = nil;
if ([[notification object] isKindOfClass:[NSWindow class]]) {
window = (NSWindow *)[notification object];
} else {
window = [NSApp keyWindow] ?: [NSApp mainWindow];
}
gonaviWriteNativeWindowLogLine(gonaviWindowDiagnosticLine(name, window));
}
@end
static GoNaviNativeWindowObserver *gonaviNativeWindowObserver = nil;
static void gonaviInstallNativeWindowObserver(void) {
if (gonaviNativeObserverInstalled) {
return;
}
gonaviNativeObserverInstalled = YES;
gonaviNativeWindowObserver = [[GoNaviNativeWindowObserver alloc] init];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
NSArray<NSString *> *windowNotifications = @[
NSWindowDidBecomeKeyNotification,
NSWindowDidResignKeyNotification,
NSWindowDidBecomeMainNotification,
NSWindowDidResignMainNotification,
NSWindowDidMiniaturizeNotification,
NSWindowDidDeminiaturizeNotification,
NSWindowDidEnterFullScreenNotification,
NSWindowDidExitFullScreenNotification,
NSWindowDidMoveNotification,
NSWindowDidResizeNotification,
NSWindowDidChangeOcclusionStateNotification,
];
for (NSString *notificationName in windowNotifications) {
[center addObserver:gonaviNativeWindowObserver selector:@selector(logNotification:) name:notificationName object:nil];
}
NSArray<NSString *> *appNotifications = @[
NSApplicationDidHideNotification,
NSApplicationDidUnhideNotification,
NSApplicationDidBecomeActiveNotification,
NSApplicationDidResignActiveNotification,
];
for (NSString *notificationName in appNotifications) {
[center addObserver:gonaviNativeWindowObserver selector:@selector(logNotification:) name:notificationName object:nil];
}
for (NSWindow *window in [NSApp windows]) {
gonaviWriteNativeWindowLogLine(gonaviWindowDiagnosticLine(@"observer:snapshot", window));
}
}
static void gonaviConfigureNativeWindowDiagnostics(const char *logPath) {
if (logPath == NULL || logPath[0] == '\0') {
return;
}
if (gonaviNativeLogPath != NULL) {
free(gonaviNativeLogPath);
gonaviNativeLogPath = NULL;
}
gonaviNativeLogPath = strdup(logPath);
dispatch_async(dispatch_get_main_queue(), ^{
gonaviInstallNativeWindowObserver();
});
}
static void gonaviSetWindowButtonsVisible(NSWindow *window, BOOL visible) {
if (window == nil) {
return;
}
for (NSWindowButton buttonType = NSWindowCloseButton; buttonType <= NSWindowZoomButton; buttonType++) {
NSButton *button = [window standardWindowButton:buttonType];
if (button != nil) {
[button setHidden:!visible];
[button setEnabled:visible];
}
}
}
static BOOL gonaviShouldApplyMacWindowStyle(NSWindow *window) {
if (window == nil) {
return NO;
}
NSString *className = NSStringFromClass([window class]) ?: @"";
NSString *delegateClassName = [window delegate] ? NSStringFromClass([[window delegate] class]) : @"";
NSString *title = [window title] ?: @"";
// 仅对主 WailsWindow 套用原生标题栏/全屏样式,避免误伤输入法候选窗、全屏过渡窗。
if ([className isEqualToString:@"WailsWindow"] || [delegateClassName isEqualToString:@"WindowDelegate"]) {
return YES;
}
return [title isEqualToString:@"GoNavi"];
}
static void gonaviApplyMacWindowStyle(BOOL enabled) {
dispatch_async(dispatch_get_main_queue(), ^{
for (NSWindow *window in [NSApp windows]) {
if (window == nil) {
continue;
}
if (!gonaviShouldApplyMacWindowStyle(window)) {
gonaviWriteNativeWindowLogLine(gonaviWindowDiagnosticLine(@"style:skip-non-app-window", window));
continue;
}
NSUInteger styleMask = [window styleMask];
styleMask |= NSWindowStyleMaskClosable;
styleMask |= NSWindowStyleMaskMiniaturizable;
styleMask |= NSWindowStyleMaskResizable;
if (enabled) {
styleMask |= NSWindowStyleMaskTitled;
styleMask |= NSWindowStyleMaskFullSizeContentView;
[window setStyleMask:styleMask];
[window setTitleVisibility:NSWindowTitleHidden];
[window setTitlebarAppearsTransparent:YES];
[window setMovableByWindowBackground:YES];
[window setCollectionBehavior:[window collectionBehavior] | NSWindowCollectionBehaviorFullScreenPrimary];
gonaviSetWindowButtonsVisible(window, YES);
} else {
styleMask &= ~NSWindowStyleMaskTitled;
styleMask &= ~NSWindowStyleMaskFullSizeContentView;
[window setStyleMask:styleMask];
[window setTitleVisibility:NSWindowTitleVisible];
[window setTitlebarAppearsTransparent:NO];
[window setMovableByWindowBackground:YES];
gonaviSetWindowButtonsVisible(window, NO);
}
[[window contentView] setNeedsDisplay:YES];
[window invalidateShadow];
gonaviWriteNativeWindowLogLine(gonaviWindowDiagnosticLine(enabled ? @"style:enable-native-controls" : @"style:disable-native-controls", window));
}
});
}
*/
import "C"
import "unsafe"
func installMacNativeWindowDiagnostics(logPath string) {
if logPath == "" {
return
}
cLogPath := C.CString(logPath)
defer C.free(unsafe.Pointer(cLogPath))
C.gonaviConfigureNativeWindowDiagnostics(cLogPath)
}
func setMacNativeWindowControls(enabled bool) {
state := resolveMacNativeWindowControlState(enabled)
if state.ShowNativeButtons {
C.gonaviApplyMacWindowStyle(C.gonaviBoolYES())
} else {
C.gonaviApplyMacWindowStyle(C.gonaviBoolNO())
}
}