diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
index 3eace0363..3947430f9 100644
--- a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
+++ b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs
@@ -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.
diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs
index 6d56e9ac7..a12b2fda0 100644
--- a/Source/Editor/Options/InterfaceOptions.cs
+++ b/Source/Editor/Options/InterfaceOptions.cs
@@ -189,12 +189,18 @@ namespace FlaxEditor.Options
///
/// Determined automatically based on the system and any known compatibility issues with native decorations.
///
+#if PLATFORM_MAC && !PLATFORM_SDL
+ [HideInEditor]
+#endif
Auto,
///
/// Automatically choose most compatible window decorations for child windows, prefer custom decorations on main window.
///
[EditorDisplay(Name = "Auto (Child Only)")]
+#if PLATFORM_MAC && !PLATFORM_SDL
+ [HideInEditor]
+#endif
AutoChildOnly,
///
@@ -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
///
/// Gets or sets a value indicating whether use native window title bar decorations in child windows. Editor restart required.
///
-#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;
diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs
index 62af28bc0..5955ccf60 100644
--- a/Source/Editor/Utilities/Utils.cs
+++ b/Source/Editor/Utilities/Utils.cs
@@ -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
diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h
index 9532d4ec4..13c0a5a79 100644
--- a/Source/Engine/Core/Types/Variant.h
+++ b/Source/Engine/Core/Types/Variant.h
@@ -10,6 +10,7 @@ struct Transform;
template
class AssetReference;
struct ScriptingTypeHandle;
+template ScriptingTypeHandle StaticType();
///
/// Represents an object type that can be interpreted as more than one type.
diff --git a/Source/Engine/Platform/Mac/MacWindow.cpp b/Source/Engine/Platform/Mac/MacWindow.cpp
index b7014681a..adb33962f 100644
--- a/Source/Engine/Platform/Mac/MacWindow.cpp
+++ b/Source/Engine/Platform/Mac/MacWindow.cpp
@@ -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];
}
diff --git a/Source/Engine/Platform/Mac/MacWindow.h b/Source/Engine/Platform/Mac/MacWindow.h
index f4a8cb706..91a1219e0 100644
--- a/Source/Engine/Platform/Mac/MacWindow.h
+++ b/Source/Engine/Platform/Mac/MacWindow.h
@@ -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;