Merge branch 'ifromstone-dev/MacOSUIImprovement'

This commit is contained in:
2026-06-08 21:10:25 +02:00
6 changed files with 129 additions and 21 deletions
@@ -1,8 +1,8 @@
#if PLATFORM_WINDOWS || PLATFORM_SDL
#if PLATFORM_WINDOWS || PLATFORM_SDL || PLATFORM_MAC
#define USE_IS_FOREGROUND
#else
#endif
#if PLATFORM_SDL
#if PLATFORM_SDL || PLATFORM_MAC
#define USE_SDL_WORKAROUNDS
#endif
// Copyright (c) Wojciech Figat. All rights reserved.
+9 -3
View File
@@ -189,12 +189,18 @@ namespace FlaxEditor.Options
/// <summary>
/// Determined automatically based on the system and any known compatibility issues with native decorations.
/// </summary>
#if PLATFORM_MAC && !PLATFORM_SDL
[HideInEditor]
#endif
Auto,
/// <summary>
/// Automatically choose most compatible window decorations for child windows, prefer custom decorations on main window.
/// </summary>
[EditorDisplay(Name = "Auto (Child Only)")]
#if PLATFORM_MAC && !PLATFORM_SDL
[HideInEditor]
#endif
AutoChildOnly,
/// <summary>
@@ -307,18 +313,18 @@ namespace FlaxEditor.Options
[EditorDisplay("Interface"), EditorOrder(322)]
public bool ScrollToScriptOnAdd { get; set; } = true;
#if PLATFORM_SDL
#if PLATFORM_SDL || PLATFORM_MAC
/// <summary>
/// Gets or sets a value indicating whether use native window title bar decorations in child windows. Editor restart required.
/// </summary>
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_MAC
[DefaultValue(WindowDecorationsType.ClientSide)]
#else
[DefaultValue(WindowDecorationsType.AutoChildOnly)]
#endif
[EditorDisplay("Tabs & Windows"), EditorOrder(70), Tooltip("Determines whether use native window title bar decorations. Editor restart required.")]
public WindowDecorationsType WindowDecorations { get; set; } =
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_MAC
WindowDecorationsType.ClientSide;
#else
WindowDecorationsType.AutoChildOnly;
+6
View File
@@ -1293,6 +1293,12 @@ namespace FlaxEditor.Utilities
};
#elif PLATFORM_WINDOWS
return !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
#elif PLATFORM_MAC
return Editor.Instance.Options.Options.Interface.WindowDecorations switch
{
Options.InterfaceOptions.WindowDecorationsType.ClientSide => true,
_ => false
};
#else
return false;
#endif
+1
View File
@@ -10,6 +10,7 @@ struct Transform;
template<typename T>
class AssetReference;
struct ScriptingTypeHandle;
template<typename Type> ScriptingTypeHandle StaticType();
/// <summary>
/// Represents an object type that can be interpreted as more than one type.
+106 -15
View File
@@ -200,6 +200,19 @@ Float2 GetMousePosition(MacWindow* window, NSEvent* event)
return Float2(point.x, frame.size.height - point.y) * MacPlatform::ScreenScale - GetWindowTitleSize(window);
}
NSRect GetFrameRectForClientBounds(MacWindow* macWindow, NSWindow* window, const Rectangle& clientArea)
{
const float screenScale = MacPlatform::ScreenScale;
NSRect rect = NSMakeRect(0, 0, clientArea.Size.X / screenScale, clientArea.Size.Y / screenScale);
rect = [window frameRectForContentRect:rect];
Float2 pos = AppleUtils::PosToCoca(clientArea.Location) / screenScale;
Float2 titleSize = GetWindowTitleSize(macWindow);
rect.origin.x = pos.X + titleSize.X;
rect.origin.y = pos.Y - rect.size.height + titleSize.Y;
return rect;
}
class MacDropData : public IGuiData
{
public:
@@ -310,6 +323,12 @@ NSDragOperation GetDragDropOperation(DragDropEffect dragDropEffect)
Window->OnLostFocus();
}
- (void)windowDidMove:(NSNotification*)notification
{
if (IsWindowInvalid(Window)) return;
Window->SyncWindowState();
}
- (void)windowWillClose:(NSNotification*)notification
{
[self setDelegate: nil];
@@ -518,6 +537,28 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
if (IsWindowInvalid(Window)) return;
Float2 mousePos = GetMousePosition(Window, event);
mousePos = Window->ClientToScreen(mousePos);
if ([event clickCount] == 1 && !Input::Mouse->IsRelative())
{
WindowHitCodes hit = WindowHitCodes::Client;
bool handled = false;
Window->OnHitTest(mousePos, hit, handled);
if (hit == WindowHitCodes::Caption)
{
bool consumed = false;
Window->OnLeftButtonHit(hit, consumed);
if (!consumed)
{
[(NSWindow*)Window->GetNativePtr() performWindowDragWithEvent:event];
Window->SyncWindowState();
}
return;
}
}
MouseButton mouseButton = MouseButton::Left;
if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
Input::Mouse->OnMouseDoubleClick(mousePos, mouseButton, Window);
@@ -835,15 +876,28 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
MacWindow::~MacWindow()
{
NSWindow* window = (NSWindow*)_window;
[window close];
[window release];
if (NSWindow* window = (NSWindow*)_window)
{
[window close];
[window release];
}
_window = nullptr;
_view = nullptr;
}
void MacWindow::SyncWindowState()
{
NSWindow* window = (NSWindow*)_window;
if (window)
{
_minimized = window.miniaturized;
_maximized = window.zoomed;
}
}
void MacWindow::CheckForResize(float width, float height)
{
SyncWindowState();
const Float2 clientSize(width, height);
if (clientSize != _clientSize)
{
@@ -940,7 +994,7 @@ void MacWindow::Hide()
[window orderOut:nil];
// Transfer focus back to the parent when hiding popup
if (_settings.Parent && wasKey)
if (_settings.Parent && wasKey && _settings.Type != WindowType::Popup && _settings.Type != WindowType::Tooltip)
{
NSWindow* parent = (NSWindow*)_settings.Parent->GetNativePtr();
[parent makeKeyAndOrderFront:nil];
@@ -951,6 +1005,27 @@ void MacWindow::Hide()
}
}
void MacWindow::Close(ClosingReason reason)
{
const BOOL wasKey = _window && [(NSWindow*)_window isKeyWindow];
WindowBase::Close(reason);
// Closing can be cancelled by managed Window.Closing handlers.
if (!IsClosed())
return;
if (NSWindow* window = (NSWindow*)_window)
{
[window close];
}
if (_settings.Parent && wasKey && _settings.Type != WindowType::Popup && _settings.Type != WindowType::Tooltip)
{
NSWindow* parent = (NSWindow*)_settings.Parent->GetNativePtr();
[parent makeKeyAndOrderFront:nil];
}
}
void MacWindow::Minimize()
{
if (!_settings.AllowMinimize)
@@ -967,17 +1042,43 @@ void MacWindow::Maximize()
if (!_settings.AllowMaximize)
return;
NSWindow* window = (NSWindow*)_window;
if (!window)
return;
if (!window.zoomed)
{
if (!_maximized)
{
_restoreClientBounds = GetClientBounds();
_hasRestoreClientBounds = true;
}
[window zoom:nil];
}
SyncWindowState();
}
void MacWindow::Restore()
{
NSWindow* window = (NSWindow*)_window;
if (!window)
return;
if (window.miniaturized)
{
[window deminiaturize:nil];
SyncWindowState();
}
else if (_maximized && _hasRestoreClientBounds)
{
const Rectangle restoreClientBounds = _restoreClientBounds;
_hasRestoreClientBounds = false;
NSRect restoreFrame = GetFrameRectForClientBounds(this, window, restoreClientBounds);
[window setFrame:restoreFrame display:YES animate:YES];
_maximized = false;
}
else if (window.zoomed)
{
[window zoom:nil];
SyncWindowState();
}
}
bool MacWindow::IsForegroundWindow() const
@@ -1001,17 +1102,7 @@ void MacWindow::SetClientBounds(const Rectangle& clientArea)
NSWindow* window = (NSWindow*)_window;
if (!window)
return;
const float screenScale = MacPlatform::ScreenScale;
NSRect oldRect = [window frame];
NSRect newRect = NSMakeRect(0, 0, clientArea.Size.X / screenScale, clientArea.Size.Y / screenScale);
newRect = [window frameRectForContentRect:newRect];
Float2 pos = AppleUtils::PosToCoca(clientArea.Location) / screenScale;
Float2 titleSize = GetWindowTitleSize(this);
newRect.origin.x = pos.X + titleSize.X;
newRect.origin.y = pos.Y - newRect.size.height + titleSize.Y;
NSRect newRect = GetFrameRectForClientBounds(this, window, clientArea);
[window setFrame:newRect display:YES];
}
+5 -1
View File
@@ -17,13 +17,16 @@ private:
void* _window = nullptr;
void* _view = nullptr;
bool _isMouseOver = false;
bool _hasRestoreClientBounds = false;
Rectangle _restoreClientBounds;
Float2 _mouseTrackPos = Float2::Minimum;
String _dragText;
public:
MacWindow(const CreateWindowSettings& settings);
~MacWindow();
~MacWindow() override;
void SyncWindowState();
void CheckForResize(float width, float height);
void SetIsMouseOver(bool value);
const String& GetDragText() const
@@ -37,6 +40,7 @@ public:
void OnUpdate(float dt) override;
void Show() override;
void Hide() override;
void Close(ClosingReason reason) override;
void Minimize() override;
void Maximize() override;
void Restore() override;