1
0
mirror of synced 2024-12-12 16:01:06 +01:00
ImHex/lib/libimhex/source/helpers/utils_macos.m
David Mentler 751eff0edf
impr: Restore native macOS title bar double click gesture in borderless mode (#1689)
### Problem description

#### Problem 1
In borderless mode ImHex disables the standard macOS titlebar rendering
and input processing. As a result double clicking the titlebar does not
trigger the native macOS behavior set in `System Settings -> Desktop &
Dock -> Double-click a window's title bar to [Zoom/Minimize/Do
nothing]`.

#### Problem 2
The ImHex window shows up as blank/transparent when de-minimizing it
from the dock.

#### Problem 3
Widgets experience ghost hover inputs from the past position of the
cursor during live resizing.

### Implementation description
ImGui elements consume input events in the order they are drawn. As a
result by "drawing" an `InvisibleButton` over the content area of the
titlebar we can catch unprocessed clicks in the titlebar area.
Connecting this button's double clicks to the native window is then a
trivial endeavour.

The blank windows was caused by the rendering stack clearing the GL
buffer, but proceeding to draw nothing in it. I have short circuited
this path.

Ghost hover inputs were squelched by consistently moving the ImGui
cursor to `0, 0` during a live resize. The OS will dispatch a cursor
positioning event once the resizing ends, restoring normal behavior.

### Screenshots
N/A

### Additional things
N/A

---------

Co-authored-by: Nik <werwolv98@gmail.com>
2024-05-20 11:27:57 +02:00

143 lines
5.1 KiB
Objective-C

#if defined(OS_MACOS)
#include <CoreFoundation/CFBundle.h>
#include <ApplicationServices/ApplicationServices.h>
#include <Foundation/NSUserDefaults.h>
#include <AppKit/NSScreen.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreText/CoreText.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#define GLFW_EXPOSE_NATIVE_COCOA
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
void errorMessageMacos(const char *cMessage) {
CFStringRef strMessage = CFStringCreateWithCString(NULL, cMessage, kCFStringEncodingUTF8);
CFUserNotificationDisplayAlert(0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, strMessage, NULL, NULL, NULL, NULL, NULL);
}
void openFile(const char *path);
void registerFont(const char *fontName, const char *fontPath);
void openWebpageMacos(const char *url) {
CFURLRef urlRef = CFURLCreateWithBytes(NULL, (uint8_t*)(url), strlen(url), kCFStringEncodingASCII, NULL);
LSOpenCFURLRef(urlRef, NULL);
CFRelease(urlRef);
}
bool isMacosSystemDarkModeEnabled(void) {
NSString * appleInterfaceStyle = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
if (appleInterfaceStyle && [appleInterfaceStyle length] > 0) {
return [[appleInterfaceStyle lowercaseString] containsString:@"dark"];
} else {
return false;
}
}
float getBackingScaleFactor(void) {
return [[NSScreen mainScreen] backingScaleFactor];
}
void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode) {
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
cocoaWindow.titleVisibility = NSWindowTitleHidden;
if (borderlessWindowMode) {
cocoaWindow.titlebarAppearsTransparent = YES;
cocoaWindow.styleMask |= NSWindowStyleMaskFullSizeContentView;
[cocoaWindow setOpaque:NO];
[cocoaWindow setHasShadow:YES];
[cocoaWindow setBackgroundColor:[NSColor colorWithWhite: 0 alpha: 0.001f]];
}
}
bool isMacosFullScreenModeEnabled(GLFWwindow *window) {
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
return (cocoaWindow.styleMask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;
}
void enumerateFontsMacos(void) {
CFArrayRef fontDescriptors = CTFontManagerCopyAvailableFontFamilyNames();
CFIndex count = CFArrayGetCount(fontDescriptors);
for (CFIndex i = 0; i < count; i++) {
CFStringRef fontName = (CFStringRef)CFArrayGetValueAtIndex(fontDescriptors, i);
// Get font path
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ (__bridge NSString *)kCTFontNameAttribute : (__bridge NSString *)fontName };
CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(attributes);
CFURLRef fontURL = CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);
CFStringRef fontPath = CFURLCopyFileSystemPath(fontURL, kCFURLPOSIXPathStyle);
registerFont([(__bridge NSString *)fontName UTF8String], [(__bridge NSString *)fontPath UTF8String]);
CFRelease(descriptor);
CFRelease(fontURL);
}
CFRelease(fontDescriptors);
}
void macosHandleTitlebarDoubleClickGesture(GLFWwindow *window) {
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
// Consult user preferences: "System Settings -> Desktop & Dock -> Double-click a window's title bar to"
NSString* action = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleActionOnDoubleClick"];
if (action == nil || [action isEqualToString:@"None"]) {
// Nothing to do
} else if ([action isEqualToString:@"Minimize"]) {
if ([cocoaWindow isMiniaturizable]) {
[cocoaWindow miniaturize:nil];
}
} else if ([action isEqualToString:@"Maximize"]) {
// `[NSWindow zoom:_ sender]` takes over pumping the main runloop for the duration of the resize,
// and would interfere with our renderer's frame logic. Schedule it for the next frame
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
if ([cocoaWindow isZoomable]) {
[cocoaWindow zoom:nil];
}
});
}
}
bool macosIsWindowBeingResizedByUser(GLFWwindow *window) {
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
return cocoaWindow.inLiveResize;
}
@interface HexDocument : NSDocument
@end
@implementation HexDocument
- (BOOL) readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError {
NSString* urlString = [url absoluteString];
const char* utf8String = [urlString UTF8String];
const char *prefix = "file://";
if (strncmp(utf8String, prefix, strlen(prefix)) == 0)
utf8String += strlen(prefix);
openFile(utf8String);
return YES;
}
@end
#endif