diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index f26506c74..f031ab383 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -159,7 +159,7 @@ namespace FlaxEditor.Options } /// - /// Options focus Game Window behaviour when play mode is entered. + /// Options for focus Game Window behaviour when play mode is entered. /// public enum PlayModeFocus { @@ -179,6 +179,22 @@ namespace FlaxEditor.Options GameWindowThenRestore, } + /// + /// Generic options for a disabled or hidden state. Used for example in create content button. + /// + public enum DisabledHidden + { + /// + /// Disabled state. + /// + Disabled, + + /// + /// Hidden state. + /// + Hidden, + } + /// /// 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. /// @@ -525,6 +541,13 @@ namespace FlaxEditor.Options [EditorDisplay("Visject", "Warn when deleting used parameter"), EditorOrder(552)] public bool WarnOnDeletingUsedVisjectParameter { get; set; } = true; + /// + /// Gets or sets a value indicating what should happen to unavaliable options in the content create menu. + /// + [DefaultValue(DisabledHidden.Hidden)] + [EditorDisplay("Content"), EditorOrder(600)] + public DisabledHidden UnavaliableContentCreateOptions { get; set; } = DisabledHidden.Hidden; + private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont); diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index 1ad225997..40dd9131b 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -39,8 +39,7 @@ namespace FlaxEditor.Windows folder = CurrentViewFolder; } Assert.IsNotNull(folder); - bool isRootFolder = CurrentViewFolder == _root.Folder; - + // Create context menu ContextMenuButton b; ContextMenu cm = new ContextMenu @@ -78,7 +77,7 @@ namespace FlaxEditor.Windows cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(item.Path))); if (!String.IsNullOrEmpty(Editor.Instance.Windows.ContentWin._itemsSearchBox.Text)) - { + { cm.AddButton("Show in Content Panel", () => { Editor.Instance.Windows.ContentWin.ClearItemsSearch(); @@ -178,19 +177,61 @@ namespace FlaxEditor.Windows cm.AddSeparator(); + CreateNewModuleMenu(cm, folder); + CreateNewFolderMenu(cm, folder, false, item); + CreateNewContentItemMenu(cm, folder); + + if (folder.CanHaveAssets) + { + cm.AddButton("Import file", () => + { + _view.ClearSelection(); + Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder); + }); + } + + // Remove any leftover separator + if (cm.ItemsContainer.Children.LastOrDefault() is ContextMenuSeparator) + cm.ItemsContainer.Children.Last().Dispose(); + + // Show it + cm.Show(this, location); + } + + private void CreateNewModuleMenu(ContextMenu menu, ContentFolder folder, bool disableUncreatable = false) + { // Check if is source folder to add new module if (folder?.ParentFolder?.Node is ProjectTreeNode parentFolderNode && folder.Node == parentFolderNode.Source) { - var button = cm.AddButton("New module"); + var button = menu.AddButton("New module"); button.CloseMenuOnClick = false; button.Clicked += () => NewModule(button, parentFolderNode.Source.Path); } - - if (!isRootFolder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectTreeNode)) + else if (disableUncreatable) { - cm.AddButton("New folder", NewFolder); + var button = menu.AddButton("New module"); + button.Enabled = false; } + } + private bool CanCreateFolder(ContentItem item = null) + { + bool canCreateFolder = CurrentViewFolder != _root.Folder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectTreeNode); + return canCreateFolder; + } + + private void CreateNewFolderMenu(ContextMenu menu, ContentFolder folder, bool disableUncreatable = false, ContentItem item = null) + { + bool canCreateFolder = CanCreateFolder(item); + if (canCreateFolder || disableUncreatable) + { + var b = menu.AddButton("New folder", NewFolder); + b.Enabled = canCreateFolder; + } + } + + private void CreateNewContentItemMenu(ContextMenu menu, ContentFolder folder, bool showNew = true, bool disableUncreatable = false) + { // Loop through each proxy and user defined json type and add them to the context menu var actorType = new ScriptType(typeof(Actor)); var scriptType = new ScriptType(typeof(Script)); @@ -230,7 +271,8 @@ namespace FlaxEditor.Windows if (p == null) continue; - if (p.CanCreate(folder)) + bool canCreate = p.CanCreate(folder); + if (canCreate || disableUncreatable) { var parts = attribute.Path.Split('/'); ContextMenuChildMenu childCM = null; @@ -238,16 +280,20 @@ namespace FlaxEditor.Windows for (int i = 0; i < parts?.Length; i++) { var part = parts[i].Trim(); + if (part == "New" && !showNew) + continue; if (i == parts.Length - 1) { if (mainCM) { - cm.AddButton(part, () => NewItem(p)); + var b = menu.AddButton(part, () => NewItem(p)); + b.Enabled = canCreate; mainCM = false; } else if (childCM != null) { - childCM.ContextMenu.AddButton(part, () => NewItem(p)); + var b = childCM.ContextMenu.AddButton(part, () => NewItem(p)); + b.Enabled = canCreate; childCM.ContextMenu.AutoSort = true; } } @@ -255,35 +301,21 @@ namespace FlaxEditor.Windows { if (mainCM) { - childCM = cm.GetOrAddChildMenu(part); + childCM = menu.GetOrAddChildMenu(part); childCM.ContextMenu.AutoSort = true; + childCM.Enabled = canCreate; mainCM = false; } else if (childCM != null) { childCM = childCM.ContextMenu.GetOrAddChildMenu(part); childCM.ContextMenu.AutoSort = true; + childCM.Enabled = canCreate; } } } } } - - if (folder.CanHaveAssets) - { - cm.AddButton("Import file", () => - { - _view.ClearSelection(); - Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder); - }); - } - - // Remove any leftover separator - if (cm.ItemsContainer.Children.LastOrDefault() is ContextMenuSeparator) - cm.ItemsContainer.Children.Last().Dispose(); - - // Show it - cm.Show(this, location); } private void OnExpandAllClicked(ContextMenuButton button) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 41382e60c..9b92b380f 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -37,6 +37,7 @@ namespace FlaxEditor.Windows private readonly ToolStrip _toolStrip; private readonly ToolStripButton _importButton; + private readonly ToolStripButton _createNewButton; private readonly ToolStripButton _navigateBackwardButton; private readonly ToolStripButton _navigateForwardButton; private readonly ToolStripButton _navigateUpButton; @@ -154,11 +155,12 @@ namespace FlaxEditor.Windows { Parent = this, }; - _importButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Import64, () => Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder)).LinkTooltip("Import content"); + _importButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Import64, () => Editor.ContentImporting.ShowImportFileDialog(CurrentViewFolder)).LinkTooltip("Import content."); + _createNewButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Add64, OnCreateNewItemButtonClicked).LinkTooltip("Create a new asset. Shift + left click to create a new folder."); _toolStrip.AddSeparator(); - _navigateBackwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Left64, NavigateBackward).LinkTooltip("Navigate backward"); - _navigateForwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Right64, NavigateForward).LinkTooltip("Navigate forward"); - _navigateUpButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Up64, NavigateUp).LinkTooltip("Navigate up"); + _navigateBackwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Left64, NavigateBackward).LinkTooltip("Navigate backward."); + _navigateForwardButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Right64, NavigateForward).LinkTooltip("Navigate forward."); + _navigateUpButton = (ToolStripButton)_toolStrip.AddButton(Editor.Icons.Up64, NavigateUp).LinkTooltip("Navigate up."); _toolStrip.AddSeparator(); // Navigation bar @@ -271,6 +273,42 @@ namespace FlaxEditor.Windows InputActions.Add(options => options.Search, () => _itemsSearchBox.Focus()); } + private void OnCreateNewItemButtonClicked() + { + if (Input.GetKey(KeyboardKeys.Shift) && CanCreateFolder()) + { + NewFolder(); + return; + } + + var menu = new ContextMenu(); + + InterfaceOptions interfaceOptions = Editor.Instance.Options.Options.Interface; + bool disableUnavaliable = interfaceOptions.UnavaliableContentCreateOptions == InterfaceOptions.DisabledHidden.Disabled; + + CreateNewFolderMenu(menu, CurrentViewFolder, disableUnavaliable); + CreateNewModuleMenu(menu, CurrentViewFolder, disableUnavaliable); + menu.AddSeparator(); + CreateNewContentItemMenu(menu, CurrentViewFolder, false, disableUnavaliable); + // Hack: Show the menu once to get the direction, then show it above or below the button depending on the direction. + menu.Show(this, _createNewButton.UpperLeft); + var direction = menu.Direction; + menu.Hide(); + bool below = false; + switch (direction) + { + case ContextMenuDirection.RightDown: + case ContextMenuDirection.LeftDown: + below = true; + break; + case ContextMenuDirection.RightUp: + case ContextMenuDirection.LeftUp: + below = false; + break; + } + menu.Show(this, below ? _createNewButton.BottomLeft : _createNewButton.UpperLeft, direction); + } + private ContextMenu OnViewDropdownPopupCreate(ComboBox comboBox) { var menu = new ContextMenu();