Refactor widowing on macOS to support screen scale and highDpi mode

This commit is contained in:
Wojtek Figat
2023-03-12 10:58:55 +01:00
parent 9fe54dc02c
commit 2c70b74814
5 changed files with 103 additions and 19 deletions

View File

@@ -125,9 +125,12 @@ namespace FlaxEditor.Options
options.CustomSettings.Add(e.Key, JsonSerializer.Serialize(e.Value())); options.CustomSettings.Add(e.Key, JsonSerializer.Serialize(e.Value()));
} }
float prevInterfaceScale = Options.Interface.InterfaceScale;
Options = options; Options = options;
OnOptionsChanged(); OnOptionsChanged();
Platform.CustomDpiScale = Options.Interface.InterfaceScale;
// Scale interface relative to the current value (eg. when using system-provided Dpi Scale)
Platform.CustomDpiScale *= Options.Interface.InterfaceScale / prevInterfaceScale;
} }
else else
{ {

View File

@@ -153,7 +153,7 @@ void SplashScreen::Show()
LOG(Info, "Showing splash screen"); LOG(Info, "Showing splash screen");
// Create window // Create window
const float dpiScale = (float)Platform::GetDpi() / (float)DefaultDPI; const float dpiScale = Platform::GetDpiScale();
CreateWindowSettings settings; CreateWindowSettings settings;
settings.Title = TEXT("Flax Editor"); settings.Title = TEXT("Flax Editor");
settings.Size.X = 500 * dpiScale; settings.Size.X = 500 * dpiScale;

View File

@@ -54,6 +54,8 @@ String UserLocale, ComputerName;
double SecondsPerCycle; double SecondsPerCycle;
NSAutoreleasePool* AutoreleasePool = nullptr; NSAutoreleasePool* AutoreleasePool = nullptr;
float MacPlatform::ScreenScale = 1.0f;
String MacUtils::ToString(CFStringRef str) String MacUtils::ToString(CFStringRef str)
{ {
if (!str) if (!str)
@@ -77,7 +79,7 @@ CFStringRef MacUtils::ToString(const StringView& str)
Float2 MacUtils::PosToCoca(const Float2& pos) Float2 MacUtils::PosToCoca(const Float2& pos)
{ {
// MacOS uses y-coordinate starting at the bottom of the screen // MacOS uses y-coordinate starting at the bottom of the screen
Float2 result = pos; Float2 result = pos;// / MacPlatform::ScreenScale;
result.Y *= -1; result.Y *= -1;
result += GetScreensOrigin(); result += GetScreensOrigin();
return result; return result;
@@ -86,10 +88,10 @@ Float2 MacUtils::PosToCoca(const Float2& pos)
Float2 MacUtils::CocaToPos(const Float2& pos) Float2 MacUtils::CocaToPos(const Float2& pos)
{ {
// MacOS uses y-coordinate starting at the bottom of the screen // MacOS uses y-coordinate starting at the bottom of the screen
Float2 result = pos; Float2 result = pos;// * MacPlatform::ScreenScale;
result -= GetScreensOrigin(); result -= GetScreensOrigin();
result.Y *= -1; result.Y *= -1;
return result; return result;// * MacPlatform::ScreenScale;
} }
Float2 MacUtils::GetScreensOrigin() Float2 MacUtils::GetScreensOrigin()
@@ -100,6 +102,7 @@ Float2 MacUtils::GetScreensOrigin()
{ {
NSRect rect = [[screenArray objectAtIndex:i] frame]; NSRect rect = [[screenArray objectAtIndex:i] frame];
Float2 pos(rect.origin.x, rect.origin.y + rect.size.height); Float2 pos(rect.origin.x, rect.origin.y + rect.size.height);
pos *= MacPlatform::ScreenScale;
if (pos.X < result.X) if (pos.X < result.X)
result.X = pos.X; result.X = pos.X;
if (pos.Y > result.Y) if (pos.Y > result.Y)
@@ -423,11 +426,25 @@ bool MacPlatform::Init()
OnPlatformUserAdd(New<User>(username)); OnPlatformUserAdd(New<User>(username));
} }
// Find the maximum scale of the display to handle high-dpi displays scaling factor
{
NSArray* screenArray = [NSScreen screens];
for (int32 i = 0; i < (int32)[screenArray count]; i++)
{
if ([[screenArray objectAtIndex:i] respondsToSelector:@selector(backingScaleFactor)])
{
ScreenScale = Math::Max(ScreenScale, (float)[[screenArray objectAtIndex:i] backingScaleFactor]);
}
}
CustomDpiScale *= ScreenScale;
}
// Init application // Init application
[NSApplication sharedApplication]; [NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
NSMenu* mainMenu = [[[NSMenu alloc] initWithTitle:@""] autorelease]; NSMenu* mainMenu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
[NSApp setMainMenu:mainMenu]; [NSApp setMainMenu:mainMenu];
// TODO: expose main menu for app (eg. to be used by Game or Editor on macOS-only)
AutoreleasePool = [[NSAutoreleasePool alloc] init]; AutoreleasePool = [[NSAutoreleasePool alloc] init];
Input::Mouse = New<MacMouse>(); Input::Mouse = New<MacMouse>();
@@ -479,6 +496,16 @@ void MacPlatform::Exit()
{ {
} }
void MacPlatform::SetHighDpiAwarenessEnabled(bool enable)
{
// Disable resolution scaling in low dpi mode
if (!enable)
{
CustomDpiScale /= ScreenScale;
ScreenScale = 1.0f;
}
}
int32 MacPlatform::GetDpi() int32 MacPlatform::GetDpi()
{ {
CGDirectDisplayID mainDisplay = CGMainDisplayID(); CGDirectDisplayID mainDisplay = CGMainDisplayID();
@@ -551,13 +578,14 @@ void MacPlatform::SetMousePosition(const Float2& pos)
Float2 MacPlatform::GetDesktopSize() Float2 MacPlatform::GetDesktopSize()
{ {
CGDirectDisplayID mainDisplay = CGMainDisplayID(); CGDirectDisplayID mainDisplay = CGMainDisplayID();
return Float2((float)CGDisplayPixelsWide(mainDisplay), (float)CGDisplayPixelsHigh(mainDisplay)); return Float2((float)CGDisplayPixelsWide(mainDisplay) * ScreenScale, (float)CGDisplayPixelsHigh(mainDisplay) * ScreenScale);
} }
Rectangle GetDisplayBounds(CGDirectDisplayID display) Rectangle GetDisplayBounds(CGDirectDisplayID display)
{ {
CGRect rect = CGDisplayBounds(display); CGRect rect = CGDisplayBounds(display);
return Rectangle(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); float screnScale = MacPlatform::ScreenScale;
return Rectangle(rect.origin.x * screnScale, rect.origin.y * screnScale, rect.size.width * screnScale, rect.size.height * screnScale);
} }
Rectangle MacPlatform::GetMonitorBounds(const Float2& screenPos) Rectangle MacPlatform::GetMonitorBounds(const Float2& screenPos)

View File

@@ -11,6 +11,9 @@
/// </summary> /// </summary>
class FLAXENGINE_API MacPlatform : public UnixPlatform class FLAXENGINE_API MacPlatform : public UnixPlatform
{ {
public:
static float ScreenScale;
public: public:
// [UnixPlatform] // [UnixPlatform]
@@ -82,6 +85,7 @@ public:
static void Tick(); static void Tick();
static void BeforeExit(); static void BeforeExit();
static void Exit(); static void Exit();
static void SetHighDpiAwarenessEnabled(bool enable);
static int32 GetDpi(); static int32 GetDpi();
static String GetUserLocaleName(); static String GetUserLocaleName();
static String GetComputerName(); static String GetComputerName();

View File

@@ -167,14 +167,14 @@ Float2 GetWindowTitleSize(const MacWindow* window)
NSRect frameStart = [(NSWindow*)window->GetNativePtr() frameRectForContentRect:NSMakeRect(0, 0, 0, 0)]; NSRect frameStart = [(NSWindow*)window->GetNativePtr() frameRectForContentRect:NSMakeRect(0, 0, 0, 0)];
size.Y = frameStart.size.height; size.Y = frameStart.size.height;
} }
return size; return size * MacPlatform::ScreenScale;
} }
Float2 GetMousePosition(MacWindow* window, NSEvent* event) Float2 GetMousePosition(MacWindow* window, NSEvent* event)
{ {
NSRect frame = [(NSWindow*)window->GetNativePtr() frame]; NSRect frame = [(NSWindow*)window->GetNativePtr() frame];
NSPoint point = [event locationInWindow]; NSPoint point = [event locationInWindow];
return Float2(point.x, frame.size.height - point.y) - GetWindowTitleSize(window); return Float2(point.x, frame.size.height - point.y) * MacPlatform::ScreenScale - GetWindowTitleSize(window);
} }
class MacDropData : public IGuiData class MacDropData : public IGuiData
@@ -255,17 +255,45 @@ NSDragOperation GetDragDropOperation(DragDropEffect dragDropEffect)
@implementation MacWindowImpl @implementation MacWindowImpl
- (void)windowDidBecomeKey:(NSNotification*)notification
{
// Handle resizing to be sure that content has valid size when window was resized
[self windowDidResize:notification];
Window->OnGotFocus();
}
- (void)windowDidResignKey:(NSNotification*)notification
{
Window->OnLostFocus();
}
- (void)windowWillClose:(NSNotification*)notification - (void)windowWillClose:(NSNotification*)notification
{ {
[self setDelegate: nil]; [self setDelegate: nil];
Window->Close(ClosingReason::User); Window->Close(ClosingReason::User);
} }
static void ConvertNSRect(NSScreen *screen, NSRect *r)
{
r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - r->origin.y - r->size.height;
}
- (void)windowDidResize:(NSNotification*)notification - (void)windowDidResize:(NSNotification*)notification
{ {
NSView* view = [self contentView]; NSView* view = [self contentView];
NSRect contextRect = [view frame]; const float screenScale = MacPlatform::ScreenScale;
Window->CheckForResize((float)contextRect.size.width, (float)contextRect.size.height); NSWindow* nswindow = (NSWindow*)Window->GetNativePtr();
NSRect rect = [nswindow contentRectForFrameRect:[nswindow frame]];
ConvertNSRect([nswindow screen], &rect);
// Rescale contents
CALayer* layer = [view layer];
if (layer)
layer.contentsScale = screenScale;
// Resize window
Window->CheckForResize((float)rect.size.width * screenScale, (float)rect.size.height * screenScale);
} }
- (void)setWindow:(MacWindow*)window - (void)setWindow:(MacWindow*)window
@@ -565,6 +593,12 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
styleMask &= ~NSWindowStyleMaskFullSizeContentView; styleMask &= ~NSWindowStyleMaskFullSizeContentView;
} }
const float screenScale = MacPlatform::ScreenScale;
frame.origin.x /= screenScale;
frame.origin.y /= screenScale;
frame.size.width /= screenScale;
frame.size.height /= screenScale;
MacWindowImpl* window = [[MacWindowImpl alloc] initWithContentRect:frame MacWindowImpl* window = [[MacWindowImpl alloc] initWithContentRect:frame
styleMask:(styleMask) styleMask:(styleMask)
backing:NSBackingStoreBuffered backing:NSBackingStoreBuffered
@@ -587,6 +621,11 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
[view registerForDraggedTypes:@[NSPasteboardTypeFileURL, NSPasteboardTypeString]]; [view registerForDraggedTypes:@[NSPasteboardTypeFileURL, NSPasteboardTypeString]];
} }
// Rescale contents
CALayer* layer = [view layer];
if (layer)
layer.contentsScale = screenScale;
// TODO: impl Parent for MacWindow // TODO: impl Parent for MacWindow
// TODO: impl StartPosition for MacWindow // TODO: impl StartPosition for MacWindow
// TODO: impl Fullscreen for MacWindow // TODO: impl Fullscreen for MacWindow
@@ -618,17 +657,19 @@ void MacWindow::SetIsMouseOver(bool value)
if (_isMouseOver == value) if (_isMouseOver == value)
return; return;
_isMouseOver = value; _isMouseOver = value;
CursorType cursor = _cursor;
if (value) if (value)
{ {
// Refresh cursor typet // Refresh cursor typet
SetCursor(_cursor); SetCursor(CursorType::Default);
SetCursor(cursor);
} }
else else
{ {
Input::Mouse->OnMouseLeave(this); Input::Mouse->OnMouseLeave(this);
if (_cursor == CursorType::Hidden) SetCursor(CursorType::Default);
[NSCursor unhide]; _cursor = cursor;
} }
} }
@@ -665,6 +706,8 @@ void MacWindow::Hide()
{ {
if (_visible) if (_visible)
{ {
SetCursor(CursorType::Default);
// Hide // Hide
NSWindow* window = (NSWindow*)_window; NSWindow* window = (NSWindow*)_window;
[window orderOut:nil]; [window orderOut:nil];
@@ -710,7 +753,7 @@ bool MacWindow::IsClosed() const
bool MacWindow::IsForegroundWindow() const bool MacWindow::IsForegroundWindow() const
{ {
return Platform::GetHasFocus(); return Platform::GetHasFocus() && IsFocused();
} }
void MacWindow::BringToFront(bool force) void MacWindow::BringToFront(bool force)
@@ -749,7 +792,7 @@ void MacWindow::SetPosition(const Float2& position)
NSWindow* window = (NSWindow*)_window; NSWindow* window = (NSWindow*)_window;
if (!window) if (!window)
return; return;
Float2 pos = MacUtils::PosToCoca(position); Float2 pos = MacUtils::PosToCoca(position) / MacPlatform::ScreenScale;
NSRect rect = [window frame]; NSRect rect = [window frame];
[window setFrameOrigin:NSMakePoint(pos.X, pos.Y - rect.size.height)]; [window setFrameOrigin:NSMakePoint(pos.X, pos.Y - rect.size.height)];
} }
@@ -760,7 +803,7 @@ Float2 MacWindow::GetPosition() const
if (!window) if (!window)
return Float2::Zero; return Float2::Zero;
NSRect rect = [window frame]; NSRect rect = [window frame];
return MacUtils::CocaToPos(Float2(rect.origin.x, rect.origin.y + rect.size.height)); return MacUtils::CocaToPos(Float2(rect.origin.x, rect.origin.y + rect.size.height) * MacPlatform::ScreenScale);
} }
Float2 MacWindow::GetSize() const Float2 MacWindow::GetSize() const
@@ -769,7 +812,7 @@ Float2 MacWindow::GetSize() const
if (!window) if (!window)
return Float2::Zero; return Float2::Zero;
NSRect rect = [window frame]; NSRect rect = [window frame];
return Float2(rect.size.width, rect.size.height); return Float2(rect.size.width, rect.size.height) * MacPlatform::ScreenScale;
} }
Float2 MacWindow::GetClientSize() const Float2 MacWindow::GetClientSize() const
@@ -836,6 +879,9 @@ DragDropEffect MacWindow::DoDragDrop(const StringView& data)
void MacWindow::SetCursor(CursorType type) void MacWindow::SetCursor(CursorType type)
{ {
CursorType prev = _cursor;
if (prev == type)
return;
WindowBase::SetCursor(type); WindowBase::SetCursor(type);
//if (!_isMouseOver) //if (!_isMouseOver)
// return; // return;
@@ -874,8 +920,11 @@ void MacWindow::SetCursor(CursorType type)
} }
if (cursor) if (cursor)
{ {
if (prev == CursorType::Hidden)
{
[NSCursor unhide];
}
[cursor set]; [cursor set];
[NSCursor unhide];
} }
} }