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;