From 478c946c1c65f04272dc09f429daf44b3f34169f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 17 Sep 2023 17:42:18 -0500 Subject: [PATCH 1/5] Add automatic module creation. --- .../Windows/ContentWindow.ContextMenu.cs | 153 ++++++++++++++++++ Source/Editor/Windows/ContentWindow.cs | 78 ++++++++- 2 files changed, 230 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index 283133688..2a398cfb9 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -1,11 +1,13 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.IO; using FlaxEditor.Content; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.Assertions; +using FlaxEngine.GUI; using FlaxEngine.Json; namespace FlaxEditor.Windows @@ -146,6 +148,157 @@ namespace FlaxEditor.Windows } cm.AddSeparator(); + + // Check if is source folder to add new module + if (item is ContentFolder sourceFolder && sourceFolder.ParentFolder.Node is ProjectTreeNode node) + { + if (sourceFolder.Node == node.Source) + { + var button = cm.AddButton("New Module"); + button.CloseMenuOnClick = false; + button.Clicked += () => + { + var popup = new ContextMenuBase + { + Size = new Float2(230, 125), + ClipChildren = false, + CullChildren = false, + }; + popup.Show(button, new Float2(button.Width, 0)); + + var nameLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Name", + HorizontalAlignment = TextAlignment.Near, + }; + nameLabel.LocalX += 10; + nameLabel.LocalY += 10; + + var nameTextBox = new TextBox + { + Parent = popup, + WatermarkText = "Module Name", + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + nameTextBox.LocalX += 100; + nameTextBox.LocalY += 10; + var defaultTextBoxBorderColor = nameTextBox.BorderColor; + var defaultTextBoxBorderSelectedColor = nameTextBox.BorderSelectedColor; + nameTextBox.TextChanged += () => + { + if (string.IsNullOrEmpty(nameTextBox.Text)) + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + return; + } + + var pluginPath = Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text); + if (Directory.Exists(pluginPath)) + { + nameTextBox.BorderColor = Color.Red; + nameTextBox.BorderSelectedColor = Color.Red; + } + else + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + } + }; + + var editorLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Editor", + HorizontalAlignment = TextAlignment.Near, + }; + editorLabel.LocalX += 10; + editorLabel.LocalY += 35; + + var editorCheckBox = new CheckBox() + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + }; + editorCheckBox.LocalY += 35; + editorCheckBox.LocalX += 100; + + var cppLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "C++", + HorizontalAlignment = TextAlignment.Near, + }; + cppLabel.LocalX += 10; + cppLabel.LocalY += 60; + + var cppCheckBox = new CheckBox() + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + }; + cppCheckBox.LocalY += 60; + cppCheckBox.LocalX += 100; + + var submitButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Create", + Width = 70, + }; + submitButton.LocalX += 40; + submitButton.LocalY += 90; + + submitButton.Clicked += () => + { + // TODO: Check all modules in project including plugins + if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text))) + { + Editor.LogWarning("Cannot create plugin due to name conflict."); + return; + } + CreateModule(node.Source.Path, nameTextBox.Text, editorCheckBox.Checked, cppCheckBox.Checked); + nameTextBox.Clear(); + editorCheckBox.Checked = false; + cppCheckBox.Checked = false; + popup.Hide(); + }; + + var cancelButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Cancel", + Width = 70, + }; + cancelButton.LocalX += 120; + cancelButton.LocalY += 90; + + cancelButton.Clicked += () => + { + nameTextBox.Clear(); + editorCheckBox.Checked = false; + cppCheckBox.Checked = false; + popup.Hide(); + }; + }; + /* + button.Size = new Float2(200, 100); + var nameLabel = new Label() + { + Text = "Name:", + Parent = button, + AutoWidth = true, + }; + */ + } + } if (!isRootFolder) { diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 0f3c12286..8dc91d711 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Xml; using FlaxEditor.Content; using FlaxEditor.Content.GUI; @@ -730,7 +731,82 @@ namespace FlaxEditor.Windows } /// - /// Stars creating the folder. + /// Starts creating a new module + /// + private async void CreateModule(string path, string moduleName, bool editorModule, bool cpp) + { + if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(path)) + { + Editor.LogWarning("Failed to create module due to no name"); + return; + } + + var sourceFolder = SelectedNode.Folder; + var sourcePath = sourceFolder.Path; + + // Create folder + var moduleFolderPath = Path.Combine(path, moduleName); + Directory.CreateDirectory(moduleFolderPath); + + // Create module + var moduleText = "using Flax.Build;\n" + + "using Flax.Build.NativeCpp;\n" + + $"\npublic class {moduleName} : Game{(editorModule ? "Editor" : "")}Module\n" + + "{\n " + + "/// \n" + + " public override void Init()\n" + + " {\n" + + " base.Init();\n" + + "\n" + + " // C#-only scripting if false\n" + + $" BuildNativeCode = {(cpp ? "true" : "false")};\n" + + " }\n" + + "\n" + + " /// \n" + + " public override void Setup(BuildOptions options)\n" + + " {" + + "\n" + + " base.Setup(options);\n" + + "\n" + + " options.ScriptingAPI.IgnoreMissingDocumentationWarnings = true;\n" + + "\n" + + " // Here you can modify the build options for your game module\n" + + " // To reference another module use: options.PublicDependencies.Add(\"Audio\");\n" + + " // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n" + + " // To learn more see scripting documentation.\n" + + " }\n" + + "}"; + + var modulePath = Path.Combine(moduleFolderPath, $"{moduleName}.Build.cs"); + await File.WriteAllTextAsync(modulePath, moduleText, new UTF8Encoding()); + Editor.Log($"Module created at {modulePath}"); + + // Get editor target and target files and add module + var files = Directory.GetFiles(sourceFolder.Path); + var targetModuleText = $"Modules.Add(\"{moduleName}\");\n "; + foreach (var file in files) + { + if (!file.Contains(".Build.cs", StringComparison.OrdinalIgnoreCase)) + continue; + + var targetText = await File.ReadAllTextAsync(file); + + if (!editorModule && targetText.Contains("GameProjectTarget", StringComparison.Ordinal)) + continue; + + // TODO: Handle edge case when there are no modules in a target + var index = targetText.IndexOf("Modules.Add"); + if (index != -1) + { + var newText = targetText.Insert(index, targetModuleText); + await File.WriteAllTextAsync(file, newText); + Editor.Log($"Module added to Target: {file}"); + } + } + } + + /// + /// Starts creating the folder. /// public void NewFolder() { From c1104e803d0557bdb399631f28aae9aebc01fd7e Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 17 Sep 2023 17:48:09 -0500 Subject: [PATCH 2/5] Code cleanup --- Source/Editor/Windows/ContentWindow.ContextMenu.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index 2a398cfb9..ab00340a5 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -288,15 +288,6 @@ namespace FlaxEditor.Windows popup.Hide(); }; }; - /* - button.Size = new Float2(200, 100); - var nameLabel = new Label() - { - Text = "Name:", - Parent = button, - AutoWidth = true, - }; - */ } } From 17383fe0b639f0599e28100224baf039ac698ed1 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 17 Sep 2023 17:49:38 -0500 Subject: [PATCH 3/5] Small fix to error message --- Source/Editor/Windows/ContentWindow.ContextMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index ab00340a5..bf457ab02 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -260,7 +260,7 @@ namespace FlaxEditor.Windows // TODO: Check all modules in project including plugins if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text))) { - Editor.LogWarning("Cannot create plugin due to name conflict."); + Editor.LogWarning("Cannot create module due to name conflict."); return; } CreateModule(node.Source.Path, nameTextBox.Text, editorCheckBox.Checked, cppCheckBox.Checked); From 6e94b21452efbe3ba85ec80c46d164bb6329c1fe Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 17 Sep 2023 20:02:22 -0500 Subject: [PATCH 4/5] Fix build issue --- Source/Editor/Windows/ContentWindow.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 8dc91d711..4169f60d3 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -776,9 +776,9 @@ namespace FlaxEditor.Windows " // To learn more see scripting documentation.\n" + " }\n" + "}"; - + moduleText = Encoding.UTF8.GetString(Encoding.Default.GetBytes(moduleText)); var modulePath = Path.Combine(moduleFolderPath, $"{moduleName}.Build.cs"); - await File.WriteAllTextAsync(modulePath, moduleText, new UTF8Encoding()); + await File.WriteAllTextAsync(modulePath, moduleText); Editor.Log($"Module created at {modulePath}"); // Get editor target and target files and add module From bd32619016bc58dd7eb06d08ac61f52fb4c1e6d2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 1 Oct 2023 21:11:08 -0500 Subject: [PATCH 5/5] Small fix --- Source/Editor/Windows/ContentWindow.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 4169f60d3..890f4f770 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -740,9 +740,6 @@ namespace FlaxEditor.Windows Editor.LogWarning("Failed to create module due to no name"); return; } - - var sourceFolder = SelectedNode.Folder; - var sourcePath = sourceFolder.Path; // Create folder var moduleFolderPath = Path.Combine(path, moduleName); @@ -782,7 +779,7 @@ namespace FlaxEditor.Windows Editor.Log($"Module created at {modulePath}"); // Get editor target and target files and add module - var files = Directory.GetFiles(sourceFolder.Path); + var files = Directory.GetFiles(path); var targetModuleText = $"Modules.Add(\"{moduleName}\");\n "; foreach (var file in files) { @@ -791,9 +788,10 @@ namespace FlaxEditor.Windows var targetText = await File.ReadAllTextAsync(file); - if (!editorModule && targetText.Contains("GameProjectTarget", StringComparison.Ordinal)) + // Skip game project if it is suppose to be an editor module + if (editorModule && targetText.Contains("GameProjectTarget", StringComparison.Ordinal)) continue; - + // TODO: Handle edge case when there are no modules in a target var index = targetText.IndexOf("Modules.Add"); if (index != -1)