diff --git a/Flax.sln.DotSettings b/Flax.sln.DotSettings index 9d82c0e65..a910e0375 100644 --- a/Flax.sln.DotSettings +++ b/Flax.sln.DotSettings @@ -248,6 +248,7 @@ True True True + True True True True diff --git a/Source/Editor/GUI/Docking/DockPanelProxy.cs b/Source/Editor/GUI/Docking/DockPanelProxy.cs index ac5e0bc4f..15ff2cad0 100644 --- a/Source/Editor/GUI/Docking/DockPanelProxy.cs +++ b/Source/Editor/GUI/Docking/DockPanelProxy.cs @@ -253,6 +253,12 @@ namespace FlaxEditor.GUI.Docking tabColor = style.BackgroundHighlighted; Render2D.FillRectangle(tabRect, tabColor); } + else + { + tabColor = style.BackgroundHighlighted; + Render2D.DrawLine(tabRect.BottomLeft - new Float2(0 , 1), tabRect.UpperLeft, tabColor); + Render2D.DrawLine(tabRect.BottomRight - new Float2(0 , 1), tabRect.UpperRight, tabColor); + } if (tab.Icon.IsValid) { diff --git a/Source/Editor/GUI/Timeline/GUI/Background.cs b/Source/Editor/GUI/Timeline/GUI/Background.cs index dc4909abb..b9eff562a 100644 --- a/Source/Editor/GUI/Timeline/GUI/Background.cs +++ b/Source/Editor/GUI/Timeline/GUI/Background.cs @@ -282,7 +282,7 @@ namespace FlaxEditor.GUI.Timeline.GUI var x = time * zoom + Timeline.StartOffset; // Header line - var lineRect = new Rectangle(x - 0.5f, -verticalLinesHeaderExtend + timeAxisHeaderOffset, 1.0f, verticalLinesHeaderExtend); + var lineRect = new Rectangle(x - 0.5f, -verticalLinesHeaderExtend * 0.6f + timeAxisHeaderOffset, 1.0f, verticalLinesHeaderExtend * 0.6f); Render2D.FillRectangle(lineRect, lineColor); // Time label @@ -300,7 +300,7 @@ namespace FlaxEditor.GUI.Timeline.GUI break; default: throw new ArgumentOutOfRangeException(); } - var labelRect = new Rectangle(x + 2, -verticalLinesHeaderExtend + timeAxisHeaderOffset, 50, verticalLinesHeaderExtend); + var labelRect = new Rectangle(x + 2, -verticalLinesHeaderExtend * 0.8f + timeAxisHeaderOffset, 50, verticalLinesHeaderExtend); Render2D.DrawText(style.FontSmall, labelText, labelRect, labelColor, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.8f); } } diff --git a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs index 7b42e86ab..bedb61a5e 100644 --- a/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs +++ b/Source/Editor/GUI/Timeline/GUI/PositionHandle.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System; +using System.Globalization; using FlaxEngine; using FlaxEngine.GUI; @@ -30,11 +32,33 @@ namespace FlaxEditor.GUI.Timeline.GUI var timeAxisOverlap = Timeline.HeaderTopAreaHeight * 0.5f; var timeAxisHeaderOffset = -_timeline.MediaBackground.ViewOffset.Y - timeAxisOverlap; + // Time label + string labelText; + switch (_timeline.TimeShowMode) + { + case Timeline.TimeShowModes.Frames: + labelText = _timeline.CurrentFrame.ToString("###0", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Seconds: + labelText = _timeline.CurrentTime.ToString("###0.##'s'", CultureInfo.InvariantCulture); + break; + case Timeline.TimeShowModes.Time: + labelText = TimeSpan.FromSeconds(_timeline.CurrentTime).ToString("g"); + break; + default: throw new ArgumentOutOfRangeException(); + } + var color = (_timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground).AlphaMultiplied(0.6f); Matrix3x3.RotationZ(Mathf.PiOverTwo, out var m1); var m2 = Matrix3x3.Translation2D(0, timeAxisHeaderOffset); Matrix3x3.Multiply(ref m1, ref m2, out var m3); Render2D.PushTransform(ref m3); - Render2D.DrawSprite(icon, new Rectangle(new Float2(4, -Width), Size), _timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground); + // TODO: Convert to its own sprite or 9 slice + Render2D.DrawSprite(icon, new Rectangle(new Float2(10, -icon.Size.X * 0.5f - 1), Size + new Float2(0, 1)), color); + Render2D.FillRectangle(new Rectangle(new Float2(-6, -icon.Size.Y * 0.5f + 7), new Float2(timeAxisOverlap, 5)), color); + Render2D.PopTransform(); + var textMatrix = Matrix3x3.Translation2D(12, timeAxisHeaderOffset); + Render2D.PushTransform(ref textMatrix); + Render2D.DrawText(style.FontSmall, labelText, style.Foreground, new Float2(2, -6)); Render2D.PopTransform(); Render2D.FillRectangle(new Rectangle(Width * 0.5f, Height + timeAxisHeaderOffset, 1, _timeline.MediaPanel.Height - timeAxisHeaderOffset - timeAxisOverlap), _timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground.RGBMultiplied(0.8f)); diff --git a/Source/Editor/GUI/Timeline/Timeline.UI.cs b/Source/Editor/GUI/Timeline/Timeline.UI.cs index a977d487e..604db541c 100644 --- a/Source/Editor/GUI/Timeline/Timeline.UI.cs +++ b/Source/Editor/GUI/Timeline/Timeline.UI.cs @@ -50,6 +50,27 @@ namespace FlaxEditor.GUI.Timeline } } + /// + public override void OnMouseEnter(Float2 location) + { + base.OnMouseEnter(location); + Cursor = CursorType.Hand; + } + + /// + public override void OnMouseLeave() + { + Cursor = CursorType.Default; + base.OnMouseLeave(); + } + + /// + public override void Defocus() + { + Cursor = CursorType.Default; + base.Defocus(); + } + private void Seek(ref Float2 location) { if (_timeline.PlaybackState == PlaybackStates.Disabled) diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs index 9800b2105..a32b35692 100644 --- a/Source/Editor/GUI/Timeline/Timeline.cs +++ b/Source/Editor/GUI/Timeline/Timeline.cs @@ -167,7 +167,7 @@ namespace FlaxEditor.GUI.Timeline /// /// The header top area height (in pixels). /// - public static readonly float HeaderTopAreaHeight = 22.0f; + public static readonly float HeaderTopAreaHeight = 40.0f; /// /// The timeline units per second (on time axis). diff --git a/Source/Editor/GUI/Timeline/Tracks/AnimationEventTrack.cs b/Source/Editor/GUI/Timeline/Tracks/AnimationEventTrack.cs index 4e78ca8ae..11a1bfe47 100644 --- a/Source/Editor/GUI/Timeline/Tracks/AnimationEventTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/AnimationEventTrack.cs @@ -298,7 +298,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks var animEventTypes = Editor.Instance.CodeEditing.All.Get().Where(x => new ScriptType(typeof(AnimEvent)).IsAssignableFrom(x)); foreach (var type in animEventTypes) { - if (type.IsAbstract || !type.CanCreateInstance) + if (type.IsAbstract || !type.CanCreateInstance || type.HasAttribute(typeof(HideInEditorAttribute), true)) continue; var add = new ScriptType(typeof(AnimContinuousEvent)).IsAssignableFrom(type) ? addContinuousEvent : addEvent; var b = add.ContextMenu.AddButton(type.Name); @@ -307,6 +307,10 @@ namespace FlaxEditor.GUI.Timeline.Tracks b.Parent.Tag = time; b.ButtonClicked += OnAddAnimEvent; } + if (!addEvent.ContextMenu.Items.Any()) + addEvent.ContextMenu.AddButton("No Anim Events found").CloseMenuOnClick = false; + if (!addContinuousEvent.ContextMenu.Items.Any()) + addContinuousEvent.ContextMenu.AddButton("No Continuous Anim Events found").CloseMenuOnClick = false; } diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 3281d130a..ad7e06ba5 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -479,6 +479,7 @@ namespace FlaxEditor.Viewport { InitFpsCounter(); _showFpsButon = ViewWidgetShowMenu.AddButton("FPS Counter", () => ShowFpsCounter = !ShowFpsCounter); + _showFpsButon.CloseMenuOnClick = false; } } diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index faefef724..979702514 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -397,9 +397,11 @@ namespace FlaxEditor.Viewport // Show grid widget _showGridButton = ViewWidgetShowMenu.AddButton("Grid", () => Grid.Enabled = !Grid.Enabled); _showGridButton.Icon = Style.Current.CheckBoxTick; + _showGridButton.CloseMenuOnClick = false; // Show navigation widget _showNavigationButton = ViewWidgetShowMenu.AddButton("Navigation", () => ShowNavigation = !ShowNavigation); + _showNavigationButton.CloseMenuOnClick = false; // Create camera widget ViewWidgetButtonMenu.AddSeparator(); diff --git a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs index 12371f947..d96d7a8f9 100644 --- a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs @@ -194,16 +194,20 @@ namespace FlaxEditor.Viewport.Previews { // Show Bounds _showBoundsButton = ViewWidgetShowMenu.AddButton("Bounds", () => ShowBounds = !ShowBounds); + _showBoundsButton.CloseMenuOnClick = false; // Show Skeleton _showNodesButton = ViewWidgetShowMenu.AddButton("Skeleton", () => ShowNodes = !ShowNodes); + _showNodesButton.CloseMenuOnClick = false; // Show Skeleton Names _showNodesNamesButton = ViewWidgetShowMenu.AddButton("Skeleton Names", () => ShowNodesNames = !ShowNodesNames); + _showNodesNamesButton.CloseMenuOnClick = false; // Show Floor _showFloorButton = ViewWidgetShowMenu.AddButton("Floor", button => ShowFloor = !ShowFloor); _showFloorButton.IndexInParent = 1; + _showFloorButton.CloseMenuOnClick = false; } // Enable shadows diff --git a/Source/Editor/Viewport/Previews/AssetPreview.cs b/Source/Editor/Viewport/Previews/AssetPreview.cs index 0bcc4e115..0fb274b5d 100644 --- a/Source/Editor/Viewport/Previews/AssetPreview.cs +++ b/Source/Editor/Viewport/Previews/AssetPreview.cs @@ -171,6 +171,7 @@ namespace FlaxEditor.Viewport.Previews // Show Default Scene _showDefaultSceneButton = ViewWidgetShowMenu.AddButton("Default Scene", () => ShowDefaultSceneActors = !ShowDefaultSceneActors); _showDefaultSceneButton.Checked = true; + _showDefaultSceneButton.CloseMenuOnClick = false; } // Setup preview scene diff --git a/Source/Editor/Viewport/Previews/ModelPreview.cs b/Source/Editor/Viewport/Previews/ModelPreview.cs index a0b6d0ca0..a6496aafe 100644 --- a/Source/Editor/Viewport/Previews/ModelPreview.cs +++ b/Source/Editor/Viewport/Previews/ModelPreview.cs @@ -199,13 +199,18 @@ namespace FlaxEditor.Viewport.Previews if (useWidgets) { _showBoundsButton = ViewWidgetShowMenu.AddButton("Bounds", () => ShowBounds = !ShowBounds); + _showBoundsButton.CloseMenuOnClick = false; _showNormalsButton = ViewWidgetShowMenu.AddButton("Normals", () => ShowNormals = !ShowNormals); + _showNormalsButton.CloseMenuOnClick = false; _showTangentsButton = ViewWidgetShowMenu.AddButton("Tangents", () => ShowTangents = !ShowTangents); + _showTangentsButton.CloseMenuOnClick = false; _showBitangentsButton = ViewWidgetShowMenu.AddButton("Bitangents", () => ShowBitangents = !ShowBitangents); + _showBitangentsButton.CloseMenuOnClick = false; // Show Floor _showFloorButton = ViewWidgetShowMenu.AddButton("Floor", button => ShowFloor = !ShowFloor); _showFloorButton.IndexInParent = 1; + _showFloorButton.CloseMenuOnClick = false; // Show current LOD widget _showCurrentLODButton = ViewWidgetShowMenu.AddButton("Current LOD", button => @@ -214,6 +219,7 @@ namespace FlaxEditor.Viewport.Previews _showCurrentLODButton.Icon = _showCurrentLOD ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; }); _showCurrentLODButton.IndexInParent = 2; + _showCurrentLODButton.CloseMenuOnClick = false; // Preview LODs mode widget var PreviewLODsMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); diff --git a/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs b/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs index 1e0c89528..fc7fcdbe1 100644 --- a/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs +++ b/Source/Editor/Viewport/Previews/ParticleSystemPreview.cs @@ -186,8 +186,11 @@ namespace FlaxEditor.Viewport.Previews if (!useWidgets) return; _showBoundsButton = ViewWidgetShowMenu.AddButton("Bounds", () => ShowBounds = !ShowBounds); + _showBoundsButton.CloseMenuOnClick = false; _showOriginButton = ViewWidgetShowMenu.AddButton("Origin", () => ShowOrigin = !ShowOrigin); + _showOriginButton.CloseMenuOnClick = false; _showParticleCounterButton = ViewWidgetShowMenu.AddButton("Particles Counter", () => ShowParticlesCounter = !ShowParticlesCounter); + _showParticleCounterButton.CloseMenuOnClick = false; // Play/Pause widget { diff --git a/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs index 08f8d8509..8bcc506d9 100644 --- a/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/SkinnedModelPreview.cs @@ -49,6 +49,7 @@ namespace FlaxEditor.Viewport.Previews _showCurrentLODButton.Icon = _showCurrentLOD ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; }); _showCurrentLODButton.IndexInParent = 2; + _showCurrentLODButton.CloseMenuOnClick = false; // PreviewLODS mode widget var PreviewLODSMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight); diff --git a/Source/Editor/Windows/Assets/JsonAssetWindow.cs b/Source/Editor/Windows/Assets/JsonAssetWindow.cs index e6606f35f..47ce09274 100644 --- a/Source/Editor/Windows/Assets/JsonAssetWindow.cs +++ b/Source/Editor/Windows/Assets/JsonAssetWindow.cs @@ -23,6 +23,7 @@ namespace FlaxEditor.Windows.Assets private readonly Undo _undo; private object _object; private bool _isRegisteredForScriptsReload; + private Label _typeText; /// /// Gets the instance of the Json asset object that is being edited. @@ -136,6 +137,22 @@ namespace FlaxEditor.Windows.Assets } } _presenter.Select(_object); + + if (_typeText != null) + _typeText.Dispose(); + var typeText = new ClickableLabel + { + Text = $"{Asset.DataTypeName}", + TooltipText = "Asset data type (full name)", + AnchorPreset = AnchorPresets.TopRight, + AutoWidth = true, + Parent = this, + }; + typeText.LocalX += -(typeText.Width + 4); + typeText.LocalY += (_toolstrip.Height - typeText.Height) * 0.5f; + typeText.RightClick = () => Clipboard.Text = Asset.DataTypeName; + _typeText = typeText; + _undo.Clear(); ClearEditedFlag(); @@ -175,6 +192,7 @@ namespace FlaxEditor.Windows.Assets _isRegisteredForScriptsReload = false; ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; } + _typeText = null; base.OnDestroy(); } diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 594692624..fac6ca225 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -247,8 +247,17 @@ namespace FlaxEditor.Windows for (int i = 0; i < _viewDropdown.Items.Count; i++) { var filterButton = filters.ContextMenu.AddButton(_viewDropdown.Items[i], OnFilterClicked); + filterButton.CloseMenuOnClick = false; filterButton.Tag = i; } + filters.ContextMenu.ButtonClicked += button => + { + foreach (var item in (filters.ContextMenu).Items) + { + if (item is ContextMenuButton filterButton) + filterButton.Checked = _viewDropdown.IsSelected(filterButton.Text); + } + }; filters.ContextMenu.VisibleChanged += control => { if (!control.Visible) @@ -340,6 +349,9 @@ namespace FlaxEditor.Windows /// The created renaming popup. public void Rename(ContentItem item) { + if (!item.CanRename) + return; + // Show element in the view Select(item, true); diff --git a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp index 698a6025a..f90c58875 100644 --- a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp +++ b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp @@ -5,6 +5,7 @@ #include "AudioBackendXAudio2.h" #include "Engine/Audio/AudioSettings.h" #include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Collections/ChunkedArray.h" #include "Engine/Core/Log.h" #include "Engine/Audio/Audio.h" #include "Engine/Audio/AudioSource.h" @@ -208,8 +209,8 @@ namespace XAudio2 bool ForceDirty = true; Listener Listeners[AUDIO_MAX_LISTENERS]; CriticalSection Locker; - Array Sources(32); // TODO: use ChunkedArray for better performance - Array Buffers(64); // TODO: use ChunkedArray for better performance or use buffers pool? + ChunkedArray Sources; + ChunkedArray Buffers; // TODO: use ChunkedArray for better performance or use buffers pool? EngineCallback Callback; Listener* GetListener() diff --git a/Source/Engine/Engine/Screen.cpp b/Source/Engine/Engine/Screen.cpp index 2fc58c7a3..2bc6e1f58 100644 --- a/Source/Engine/Engine/Screen.cpp +++ b/Source/Engine/Engine/Screen.cpp @@ -135,6 +135,52 @@ void Screen::SetCursorLock(CursorLockMode mode) CursorLock = mode; } +GameWindowMode Screen::GetGameWindowMode() +{ + GameWindowMode result = GameWindowMode::Windowed; +#if !USE_EDITOR + auto win = Engine::MainWindow; + if (win) + { + if (GetIsFullscreen() || win->IsFullscreen()) + result = GameWindowMode::Fullscreen; + else if (win->GetSettings().HasBorder) + result = GameWindowMode::Windowed; + else if (win->GetClientPosition().IsZero() && win->GetSize() == Platform::GetDesktopSize()) + result = GameWindowMode::FullscreenBorderless; + else + result = GameWindowMode::Borderless; + } +#endif + return result; +} + +void Screen::SetGameWindowMode(GameWindowMode windowMode) +{ +#if !USE_EDITOR + auto win = Engine::MainWindow; + if (!win) + return; + switch (windowMode) + { + case GameWindowMode::Windowed: + if (GetIsFullscreen()) + SetIsFullscreen(false); + win->SetBorderless(false, false); + break; + case GameWindowMode::Fullscreen: + SetIsFullscreen(true); + break; + case GameWindowMode::Borderless: + win->SetBorderless(true, false); + break; + case GameWindowMode::FullscreenBorderless: + win->SetBorderless(true, true); + break; + } +#endif +} + void ScreenService::Update() { #if USE_EDITOR diff --git a/Source/Engine/Engine/Screen.h b/Source/Engine/Engine/Screen.h index 532529980..42be20a38 100644 --- a/Source/Engine/Engine/Screen.h +++ b/Source/Engine/Engine/Screen.h @@ -2,6 +2,7 @@ #pragma once +#include "Engine/Core/Config/PlatformSettingsBase.h" #include "Engine/Scripting/ScriptingType.h" #include "Engine/Input/Enums.h" #include "Engine/Core/Math/Vector2.h" @@ -80,4 +81,19 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Screen); /// /// The mode. API_PROPERTY() static void SetCursorLock(CursorLockMode mode); + + /// + /// Gets the game window mode. + /// + /// The current window mode. + API_PROPERTY() static GameWindowMode GetGameWindowMode(); + + /// + /// Sets the game window mode. + /// + /// + /// A fullscreen mode switch may not happen immediately. It will be performed before next frame rendering. Will not work in editor. + /// + /// The window mode. + API_PROPERTY() static void SetGameWindowMode(GameWindowMode windowMode); }; diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp index 53453afe9..5ea8ad0a4 100644 --- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp @@ -675,20 +675,12 @@ bool Prefab::ApplyAll(Actor* targetActor) } } - // Setup default instances - for (int32 i = 0; i < allPrefabs.Count(); i++) + // Setup default instances (skip invalid prefabs) + for (int32 i = allPrefabs.Count() - 1; i >= 0; i--) { Prefab* prefab = allPrefabs[i]; - if (prefab->WaitForLoaded()) - { - LOG(Warning, "Waiting for nesting prefab asset load failed."); - return true; - } - if (prefab->GetDefaultInstance() == nullptr) - { - LOG(Warning, "Failed to create default prefab instance for the nested prefab asset."); - return true; - } + if (prefab->WaitForLoaded() || prefab->GetDefaultInstance() == nullptr) + allPrefabs.RemoveAt(i); } } diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 66b03479f..ada6a4bd3 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -1412,9 +1412,9 @@ void NetworkInternal::NetworkReplicatorUpdate() if (!CachedReplicationResult) CachedReplicationResult = New(); CachedReplicationResult->Init(); - if (!isClient && NetworkManager::Clients.IsEmpty()) + if ((!isClient && NetworkManager::Clients.IsEmpty()) || NetworkManager::NetworkFPS < -ZeroTolerance) { - // No need to update replication when nobody's around + // No need to update replication when nobody's around or when replication is disabled } else if (Hierarchy) { diff --git a/Source/Engine/Platform/Base/WindowBase.h b/Source/Engine/Platform/Base/WindowBase.h index 4220f1054..e4e7d2080 100644 --- a/Source/Engine/Platform/Base/WindowBase.h +++ b/Source/Engine/Platform/Base/WindowBase.h @@ -445,6 +445,15 @@ public: { } + /// + /// Sets the window to be borderless or not and to be fullscreen. + /// + /// Whether or not to have borders on window. + /// Whether or not to make the borderless window fullscreen (maximize to cover whole screen). + API_FUNCTION() virtual void SetBorderless(bool isBorderless, bool maximized = false) + { + } + /// /// Restores the window state before minimizing or maximizing. /// diff --git a/Source/Engine/Platform/Mac/MacWindow.cpp b/Source/Engine/Platform/Mac/MacWindow.cpp index aeee93e37..000accbfa 100644 --- a/Source/Engine/Platform/Mac/MacWindow.cpp +++ b/Source/Engine/Platform/Mac/MacWindow.cpp @@ -202,8 +202,7 @@ void GetDragDropData(const MacWindow* window, id sender, Float2& { NSRect frame = [(NSWindow*)window->GetNativePtr() frame]; NSPoint point = [sender draggingLocation]; - Float2 titleSize = GetWindowTitleSize(window); - mousePos = Float2(point.x, frame.size.height - point.y) - titleSize; + mousePos = Float2(point.x, frame.size.height - point.y) * MacPlatform::ScreenScale - GetWindowTitleSize(window); NSPasteboard* pasteboard = [sender draggingPasteboard]; if ([[pasteboard types] containsObject:NSPasteboardTypeString]) { diff --git a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp index 83f042730..38384f05f 100644 --- a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp +++ b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp @@ -316,7 +316,8 @@ bool WindowsFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const Strin if (SUCCEEDED(SHCreateItemFromParsingName(initialDirectory.Get(), NULL, IID_PPV_ARGS(&defaultFolder)))) fd->SetFolder(defaultFolder); - if (SUCCEEDED(fd->Show(parentWindow->GetHWND()))) + HWND hwndOwner = parentWindow ? parentWindow->GetHWND() : NULL; + if (SUCCEEDED(fd->Show(hwndOwner))) { ComPtr si; if (SUCCEEDED(fd->GetResult(&si))) diff --git a/Source/Engine/Platform/Windows/WindowsWindow.cpp b/Source/Engine/Platform/Windows/WindowsWindow.cpp index 687eeed28..d712102fa 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.cpp +++ b/Source/Engine/Platform/Windows/WindowsWindow.cpp @@ -260,6 +260,75 @@ void WindowsWindow::Maximize() _isDuringMaximize = false; } +void WindowsWindow::SetBorderless(bool isBorderless, bool maximized) +{ + ASSERT(HasHWND()); + + if (IsFullscreen()) + SetIsFullscreen(false); + + // Fixes issue of borderless window not going full screen + if (IsMaximized()) + Restore(); + + _settings.HasBorder = !isBorderless; + + BringToFront(); + + if (isBorderless) + { + LONG lStyle = GetWindowLong(_handle, GWL_STYLE); + lStyle &= ~(WS_THICKFRAME | WS_SYSMENU | WS_OVERLAPPED | WS_BORDER | WS_CAPTION); + lStyle |= WS_POPUP; + lStyle |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS; +#if WINDOWS_USE_NEW_BORDER_LESS + if (_settings.IsRegularWindow) + style |= WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_SYSMENU | WS_THICKFRAME | WS_GROUP; +#elif WINDOWS_USE_NEWER_BORDER_LESS + if (_settings.IsRegularWindow) + lStyle |= WS_THICKFRAME | WS_SYSMENU; +#endif + + SetWindowLong(_handle, GWL_STYLE, lStyle); + SetWindowPos(_handle, HWND_TOP, 0, 0,0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + + if (maximized) + { + ShowWindow(_handle, SW_SHOWMAXIMIZED); + } + else + { + ShowWindow(_handle, SW_SHOW); + } + } + else + { + LONG lStyle = GetWindowLong(_handle, GWL_STYLE); + lStyle &= ~(WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + if (_settings.AllowMaximize) + lStyle |= WS_MAXIMIZEBOX; + if (_settings.AllowMinimize) + lStyle |= WS_MINIMIZEBOX; + if (_settings.HasSizingFrame) + lStyle |= WS_THICKFRAME; + lStyle |= WS_OVERLAPPED | WS_SYSMENU | WS_BORDER | WS_CAPTION; + + SetWindowLong(_handle, GWL_STYLE, lStyle); + SetWindowPos(_handle, nullptr, 0, 0, (int)_settings.Size.X, (int)_settings.Size.Y, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + + if (maximized) + { + Maximize(); + } + else + { + ShowWindow(_handle, SW_SHOW); + } + } + + CheckForWindowResize(); +} + void WindowsWindow::Restore() { ASSERT(HasHWND()); diff --git a/Source/Engine/Platform/Windows/WindowsWindow.h b/Source/Engine/Platform/Windows/WindowsWindow.h index 74e923f86..e15f86a45 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.h +++ b/Source/Engine/Platform/Windows/WindowsWindow.h @@ -100,6 +100,7 @@ public: void Hide() override; void Minimize() override; void Maximize() override; + void SetBorderless(bool isBorderless, bool maximized = false) override; void Restore() override; bool IsClosed() const override; bool IsForegroundWindow() const override; diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index d98827ce1..92a8c09d3 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -1125,42 +1125,67 @@ bool TerrainPatch::InitializeHeightMap() float* TerrainPatch::GetHeightmapData() { + PROFILE_CPU_NAMED("Terrain.GetHeightmapData"); + if (_cachedHeightMap.HasItems()) return _cachedHeightMap.Get(); - PROFILE_CPU_NAMED("Terrain.GetHeightmapData"); - CacheHeightData(); return _cachedHeightMap.Get(); } +void TerrainPatch::ClearHeightmapCache() +{ + PROFILE_CPU_NAMED("Terrain.ClearHeightmapCache"); + _cachedHeightMap.Clear(); +} + byte* TerrainPatch::GetHolesMaskData() { + PROFILE_CPU_NAMED("Terrain.GetHolesMaskData"); + if (_cachedHolesMask.HasItems()) return _cachedHolesMask.Get(); - PROFILE_CPU_NAMED("Terrain.GetHolesMaskData"); - CacheHeightData(); return _cachedHolesMask.Get(); } +void TerrainPatch::ClearHolesMaskCache() +{ + PROFILE_CPU_NAMED("Terrain.ClearHolesMaskCache"); + _cachedHolesMask.Clear(); +} + Color32* TerrainPatch::GetSplatMapData(int32 index) { ASSERT(index >= 0 && index < TERRAIN_MAX_SPLATMAPS_COUNT); + PROFILE_CPU_NAMED("Terrain.GetSplatMapData"); + if (_cachedSplatMap[index].HasItems()) return _cachedSplatMap[index].Get(); - PROFILE_CPU_NAMED("Terrain.GetSplatMapData"); - CacheSplatData(); return _cachedSplatMap[index].Get(); } +void TerrainPatch::ClearSplatMapCache() +{ + PROFILE_CPU_NAMED("Terrain.ClearSplatMapCache"); + _cachedSplatMap->Clear(); +} + +void TerrainPatch::ClearCache() +{ + ClearHeightmapCache(); + ClearHolesMaskCache(); + ClearSplatMapCache(); +} + void TerrainPatch::CacheHeightData() { PROFILE_CPU_NAMED("Terrain.CacheHeightData"); diff --git a/Source/Engine/Terrain/TerrainPatch.h b/Source/Engine/Terrain/TerrainPatch.h index 44a768f90..689c629c5 100644 --- a/Source/Engine/Terrain/TerrainPatch.h +++ b/Source/Engine/Terrain/TerrainPatch.h @@ -229,12 +229,22 @@ public: /// The heightmap data. float* GetHeightmapData(); + /// + /// Clears cache of the heightmap data. + /// + void ClearHeightmapCache(); + /// /// Gets the raw pointer to the holes mask data. /// /// The holes mask data. byte* GetHolesMaskData(); + /// + /// Clears cache of the holes mask data. + /// + void ClearHolesMaskCache(); + /// /// Gets the raw pointer to the splat map data. /// @@ -242,6 +252,16 @@ public: /// The splat map data. Color32* GetSplatMapData(int32 index); + /// + /// Clears cache of the splat map data. + /// + void ClearSplatMapCache(); + + /// + /// Clears all caches. + /// + void ClearCache(); + /// /// Modifies the terrain patch heightmap with the given samples. /// diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 8ab6366eb..88ee0437e 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -80,21 +80,27 @@ namespace FlaxEngine.GUI public Color BackgroundColorSelected { get; set; } /// - /// Gets or sets the color of the border. + /// Gets or sets whether the button has a border. /// [EditorDisplay("Border Style"), EditorOrder(2010), ExpandGroups] + public bool HasBorder { get; set; } = true; + + /// + /// Gets or sets the color of the border. + /// + [EditorDisplay("Border Style"), EditorOrder(2011), ExpandGroups] public Color BorderColor { get; set; } /// /// Gets or sets the border color when button is highlighted. /// - [EditorDisplay("Border Style"), EditorOrder(2011)] + [EditorDisplay("Border Style"), EditorOrder(2012)] public Color BorderColorHighlighted { get; set; } /// /// Gets or sets the border color when button is selected. /// - [EditorDisplay("Border Style"), EditorOrder(2012)] + [EditorDisplay("Border Style"), EditorOrder(2013)] public Color BorderColorSelected { get; set; } /// @@ -245,7 +251,8 @@ namespace FlaxEngine.GUI BackgroundBrush.Draw(clientRect, backgroundColor); else Render2D.FillRectangle(clientRect, backgroundColor); - Render2D.DrawRectangle(clientRect, borderColor); + if (HasBorder) + Render2D.DrawRectangle(clientRect, borderColor); // Draw text Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center);