diff --git a/Source/Editor/Modules/SimulationModule.cs b/Source/Editor/Modules/SimulationModule.cs index a5b2f13ba..13a3a8fab 100644 --- a/Source/Editor/Modules/SimulationModule.cs +++ b/Source/Editor/Modules/SimulationModule.cs @@ -264,11 +264,14 @@ namespace FlaxEditor.Modules _enterPlayFocusedWindow = gameWin; // Show Game widow if hidden - if (gameWin != null && gameWin.FocusOnPlay) + if (gameWin != null) { - gameWin.FocusGameViewport(); + if (gameWin.FocusOnPlay) + gameWin.FocusGameViewport(); + gameWin.SetWindowMode(Editor.Options.Options.Interface.DefaultGameWindowMode); } + Editor.Log("[PlayMode] Enter"); } diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 3386d411c..a7735a37d 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -737,6 +737,17 @@ namespace FlaxEditor.Modules playActionGroup.SelectedChanged = SetPlayAction; Editor.Options.OptionsChanged += options => { playActionGroup.Selected = options.Interface.PlayButtonAction; }; + var windowModesGroup = new ContextMenuSingleSelectGroup(); + var windowTypeMenu = _toolStripPlay.ContextMenu.AddChildMenu("Game window mode"); + windowModesGroup.AddItem("Docked", InterfaceOptions.GameWindowMode.Docked, null, "Shows the game window docked, inside the editor"); + windowModesGroup.AddItem("Popup", InterfaceOptions.GameWindowMode.PopupWindow, null, "Shows the game window as a popup"); + windowModesGroup.AddItem("Maximized", InterfaceOptions.GameWindowMode.MaximizedWindow, null, "Shows the game window maximized (Same as pressing F11)"); + windowModesGroup.AddItem("Borderless", InterfaceOptions.GameWindowMode.BorderlessWindow, null, "Shows the game window borderless"); + windowModesGroup.AddItemsToContextMenu(windowTypeMenu.ContextMenu); + windowModesGroup.Selected = Editor.Options.Options.Interface.DefaultGameWindowMode; + windowModesGroup.SelectedChanged = SetGameWindowMode; + Editor.Options.OptionsChanged += options => { windowModesGroup.Selected = options.Interface.DefaultGameWindowMode; }; + _toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game ({inputOptions.Pause})"); _toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game"); @@ -1033,6 +1044,13 @@ namespace FlaxEditor.Modules Editor.Options.Apply(options); } + private void SetGameWindowMode(InterfaceOptions.GameWindowMode newGameWindowMode) + { + var options = Editor.Options.Options; + options.Interface.DefaultGameWindowMode = newGameWindowMode; + Editor.Options.Apply(options); + } + private void OnMainWindowClosing() { // Clear UI references (GUI cannot be used after window closing) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 95a273f19..95cf84682 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -90,6 +90,32 @@ namespace FlaxEditor.Options PlayScenes, } + /// + /// Available window modes for the game window. + /// + public enum GameWindowMode + { + /// + /// Shows the game window docked, inside the editor. + /// + Docked, + + /// + /// Shows the game window as a popup. + /// + PopupWindow, + + /// + /// Shows the game window maximized. (Same as pressing F11) + /// + MaximizedWindow, + + /// + /// Shows the game window borderless. + /// + BorderlessWindow, + } + /// /// 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. /// @@ -229,6 +255,13 @@ namespace FlaxEditor.Options [EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(410)] public PlayAction PlayButtonAction { get; set; } = PlayAction.PlayScenes; + /// + /// Gets or sets a value indicating how the game window should be displayed when the game is launched. + /// + [DefaultValue(GameWindowMode.Docked)] + [EditorDisplay("Play In-Editor", "Game Window Mode"), EditorOrder(420), Tooltip("Determines how the game window is displayed when the game is launched.")] + public GameWindowMode DefaultGameWindowMode { get; set; } = GameWindowMode.Docked; + /// /// Gets or sets a value indicating the number of game clients to launch when building and/or running cooked game. /// diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 4a8c11176..1d6bf4022 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -23,6 +23,7 @@ namespace FlaxEditor.Windows private bool _showGUI = true; private bool _showDebugDraw = false; private bool _isMaximized = false, _isUnlockingMouse = false; + private bool _isFloating = false, _isBorderless = false; private bool _cursorVisible = true; private float _gameStartTime; private GUI.Docking.DockState _maximizeRestoreDockState; @@ -68,7 +69,7 @@ namespace FlaxEditor.Windows } /// - /// Gets or sets a value indicating whether game window is maximized (only in play mode). + /// Gets or sets a value indicating whether the game window is maximized (only in play mode). /// private bool IsMaximized { @@ -78,20 +79,42 @@ namespace FlaxEditor.Windows if (_isMaximized == value) return; _isMaximized = value; + if (value) + { + IsFloating = true; + var rootWindow = RootWindow; + rootWindow.Maximize(); + } + else + { + IsFloating = false; + } + } + } + + /// + /// Gets or sets a value indicating whether the game window is floating (popup, only in play mode). + /// + private bool IsFloating + { + get => _isFloating; + set + { + if (_isFloating == value) + return; + _isFloating = value; var rootWindow = RootWindow; if (value) { - // Maximize _maximizeRestoreDockTo = _dockedTo; _maximizeRestoreDockState = _dockedTo.TryGetDockState(out _); if (_maximizeRestoreDockState != GUI.Docking.DockState.Float) { var monitorBounds = Platform.GetMonitorBounds(PointToScreen(Size * 0.5f)); - ShowFloating(monitorBounds.Location + new Float2(200, 200), Float2.Zero, WindowStartPosition.Manual); - rootWindow = RootWindow; + var size = DefaultSize; + var location = monitorBounds.Location + monitorBounds.Size * 0.5f - size * 0.5f; + ShowFloating(location, size, WindowStartPosition.Manual); } - if (rootWindow != null && !rootWindow.IsMaximized) - rootWindow.Maximize(); } else { @@ -105,6 +128,33 @@ namespace FlaxEditor.Windows } } + /// + /// Gets or sets a value indicating whether the game window is borderless (only in play mode). + /// + private bool IsBorderless + { + get => _isBorderless; + set + { + if (_isBorderless == value) + return; + _isBorderless = value; + if (value) + { + IsFloating = true; + var rootWindow = RootWindow; + var monitorBounds = Platform.GetMonitorBounds(rootWindow.RootWindow.Window.ClientPosition); + rootWindow.Window.Position = monitorBounds.Location; + rootWindow.Window.SetBorderless(true); + rootWindow.Window.ClientSize = monitorBounds.Size; + } + else + { + IsFloating = false; + } + } + } + /// /// Gets or sets a value indicating whether center mouse position on window focus in play mode. Helps when working with games that lock mouse cursor. /// @@ -464,7 +514,9 @@ namespace FlaxEditor.Windows /// public override void OnPlayEnd() { + IsFloating = false; IsMaximized = false; + IsBorderless = false; Cursor = CursorType.Default; } @@ -930,6 +982,29 @@ namespace FlaxEditor.Windows Focus(); } + /// + /// Apply the selected window mode to the game window. + /// + /// + public void SetWindowMode(InterfaceOptions.GameWindowMode mode) + { + switch (mode) + { + case InterfaceOptions.GameWindowMode.Docked: + break; + case InterfaceOptions.GameWindowMode.PopupWindow: + IsFloating = true; + break; + case InterfaceOptions.GameWindowMode.MaximizedWindow: + IsMaximized = true; + break; + case InterfaceOptions.GameWindowMode.BorderlessWindow: + IsBorderless = true; + break; + default: throw new ArgumentOutOfRangeException(nameof(mode), mode, null); + } + } + /// /// Takes the screenshot of the current viewport. ///