From 39f4c00135eb46232a051131b01ff23d14e3871f Mon Sep 17 00:00:00 2001 From: envision3d Date: Wed, 28 Jun 2023 02:02:10 -0500 Subject: [PATCH 1/3] add play button actions, number of players - add context menu support for toolstrip buttons - add "Play Game" vs. "Play Scenes" - add context menu for choosing play button action to toolstrip button - add number of game client selection for cook & run - add context menu for cook & run to toolstrip button - add menu items for the above - add editor option entries for saving user preferences for the above --- .../ContextMenuSingleSelectGroup.cs | 108 ++++++++++++ Source/Editor/GUI/ToolStrip.cs | 24 ++- Source/Editor/GUI/ToolStripButton.cs | 62 +++++-- Source/Editor/Modules/UIModule.cs | 140 +++++++++++++++- Source/Editor/Options/InterfaceOptions.cs | 38 ++++- Source/Editor/Windows/GameCookerWindow.cs | 157 ++++++++++-------- .../Editor/Windows/Profiler/ProfilerWindow.cs | 2 +- 7 files changed, 427 insertions(+), 104 deletions(-) create mode 100644 Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs new file mode 100644 index 000000000..794729547 --- /dev/null +++ b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs @@ -0,0 +1,108 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.GUI.ContextMenu +{ + /// + /// Context menu single select group. + /// + [HideInEditor] + class ContextMenuSingleSelectGroup + { + public struct SingleSelectGroupItem + { + public string text; + public U value; + public Action onSelected; + public List buttons; + } + + private List _menus = new List(); + private List> _items = new List>(); + public Action OnSelectionChanged; + + public SingleSelectGroupItem activeItem; + + public ContextMenuSingleSelectGroup AddItem(string text, T value, Action onSelected = null) + { + var item = new SingleSelectGroupItem + { + text = text, + value = value, + onSelected = onSelected, + buttons = new List() + }; + _items.Add(item); + + foreach (var contextMenu in _menus) + AddItemToContextMenu(contextMenu, item); + + return this; + } + + public ContextMenuSingleSelectGroup AddItemsToContextMenu(ContextMenu contextMenu) + { + _menus.Add(contextMenu); + + for (int i = 0; i < _items.Count; i++) + { + AddItemToContextMenu(contextMenu, _items[i]); + } + + return this; + } + + private void AddItemToContextMenu(ContextMenu contextMenu, SingleSelectGroupItem item) + { + var btn = contextMenu.AddButton(item.text, () => + { + SetItemAsActive(item); + }); + + item.buttons.Add(btn); + + if (item.Equals(activeItem)) + { + btn.Checked = true; + } + } + + private void DeselectAll() + { + foreach (var item in _items) + { + foreach (var btn in item.buttons) btn.Checked = false; + } + } + + public void SetItemAsActive(T value) + { + var index = _items.FindIndex(x => x.value.Equals(value)); + + if (index == -1) return; + + SetItemAsActive(_items[index]); + } + + private void SetItemAsActive(SingleSelectGroupItem item) + { + DeselectAll(); + + var index = _items.IndexOf(item); + OnSelectionChanged?.Invoke(item.value); + item.onSelected?.Invoke(); + + foreach (var btn in item.buttons) + { + btn.Checked = true; + } + + activeItem = item; + } + } +} diff --git a/Source/Editor/GUI/ToolStrip.cs b/Source/Editor/GUI/ToolStrip.cs index 6a881281f..858e21b51 100644 --- a/Source/Editor/GUI/ToolStrip.cs +++ b/Source/Editor/GUI/ToolStrip.cs @@ -23,9 +23,14 @@ namespace FlaxEditor.GUI public const int DefaultMarginH = 2; /// - /// Event fired when button gets clicked. + /// Event fired when button gets clicked with the primary mouse button. /// - public Action ButtonClicked; + public Action ButtonPrimaryClicked; + + /// + /// Event fired when button gets clicked with the secondary mouse button. + /// + public Action ButtonSecondaryClicked; /// /// Tries to get the last button. @@ -91,7 +96,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.Clicked += onClick; + button.PrimaryClicked += onClick; return button; } @@ -110,7 +115,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.Clicked += onClick; + button.PrimaryClicked += onClick; return button; } @@ -128,7 +133,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.Clicked += onClick; + button.PrimaryClicked += onClick; return button; } @@ -141,9 +146,14 @@ namespace FlaxEditor.GUI return AddChild(new ToolStripSeparator(ItemsHeight)); } - internal void OnButtonClicked(ToolStripButton button) + internal void OnButtonPrimaryClicked(ToolStripButton button) { - ButtonClicked?.Invoke(button); + ButtonPrimaryClicked?.Invoke(button); + } + + internal void OnButtonSecondaryClicked(ToolStripButton button) + { + ButtonSecondaryClicked?.Invoke(button); } /// diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index 09a26b940..f8d032696 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -11,8 +11,12 @@ namespace FlaxEditor.GUI /// /// [HideInEditor] - public class ToolStripButton : Control + public partial class ToolStripButton : Control { + // TODO: abstracted for potential future in-editor input configuration (ex. for left handed mouse users) + private const MouseButton PRIMARY_MOUSE_BUTTON = MouseButton.Left; + private const MouseButton SECONDARY_MOUSE_BUTTON = MouseButton.Right; + /// /// The default margin for button parts (icon, text, etc.). /// @@ -20,12 +24,18 @@ namespace FlaxEditor.GUI private SpriteHandle _icon; private string _text; - private bool _mouseDown; + private bool _primaryMouseDown; + private bool _secondaryMouseDown; /// /// Event fired when user clicks the button. /// - public Action Clicked; + public Action PrimaryClicked; + + /// + /// Event fired when user clicks the button. + /// + public Action SecondaryClicked; /// /// The checked state. @@ -63,6 +73,11 @@ namespace FlaxEditor.GUI } } + /// + /// A reference to a context menu to raise when the secondary mouse button is pressed. + /// + public ContextMenu.ContextMenu ContextMenu; + /// /// Initializes a new instance of the class. /// @@ -108,10 +123,11 @@ namespace FlaxEditor.GUI var iconRect = new Rectangle(DefaultMargin, DefaultMargin, iconSize, iconSize); var textRect = new Rectangle(DefaultMargin, 0, 0, Height); bool enabled = EnabledInHierarchy; + bool mouseButtonDown = _primaryMouseDown || _secondaryMouseDown; // Draw background if (enabled && (IsMouseOver || IsNavFocused || Checked)) - Render2D.FillRectangle(clientRect, Checked ? style.BackgroundSelected : _mouseDown ? style.BackgroundHighlighted : (style.LightBackground * 1.3f)); + Render2D.FillRectangle(clientRect, Checked ? style.BackgroundSelected : mouseButtonDown ? style.BackgroundHighlighted : (style.LightBackground * 1.3f)); // Draw icon if (_icon.IsValid) @@ -153,10 +169,18 @@ namespace FlaxEditor.GUI /// public override bool OnMouseDown(Float2 location, MouseButton button) { - if (button == MouseButton.Left) + if (button == PRIMARY_MOUSE_BUTTON) { // Set flag - _mouseDown = true; + _primaryMouseDown = true; + + Focus(); + return true; + } + else if (button == SECONDARY_MOUSE_BUTTON) + { + // Set flag + _secondaryMouseDown = true; Focus(); return true; @@ -168,16 +192,28 @@ namespace FlaxEditor.GUI /// public override bool OnMouseUp(Float2 location, MouseButton button) { - if (button == MouseButton.Left && _mouseDown) + if (button == PRIMARY_MOUSE_BUTTON && _primaryMouseDown) { // Clear flag - _mouseDown = false; + _primaryMouseDown = false; // Fire events if (AutoCheck) Checked = !Checked; - Clicked?.Invoke(); - (Parent as ToolStrip)?.OnButtonClicked(this); + PrimaryClicked?.Invoke(); + (Parent as ToolStrip)?.OnButtonPrimaryClicked(this); + + return true; + } + else if (button == SECONDARY_MOUSE_BUTTON && _secondaryMouseDown) + { + // Clear flag + _secondaryMouseDown = false; + + SecondaryClicked?.Invoke(); + (Parent as ToolStrip)?.OnButtonSecondaryClicked(this); + + ContextMenu?.Show(this, new Float2(0, Height)); return true; } @@ -189,7 +225,8 @@ namespace FlaxEditor.GUI public override void OnMouseLeave() { // Clear flag - _mouseDown = false; + _primaryMouseDown = false; + _secondaryMouseDown = false; base.OnMouseLeave(); } @@ -198,7 +235,8 @@ namespace FlaxEditor.GUI public override void OnLostFocus() { // Clear flag - _mouseDown = false; + _primaryMouseDown = false; + _secondaryMouseDown = false; base.OnLostFocus(); } diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 858ef2b1a..c41ccbec3 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -20,6 +20,8 @@ using FlaxEngine.GUI; using FlaxEngine.Json; using DockHintWindow = FlaxEditor.GUI.Docking.DockHintWindow; using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel; +using FlaxEditor.Content.Settings; +using static FlaxEditor.Options.InterfaceOptions; namespace FlaxEditor.Modules { @@ -36,6 +38,9 @@ namespace FlaxEditor.Modules private ContentStats _contentStats; private bool _progressFailed; + ContextMenuSingleSelectGroup _numberOfClientsGroup = new ContextMenuSingleSelectGroup(); + private Scene[] _scenesToReload; + private ContextMenuButton _menuFileSaveScenes; private ContextMenuButton _menuFileCloseScenes; private ContextMenuButton _menuFileGenerateScriptsProjectFiles; @@ -54,7 +59,9 @@ namespace FlaxEditor.Modules private ContextMenuButton _menuSceneAlignViewportWithActor; private ContextMenuButton _menuScenePilotActor; private ContextMenuButton _menuSceneCreateTerrain; - private ContextMenuButton _menuGamePlay; + private ContextMenuButton _menuGamePlayGame; + private ContextMenuButton _menuGamePlayCurrentScenes; + private ContextMenuButton _menuGameStop; private ContextMenuButton _menuGamePause; private ContextMenuButton _menuToolsBuildScenes; private ContextMenuButton _menuToolsBakeLightmaps; @@ -74,6 +81,7 @@ namespace FlaxEditor.Modules private ToolStripButton _toolStripRotate; private ToolStripButton _toolStripScale; private ToolStripButton _toolStripBuildScenes; + private ToolStripButton _toolStripCook; private ToolStripButton _toolStripPlay; private ToolStripButton _toolStripPause; private ToolStripButton _toolStripStep; @@ -351,6 +359,7 @@ namespace FlaxEditor.Modules // Update window background mainWindow.BackgroundColor = Style.Current.Background; + InitSharedMenus(); InitMainMenu(mainWindow); InitToolstrip(mainWindow); InitStatusBar(mainWindow); @@ -466,6 +475,19 @@ namespace FlaxEditor.Modules return dialog; } + private void InitSharedMenus() + { + for (int i = 1; i <= 4; i++) _numberOfClientsGroup.AddItem(i.ToString(), i); + + _numberOfClientsGroup.SetItemAsActive(Editor.Options.Options.Interface.NumberOfGameClientsToLaunch); + _numberOfClientsGroup.OnSelectionChanged = (value) => + { + var options = Editor.Options.Options; + options.Interface.NumberOfGameClientsToLaunch = value; + Editor.Options.Apply(options); + }; + } + private void InitMainMenu(RootControl mainWindow) { MainMenu = new MainMenu(mainWindow) @@ -523,11 +545,19 @@ namespace FlaxEditor.Modules MenuGame = MainMenu.AddButton("Game"); cm = MenuGame.ContextMenu; cm.VisibleChanged += OnMenuGameShowHide; - _menuGamePlay = cm.AddButton("Play", inputOptions.Play.ToString(), Editor.Simulation.RequestStartPlay); + + _menuGamePlayGame = cm.AddButton("Play Game", PlayGame); + _menuGamePlayCurrentScenes = cm.AddButton("Play Current Scenes", inputOptions.Play.ToString(), PlayScenes); + _menuGameStop = cm.AddButton("Stop Game", Editor.Simulation.RequestStopPlay); _menuGamePause = cm.AddButton("Pause", inputOptions.Pause.ToString(), Editor.Simulation.RequestPausePlay); + cm.AddSeparator(); - cm.AddButton("Cook & Run", Editor.Windows.GameCookerWin.BuildAndRun).LinkTooltip("Runs Game Cooker to build the game for this platform and runs the game after."); - cm.AddButton("Run cooked game", Editor.Windows.GameCookerWin.RunCooked).LinkTooltip("Runs the game build from the last cooking output. Use Cook&Play or Game Cooker first."); + var numberOfClientsMenu = cm.AddChildMenu("Number of game clients"); + _numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu); + + cm.AddSeparator(); + cm.AddButton("Cook & Run", CookAndRun).LinkTooltip("Runs Game Cooker to build the game for this platform and runs the game after."); + cm.AddButton("Run cooked game", RunCookedGame).LinkTooltip("Runs the game build from the last cooking output. Use Cook&Play or Game Cooker first."); // Tools MenuTools = MainMenu.AddButton("Tools"); @@ -603,7 +633,7 @@ namespace FlaxEditor.Modules _menuEditDuplicate.ShortKeys = inputOptions.Duplicate.ToString(); _menuEditSelectAll.ShortKeys = inputOptions.SelectAll.ToString(); _menuEditFind.ShortKeys = inputOptions.Search.ToString(); - _menuGamePlay.ShortKeys = inputOptions.Play.ToString(); + _menuGamePlayCurrentScenes.ShortKeys = inputOptions.Play.ToString(); _menuGamePause.ShortKeys = inputOptions.Pause.ToString(); MainMenuShortcutKeysUpdated?.Invoke(); @@ -611,6 +641,8 @@ namespace FlaxEditor.Modules private void InitToolstrip(RootControl mainWindow) { + var inputOptions = Editor.Options.Options.Input; + ToolStrip = new ToolStrip(34.0f, MainMenu.Bottom) { Parent = mainWindow, @@ -627,8 +659,34 @@ namespace FlaxEditor.Modules ToolStrip.AddSeparator(); _toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options (Ctrl+F10)"); ToolStrip.AddSeparator(); - _toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play64, Editor.Simulation.RequestPlayOrStopPlay).LinkTooltip("Start/Stop game (F5)"); - _toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip("Pause/Resume game(F6)"); + + // build + _toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.BuildSettings128, CookAndRun).LinkTooltip("Cook and run"); + _toolStripCook.ContextMenu = new ContextMenu(); + _toolStripCook.ContextMenu.AddButton("Run cooked game", RunCookedGame); + _toolStripCook.ContextMenu.AddSeparator(); + var numberOfClientsMenu = _toolStripCook.ContextMenu.AddChildMenu("Number of game clients"); + _numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu); + + // play + _toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play64, OnPlayPressed).LinkTooltip("Play Game"); + _toolStripPlay.ContextMenu = new ContextMenu(); + ContextMenuChildMenu playSubMenu; + + // play - button action + playSubMenu = _toolStripPlay.ContextMenu.AddChildMenu("Play button action"); + var playActionGroup = new ContextMenuSingleSelectGroup(); + playActionGroup.AddItem("Play Game", PlayAction.PlayGame); + playActionGroup.AddItem("Play Scenes", PlayAction.PlayScenes); + playActionGroup.AddItemsToContextMenu(playSubMenu.ContextMenu); + playActionGroup.SetItemAsActive(Editor.Options.Options.Interface.PlayButtonAction); + // important to add the handler after setting the initial item as active above or the editor will crash + playActionGroup.OnSelectionChanged = SetPlayAction; + // TODO: there are some holes in the syncing of these values + // - when changing in the editor, the options will be updated, but the options UI in-editor will not + // - when updating the value in the options UI, the value in the tool strip will not update (adding an options changed event handler results in a crash) + + _toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game({inputOptions.Pause.ToString()})"); _toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game"); UpdateToolstrip(); @@ -786,7 +844,9 @@ namespace FlaxEditor.Modules var isPlayMode = Editor.StateMachine.IsPlayMode; var canPlay = Level.IsAnySceneLoaded; - _menuGamePlay.Enabled = !isPlayMode && canPlay; + _menuGamePlayGame.Enabled = !isPlayMode && canPlay; + _menuGamePlayCurrentScenes.Enabled = !isPlayMode && canPlay; + _menuGameStop.Enabled = isPlayMode && canPlay; _menuGamePause.Enabled = isPlayMode && canPlay; c.PerformLayout(); @@ -968,6 +1028,70 @@ namespace FlaxEditor.Modules projectInfo.Save(); } + private void SetPlayAction(PlayAction newPlayAction) + { + var options = Editor.Options.Options; + options.Interface.PlayButtonAction = newPlayAction; + Editor.Options.Apply(options); + } + + private void OnPlayPressed() + { + switch (Editor.Options.Options.Interface.PlayButtonAction) + { + case PlayAction.PlayGame: + if (Editor.IsPlayMode) + Editor.Simulation.RequestStopPlay(); + else + PlayGame(); + return; + case PlayAction.PlayScenes: PlayScenes(); return; + } + } + + private void PlayGame() + { + var firstScene = GameSettings.Load().FirstScene; + + if (firstScene == Guid.Empty) + { + if (Level.IsAnySceneLoaded) Editor.Simulation.RequestStartPlay(); + return; + } + + _scenesToReload = Level.Scenes; + Level.UnloadAllScenes(); + Level.LoadScene(firstScene); + + Editor.PlayModeEnd += OnPlayGameSceneEnding; + Editor.Simulation.RequestPlayOrStopPlay(); + } + + private void OnPlayGameSceneEnding() + { + Editor.PlayModeEnd -= OnPlayGameSceneEnding; + + Level.UnloadAllScenes(); + + foreach (var scene in _scenesToReload) + Level.LoadScene(scene.ID); + } + + private void PlayScenes() + { + Editor.Simulation.RequestPlayOrStopPlay(); + } + + private void CookAndRun() + { + Editor.Windows.GameCookerWin.BuildAndRun(); + } + + private void RunCookedGame() + { + Editor.Windows.GameCookerWin.RunCooked(); + } + 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 acfb7f5e8..7d97fecd6 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -74,6 +74,21 @@ namespace FlaxEditor.Options DockBottom = DockState.DockBottom } + /// + /// Options for the action taken by the play button. + /// + public enum PlayAction + { + /// + /// Launches the game from the First Scene defined in the project settings. + /// + PlayGame, + /// + /// Launches the game using the scenes currently loaded in the editor. + /// + PlayScenes + } + /// /// 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. /// @@ -196,30 +211,45 @@ namespace FlaxEditor.Options [EditorDisplay("Play In-Editor", "Focus Game Window On Play"), EditorOrder(400), Tooltip("Determines whether auto-focus game window on play mode start.")] public bool FocusGameWinOnPlay { get; set; } = true; + /// + /// Gets or sets a value indicating what action should be taken upon pressing the play button. + /// + [DefaultValue(PlayAction.PlayScenes)] + [EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(410), Tooltip("Determines the action taken when the play button is pressed.")] + public PlayAction PlayButtonAction { get; set; } = PlayAction.PlayScenes; + + /// + /// Gets or sets a value indicating the number of game clients to launch when building and/or running cooked game. + /// + [DefaultValue(1)] + [EditorDisplay("Cook & Run", "Number Of GameClients To Launch"), EditorOrder(500), Tooltip("Determines the number of game clients to launch when building and/or running cooked game.")] + [Range(1, 4)] + public int NumberOfGameClientsToLaunch = 1; + private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); /// /// Gets or sets the title font for editor UI. /// - [EditorDisplay("Fonts"), EditorOrder(500), Tooltip("The title font for editor UI.")] + [EditorDisplay("Fonts"), EditorOrder(600), Tooltip("The title font for editor UI.")] public FontReference TitleFont { get; set; } = new FontReference(DefaultFont, 18); /// /// Gets or sets the large font for editor UI. /// - [EditorDisplay("Fonts"), EditorOrder(510), Tooltip("The large font for editor UI.")] + [EditorDisplay("Fonts"), EditorOrder(610), Tooltip("The large font for editor UI.")] public FontReference LargeFont { get; set; } = new FontReference(DefaultFont, 14); /// /// Gets or sets the medium font for editor UI. /// - [EditorDisplay("Fonts"), EditorOrder(520), Tooltip("The medium font for editor UI.")] + [EditorDisplay("Fonts"), EditorOrder(620), Tooltip("The medium font for editor UI.")] public FontReference MediumFont { get; set; } = new FontReference(DefaultFont, 9); /// /// Gets or sets the small font for editor UI. /// - [EditorDisplay("Fonts"), EditorOrder(530), Tooltip("The small font for editor UI.")] + [EditorDisplay("Fonts"), EditorOrder(630), Tooltip("The small font for editor UI.")] public FontReference SmallFont { get; set; } = new FontReference(DefaultFont, 9); } } diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index f3fd753d1..f88fa36f5 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -110,14 +110,14 @@ namespace FlaxEditor.Windows #if PLATFORM_WINDOWS switch (BuildPlatform) { - case BuildPlatform.MacOSx64: - case BuildPlatform.MacOSARM64: - case BuildPlatform.iOSARM64: - IsSupported = false; - break; - default: - IsSupported = true; - break; + case BuildPlatform.MacOSx64: + case BuildPlatform.MacOSARM64: + case BuildPlatform.iOSARM64: + IsSupported = false; + break; + default: + IsSupported = true; + break; } #elif PLATFORM_LINUX switch (BuildPlatform) @@ -160,17 +160,17 @@ namespace FlaxEditor.Windows { switch (BuildPlatform) { - case BuildPlatform.Windows32: - case BuildPlatform.Windows64: - case BuildPlatform.UWPx86: - case BuildPlatform.UWPx64: - case BuildPlatform.LinuxX64: - case BuildPlatform.AndroidARM64: - layout.Label("Use Flax Launcher and download the required package.", TextAlignment.Center); - break; - default: - layout.Label("Engine source is required to target this platform.", TextAlignment.Center); - break; + case BuildPlatform.Windows32: + case BuildPlatform.Windows64: + case BuildPlatform.UWPx86: + case BuildPlatform.UWPx64: + case BuildPlatform.LinuxX64: + case BuildPlatform.AndroidARM64: + layout.Label("Use Flax Launcher and download the required package.", TextAlignment.Center); + break; + default: + layout.Label("Engine source is required to target this platform.", TextAlignment.Center); + break; } } else @@ -272,43 +272,43 @@ namespace FlaxEditor.Windows string name; switch (_platform) { - case PlatformType.Windows: - name = "Windows"; - break; - case PlatformType.XboxOne: - name = "Xbox One"; - break; - case PlatformType.UWP: - name = "Windows Store"; - layout.Label("UWP (Windows Store) platform has been deprecated and is no longer supported", TextAlignment.Center).Label.TextColor = Color.Red; - break; - case PlatformType.Linux: - name = "Linux"; - break; - case PlatformType.PS4: - name = "PlayStation 4"; - break; - case PlatformType.XboxScarlett: - name = "Xbox Scarlett"; - break; - case PlatformType.Android: - name = "Android"; - break; - case PlatformType.Switch: - name = "Switch"; - break; - case PlatformType.PS5: - name = "PlayStation 5"; - break; - case PlatformType.Mac: - name = "Mac"; - break; - case PlatformType.iOS: - name = "iOS"; - break; - default: - name = Utilities.Utils.GetPropertyNameUI(_platform.ToString()); - break; + case PlatformType.Windows: + name = "Windows"; + break; + case PlatformType.XboxOne: + name = "Xbox One"; + break; + case PlatformType.UWP: + name = "Windows Store"; + layout.Label("UWP (Windows Store) platform has been deprecated and is no longer supported", TextAlignment.Center).Label.TextColor = Color.Red; + break; + case PlatformType.Linux: + name = "Linux"; + break; + case PlatformType.PS4: + name = "PlayStation 4"; + break; + case PlatformType.XboxScarlett: + name = "Xbox Scarlett"; + break; + case PlatformType.Android: + name = "Android"; + break; + case PlatformType.Switch: + name = "Switch"; + break; + case PlatformType.PS5: + name = "PlayStation 5"; + break; + case PlatformType.Mac: + name = "Mac"; + break; + case PlatformType.iOS: + name = "iOS"; + break; + default: + name = Utilities.Utils.GetPropertyNameUI(_platform.ToString()); + break; } var group = layout.Group(name); @@ -659,16 +659,24 @@ namespace FlaxEditor.Windows { Editor.Log("Building and running"); GameCooker.GetCurrentPlatform(out var platform, out var buildPlatform, out var buildConfiguration); - _buildingQueue.Enqueue(new QueueItem + var numberOfClients = Editor.Options.Options.Interface.NumberOfGameClientsToLaunch; + + for (int i = 0; i < numberOfClients; i++) { - Target = new BuildTarget + var buildOptions = BuildOptions.AutoRun; + if (i > 0) buildOptions |= BuildOptions.NoCook; + + _buildingQueue.Enqueue(new QueueItem { - Output = _buildTabProxy.PerPlatformOptions[platform].Output, - Platform = buildPlatform, - Mode = buildConfiguration, - }, - Options = BuildOptions.AutoRun, - }); + Target = new BuildTarget + { + Output = _buildTabProxy.PerPlatformOptions[platform].Output, + Platform = buildPlatform, + Mode = buildConfiguration, + }, + Options = buildOptions, + }); + } } /// @@ -678,16 +686,21 @@ namespace FlaxEditor.Windows { Editor.Log("Running cooked build"); GameCooker.GetCurrentPlatform(out var platform, out var buildPlatform, out var buildConfiguration); - _buildingQueue.Enqueue(new QueueItem + var numberOfClients = Editor.Options.Options.Interface.NumberOfGameClientsToLaunch; + + for (int i = 0; i < numberOfClients; i++) { - Target = new BuildTarget + _buildingQueue.Enqueue(new QueueItem { - Output = _buildTabProxy.PerPlatformOptions[platform].Output, - Platform = buildPlatform, - Mode = buildConfiguration, - }, - Options = BuildOptions.AutoRun | BuildOptions.NoCook, - }); + Target = new BuildTarget + { + Output = _buildTabProxy.PerPlatformOptions[platform].Output, + Platform = buildPlatform, + Mode = buildConfiguration, + }, + Options = BuildOptions.AutoRun | BuildOptions.NoCook, + }); + } } private void BuildTarget() diff --git a/Source/Editor/Windows/Profiler/ProfilerWindow.cs b/Source/Editor/Windows/Profiler/ProfilerWindow.cs index f5a5c6f86..b35641bf6 100644 --- a/Source/Editor/Windows/Profiler/ProfilerWindow.cs +++ b/Source/Editor/Windows/Profiler/ProfilerWindow.cs @@ -93,7 +93,7 @@ namespace FlaxEditor.Windows.Profiler _liveRecordingButton = toolstrip.AddButton(editor.Icons.Play64); _liveRecordingButton.LinkTooltip("Live profiling events recording"); _liveRecordingButton.AutoCheck = true; - _liveRecordingButton.Clicked += () => _liveRecordingButton.Icon = LiveRecording ? editor.Icons.Stop64 : editor.Icons.Play64; + _liveRecordingButton.PrimaryClicked += () => _liveRecordingButton.Icon = LiveRecording ? editor.Icons.Stop64 : editor.Icons.Play64; _clearButton = toolstrip.AddButton(editor.Icons.Rotate32, Clear); _clearButton.LinkTooltip("Clear data"); toolstrip.AddSeparator(); From 940b0e02e5d8410b3e6db7f39dfc40ce2faecdc7 Mon Sep 17 00:00:00 2001 From: envision3d Date: Wed, 28 Jun 2023 04:08:36 -0500 Subject: [PATCH 2/3] improve state syncing of context menus and editor options --- .../ContextMenuSingleSelectGroup.cs | 3 +-- Source/Editor/Modules/UIModule.cs | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs index 794729547..9b58c09a8 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs @@ -92,6 +92,7 @@ namespace FlaxEditor.GUI.ContextMenu private void SetItemAsActive(SingleSelectGroupItem item) { DeselectAll(); + activeItem = item; var index = _items.IndexOf(item); OnSelectionChanged?.Invoke(item.value); @@ -101,8 +102,6 @@ namespace FlaxEditor.GUI.ContextMenu { btn.Checked = true; } - - activeItem = item; } } } diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index c41ccbec3..5e0af20fe 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -486,6 +486,14 @@ namespace FlaxEditor.Modules options.Interface.NumberOfGameClientsToLaunch = value; Editor.Options.Apply(options); }; + + Editor.Options.OptionsChanged += (options) => + { + if (options.Interface.NumberOfGameClientsToLaunch != _numberOfClientsGroup.activeItem.value) + { + _numberOfClientsGroup.SetItemAsActive(options.Interface.NumberOfGameClientsToLaunch); + } + }; } private void InitMainMenu(RootControl mainWindow) @@ -682,9 +690,16 @@ namespace FlaxEditor.Modules playActionGroup.SetItemAsActive(Editor.Options.Options.Interface.PlayButtonAction); // important to add the handler after setting the initial item as active above or the editor will crash playActionGroup.OnSelectionChanged = SetPlayAction; - // TODO: there are some holes in the syncing of these values + // TODO: there is a hole in the syncing of these values: // - when changing in the editor, the options will be updated, but the options UI in-editor will not - // - when updating the value in the options UI, the value in the tool strip will not update (adding an options changed event handler results in a crash) + + Editor.Options.OptionsChanged += (options) => + { + if (options.Interface.PlayButtonAction != playActionGroup.activeItem.value) + { + playActionGroup.SetItemAsActive(options.Interface.PlayButtonAction); + } + }; _toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game({inputOptions.Pause.ToString()})"); _toolStripStep = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Skip64, Editor.Simulation.RequestPlayOneFrame).LinkTooltip("Step one frame in game"); From 81860e5f189de65f0ca23e954fdf22dee83f40b0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 31 Jul 2023 19:02:53 +0200 Subject: [PATCH 3/3] Cleanup code #1215 and add cook game icon --- Content/Editor/IconsAtlas.flax | 4 +- Source/Editor/EditorIcons.cs | 1 + .../ContextMenuSingleSelectGroup.cs | 104 ++++++-------- Source/Editor/GUI/ToolStrip.cs | 18 +-- Source/Editor/GUI/ToolStripButton.cs | 43 ++---- Source/Editor/Modules/UIModule.cs | 93 ++++++------- Source/Editor/Options/InterfaceOptions.cs | 12 +- Source/Editor/Windows/GameCookerWindow.cs | 130 ++++++++++-------- .../Editor/Windows/Profiler/ProfilerWindow.cs | 2 +- 9 files changed, 186 insertions(+), 221 deletions(-) diff --git a/Content/Editor/IconsAtlas.flax b/Content/Editor/IconsAtlas.flax index 3318b811b..ae46fcf06 100644 --- a/Content/Editor/IconsAtlas.flax +++ b/Content/Editor/IconsAtlas.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d867a994701f09964335ddf51cd4060a54728b9de4420ad3f0bfbd8847bc7602 -size 5611659 +oid sha256:745ce418c493445abde98aea91a34592b00c5c46b68cd7e61604a05b3043c76c +size 5608587 diff --git a/Source/Editor/EditorIcons.cs b/Source/Editor/EditorIcons.cs index 127ba3f6f..fb3c46a41 100644 --- a/Source/Editor/EditorIcons.cs +++ b/Source/Editor/EditorIcons.cs @@ -96,6 +96,7 @@ namespace FlaxEditor public SpriteHandle Link64; public SpriteHandle Build64; public SpriteHandle Add64; + public SpriteHandle ShipIt64; // 96px public SpriteHandle Toolbox96; diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs index 9b58c09a8..5abb52b4a 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuSingleSelectGroup.cs @@ -2,106 +2,92 @@ using System; using System.Collections.Generic; -using System.Linq; using FlaxEngine; -using FlaxEngine.GUI; namespace FlaxEditor.GUI.ContextMenu { /// - /// Context menu single select group. + /// Context menu for a single selectable option from range of values (eg. enum). /// [HideInEditor] class ContextMenuSingleSelectGroup { - public struct SingleSelectGroupItem + private struct SingleSelectGroupItem { - public string text; - public U value; - public Action onSelected; - public List buttons; + public string Text; + public string Tooltip; + public T Value; + public Action Selected; + public List Buttons; } private List _menus = new List(); - private List> _items = new List>(); - public Action OnSelectionChanged; + private List _items = new List(); + private SingleSelectGroupItem _selectedItem; - public SingleSelectGroupItem activeItem; - - public ContextMenuSingleSelectGroup AddItem(string text, T value, Action onSelected = null) + public T Selected { - var item = new SingleSelectGroupItem + get => _selectedItem.Value; + set { - text = text, - value = value, - onSelected = onSelected, - buttons = new List() + var index = _items.FindIndex(x => x.Value.Equals(value)); + if (index != -1 && !_selectedItem.Value.Equals(value)) + { + SetSelected(_items[index]); + } + } + } + + public Action SelectedChanged; + + public ContextMenuSingleSelectGroup AddItem(string text, T value, Action selected = null, string tooltip = null) + { + var item = new SingleSelectGroupItem + { + Text = text, + Tooltip = tooltip, + Value = value, + Selected = selected, + Buttons = new List() }; _items.Add(item); - foreach (var contextMenu in _menus) AddItemToContextMenu(contextMenu, item); - return this; } public ContextMenuSingleSelectGroup AddItemsToContextMenu(ContextMenu contextMenu) { _menus.Add(contextMenu); - for (int i = 0; i < _items.Count; i++) - { AddItemToContextMenu(contextMenu, _items[i]); - } - return this; } - private void AddItemToContextMenu(ContextMenu contextMenu, SingleSelectGroupItem item) + private void AddItemToContextMenu(ContextMenu contextMenu, SingleSelectGroupItem item) { - var btn = contextMenu.AddButton(item.text, () => - { - SetItemAsActive(item); - }); - - item.buttons.Add(btn); - - if (item.Equals(activeItem)) - { + var btn = contextMenu.AddButton(item.Text, () => { SetSelected(item); }); + if (item.Tooltip != null) + btn.TooltipText = item.Tooltip; + item.Buttons.Add(btn); + if (item.Equals(_selectedItem)) btn.Checked = true; - } } - private void DeselectAll() + private void SetSelected(SingleSelectGroupItem item) { - foreach (var item in _items) + foreach (var e in _items) { - foreach (var btn in item.buttons) btn.Checked = false; + foreach (var btn in e.Buttons) + btn.Checked = false; } - } + _selectedItem = item; - public void SetItemAsActive(T value) - { - var index = _items.FindIndex(x => x.value.Equals(value)); + SelectedChanged?.Invoke(item.Value); + item.Selected?.Invoke(); - if (index == -1) return; - - SetItemAsActive(_items[index]); - } - - private void SetItemAsActive(SingleSelectGroupItem item) - { - DeselectAll(); - activeItem = item; - - var index = _items.IndexOf(item); - OnSelectionChanged?.Invoke(item.value); - item.onSelected?.Invoke(); - - foreach (var btn in item.buttons) - { + foreach (var btn in item.Buttons) btn.Checked = true; - } } } } diff --git a/Source/Editor/GUI/ToolStrip.cs b/Source/Editor/GUI/ToolStrip.cs index 858e21b51..0dac241ed 100644 --- a/Source/Editor/GUI/ToolStrip.cs +++ b/Source/Editor/GUI/ToolStrip.cs @@ -25,12 +25,12 @@ namespace FlaxEditor.GUI /// /// Event fired when button gets clicked with the primary mouse button. /// - public Action ButtonPrimaryClicked; + public Action ButtonClicked; /// /// Event fired when button gets clicked with the secondary mouse button. /// - public Action ButtonSecondaryClicked; + public Action SecondaryButtonClicked; /// /// Tries to get the last button. @@ -96,7 +96,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.PrimaryClicked += onClick; + button.Clicked += onClick; return button; } @@ -115,7 +115,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.PrimaryClicked += onClick; + button.Clicked += onClick; return button; } @@ -133,7 +133,7 @@ namespace FlaxEditor.GUI Parent = this, }; if (onClick != null) - button.PrimaryClicked += onClick; + button.Clicked += onClick; return button; } @@ -146,14 +146,14 @@ namespace FlaxEditor.GUI return AddChild(new ToolStripSeparator(ItemsHeight)); } - internal void OnButtonPrimaryClicked(ToolStripButton button) + internal void OnButtonClicked(ToolStripButton button) { - ButtonPrimaryClicked?.Invoke(button); + ButtonClicked?.Invoke(button); } - internal void OnButtonSecondaryClicked(ToolStripButton button) + internal void OnSecondaryButtonClicked(ToolStripButton button) { - ButtonSecondaryClicked?.Invoke(button); + SecondaryButtonClicked?.Invoke(button); } /// diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index f8d032696..e839a7356 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -11,12 +11,8 @@ namespace FlaxEditor.GUI /// /// [HideInEditor] - public partial class ToolStripButton : Control + public class ToolStripButton : Control { - // TODO: abstracted for potential future in-editor input configuration (ex. for left handed mouse users) - private const MouseButton PRIMARY_MOUSE_BUTTON = MouseButton.Left; - private const MouseButton SECONDARY_MOUSE_BUTTON = MouseButton.Right; - /// /// The default margin for button parts (icon, text, etc.). /// @@ -30,7 +26,7 @@ namespace FlaxEditor.GUI /// /// Event fired when user clicks the button. /// - public Action PrimaryClicked; + public Action Clicked; /// /// Event fired when user clicks the button. @@ -140,13 +136,7 @@ namespace FlaxEditor.GUI if (!string.IsNullOrEmpty(_text)) { textRect.Size.X = Width - DefaultMargin - textRect.Left; - Render2D.DrawText( - style.FontMedium, - _text, - textRect, - enabled ? style.Foreground : style.ForegroundDisabled, - TextAlignment.Near, - TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _text, textRect, enabled ? style.Foreground : style.ForegroundDisabled, TextAlignment.Near, TextAlignment.Center); } } @@ -169,19 +159,15 @@ namespace FlaxEditor.GUI /// public override bool OnMouseDown(Float2 location, MouseButton button) { - if (button == PRIMARY_MOUSE_BUTTON) + if (button == MouseButton.Left) { - // Set flag _primaryMouseDown = true; - Focus(); return true; } - else if (button == SECONDARY_MOUSE_BUTTON) + if (button == MouseButton.Right) { - // Set flag _secondaryMouseDown = true; - Focus(); return true; } @@ -192,29 +178,22 @@ namespace FlaxEditor.GUI /// public override bool OnMouseUp(Float2 location, MouseButton button) { - if (button == PRIMARY_MOUSE_BUTTON && _primaryMouseDown) + if (button == MouseButton.Left && _primaryMouseDown) { - // Clear flag _primaryMouseDown = false; - - // Fire events if (AutoCheck) Checked = !Checked; - PrimaryClicked?.Invoke(); - (Parent as ToolStrip)?.OnButtonPrimaryClicked(this); + Clicked?.Invoke(); + (Parent as ToolStrip)?.OnButtonClicked(this); return true; } - else if (button == SECONDARY_MOUSE_BUTTON && _secondaryMouseDown) + if (button == MouseButton.Right && _secondaryMouseDown) { - // Clear flag _secondaryMouseDown = false; - SecondaryClicked?.Invoke(); - (Parent as ToolStrip)?.OnButtonSecondaryClicked(this); - + (Parent as ToolStrip)?.OnSecondaryButtonClicked(this); ContextMenu?.Show(this, new Float2(0, Height)); - return true; } @@ -224,7 +203,6 @@ namespace FlaxEditor.GUI /// public override void OnMouseLeave() { - // Clear flag _primaryMouseDown = false; _secondaryMouseDown = false; @@ -234,7 +212,6 @@ namespace FlaxEditor.GUI /// public override void OnLostFocus() { - // Clear flag _primaryMouseDown = false; _secondaryMouseDown = false; diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 5e0af20fe..38c61942a 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -21,7 +21,7 @@ using FlaxEngine.Json; using DockHintWindow = FlaxEditor.GUI.Docking.DockHintWindow; using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel; using FlaxEditor.Content.Settings; -using static FlaxEditor.Options.InterfaceOptions; +using FlaxEditor.Options; namespace FlaxEditor.Modules { @@ -203,6 +203,7 @@ namespace FlaxEditor.Modules _toolStripScale.Checked = gizmoMode == TransformGizmoBase.Mode.Scale; // _toolStripBuildScenes.Enabled = (canEditScene && !isPlayMode) || Editor.StateMachine.BuildingScenesState.IsActive; + _toolStripCook.Enabled = Editor.Windows.GameCookerWin.CanBuild(Platform.PlatformType) && !GameCooker.IsRunning; // var play = _toolStripPlay; var pause = _toolStripPause; @@ -418,6 +419,7 @@ namespace FlaxEditor.Modules Editor.Undo.UndoDone += OnUndoEvent; Editor.Undo.RedoDone += OnUndoEvent; Editor.Undo.ActionDone += OnUndoEvent; + GameCooker.Event += OnGameCookerEvent; UpdateToolstrip(); } @@ -433,6 +435,11 @@ namespace FlaxEditor.Modules UpdateStatusBar(); } + private void OnGameCookerEvent(GameCooker.EventType type) + { + UpdateToolstrip(); + } + /// public override void OnExit() { @@ -477,23 +484,18 @@ namespace FlaxEditor.Modules private void InitSharedMenus() { - for (int i = 1; i <= 4; i++) _numberOfClientsGroup.AddItem(i.ToString(), i); + for (int i = 1; i <= 4; i++) + _numberOfClientsGroup.AddItem(i.ToString(), i); - _numberOfClientsGroup.SetItemAsActive(Editor.Options.Options.Interface.NumberOfGameClientsToLaunch); - _numberOfClientsGroup.OnSelectionChanged = (value) => + _numberOfClientsGroup.Selected = Editor.Options.Options.Interface.NumberOfGameClientsToLaunch; + _numberOfClientsGroup.SelectedChanged = value => { var options = Editor.Options.Options; options.Interface.NumberOfGameClientsToLaunch = value; Editor.Options.Apply(options); }; - Editor.Options.OptionsChanged += (options) => - { - if (options.Interface.NumberOfGameClientsToLaunch != _numberOfClientsGroup.activeItem.value) - { - _numberOfClientsGroup.SetItemAsActive(options.Interface.NumberOfGameClientsToLaunch); - } - }; + Editor.Options.OptionsChanged += options => { _numberOfClientsGroup.Selected = options.Interface.NumberOfGameClientsToLaunch; }; } private void InitMainMenu(RootControl mainWindow) @@ -665,43 +667,33 @@ namespace FlaxEditor.Modules _toolStripRotate = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Rotate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip("Change Gizmo tool mode to Rotate (2)"); _toolStripScale = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip("Change Gizmo tool mode to Scale (3)"); ToolStrip.AddSeparator(); - _toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options (Ctrl+F10)"); - ToolStrip.AddSeparator(); - // build - _toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.BuildSettings128, CookAndRun).LinkTooltip("Cook and run"); + // Cook scenes + _toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build64, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options (Ctrl+F10)"); + + // Cook and run + _toolStripCook = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.ShipIt64, CookAndRun).LinkTooltip("Cook & Run - build game for the current platform and run it locally"); _toolStripCook.ContextMenu = new ContextMenu(); _toolStripCook.ContextMenu.AddButton("Run cooked game", RunCookedGame); _toolStripCook.ContextMenu.AddSeparator(); var numberOfClientsMenu = _toolStripCook.ContextMenu.AddChildMenu("Number of game clients"); _numberOfClientsGroup.AddItemsToContextMenu(numberOfClientsMenu.ContextMenu); - // play + ToolStrip.AddSeparator(); + + // Play _toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play64, OnPlayPressed).LinkTooltip("Play Game"); _toolStripPlay.ContextMenu = new ContextMenu(); - ContextMenuChildMenu playSubMenu; - - // play - button action - playSubMenu = _toolStripPlay.ContextMenu.AddChildMenu("Play button action"); - var playActionGroup = new ContextMenuSingleSelectGroup(); - playActionGroup.AddItem("Play Game", PlayAction.PlayGame); - playActionGroup.AddItem("Play Scenes", PlayAction.PlayScenes); + var playSubMenu = _toolStripPlay.ContextMenu.AddChildMenu("Play button action"); + var playActionGroup = new ContextMenuSingleSelectGroup(); + playActionGroup.AddItem("Play Game", InterfaceOptions.PlayAction.PlayGame, null, "Launches the game from the First Scene defined in the project settings."); + playActionGroup.AddItem("Play Scenes", InterfaceOptions.PlayAction.PlayScenes, null, "Launches the game using the scenes currently loaded in the editor."); playActionGroup.AddItemsToContextMenu(playSubMenu.ContextMenu); - playActionGroup.SetItemAsActive(Editor.Options.Options.Interface.PlayButtonAction); - // important to add the handler after setting the initial item as active above or the editor will crash - playActionGroup.OnSelectionChanged = SetPlayAction; - // TODO: there is a hole in the syncing of these values: - // - when changing in the editor, the options will be updated, but the options UI in-editor will not + playActionGroup.Selected = Editor.Options.Options.Interface.PlayButtonAction; + playActionGroup.SelectedChanged = SetPlayAction; + Editor.Options.OptionsChanged += options => { playActionGroup.Selected = options.Interface.PlayButtonAction; }; - Editor.Options.OptionsChanged += (options) => - { - if (options.Interface.PlayButtonAction != playActionGroup.activeItem.value) - { - playActionGroup.SetItemAsActive(options.Interface.PlayButtonAction); - } - }; - - _toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause64, Editor.Simulation.RequestResumeOrPause).LinkTooltip($"Pause/Resume game({inputOptions.Pause.ToString()})"); + _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"); UpdateToolstrip(); @@ -736,10 +728,7 @@ namespace FlaxEditor.Modules var defaultTextColor = StatusBar.TextColor; _outputLogButton.HoverBegin += () => StatusBar.TextColor = Style.Current.BackgroundSelected; _outputLogButton.HoverEnd += () => StatusBar.TextColor = defaultTextColor; - _outputLogButton.Clicked += () => - { - Editor.Windows.OutputLogWin.FocusOrShow(); - }; + _outputLogButton.Clicked += () => { Editor.Windows.OutputLogWin.FocusOrShow(); }; // Progress bar with label const float progressBarWidth = 120.0f; @@ -1043,7 +1032,7 @@ namespace FlaxEditor.Modules projectInfo.Save(); } - private void SetPlayAction(PlayAction newPlayAction) + private void SetPlayAction(InterfaceOptions.PlayAction newPlayAction) { var options = Editor.Options.Options; options.Interface.PlayButtonAction = newPlayAction; @@ -1054,23 +1043,25 @@ namespace FlaxEditor.Modules { switch (Editor.Options.Options.Interface.PlayButtonAction) { - case PlayAction.PlayGame: - if (Editor.IsPlayMode) - Editor.Simulation.RequestStopPlay(); - else - PlayGame(); - return; - case PlayAction.PlayScenes: PlayScenes(); return; + case InterfaceOptions.PlayAction.PlayGame: + if (Editor.IsPlayMode) + Editor.Simulation.RequestStopPlay(); + else + PlayGame(); + return; + case InterfaceOptions.PlayAction.PlayScenes: + PlayScenes(); + return; } } private void PlayGame() { var firstScene = GameSettings.Load().FirstScene; - if (firstScene == Guid.Empty) { - if (Level.IsAnySceneLoaded) Editor.Simulation.RequestStartPlay(); + if (Level.IsAnySceneLoaded) + Editor.Simulation.RequestStartPlay(); return; } diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 7d97fecd6..5fd8c31c7 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -83,10 +83,11 @@ namespace FlaxEditor.Options /// Launches the game from the First Scene defined in the project settings. /// PlayGame, + /// /// Launches the game using the scenes currently loaded in the editor. /// - PlayScenes + PlayScenes, } /// @@ -97,14 +98,12 @@ namespace FlaxEditor.Options public float InterfaceScale { get; set; } = 1.0f; #if PLATFORM_WINDOWS - /// /// Gets or sets a value indicating whether use native window title bar. 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 /// @@ -215,15 +214,14 @@ namespace FlaxEditor.Options /// Gets or sets a value indicating what action should be taken upon pressing the play button. /// [DefaultValue(PlayAction.PlayScenes)] - [EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(410), Tooltip("Determines the action taken when the play button is pressed.")] + [EditorDisplay("Play In-Editor", "Play Button Action"), EditorOrder(410)] public PlayAction PlayButtonAction { get; set; } = PlayAction.PlayScenes; /// /// Gets or sets a value indicating the number of game clients to launch when building and/or running cooked game. /// - [DefaultValue(1)] - [EditorDisplay("Cook & Run", "Number Of GameClients To Launch"), EditorOrder(500), Tooltip("Determines the number of game clients to launch when building and/or running cooked game.")] - [Range(1, 4)] + [DefaultValue(1), Range(1, 4)] + [EditorDisplay("Cook & Run"), EditorOrder(500)] public int NumberOfGameClientsToLaunch = 1; private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index f88fa36f5..5ece067a0 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -110,14 +110,14 @@ namespace FlaxEditor.Windows #if PLATFORM_WINDOWS switch (BuildPlatform) { - case BuildPlatform.MacOSx64: - case BuildPlatform.MacOSARM64: - case BuildPlatform.iOSARM64: - IsSupported = false; - break; - default: - IsSupported = true; - break; + case BuildPlatform.MacOSx64: + case BuildPlatform.MacOSARM64: + case BuildPlatform.iOSARM64: + IsSupported = false; + break; + default: + IsSupported = true; + break; } #elif PLATFORM_LINUX switch (BuildPlatform) @@ -160,17 +160,17 @@ namespace FlaxEditor.Windows { switch (BuildPlatform) { - case BuildPlatform.Windows32: - case BuildPlatform.Windows64: - case BuildPlatform.UWPx86: - case BuildPlatform.UWPx64: - case BuildPlatform.LinuxX64: - case BuildPlatform.AndroidARM64: - layout.Label("Use Flax Launcher and download the required package.", TextAlignment.Center); - break; - default: - layout.Label("Engine source is required to target this platform.", TextAlignment.Center); - break; + case BuildPlatform.Windows32: + case BuildPlatform.Windows64: + case BuildPlatform.UWPx86: + case BuildPlatform.UWPx64: + case BuildPlatform.LinuxX64: + case BuildPlatform.AndroidARM64: + layout.Label("Use Flax Launcher and download the required package.", TextAlignment.Center); + break; + default: + layout.Label("Engine source is required to target this platform.", TextAlignment.Center); + break; } } else @@ -238,6 +238,7 @@ namespace FlaxEditor.Windows { [EditorDisplay(null, "arm64")] ARM64, + [EditorDisplay(null, "x64")] x64, } @@ -272,43 +273,43 @@ namespace FlaxEditor.Windows string name; switch (_platform) { - case PlatformType.Windows: - name = "Windows"; - break; - case PlatformType.XboxOne: - name = "Xbox One"; - break; - case PlatformType.UWP: - name = "Windows Store"; - layout.Label("UWP (Windows Store) platform has been deprecated and is no longer supported", TextAlignment.Center).Label.TextColor = Color.Red; - break; - case PlatformType.Linux: - name = "Linux"; - break; - case PlatformType.PS4: - name = "PlayStation 4"; - break; - case PlatformType.XboxScarlett: - name = "Xbox Scarlett"; - break; - case PlatformType.Android: - name = "Android"; - break; - case PlatformType.Switch: - name = "Switch"; - break; - case PlatformType.PS5: - name = "PlayStation 5"; - break; - case PlatformType.Mac: - name = "Mac"; - break; - case PlatformType.iOS: - name = "iOS"; - break; - default: - name = Utilities.Utils.GetPropertyNameUI(_platform.ToString()); - break; + case PlatformType.Windows: + name = "Windows"; + break; + case PlatformType.XboxOne: + name = "Xbox One"; + break; + case PlatformType.UWP: + name = "Windows Store"; + layout.Label("UWP (Windows Store) platform has been deprecated and is no longer supported", TextAlignment.Center).Label.TextColor = Color.Red; + break; + case PlatformType.Linux: + name = "Linux"; + break; + case PlatformType.PS4: + name = "PlayStation 4"; + break; + case PlatformType.XboxScarlett: + name = "Xbox Scarlett"; + break; + case PlatformType.Android: + name = "Android"; + break; + case PlatformType.Switch: + name = "Switch"; + break; + case PlatformType.PS5: + name = "PlayStation 5"; + break; + case PlatformType.Mac: + name = "Mac"; + break; + case PlatformType.iOS: + name = "iOS"; + break; + default: + name = Utilities.Utils.GetPropertyNameUI(_platform.ToString()); + break; } var group = layout.Group(name); @@ -614,6 +615,18 @@ namespace FlaxEditor.Windows _exitOnBuildEnd = true; } + /// + /// Returns true if can build for the given platform (both supported and available). + /// + /// The platform. + /// True if can build, otherwise false. + public bool CanBuild(PlatformType platformType) + { + if (_buildTabProxy.PerPlatformOptions.TryGetValue(platformType, out var platform)) + return platform.IsAvailable && platform.IsSupported; + return false; + } + /// /// Builds all the targets from the given preset. /// @@ -660,11 +673,11 @@ namespace FlaxEditor.Windows Editor.Log("Building and running"); GameCooker.GetCurrentPlatform(out var platform, out var buildPlatform, out var buildConfiguration); var numberOfClients = Editor.Options.Options.Interface.NumberOfGameClientsToLaunch; - for (int i = 0; i < numberOfClients; i++) { var buildOptions = BuildOptions.AutoRun; - if (i > 0) buildOptions |= BuildOptions.NoCook; + if (i > 0) + buildOptions |= BuildOptions.NoCook; _buildingQueue.Enqueue(new QueueItem { @@ -687,7 +700,6 @@ namespace FlaxEditor.Windows Editor.Log("Running cooked build"); GameCooker.GetCurrentPlatform(out var platform, out var buildPlatform, out var buildConfiguration); var numberOfClients = Editor.Options.Options.Interface.NumberOfGameClientsToLaunch; - for (int i = 0; i < numberOfClients; i++) { _buildingQueue.Enqueue(new QueueItem diff --git a/Source/Editor/Windows/Profiler/ProfilerWindow.cs b/Source/Editor/Windows/Profiler/ProfilerWindow.cs index b35641bf6..f5a5c6f86 100644 --- a/Source/Editor/Windows/Profiler/ProfilerWindow.cs +++ b/Source/Editor/Windows/Profiler/ProfilerWindow.cs @@ -93,7 +93,7 @@ namespace FlaxEditor.Windows.Profiler _liveRecordingButton = toolstrip.AddButton(editor.Icons.Play64); _liveRecordingButton.LinkTooltip("Live profiling events recording"); _liveRecordingButton.AutoCheck = true; - _liveRecordingButton.PrimaryClicked += () => _liveRecordingButton.Icon = LiveRecording ? editor.Icons.Stop64 : editor.Icons.Play64; + _liveRecordingButton.Clicked += () => _liveRecordingButton.Icon = LiveRecording ? editor.Icons.Stop64 : editor.Icons.Play64; _clearButton = toolstrip.AddButton(editor.Icons.Rotate32, Clear); _clearButton.LinkTooltip("Clear data"); toolstrip.AddSeparator();