From aab0d772a49d7f41f4c78c3fe4068007514a9c75 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Thu, 3 Jul 2025 20:19:49 +0300 Subject: [PATCH] Implement client-side window decorations for Editor windows --- .../GUI/Docking/FloatWindowDockPanel.cs | 62 ++++ Source/Editor/GUI/MainMenu.cs | 270 +------------- Source/Editor/GUI/MainMenuButton.cs | 4 +- Source/Editor/GUI/WindowDecorations.cs | 339 ++++++++++++++++++ Source/Editor/Modules/UIModule.cs | 90 +++-- Source/Editor/Modules/WindowsModule.cs | 10 +- Source/Editor/Options/InterfaceOptions.cs | 40 ++- Source/Editor/Utilities/Utils.cs | 12 + Source/Engine/Platform/Base/PlatformBase.cpp | 5 + Source/Engine/Platform/Base/PlatformBase.h | 5 + .../Engine/Platform/SDL/SDLPlatform.Linux.cpp | 17 +- Source/Engine/Platform/SDL/SDLPlatform.cpp | 41 ++- Source/Engine/Platform/SDL/SDLPlatform.h | 1 + Source/Engine/Platform/SDL/SDLWindow.cpp | 106 +++--- Source/Engine/Platform/SDL/SDLWindow.h | 1 + .../Windows/WindowsWindow.DragDrop.cpp | 5 +- 16 files changed, 638 insertions(+), 370 deletions(-) create mode 100644 Source/Editor/GUI/WindowDecorations.cs diff --git a/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs b/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs index c96614217..df19245e4 100644 --- a/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs +++ b/Source/Editor/GUI/Docking/FloatWindowDockPanel.cs @@ -11,6 +11,42 @@ namespace FlaxEditor.GUI.Docking /// public class FloatWindowDockPanel : DockPanel { + private class FloatWindowDecorations : WindowDecorations + { + private FloatWindowDockPanel _panel; + + public FloatWindowDecorations(FloatWindowDockPanel panel) + : base(panel.RootWindow) + { + _panel = panel; + } + + /// + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (Title.Bounds.Contains(location) && button == MouseButton.Left) + { + _panel.BeginDrag(); + return true; + } + return base.OnMouseDown(location, button); + } + +#if !PLATFORM_WINDOWS + /// + protected override WindowHitCodes OnHitTest(ref Float2 mouse) + { + var hit = base.OnHitTest(ref mouse); + if (hit == WindowHitCodes.Caption) + { + // Override the system behaviour when interacting with the caption area + hit = WindowHitCodes.Client; + } + return hit; + } +#endif + } + private MasterDockPanel _masterPanel; private WindowRootControl _window; @@ -40,6 +76,26 @@ namespace FlaxEditor.GUI.Docking Parent = window; _window.Window.Closing += OnClosing; _window.Window.LeftButtonHit += OnLeftButtonHit; + + if (Utilities.Utils.UseCustomWindowDecorations()) + { + var decorations = Parent.AddChild(new FloatWindowDecorations(this)); + decorations.SetAnchorPreset(AnchorPresets.HorizontalStretchTop, false); + } + } + + /// + protected override void PerformLayoutBeforeChildren() + { + base.PerformLayoutBeforeChildren(); + + var decorations = Parent.GetChild(); + if (decorations != null) + { + // Apply offset for the title bar + foreach (var child in Children) + child.Bounds = child.Bounds with { Y = decorations.Height, Height = Parent.Height - decorations.Height }; + } } /// @@ -87,6 +143,12 @@ namespace FlaxEditor.GUI.Docking settings.ShowAfterFirstPaint = false; settings.ShowInTaskbar = true; settings.StartPosition = startPosition; + + if (Utilities.Utils.UseCustomWindowDecorations()) + { + settings.HasBorder = false; + //settings.HasSizingFrame = false; + } // Create window return Platform.CreateWindow(ref settings); diff --git a/Source/Editor/GUI/MainMenu.cs b/Source/Editor/GUI/MainMenu.cs index 3dccf399c..6479938a1 100644 --- a/Source/Editor/GUI/MainMenu.cs +++ b/Source/Editor/GUI/MainMenu.cs @@ -12,16 +12,6 @@ namespace FlaxEditor.GUI /// public sealed class MainMenu : ContainerControl { -#if PLATFORM_WINDOWS || PLATFORM_SDL - private bool _useCustomWindowSystem; - private Image _icon; - private Label _title; - private Button _closeButton; - private Button _minimizeButton; - private Button _maximizeButton; - private LocalizedString _charChromeRestore, _charChromeMaximize; - private Window _window; -#endif private MainMenuButton _selected; /// @@ -60,202 +50,12 @@ namespace FlaxEditor.GUI /// /// Initializes a new instance of the class. /// - /// The main window. - public MainMenu(RootControl mainWindow) + public MainMenu() : base(0, 0, 0, 20) { AutoFocus = false; AnchorPreset = AnchorPresets.HorizontalStretchTop; - -#if PLATFORM_WINDOWS || PLATFORM_SDL - _useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem; - if (_useCustomWindowSystem) - { - BackgroundColor = Style.Current.LightBackground; - Height = 28; - - var windowIcon = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIcon); - FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIconsFont); - Font iconFont = windowIconsFont?.CreateFont(9); - - _window = mainWindow.RootWindow.Window; - _window.HitTest += OnHitTest; - _window.Closed += OnWindowClosed; - - ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration); - - var driver = Platform.DisplayServer; - - _icon = new Image - { - Margin = new Margin(6, 6, 6, 6), - Brush = new TextureBrush(windowIcon), - Color = Style.Current.Foreground, - KeepAspectRatio = false, - TooltipText = string.Format("{0}\nVersion {1}\nConfiguration {3}\nGraphics {2} {4}", _window.Title, Globals.EngineVersion, GPUDevice.Instance.RendererType, configuration, driver), - Parent = this, - }; - - _title = new Label(0, 0, Width, Height) - { - Text = _window.Title, - HorizontalAlignment = TextAlignment.Center, - VerticalAlignment = TextAlignment.Center, - ClipText = true, - TextColor = Style.Current.ForegroundGrey, - TextColorHighlighted = Style.Current.ForegroundGrey, - Parent = this, - }; - - _closeButton = new Button - { - Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(), - Font = new FontReference(iconFont), - BackgroundColor = Color.Transparent, - BorderColor = Color.Transparent, - BorderColorHighlighted = Color.Transparent, - BorderColorSelected = Color.Transparent, - TextColor = Style.Current.Foreground, - Width = 46, - BackgroundColorHighlighted = Color.Red, - BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f), - Parent = this, - }; - _closeButton.Clicked += () => _window.Close(ClosingReason.User); - - _minimizeButton = new Button - { - Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(), - Font = new FontReference(iconFont), - BackgroundColor = Color.Transparent, - BorderColor = Color.Transparent, - BorderColorHighlighted = Color.Transparent, - BorderColorSelected = Color.Transparent, - TextColor = Style.Current.Foreground, - Width = 46, - BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f), - Parent = this, - }; - _minimizeButton.Clicked += () => _window.Minimize(); - - _maximizeButton = new Button - { - Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(), - Font = new FontReference(iconFont), - BackgroundColor = Color.Transparent, - BorderColor = Color.Transparent, - BorderColorHighlighted = Color.Transparent, - BorderColorSelected = Color.Transparent, - TextColor = Style.Current.Foreground, - Width = 46, - BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f), - Parent = this, - }; - _maximizeButton.Clicked += () => - { - if (_window.IsMaximized) - _window.Restore(); - else - _window.Maximize(); - }; - _charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString(); - _charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString(); - } - else -#endif - { - BackgroundColor = Style.Current.LightBackground; - } - } - -#if PLATFORM_WINDOWS || PLATFORM_SDL - /// - public override void Update(float deltaTime) - { - base.Update(deltaTime); - - if (_maximizeButton != null) - { - _maximizeButton.Text = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize; - } - } - - private void OnWindowClosed() - { - if (_window != null) - { - _window.HitTest = null; - _window = null; - } - } - - private WindowHitCodes OnHitTest(ref Float2 mouse) - { - var dpiScale = _window.DpiScale; - - if (_window.IsMinimized) - return WindowHitCodes.NoWhere; - - if (!_window.IsMaximized) - { - var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted - var winSize = _window.Size; - - // Distance from which the mouse is considered to be on the border/corner - float distance = 5.0f * dpiScale; - - if (pos.Y > winSize.Y - distance && pos.X < distance) - return WindowHitCodes.BottomLeft; - - if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance) - return WindowHitCodes.BottomRight; - - if (pos.Y < distance && pos.X < distance) - return WindowHitCodes.TopLeft; - - if (pos.Y < distance && pos.X > winSize.X - distance) - return WindowHitCodes.TopRight; - - if (pos.X > winSize.X - distance) - return WindowHitCodes.Right; - - if (pos.X < distance) - return WindowHitCodes.Left; - - if (pos.Y < distance) - return WindowHitCodes.Top; - - if (pos.Y > winSize.Y - distance) - return WindowHitCodes.Bottom; - } - - var mousePos = PointFromScreen(mouse * dpiScale); - var controlUnderMouse = GetChildAt(mousePos); - var isMouseOverSth = controlUnderMouse != null && controlUnderMouse != _title; - var rb = GetRightButton(); - if (rb != null && _minimizeButton != null && new Rectangle(rb.UpperRight, _minimizeButton.BottomLeft - rb.UpperRight).Contains(ref mousePos) && !isMouseOverSth) - return WindowHitCodes.Caption; - - return WindowHitCodes.Client; - } -#endif - - /// - /// Return the rightmost button. - /// - /// Rightmost button, null if there is no - private MainMenuButton GetRightButton() - { - MainMenuButton b = null; - foreach (var control in Children) - { - if (b == null && control is MainMenuButton) - b = (MainMenuButton)control; - - if (control is MainMenuButton && control.Right > b.Right) - b = (MainMenuButton)control; - } - return b; + BackgroundColor = Style.Current.LightBackground; } /// @@ -300,26 +100,6 @@ namespace FlaxEditor.GUI return result; } - /// - public override bool OnMouseDoubleClick(Float2 location, MouseButton button) - { - if (base.OnMouseDoubleClick(location, button)) - return true; - -#if PLATFORM_WINDOWS || PLATFORM_SDL - var child = GetChildAtRecursive(location); - if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton) - { - if (_window.IsMaximized) - _window.Restore(); - else - _window.Maximize(); - } -#endif - - return true; - } - /// public override bool OnKeyDown(KeyboardKeys key) { @@ -335,16 +115,8 @@ namespace FlaxEditor.GUI protected override void PerformLayoutAfterChildren() { float x = 0; - -#if PLATFORM_WINDOWS || PLATFORM_SDL - if (_useCustomWindowSystem) - { - // Icon - _icon.X = x; - _icon.Size = new Float2(Height); - x += _icon.Width; - } -#endif + WindowDecorations decorations = Parent.GetChild(); + x += decorations?.Icon?.Width ?? 0; // Arrange controls MainMenuButton rightMostButton = null; @@ -363,37 +135,21 @@ namespace FlaxEditor.GUI x += b.Width; } } - -#if PLATFORM_WINDOWS || PLATFORM_SDL - if (_useCustomWindowSystem) - { - // Buttons - _closeButton.Height = Height; - _closeButton.X = Width - _closeButton.Width; - _maximizeButton.Height = Height; - _maximizeButton.X = _closeButton.X - _maximizeButton.Width; - _minimizeButton.Height = Height; - _minimizeButton.X = _maximizeButton.X - _minimizeButton.Width; - - // Title - _title.Bounds = new Rectangle(x + 2, 0, _minimizeButton.Left - x - 4, Height); - //_title.Text = _title.Width < 300.0f ? Editor.Instance.ProjectInfo.Name : _window.Title; - } -#endif + + // Fill the right side if title and buttons are not present + if (decorations?.Title == null) + Width = Parent.Width; + else + Width = x; } -#if PLATFORM_WINDOWS || PLATFORM_SDL /// public override void OnDestroy() { base.OnDestroy(); - - if (_window != null) - { - _window.Closed -= OnWindowClosed; - OnWindowClosed(); - } + + if (_selected != null) + Selected = null; } -#endif } } diff --git a/Source/Editor/GUI/MainMenuButton.cs b/Source/Editor/GUI/MainMenuButton.cs index 2c64b59c1..5007af92e 100644 --- a/Source/Editor/GUI/MainMenuButton.cs +++ b/Source/Editor/GUI/MainMenuButton.cs @@ -42,14 +42,12 @@ namespace FlaxEditor.GUI Text = text; var style = Style.Current; -#if PLATFORM_WINDOWS || PLATFORM_SDL - if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem) + if (!Utilities.Utils.UseCustomWindowDecorations()) { BackgroundColorMouseOver = style.BackgroundHighlighted; BackgroundColorMouseOverOpened = style.Background; } else -#endif { BackgroundColorMouseOver = BackgroundColorMouseOverOpened = style.LightBackground * 1.3f; } diff --git a/Source/Editor/GUI/WindowDecorations.cs b/Source/Editor/GUI/WindowDecorations.cs new file mode 100644 index 000000000..d678acbd0 --- /dev/null +++ b/Source/Editor/GUI/WindowDecorations.cs @@ -0,0 +1,339 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +using System; +using FlaxEditor.GUI.Docking; +using FlaxEditor.Options; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.GUI; + +/// +/// Represents the title bar of the window with buttons. +/// +/// +public class WindowDecorations : ContainerControl +{ + private Image _icon; + private Label _title; + private Button _closeButton; + private Button _minimizeButton; + private Button _maximizeButton; + private LocalizedString _charChromeRestore, _charChromeMaximize; + private Window _window; + + /// + /// The title label in the title bar. + /// + public Label Title => _title; + + /// + /// The icon used in the title bar. + /// + public Image Icon => _icon; + + /// + /// The tooltip shown when hovering over the icon. + /// + public string IconTooltipText + { + get => _icon?.TooltipText ?? null; + set + { + if (_icon != null) + _icon.TooltipText = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The window. + /// When set, omit drawing title and buttons. + public WindowDecorations(RootControl window, bool iconOnly = false) + : base(0, 0, 0, 20) + { + _window = window.RootWindow.Window; + + AutoFocus = false; + AnchorPreset = AnchorPresets.HorizontalStretchTop; + BackgroundColor = Color.Transparent; + + var windowIcon = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIcon); + + _icon = new Image + { + Margin = new Margin(4, 4, 4, 4), + Brush = new TextureBrush(windowIcon), + Color = Style.Current.Foreground, + BackgroundColor = Style.Current.LightBackground, + KeepAspectRatio = false, + Parent = this, + }; + + if (!iconOnly) + { + _icon.Margin = new Margin(6, 6, 6, 6); + Height = 28; + + _window.HitTest += OnHitTest; + _window.Closed += OnWindowClosed; + + FontAsset windowIconsFont = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WindowIconsFont); + Font iconFont = windowIconsFont?.CreateFont(9); + + _title = new Label(0, 0, Width, Height) + { + Text = _window.Title, + HorizontalAlignment = TextAlignment.Center, + VerticalAlignment = TextAlignment.Center, + ClipText = true, + TextColor = Style.Current.ForegroundGrey, + TextColorHighlighted = Style.Current.ForegroundGrey, + BackgroundColor = Style.Current.LightBackground, + Parent = this, + }; + + _closeButton = new Button + { + Text = ((char)EditorAssets.SegMDL2Icons.ChromeClose).ToString(), + Font = new FontReference(iconFont), + BackgroundColor = Style.Current.LightBackground, + BorderColor = Color.Transparent, + BorderColorHighlighted = Color.Transparent, + BorderColorSelected = Color.Transparent, + TextColor = Style.Current.Foreground, + Width = 46, + BackgroundColorHighlighted = Color.Red, + BackgroundColorSelected = Color.Red.RGBMultiplied(1.3f), + Parent = this, + }; + _closeButton.Clicked += () => _window.Close(ClosingReason.User); + + _minimizeButton = new Button + { + Text = ((char)EditorAssets.SegMDL2Icons.ChromeMinimize).ToString(), + Font = new FontReference(iconFont), + BackgroundColor = Style.Current.LightBackground, + BorderColor = Color.Transparent, + BorderColorHighlighted = Color.Transparent, + BorderColorSelected = Color.Transparent, + TextColor = Style.Current.Foreground, + Width = 46, + BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f), + Parent = this, + }; + _minimizeButton.Clicked += () => _window.Minimize(); + + _maximizeButton = new Button + { + Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(), + Font = new FontReference(iconFont), + BackgroundColor = Style.Current.LightBackground, + BorderColor = Color.Transparent, + BorderColorHighlighted = Color.Transparent, + BorderColorSelected = Color.Transparent, + TextColor = Style.Current.Foreground, + Width = 46, + BackgroundColorHighlighted = Style.Current.LightBackground.RGBMultiplied(1.3f), + Parent = this, + }; + _maximizeButton.Clicked += () => + { + if (_window.IsMaximized) + _window.Restore(); + else + _window.Maximize(); + }; + + _charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString(); + _charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString(); + } + } + + /// + public override void Update(float deltaTime) + { + base.Update(deltaTime); + + if (_maximizeButton != null) + { + var maximizeText = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize; + if (_maximizeButton.Text != maximizeText) + _maximizeButton.Text = maximizeText; + } + } + + private void OnWindowClosed() + { + if (_window != null) + { + _window.HitTest -= OnHitTest; + _window = null; + } + } + + /// + /// Perform hit test on the window. + /// + /// The mouse position + /// The hit code for given position. + protected virtual WindowHitCodes OnHitTest(ref Float2 mouse) + { + if (_window.IsMinimized) + return WindowHitCodes.NoWhere; + + var dpiScale = _window.DpiScale; + var pos = _window.ScreenToClient(mouse * dpiScale); // pos is not DPI adjusted + if (!_window.IsMaximized) + { + var winSize = _window.Size; + + // Distance from which the mouse is considered to be on the border/corner + float distance = 5.0f * dpiScale; + + if (pos.Y > winSize.Y - distance && pos.X < distance) + return WindowHitCodes.BottomLeft; + + if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance) + return WindowHitCodes.BottomRight; + + if (pos.Y < distance && pos.X < distance) + return WindowHitCodes.TopLeft; + + if (pos.Y < distance && pos.X > winSize.X - distance) + return WindowHitCodes.TopRight; + + if (pos.X > winSize.X - distance) + return WindowHitCodes.Right; + + if (pos.X < distance) + return WindowHitCodes.Left; + + if (pos.Y < distance) + return WindowHitCodes.Top; + + if (pos.Y > winSize.Y - distance) + return WindowHitCodes.Bottom; + } + + var controlUnderMouse = GetChildAt(pos, control => control != _title); + if (_title.Bounds.Contains(pos) && controlUnderMouse == null) + return WindowHitCodes.Caption; + + return WindowHitCodes.Client; + } + + /// + public override bool OnMouseDoubleClick(Float2 location, MouseButton button) + { + // These may not work with main window due to SDL not passing mouse events + // when interacting with hit tests on caption area... + + if (Title.Bounds.Contains(location) && button == MouseButton.Left) + { + if (_window.IsMaximized) + _window.Restore(); + else + _window.Maximize(); + return true; + } + else if (Icon.Bounds.Contains(location) && button == MouseButton.Left) + { + _window.Close(ClosingReason.User); + return true; + } + return base.OnMouseDoubleClick(location, button); + } + + /// + protected override void PerformLayoutAfterChildren() + { + // Calculate extents for title bounds area excluding the icon and main menu area + float x = 0; + + // Icon + if (_icon != null) + { + _icon.X = x; + _icon.Size = new Float2(Height); + x += _icon.Width; + } + + // Main menu if present + if (Parent.GetChild() is MainMenu mainMenu) + { + for (int i = 0; i < mainMenu.Children.Count; i++) + { + var c = mainMenu.Children[i]; + if (c is MainMenuButton b && c.Visible) + { + b.Bounds = new Rectangle(x, 0, b.Width, Height); + x += b.Width; + } + } + } + + // Buttons + float rightMostButtonX = Width; + if (_closeButton != null) + { + _closeButton.Height = Height; + _closeButton.X = rightMostButtonX - _closeButton.Width; + rightMostButtonX = _closeButton.X; + } + if (_maximizeButton != null) + { + _maximizeButton.Height = Height; + _maximizeButton.X = rightMostButtonX - _maximizeButton.Width; + rightMostButtonX = _maximizeButton.X; + } + if (_minimizeButton != null) + { + _minimizeButton.Height = Height; + _minimizeButton.X = rightMostButtonX - _minimizeButton.Width; + rightMostButtonX = _minimizeButton.X; + } + + // Title + if (_title != null) + { + _title.Text = _window.Title; + _title.Bounds = new Rectangle(x, 0, rightMostButtonX - x, Height); + } + } + + /// + public override void Draw() + { + base.Draw(); + DrawBorders(); + } + + /// + /// Draw borders around the window. + /// + public virtual void DrawBorders() + { + var win = RootWindow.Window; + if (win.IsMaximized) + return; + + const float thickness = 1.0f; + Color color = Editor.Instance.UI.StatusBar.StatusColor; + Rectangle rect = new Rectangle(thickness * 0.5f, thickness * 0.5f, Parent.Width - thickness, Parent.Height - thickness); + Render2D.DrawRectangle(rect, color); + } + + /// + public override void OnDestroy() + { + base.OnDestroy(); + + if (_window != null) + { + _window.Closed -= OnWindowClosed; + OnWindowClosed(); + } + } +} diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 191f3a975..cae120ab6 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -16,7 +16,6 @@ using FlaxEditor.Windows; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; -using WindowDragHelper = FlaxEditor.GUI.Docking.WindowDragHelper; using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel; using FlaxEditor.Content.Settings; using FlaxEditor.Options; @@ -29,6 +28,40 @@ namespace FlaxEditor.Modules /// public sealed class UIModule : EditorModule { + private class MainWindowDecorations : WindowDecorations + { + public MainWindowDecorations(RootControl window, bool iconOnly) + : base(window, iconOnly) + { + } + + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (base.OnKeyDown(key)) + return true; + + // Fallback to the edit window for shortcuts + var editor = Editor.Instance; + return editor.Windows.EditWin.InputActions.Process(editor, this, key); + } + + /// + public override void DrawBorders() + { + // Draw main window borders if using a custom style + var win = RootWindow.Window; + if (win.IsMaximized) + return; + + var color = Editor.Instance.UI.StatusBar.StatusColor; + var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight); + Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color); + Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color); + Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color); + } + } + private Label _progressLabel; private ProgressBar _progressBar; private Button _outputLogButton; @@ -97,6 +130,11 @@ namespace FlaxEditor.Modules /// public MainMenu MainMenu; + /// + /// The window decorations (title bar with buttons) + /// + public WindowDecorations WindowDecorations; + /// /// The tool strip control. /// @@ -377,19 +415,11 @@ namespace FlaxEditor.Modules InitToolstrip(mainWindow); InitStatusBar(mainWindow); InitDockPanel(mainWindow); + InitWindowDecorations(mainWindow); Editor.Options.OptionsChanged += OnOptionsChanged; - - // Add dummy control for drawing the main window borders if using a custom style -#if PLATFORM_WINDOWS || PLATFORM_SDL - if (!Editor.Options.Options.Interface.UseNativeWindowSystem) -#endif - { - mainWindow.AddChild(new CustomWindowBorderControl - { - Size = Float2.Zero, - }); - } + + mainWindow.PerformLayout(true); } /// @@ -410,23 +440,6 @@ namespace FlaxEditor.Modules } } - private class CustomWindowBorderControl : Control - { - /// - public override void Draw() - { - var win = RootWindow.Window; - if (win.IsMaximized) - return; - - var color = Editor.Instance.UI.StatusBar.StatusColor; - var rect = new Rectangle(0.5f, 0.5f, Parent.Width - 1.0f, Parent.Height - 1.0f - StatusBar.DefaultHeight); - Render2D.DrawLine(rect.UpperLeft, rect.UpperRight, color); - Render2D.DrawLine(rect.UpperLeft, rect.BottomLeft, color); - Render2D.DrawLine(rect.UpperRight, rect.BottomRight, color); - } - } - /// public override void OnEndInit() { @@ -511,10 +524,12 @@ namespace FlaxEditor.Modules private void InitMainMenu(RootControl mainWindow) { - MainMenu = new MainMenu(mainWindow) + MainMenu = new MainMenu() { Parent = mainWindow }; + if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true)) + MainMenu.Height = 28; var inputOptions = Editor.Options.Options.Input; @@ -654,6 +669,20 @@ namespace FlaxEditor.Modules cm.AddButton("Information about Flax", () => new AboutDialog().Show()); } + private void InitWindowDecorations(RootControl mainWindow) + { + ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration); + var driver = Platform.DisplayServer; + if (!string.IsNullOrEmpty(driver)) + driver = $" ({driver})"; + + WindowDecorations = new MainWindowDecorations(mainWindow, !Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true)) + { + Parent = mainWindow, + IconTooltipText = $"{mainWindow.RootWindow.Title}\nVersion {Globals.EngineVersion}\nConfiguration {configuration}\nGraphics {GPUDevice.Instance.RendererType}{driver}", + }; + } + private void OnOptionsChanged(EditorOptions options) { var inputOptions = options.Input; @@ -1063,6 +1092,7 @@ namespace FlaxEditor.Modules { // Clear UI references (GUI cannot be used after window closing) MainMenu = null; + WindowDecorations = null; ToolStrip = null; MasterPanel = null; StatusBar = null; diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 3e7ce2d9f..0052a37a4 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -10,7 +10,6 @@ using System.Text; using System.Xml; using FlaxEditor.Content; using FlaxEditor.GUI.Dialogs; -using FlaxEditor.GUI.Docking; using FlaxEditor.Windows; using FlaxEditor.Windows.Assets; using FlaxEditor.Windows.Profiler; @@ -761,17 +760,16 @@ namespace FlaxEditor.Modules settings.MinimumSize = new Float2(200, 150); settings.StartPosition = WindowStartPosition.CenterScreen; settings.ShowAfterFirstPaint = true; -#if PLATFORM_WINDOWS - if (!Editor.Instance.Options.Options.Interface.UseNativeWindowSystem) + + if (Utilities.Utils.UseCustomWindowDecorations(isMainWindow: true)) { settings.HasBorder = false; - -#if !PLATFORM_SDL +#if PLATFORM_WINDOWS && !PLATFORM_SDL // Skip OS sizing frame and implement it using LeftButtonHit settings.HasSizingFrame = false; #endif } -#elif PLATFORM_LINUX +#if PLATFORM_LINUX && !PLATFORM_SDL settings.HasBorder = false; #endif MainWindow = Platform.CreateWindow(ref settings); diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index ffa13ac29..3d26b2cfc 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -160,21 +160,47 @@ namespace FlaxEditor.Options GameWindowThenRestore, } + /// + /// Options for type of window decorations to use. + /// + public enum WindowDecorationsType + { + /// + /// Determined automatically based on the system and any known compatibility issues with native decorations. + /// + Auto, + + /// + /// Automatically choose most compatible window decorations for child windows, prefer custom decorations on main window. + /// + [EditorDisplay(Name = "Auto (Child Only)")] + AutoChildOnly, + + /// + /// Use native system window decorations on all windows. + /// + Native, + + /// + /// Use custom client-side window decorations on all windows. + /// + [EditorDisplay(Name = "Client-side")] + ClientSide, + } + /// /// Gets or sets the Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required. /// [DefaultValue(1.0f), Limit(0.1f, 10.0f)] [EditorDisplay("Interface"), EditorOrder(10), Tooltip("Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.")] public float InterfaceScale { get; set; } = 1.0f; - -#if PLATFORM_WINDOWS || PLATFORM_SDL + /// - /// Gets or sets a value indicating whether use native window title bar. Editor restart required. + /// Gets or sets a value indicating whether use native window title bar decorations in child windows. Editor restart required. /// - [DefaultValue(false)] - [EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar. Editor restart required.")] - public bool UseNativeWindowSystem { get; set; } = false; -#endif + [DefaultValue(WindowDecorationsType.AutoChildOnly)] + [EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar decorations. Editor restart required.")] + public WindowDecorationsType WindowDecorations { get; set; } = WindowDecorationsType.AutoChildOnly; /// /// Gets or sets a value indicating whether show selected camera preview in the editor window. diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 949479783..284a0b6c6 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1559,5 +1559,17 @@ namespace FlaxEditor.Utilities c = c.Parent; return c as ISceneEditingContext; } + + internal static bool UseCustomWindowDecorations(bool isMainWindow = false) + { + return Editor.Instance.Options.Options.Interface.WindowDecorations switch + { + Options.InterfaceOptions.WindowDecorationsType.Auto => !Platform.SupportsNativeDecorations, + Options.InterfaceOptions.WindowDecorationsType.AutoChildOnly => !isMainWindow ? !Platform.SupportsNativeDecorations : true, + Options.InterfaceOptions.WindowDecorationsType.Native => false, + Options.InterfaceOptions.WindowDecorationsType.ClientSide => true, + _ => throw new ArgumentOutOfRangeException() + }; + } } } diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index d16521c24..7a93c24ed 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -269,6 +269,11 @@ String PlatformBase::GetDisplayServer() return String::Empty; } +bool PlatformBase::SupportsNativeDecorations() +{ + return true; +} + #endif bool PlatformBase::Is64BitApp() diff --git a/Source/Engine/Platform/Base/PlatformBase.h b/Source/Engine/Platform/Base/PlatformBase.h index ae8f6537e..97928c376 100644 --- a/Source/Engine/Platform/Base/PlatformBase.h +++ b/Source/Engine/Platform/Base/PlatformBase.h @@ -374,6 +374,11 @@ public: /// API_PROPERTY() static String GetDisplayServer(); + /// + /// Returns true if system provides decorations for windows. + /// + API_PROPERTY() static bool SupportsNativeDecorations(); + /// /// Returns true if is running 64 bit application (otherwise 32 bit). It's compile-time constant. /// diff --git a/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp b/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp index 4b300a3f0..3d7e72e83 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp +++ b/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp @@ -35,6 +35,12 @@ #include #include +namespace SDLImpl +{ + extern String WaylandDisplayEnv; + extern String XDGCurrentDesktop; +} + class LinuxDropFilesData : public IGuiData { public: @@ -1611,8 +1617,7 @@ bool SDLPlatform::InitInternal() if (!CommandLine::Options.Headless.IsTrue() && waylandRequested) { // Ignore in X11 session - String waylandDisplayEnv; - if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), waylandDisplayEnv)) + if (!SDLImpl::WaylandDisplayEnv.IsEmpty()) { WaylandImpl::WaylandDisplay = (wl_display*)SDL_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, nullptr); if (WaylandImpl::WaylandDisplay != nullptr) @@ -1721,11 +1726,15 @@ DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offse if (SDLPlatform::UsesWayland()) { Float2 dragOffset = offset; - if (_settings.HasBorder && dragSourceWindow == this) + if (SDLPlatform::SupportsNativeDecorations() && _settings.HasBorder && dragSourceWindow == this) { // Wayland includes the decorations in the client-space coordinates, adjust the offset for it. - // Assume the title decoration is 25px thick... + // Assume the title decoration based on the current desktop environment... float topOffset = 25.0f; + if (SDLImpl::XDGCurrentDesktop.Compare(String("KDE"), StringSearchCase::IgnoreCase) == 0) + topOffset = 25.0f; + else if (SDLImpl::XDGCurrentDesktop.Compare(String("GNOME"), StringSearchCase::IgnoreCase) == 0) + topOffset = 48.0f; dragOffset += Float2(0.0f, topOffset); } diff --git a/Source/Engine/Platform/SDL/SDLPlatform.cpp b/Source/Engine/Platform/SDL/SDLPlatform.cpp index b245a6e22..181412319 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.cpp +++ b/Source/Engine/Platform/SDL/SDLPlatform.cpp @@ -27,25 +27,41 @@ #define DefaultDPI 96 -namespace +namespace SDLImpl { int32 SystemDpi = 96; String UserLocale("en"); + bool WindowDecorationsSupported = true; + String WaylandDisplayEnv; + String XDGCurrentDesktop; } bool SDLPlatform::Init() { #if PLATFORM_LINUX + bool waylandSession = false; + if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), SDLImpl::WaylandDisplayEnv)) + waylandSession = true; + GetEnvironmentVariable(String("XDG_CURRENT_DESKTOP"), SDLImpl::XDGCurrentDesktop); + if (CommandLine::Options.X11.IsTrue()) + { SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "x11", SDL_HINT_OVERRIDE); + waylandSession = false; + } else if (CommandLine::Options.Wayland.IsTrue()) SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE); - else + else if (waylandSession) { // Override the X11 preference when running in Wayland session - String waylandDisplayEnv; - if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), waylandDisplayEnv)) - SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE); + SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE); + } + + // Workaround for libdecor in Gnome+Wayland causing freezes when interacting with the native decorations + if (waylandSession && SDLImpl::XDGCurrentDesktop.Compare(String("GNOME"), StringSearchCase::IgnoreCase) == 0) + { + SDL_SetHint(SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR, "0"); + SDLImpl::WindowDecorationsSupported = false; } #endif @@ -95,9 +111,9 @@ bool SDLPlatform::Init() if (language.StartsWith("en")) { if (country != nullptr) - UserLocale = String::Format(TEXT("{0}-{1}"), String(language), String(locales[i]->country)); + SDLImpl::UserLocale = String::Format(TEXT("{0}-{1}"), String(language), String(locales[i]->country)); else - UserLocale = String(language); + SDLImpl::UserLocale = String(language); break; } } @@ -124,7 +140,7 @@ bool SDLPlatform::Init() SDLInput::Init(); SDLWindow::Init(); - SystemDpi = (int)(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()) * DefaultDPI); + SDLImpl::SystemDpi = (int)(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()) * DefaultDPI); //SDL_StartTextInput(); // TODO: Call this only when text input is expected (shows virtual keyboard in some cases) @@ -183,6 +199,11 @@ String SDLPlatform::GetDisplayServer() #endif } +bool SDLPlatform::SupportsNativeDecorations() +{ + return SDLImpl::WindowDecorationsSupported; +} + BatteryInfo SDLPlatform::GetBatteryInfo() { BatteryInfo info; @@ -213,12 +234,12 @@ BatteryInfo SDLPlatform::GetBatteryInfo() int32 SDLPlatform::GetDpi() { - return SystemDpi; + return SDLImpl::SystemDpi; } String SDLPlatform::GetUserLocaleName() { - return UserLocale; + return SDLImpl::UserLocale; } void SDLPlatform::OpenUrl(const StringView& url) diff --git a/Source/Engine/Platform/SDL/SDLPlatform.h b/Source/Engine/Platform/SDL/SDLPlatform.h index a30c8d8c3..b7e556395 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.h +++ b/Source/Engine/Platform/SDL/SDLPlatform.h @@ -74,6 +74,7 @@ public: static void LogInfo(); static void Tick(); static String GetDisplayServer(); + static bool SupportsNativeDecorations(); static void SetHighDpiAwarenessEnabled(bool enable); static BatteryInfo GetBatteryInfo(); static int32 GetDpi(); diff --git a/Source/Engine/Platform/SDL/SDLWindow.cpp b/Source/Engine/Platform/SDL/SDLWindow.cpp index 6879d0659..13c759b1d 100644 --- a/Source/Engine/Platform/SDL/SDLWindow.cpp +++ b/Source/Engine/Platform/SDL/SDLWindow.cpp @@ -25,7 +25,6 @@ #define NOGDI #include -#include #include #include #undef CreateWindow @@ -45,12 +44,12 @@ static_assert(false, "Unsupported Platform"); #define DefaultDPI 96 -namespace WindowImpl +namespace SDLImpl { SDLWindow* LastEventWindow = nullptr; - static SDL_Cursor* Cursors[SDL_SYSTEM_CURSOR_COUNT] = { nullptr }; + SDL_Cursor* Cursors[SDL_SYSTEM_CURSOR_COUNT] = { nullptr }; + extern String XDGCurrentDesktop; } -using namespace WindowImpl; SDL_HitTestResult OnWindowHitTest(SDL_Window* win, const SDL_Point* area, void* data); void GetRelativeWindowOffset(WindowType type, SDLWindow* parentWindow, Int2& positionOffset); @@ -208,7 +207,7 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings) } #endif - LastEventWindow = this; + SDLImpl::LastEventWindow = this; #if PLATFORM_LINUX // Initialize using the shared Display instance from SDL @@ -263,8 +262,8 @@ void* SDLWindow::GetX11Display() const SDLWindow::~SDLWindow() { - if (LastEventWindow == this) - LastEventWindow = nullptr; + if (SDLImpl::LastEventWindow == this) + SDLImpl::LastEventWindow = nullptr; if (_window == nullptr) return; @@ -281,45 +280,48 @@ SDLWindow::~SDLWindow() _visible = false; } +WindowHitCodes SDLWindow::OnWindowHit(const Float2 point) +{ + WindowHitCodes hit = WindowHitCodes::Client; + if (!IsFullscreen()) + { + Float2 screenPosition = ClientToScreen(point); + bool handled = false; + OnHitTest(screenPosition, hit, handled); + if (!handled) + { + int margin = _settings.HasBorder ? 0 : 0; + auto size = GetClientSize(); + //if (point.Y < 0) + // hit = WindowHitCodes::Caption; + if (point.Y < margin && point.X < margin) + hit = WindowHitCodes::TopLeft; + else if (point.Y < margin && point.X > size.X - margin) + hit = WindowHitCodes::TopRight; + else if (point.Y < margin) + hit = WindowHitCodes::Top; + else if (point.X < margin && point.Y > size.Y - margin) + hit = WindowHitCodes::BottomLeft; + else if (point.X < margin) + hit = WindowHitCodes::Left; + else if (point.X > size.X - margin && point.Y > size.Y - margin) + hit = WindowHitCodes::BottomRight; + else if (point.X > size.X - margin) + hit = WindowHitCodes::Right; + else if (point.Y > size.Y - margin) + hit = WindowHitCodes::Bottom; + else + hit = WindowHitCodes::Client; + } + } + return hit; +} + SDL_HitTestResult OnWindowHitTest(SDL_Window* win, const SDL_Point* area, void* data) { - SDLWindow* window = (SDLWindow*)data; - if (window->IsFullscreen()) - return SDL_HITTEST_NORMAL; - - Float2 clientPosition = Float2(static_cast(area->x), static_cast(area->y)); - Float2 screenPosition = window->ClientToScreen(clientPosition); - - WindowHitCodes hit = WindowHitCodes::Client; - bool handled = false; - window->OnHitTest(screenPosition, hit, handled); - - if (!handled) - { - int margin = window->GetSettings().HasBorder ? 0 : 0; - auto size = window->GetClientSize(); - //if (clientPosition.Y < 0) - // return SDL_HITTEST_DRAGGABLE; - if (clientPosition.Y < margin && clientPosition.X < margin) - return SDL_HITTEST_RESIZE_TOPLEFT; - else if (clientPosition.Y < margin && clientPosition.X > size.X - margin) - return SDL_HITTEST_RESIZE_TOPRIGHT; - else if (clientPosition.Y < margin) - return SDL_HITTEST_RESIZE_TOP; - else if (clientPosition.X < margin && clientPosition.Y > size.Y - margin) - return SDL_HITTEST_RESIZE_BOTTOMLEFT; - else if (clientPosition.X < margin) - return SDL_HITTEST_RESIZE_LEFT; - else if (clientPosition.X > size.X - margin && clientPosition.Y > size.Y - margin) - return SDL_HITTEST_RESIZE_BOTTOMRIGHT; - else if (clientPosition.X > size.X - margin) - return SDL_HITTEST_RESIZE_RIGHT; - else if (clientPosition.Y > size.Y - margin) - return SDL_HITTEST_RESIZE_BOTTOM; - else - return SDL_HITTEST_NORMAL; - } - + SDLWindow* window = static_cast(data); + const Float2 point(static_cast(area->x), static_cast(area->y)); + WindowHitCodes hit = window->OnWindowHit(point); switch (hit) { case WindowHitCodes::Caption: @@ -350,9 +352,9 @@ SDLWindow* SDLWindow::GetWindowFromEvent(const SDL_Event& event) SDL_Window* window = SDL_GetWindowFromEvent(&event); if (window == nullptr) return nullptr; - if (LastEventWindow == nullptr || window != LastEventWindow->_window) - LastEventWindow = GetWindowWithSDLWindow(window); - return LastEventWindow; + if (SDLImpl::LastEventWindow == nullptr || window != SDLImpl::LastEventWindow->_window) + SDLImpl::LastEventWindow = GetWindowWithSDLWindow(window); + return SDLImpl::LastEventWindow; } SDLWindow* SDLWindow::GetWindowWithSDLWindow(SDL_Window* window) @@ -779,8 +781,8 @@ Float2 SDLWindow::ClientToScreen(const Float2& clientPos) const void SDLWindow::FlashWindow() { #if PLATFORM_LINUX - // Flashing brings the window on top of other windows, disable it for now - if (SDLPlatform::UsesWayland()) + // KDE bug: flashing brings the window on top of other windows, disable it for now... + if (SDLPlatform::UsesWayland() && SDLImpl::XDGCurrentDesktop.Compare(String("KDE"), StringSearchCase::IgnoreCase) == 0) return; #endif SDL_FlashWindow(_window, SDL_FLASH_UNTIL_FOCUSED); @@ -989,9 +991,9 @@ void SDLWindow::UpdateCursor() break; } - if (Cursors[index] == nullptr) - Cursors[index] = SDL_CreateSystemCursor(static_cast(index)); - SDL_SetCursor(Cursors[index]); + if (SDLImpl::Cursors[index] == nullptr) + SDLImpl::Cursors[index] = SDL_CreateSystemCursor(static_cast(index)); + SDL_SetCursor(SDLImpl::Cursors[index]); } void SDLWindow::SetIcon(TextureData& icon) diff --git a/Source/Engine/Platform/SDL/SDLWindow.h b/Source/Engine/Platform/SDL/SDLWindow.h index 3c3f02f26..24dbc49cc 100644 --- a/Source/Engine/Platform/SDL/SDLWindow.h +++ b/Source/Engine/Platform/SDL/SDLWindow.h @@ -69,6 +69,7 @@ public: void* GetWaylandDisplay() const; void* GetX11Display() const; #endif + WindowHitCodes OnWindowHit(const Float2 point); // [WindowBase] void* GetNativePtr() const override; diff --git a/Source/Engine/Platform/Windows/WindowsWindow.DragDrop.cpp b/Source/Engine/Platform/Windows/WindowsWindow.DragDrop.cpp index 3f17ed2c3..f98a189c4 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.DragDrop.cpp +++ b/Source/Engine/Platform/Windows/WindowsWindow.DragDrop.cpp @@ -10,13 +10,16 @@ #if USE_EDITOR #include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Log.h" #include "Engine/Platform/IGuiData.h" #include "Engine/Platform/Base/DragDropHelper.h" #include "Engine/Input/Input.h" #include "Engine/Input/Mouse.h" #endif -#include "../Win32/IncludeWindowsHeaders.h" + +#include "Engine/Platform/Win32/IncludeWindowsHeaders.h" #include + #if USE_EDITOR #include #include