From 6a65ccd5efd3b807d0138308d33cc6574ec480ea Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Mon, 24 Jul 2023 22:10:12 +0200 Subject: [PATCH 01/40] rev1 --- Source/Editor/Content/ContentFilter.cs | 262 ++++++++++++++++++ .../Content/GUI/ContentNavigationBar.cs | 27 ++ .../Content/GUI/ContentNavigationButton.cs | 2 +- .../GUI/ContentNavigationButtonSeparator.cs | 85 ++++++ .../Content/GUI/ContentSettingsDropdown.cs | 95 +++++++ Source/Editor/Content/GUI/ContentView.cs | 9 +- Source/Editor/Content/Items/ContentItem.cs | 8 +- Source/Editor/GUI/ComboBox.cs | 72 +++-- .../Windows/ContentWindow.Navigation.cs | 10 +- Source/Editor/Windows/ContentWindow.cs | 59 ++-- 10 files changed, 588 insertions(+), 41 deletions(-) create mode 100644 Source/Editor/Content/ContentFilter.cs create mode 100644 Source/Editor/Content/GUI/ContentNavigationBar.cs create mode 100644 Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs create mode 100644 Source/Editor/Content/GUI/ContentSettingsDropdown.cs diff --git a/Source/Editor/Content/ContentFilter.cs b/Source/Editor/Content/ContentFilter.cs new file mode 100644 index 000000000..2cbd78e65 --- /dev/null +++ b/Source/Editor/Content/ContentFilter.cs @@ -0,0 +1,262 @@ +using FlaxEditor.Content; +using FlaxEditor.Content.GUI; +using FlaxEngine; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +namespace FlaxEditor.Content +{ + /// + /// class with is controling visability of items in content window + /// + internal class ContentFilter + { + #region Filters Config + /// + /// all suported files by engine Content Folder + /// + public static readonly List SuportedFileExtencionsInContentFolder = new List() + { + ".flax", + ".json", + ".scene", + ".prefab", + }; + /// + /// all suported files by engine Source Folder + /// + public static readonly List SuportedFileExtencionsInSourceFolder = new List() + { + ".shader", + ".cs", + ".h", + ".cpp", + }; + /// + /// ignores folders in source folder (top layer), + /// for example obj folder is default c# project folder + /// + internal static readonly List HideFoldersInSourceFolder = new List() + { + "obj", // default c# project folder + "Properties", // c# project stuff ? + }; + /// + /// ignores files in source folder (top layer), + /// + internal static readonly List HideFilesInSourceFolder = new List() //dont edit + { + "Game.csproj", //solucion file + "Game.Gen.cs", //auto-generated not be edited + "GameEditorTarget.Build.cs", + "GameTarget.Build.cs", + }; + #endregion + internal static ContentItem gameSettings; + internal static List buildFiles; + + internal static ContentTreeNode settings; + internal static ContentTreeNode shaders; + internal static ProjectTreeNode engine; + internal static List plugins = new(); + internal static List FilterFolder(ContentFolder folder) + { + return FilterFolder(folder, SuportedFileExtencionsInContentFolder.ToArray(), SuportedFileExtencionsInSourceFolder.ToArray()); + } + internal static List FilterFolder(ContentFolder folder, string[] FileExtencionsInContentFolder, string[] ExtencionsInSourceFolder) + { + if (folder.CanHaveAssets) + { + for (int i = 0; i < folder.Children.Count; i++) + { + bool Visible = false; + for (int j = 0; j < FileExtencionsInContentFolder.Length; j++) + { + if ((folder.Children[i].Path.EndsWith(FileExtencionsInContentFolder[j]) || folder.Children[i].IsFolder)) + { + if (folder.Children[i].Visible) + { + Visible = true; + } + break; + } + } + folder.Children[i].Visible = Visible; + } + } + else if (folder.CanHaveScripts) + { + for (int i = 0; i < folder.Children.Count; i++) + { + bool Visible = false; + for (int j = 0; j < ExtencionsInSourceFolder.Length; j++) + { + if ((folder.Children[i].Path.EndsWith(ExtencionsInSourceFolder[j]) || folder.Children[i].IsFolder)) + { + if (folder.Children[i].Visible) + { + Visible = true; + } + break; + } + } + folder.Children[i].Visible = Visible; + } + } + return folder.Children; + } + internal static ProjectTreeNode Filter(ProjectTreeNode tree) + { + var content = tree.Children[0] as ContentTreeNode; + var source = tree.Children[1] as ContentTreeNode; + //filter content folder (top layer) + buildFiles = new(); + for (int i = 0; i < content.Folder.Children.Count; i++) + { + if (content.Folder.Children[i].FileName == "GameSettings.json") + { + gameSettings = content.Folder.Children[i]; + content.Folder.Children[i].Visible = false; + break; + } + } + + int hindenCount = 0; + + for (int i = content.Children.Count - 1; i >= 0; i--)//we are starting from back it faster + { + var node = content.Children[i] as ContentTreeNode; + if (node.Folder.FileName == "Settings") + { + settings = node; + hindenCount++; + node.Visible = false; + node.Folder.Visible = false; + } + if (node.Folder.FileName == "Shaders") + { + shaders = node; + hindenCount++; + node.Visible = false; + node.Folder.Visible = false; + + } + if (hindenCount == 2) + break; + } + + + //----------------------------------------------------------------------------------------------------- + + //filter source folder (top layer) + hindenCount = 0; + for (int i = 0; i < source.Folder.Children.Count; i++) + { + for (int j = 0; j < HideFilesInSourceFolder.Count; j++) + { + if (source.Folder.Children[i].FileName == HideFilesInSourceFolder[j]) + { + source.Folder.Children[i].Visible = false; + hindenCount++; + if(i > 1) + { + buildFiles.Add(source.Folder.Children[i]); + } + if (HideFilesInSourceFolder.Count == hindenCount) goto HideFilesInSourceFolderComplited; + break; + } + } + } + HideFilesInSourceFolderComplited: + hindenCount = 0; + for (int i = source.Children.Count - 1; i >= 0; i--) + { + var node = source.Children[i] as ContentTreeNode; + for (int j = 0; j < HideFoldersInSourceFolder.Count; j++) + { + if (node.Folder.FileName == HideFoldersInSourceFolder[j]) + { + node.Visible = false; + node.Folder.Visible = false; + hindenCount++; + if (HideFoldersInSourceFolder.Count == hindenCount) goto HideFoldersInSourceFolderComplited; + break; + } + } + } + HideFoldersInSourceFolderComplited: + //content + return tree; + } + internal static void UpdateFilterVisability(ContentSettingsDropdown dropdown) + { + engine.Visible = false; + engine.Folder.Visible = false; + + foreach (var item in plugins) + { + item.Visible = false; + item.Folder.Visible = false; + } + foreach (var item in buildFiles) + { + item.Visible = false; + } + gameSettings.Visible = false; + settings.Visible = false; + settings.Folder.Visible = false; + + shaders.Visible = false; + shaders.Folder.Visible = false; + + for (int i = 0; i < dropdown.Selection.Count; i++) + { + if (dropdown.Selection[i] == 0) //Show Engine Content + { + engine.Visible = true; + engine.Folder.Visible = true; + Editor.Instance.Windows.ContentWin.RefreshView(engine); + } + + if (dropdown.Selection[i] == 1)//Show Plugin Content + { + foreach (var item in plugins) + { + item.Visible = true; + item.Folder.Visible = true; + Editor.Instance.Windows.ContentWin.RefreshView(item); + } + } + + if (dropdown.Selection[i] == 2)//Show Build Files + { + foreach (var item in buildFiles) + { + item.Visible = true; + } + } + + if (dropdown.Selection[i] == 3)//Show Game Settings + { + gameSettings.Visible = true; + settings.Visible = true; + settings.Folder.Visible = true; + Editor.Instance.Windows.ContentWin.RefreshView((ContentTreeNode)settings.Parent); + } + + if (dropdown.Selection[i] == 4)//"Show Shader Source" + { + shaders.Visible = true; + shaders.Folder.Visible = true; + Editor.Instance.Windows.ContentWin.RefreshView(shaders); + } + } + + + + } + } +} diff --git a/Source/Editor/Content/GUI/ContentNavigationBar.cs b/Source/Editor/Content/GUI/ContentNavigationBar.cs new file mode 100644 index 000000000..673ac806e --- /dev/null +++ b/Source/Editor/Content/GUI/ContentNavigationBar.cs @@ -0,0 +1,27 @@ +using FlaxEditor.GUI; +using FlaxEngine; + +namespace FlaxEditor.Content.GUI +{ + internal class ContentNavigationBar : NavigationBar + { + ToolStrip _toolstrip; + internal float ofssetFromRightEdge = 80; + internal ContentNavigationBar(ToolStrip toolstrip) : base() + { + _toolstrip = toolstrip; + } + /// + protected override void Arrange() + { + base.Arrange(); + var lastToolstripButton = _toolstrip.LastButton; + var parentSize = Parent.Size; + Bounds = new Rectangle + ( + new Float2(lastToolstripButton.Right, 0), + new Float2(parentSize.X - X - ofssetFromRightEdge, _toolstrip.Height) + ); + } + } +} diff --git a/Source/Editor/Content/GUI/ContentNavigationButton.cs b/Source/Editor/Content/GUI/ContentNavigationButton.cs index d37e1629b..beec247da 100644 --- a/Source/Editor/Content/GUI/ContentNavigationButton.cs +++ b/Source/Editor/Content/GUI/ContentNavigationButton.cs @@ -31,7 +31,7 @@ namespace FlaxEditor.Content.GUI : base(x, y, height) { TargetNode = targetNode; - Text = targetNode.NavButtonLabel + "/"; + Text = targetNode.NavButtonLabel; } /// diff --git a/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs b/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs new file mode 100644 index 000000000..cad63066d --- /dev/null +++ b/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs @@ -0,0 +1,85 @@ +using FlaxEditor; +using FlaxEditor.GUI; +using FlaxEditor.GUI.ContextMenu; +using FlaxEngine; +using FlaxEngine.GUI; +using System.Collections.Generic; + +namespace FlaxEditor.Content.GUI +{ + internal class ContentNavigationButtonSeparator : ComboBox + { + public ContentNavigationButton Target { get; } + public ContentNavigationButtonSeparator(ContentNavigationButton target, float x, float y,float height) + { + Target = target; + Bounds = new Rectangle(x, y, 16, height); + Offsets = new Margin(Bounds.X, Bounds.Width, Bounds.Y, Bounds.Height); + UpdateTransform(); + + MaximumItemsInViewCount = 20; + var style = Style.Current; + BackgroundColor = style.BackgroundNormal; + BackgroundColorHighlighted = BackgroundColor; + BackgroundColorSelected = BackgroundColor; + } + protected override ContextMenu OnCreatePopup() + { + UpdateDropDownItems(); + return base.OnCreatePopup(); + } + internal void UpdateDropDownItems() + { + ClearItems(); + _items = new(); + for (int i = 0; i < Target.TargetNode.Children.Count; i++) + { + if (Target.TargetNode.Children[i] is ContentTreeNode node) + { + if (node.Folder.VisibleInHierarchy) // respect the filter set by ContentFilterConfig.Filter(...) + AddItem(node.Folder.ShortName); + } + } + } + /// + public override void Draw() + { + // Cache data + var style = Style.Current; + var clientRect = new Rectangle(Float2.Zero, Size); + // Draw background + if (IsDragOver) + { + Render2D.FillRectangle(clientRect, Style.Current.BackgroundSelected * 0.6f); + } + else if (_mouseDown) + { + Render2D.FillRectangle(clientRect, style.BackgroundSelected); + } + else if (IsMouseOver) + { + Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); + } + + Render2D.DrawSprite(Editor.Instance.Icons.ArrowRight12, new Rectangle(clientRect.Location.X, clientRect.Y + ((Size.Y / 2f)/2f), Size.X, Size.X), EnabledInHierarchy ? Style.Current.Foreground : Style.Current.ForegroundDisabled); + } + protected override void OnLayoutMenuButton(ref ContextMenuButton button, int index, bool construct = false) + { + var style = Style.Current; + button.Icon = Editor.Instance.Icons.FolderClosed32; + if (_tooltips != null && _tooltips.Length > index) + { + button.TooltipText = _tooltips[index]; + } + } + protected override void OnItemClicked(int index) + { + base.OnItemClicked(index); + if (Target.TargetNode.Children[index] is ContentTreeNode node) + { + Editor.Instance.Windows.ContentWin.Navigate(node); + } + // Navigate calls the OnDestroy at some point dont place code below or editor will crash + } + } +} diff --git a/Source/Editor/Content/GUI/ContentSettingsDropdown.cs b/Source/Editor/Content/GUI/ContentSettingsDropdown.cs new file mode 100644 index 000000000..04722a674 --- /dev/null +++ b/Source/Editor/Content/GUI/ContentSettingsDropdown.cs @@ -0,0 +1,95 @@ +using FlaxEditor.Content; +using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.GUI; +using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.States; +using FlaxEditor.Utilities; +using FlaxEngine; +using FlaxEngine.GUI; +using FlaxEngine.Json; +using System.Collections.Generic; +using System.Linq; + +namespace FlaxEditor.Content.GUI +{ + class ContentSettingsDropdown : ComboBox + { + internal readonly List Settings = new() + { + "Show Engine Content", + "Show Plugin Content", + "Show Build Files", + "Show Game Settings", + "Show Shader Source" + }; + public ContentSettingsDropdown() + { + SupportMultiSelect = true; + MaximumItemsInViewCount = 20; + var style = Style.Current; + BackgroundColor = style.BackgroundNormal; + BackgroundColorHighlighted = BackgroundColor; + BackgroundColorSelected = BackgroundColor; + + } + protected override ContextMenu OnCreatePopup() + { + UpdateDropDownItems(); + return base.OnCreatePopup(); + } + internal void UpdateDropDownItems() + { + ClearItems(); + + for (int i = 0; i < Settings.Count; i++) + { + AddItem(Settings[i]); + } + } + /// + public override void Draw() + { + // Cache data + var style = Style.Current; + var clientRect = new Rectangle(Float2.Zero, Size); + // Draw background + if (IsDragOver) + { + Render2D.FillRectangle(clientRect, Style.Current.BackgroundSelected * 0.6f); + } + else if (_mouseDown) + { + Render2D.FillRectangle(clientRect, style.BackgroundSelected); + } + else if (IsMouseOver) + { + Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); + } + float size = (Size.Y / 1.5f); //Icon size + // Draw text + Render2D.DrawText(Font.GetFont(), "Settings", new Rectangle(size, 0, Size.X - Size.Y, Size.Y), TextColor, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawSprite(Editor.Instance.Icons.Settings12, new Rectangle((Size.Y - size) / 2.0f, (Size.Y - size) / 2.0f, size, size)); + } + protected override void OnLayoutMenuButton(ref ContextMenuButton button, int index, bool construct = false) + { + var style = Style.Current; + if (_selectedIndices.Contains(index)) + { + button.Icon = style.CheckBoxTick; + } + else + { + button.Icon = SpriteHandle.Default; + } + if (_tooltips != null && _tooltips.Length > index) + { + button.TooltipText = _tooltips[index]; + } + } + protected override void OnSelectedIndexChanged() + { + ContentFilter.UpdateFilterVisability(this); + base.OnSelectedIndexChanged(); + } + } +} diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 6065ca9f8..9dca03953 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -261,11 +261,14 @@ namespace FlaxEditor.Content.GUI ClearItems(); // Add references and link items - _items.AddRange(items); for (int i = 0; i < items.Count; i++) { - items[i].Parent = this; - items[i].AddReference(this); + if (items[i].Visible) + { + items[i].Parent = this; + items[i].AddReference(this); + _items.Add(items[i]); + } } if (selection != null) { diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 66825fb42..377c6dac0 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -441,6 +441,11 @@ namespace FlaxEditor.Content { get { + if (!Visible) + { + return Rectangle.Empty; + } + var view = Parent as ContentView; var size = Size; switch (view?.ViewType ?? ContentViewType.Tiles) @@ -448,7 +453,7 @@ namespace FlaxEditor.Content case ContentViewType.Tiles: { var textHeight = DefaultTextHeight * size.X / DefaultWidth; - return new Rectangle(0, size.Y - textHeight, size.X, textHeight); + return new Rectangle(0, size.Y - textHeight, size.X - 2, textHeight); } case ContentViewType.List: { @@ -642,6 +647,7 @@ namespace FlaxEditor.Content /// public override void Draw() { + // Cache data var size = Size; var style = Style.Current; diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index 2481316d0..da38268b2 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using FlaxEngine; using FlaxEngine.GUI; +using FlaxEditor.GUI.ContextMenu; namespace FlaxEditor.GUI { @@ -396,14 +397,23 @@ namespace FlaxEditor.GUI _popupMenu.ButtonClicked += btn => { OnItemClicked((int)btn.Tag); - _popupMenu?.Hide(); + if (SupportMultiSelect) + { + UpdateButtons(); + _popupMenu?.PerformLayout(); + } + else + { + _popupMenu?.Hide(); + }//[nori_sc] don't hide it Support MultiSelect is on actions per min is important for UX, if some one wont to set more then 5 elements in multi select menu let them do it }; } // Check if menu hs been already shown if (_popupMenu.Visible) { - _popupMenu.Hide(); + if (!SupportMultiSelect) + _popupMenu.Hide(); return; } @@ -412,31 +422,59 @@ namespace FlaxEditor.GUI // Check if has any items if (_items.Count > 0) { - // Setup items list + UpdateButtons(); + // Show dropdown list + _popupMenu.MinimumWidth = Width; + _popupMenu.Show(this, new Float2(1, Height)); + } + } + /// + /// update buttons layout and repains + /// + private void UpdateButtons() + { + if (_popupMenu.Items.Count() != _items.Count) + { var itemControls = _popupMenu.Items.ToArray(); foreach (var e in itemControls) e.Dispose(); if (Sorted) _items.Sort(); - var style = Style.Current; for (int i = 0; i < _items.Count; i++) { var btn = _popupMenu.AddButton(_items[i]); - if (_selectedIndices.Contains(i)) - { - btn.Icon = style.CheckBoxTick; - } - if (_tooltips != null && _tooltips.Length > i) - { - btn.TooltipText = _tooltips[i]; - } - + OnLayoutMenuButton(ref btn, i, true); btn.Tag = i; } - - // Show dropdown list - _popupMenu.MinimumWidth = Width; - _popupMenu.Show(this, new Float2(1, Height)); + } + else + { + var itemControls = _popupMenu.Items.ToArray(); + if (Sorted) + _items.Sort(); + for (int i = 0; i < _items.Count; i++) + { + if (itemControls[i] is ContextMenuButton btn) + { + btn.Text = _items[i]; + OnLayoutMenuButton(ref btn, i, true); + } + } + } + } + /// + /// caled when button is created or repainted u can overite it to give the button custom look + /// + /// button refrance + /// curent item index + /// true if button is created else it is repainting the button + protected virtual void OnLayoutMenuButton(ref FlaxEditor.GUI.ContextMenu.ContextMenuButton button,int index, bool construct = false) + { + var style = Style.Current; + button.Checked = _selectedIndices.Contains(index); + if (_tooltips != null && _tooltips.Length > index) + { + button.TooltipText = _tooltips[index]; } } diff --git a/Source/Editor/Windows/ContentWindow.Navigation.cs b/Source/Editor/Windows/ContentWindow.Navigation.cs index ad690c2d0..ca3de992d 100644 --- a/Source/Editor/Windows/ContentWindow.Navigation.cs +++ b/Source/Editor/Windows/ContentWindow.Navigation.cs @@ -196,12 +196,18 @@ namespace FlaxEditor.Windows } float x = NavigationBar.DefaultButtonsMargin; float h = _toolStrip.ItemsHeight - 2 * ToolStrip.DefaultMarginV; - for (int i = nodes.Count - 1; i >= 0; i--) + for (int i = nodes.Count - 2; i >= 0; i--) { - var button = new ContentNavigationButton(nodes[i], x, ToolStrip.DefaultMarginV, h); + var button = new ContentNavigationButton(nodes[i], x - 100, ToolStrip.DefaultMarginV, h); button.PerformLayout(); x += button.Width + NavigationBar.DefaultButtonsMargin; _navigationBar.AddChild(button); + if (i == 0) + continue; + var buttonSeparator = new ContentNavigationButtonSeparator(button, x, ToolStrip.DefaultMarginV, h); + buttonSeparator.PerformLayout(); + x += buttonSeparator.Width + NavigationBar.DefaultButtonsMargin; + _navigationBar.AddChild(buttonSeparator); } nodes.Clear(); diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index f993b79f9..58aaf3e4c 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -39,11 +39,13 @@ namespace FlaxEditor.Windows private readonly ToolStripButton _navigateForwardButton; private readonly ToolStripButton _navigateUpButton; - private NavigationBar _navigationBar; + private ContentNavigationBar _navigationBar; private Tree _tree; private TextBox _foldersSearchBox; private TextBox _itemsSearchBox; private ViewDropdown _viewDropdown; + private ContentSettingsDropdown _ContentSettingDropdown; + private const float _ContentDropdownSizeX = 100; private SortType _sortType; private RootContentTreeNode _root; @@ -73,7 +75,7 @@ namespace FlaxEditor.Windows { Title = "Content"; Icon = editor.Icons.Folder32; - + // Content database events editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true; editor.ContentDatabase.ItemRemoved += OnContentDatabaseItemRemoved; @@ -107,11 +109,25 @@ namespace FlaxEditor.Windows _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 - _navigationBar = new NavigationBar + + _navigationBar = new ContentNavigationBar(_toolStrip) { - Parent = this, + Parent = _toolStrip, + ofssetFromRightEdge = _ContentDropdownSizeX + }; + var DropdownSettingsPanel = new Panel(ScrollBars.None) + { + Parent = _toolStrip, + Size = new Float2(_ContentDropdownSizeX, _toolStrip.Height) + }; + //setings Dropdown + _ContentSettingDropdown = new ContentSettingsDropdown + { + AnchorPreset = AnchorPresets.StretchAll, + Parent = DropdownSettingsPanel, + Offsets = new Margin(2, 4, 4, 4) }; // Split panel @@ -215,7 +231,7 @@ namespace FlaxEditor.Windows private ContextMenu OnViewDropdownPopupCreate(ComboBox comboBox) { var menu = new ContextMenu(); - + var showFileExtensionsButton = menu.AddButton("Show file extensions", () => View.ShowFileExtensions = !View.ShowFileExtensions); showFileExtensionsButton.Checked = View.ShowFileExtensions; showFileExtensionsButton.AutoCheck = true; @@ -877,8 +893,9 @@ namespace FlaxEditor.Windows } else { + // Show folder contents - _view.ShowItems(target.Folder.Children, _sortType, false, true); + _view.ShowItems(ContentFilter.FilterFolder(target.Folder), _sortType, false, true); } } @@ -918,15 +935,17 @@ namespace FlaxEditor.Windows _root.Expand(true); // Add game project on top, plugins in the middle and engine at bottom - _root.AddChild(Editor.ContentDatabase.Game); + _root.AddChild(ContentFilter.Filter(Editor.ContentDatabase.Game)); foreach (var project in Editor.ContentDatabase.Projects) { project.SortChildrenRecursive(); if (project == Editor.ContentDatabase.Game || project == Editor.ContentDatabase.Engine) continue; _root.AddChild(project); + ContentFilter.plugins.Add(project); } _root.AddChild(Editor.ContentDatabase.Engine); + ContentFilter.engine = Editor.ContentDatabase.Engine; Editor.ContentDatabase.Game?.Expand(true); _tree.Margin = new Margin(0.0f, 0.0f, -16.0f, 2.0f); // Hide root node @@ -1035,15 +1054,6 @@ namespace FlaxEditor.Windows return base.OnMouseUp(location, button); } - - /// - protected override void PerformLayoutBeforeChildren() - { - base.PerformLayoutBeforeChildren(); - - _navigationBar?.UpdateBounds(_toolStrip); - } - /// public override bool UseLayoutData => true; @@ -1054,6 +1064,12 @@ namespace FlaxEditor.Windows writer.WriteAttributeString("Scale", _view.ViewScale.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("ShowFileExtensions", _view.ShowFileExtensions.ToString()); writer.WriteAttributeString("ViewType", _view.ViewType.ToString()); + for (int i = 0; i < _ContentSettingDropdown.Selection.Count; i++) + { + if(_ContentSettingDropdown.Selection.Count != 0) + writer.WriteAttributeString(_ContentSettingDropdown.Settings[_ContentSettingDropdown.Selection[i]].Replace(' ', '_'), _ContentSettingDropdown.Selection[i].ToString()); + } + } /// @@ -1066,6 +1082,15 @@ namespace FlaxEditor.Windows _view.ShowFileExtensions = value2; if (Enum.TryParse(node.GetAttribute("ViewType"), out ContentViewType viewType)) _view.ViewType = viewType; + + for (int i = 0; i < _ContentSettingDropdown.Settings.Count; i++) + { + int flag; + if (int.TryParse(node.GetAttribute(_ContentSettingDropdown.Settings[i].Replace(' ', '_')), out flag)) + _ContentSettingDropdown.Selection.Add(flag); + + } + ContentFilter.UpdateFilterVisability(_ContentSettingDropdown); } /// From 131060efa7c1030ae2010acfcd7b4eedb5fc3a90 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Fri, 28 Jul 2023 12:48:43 +0200 Subject: [PATCH 02/40] bug fix --- Source/Editor/Content/ContentFilter.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Content/ContentFilter.cs b/Source/Editor/Content/ContentFilter.cs index 2cbd78e65..12cca5c41 100644 --- a/Source/Editor/Content/ContentFilter.cs +++ b/Source/Editor/Content/ContentFilter.cs @@ -195,7 +195,7 @@ namespace FlaxEditor.Content { engine.Visible = false; engine.Folder.Visible = false; - + foreach (var item in plugins) { item.Visible = false; @@ -218,7 +218,6 @@ namespace FlaxEditor.Content { engine.Visible = true; engine.Folder.Visible = true; - Editor.Instance.Windows.ContentWin.RefreshView(engine); } if (dropdown.Selection[i] == 1)//Show Plugin Content @@ -227,7 +226,6 @@ namespace FlaxEditor.Content { item.Visible = true; item.Folder.Visible = true; - Editor.Instance.Windows.ContentWin.RefreshView(item); } } @@ -244,19 +242,16 @@ namespace FlaxEditor.Content gameSettings.Visible = true; settings.Visible = true; settings.Folder.Visible = true; - Editor.Instance.Windows.ContentWin.RefreshView((ContentTreeNode)settings.Parent); } if (dropdown.Selection[i] == 4)//"Show Shader Source" { shaders.Visible = true; shaders.Folder.Visible = true; - Editor.Instance.Windows.ContentWin.RefreshView(shaders); } } - - - + engine.ParentTree.PerformLayout(); + Editor.Instance.Windows.ContentWin.RefreshView(); } } } From fb69bc64f8dc0c85c244f1742e87558b579bb2ac Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 24 Aug 2023 11:38:14 -0500 Subject: [PATCH 03/40] Add automated plugin project creation and Git cloning. --- Source/Editor/Windows/PluginsWindow.cs | 556 ++++++++++++++++++++++++- 1 file changed, 554 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index 694551345..451e58afa 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -2,11 +2,19 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; using FlaxEditor.GUI; +using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Tabs; using FlaxEngine; using FlaxEngine.GUI; +using FlaxEngine.Json; +using Debug = FlaxEngine.Debug; namespace FlaxEditor.Windows { @@ -17,6 +25,8 @@ namespace FlaxEditor.Windows public sealed class PluginsWindow : EditorWindow { private Tabs _tabs; + private Button _addPluginProjectButton; + private Button _cloneProjectButton; private readonly List _categories = new List(); private readonly Dictionary _entries = new Dictionary(); @@ -174,19 +184,557 @@ namespace FlaxEditor.Windows { Title = "Plugins"; + var vp = new Panel + { + AnchorPreset = AnchorPresets.StretchAll, + Parent = this, + }; + _addPluginProjectButton = new Button + { + Text = "Create Plugin Project", + TooltipText = "Add new plugin project.", + AnchorPreset = AnchorPresets.TopLeft, + LocalLocation = new Float2(70,18), + Size = new Float2(150, 25), + Parent = vp, + }; + _addPluginProjectButton.Clicked += OnAddButtonClicked; + + _cloneProjectButton = new Button + { + Text = "Clone Plugin Project", + TooltipText = "Git Clone a plugin project.", + AnchorPreset = AnchorPresets.TopLeft, + LocalLocation = new Float2(70 + _addPluginProjectButton.Size.X + 8, 18), + Size = new Float2(150, 25), + Parent = vp, + }; + _cloneProjectButton.Clicked += OnCloneProjectButtonClicked; + _tabs = new Tabs { Orientation = Orientation.Vertical, AnchorPreset = AnchorPresets.StretchAll, - Offsets = Margin.Zero, + Offsets = new Margin(0, 0, _addPluginProjectButton.Bottom + 8, 0), TabsSize = new Float2(120, 32), - Parent = this + Parent = vp }; OnPluginsChanged(); PluginManager.PluginsChanged += OnPluginsChanged; } + private void OnCloneProjectButtonClicked() + { + var popup = new ContextMenuBase + { + Size = new Float2(300, 125), + ClipChildren = false, + CullChildren = false, + }; + popup.Show(_cloneProjectButton, new Float2(_cloneProjectButton.Width, 0)); + + var nameLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + AutoWidth = true, + Text = "Name", + HorizontalAlignment = TextAlignment.Near, + }; + nameLabel.LocalY += 10; + + var nameTextBox = new TextBox + { + Parent = popup, + WatermarkText = "Plugin Name", + TooltipText = "If left blank, this will take the git name.", + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + nameTextBox.LocalX += (300 - (10)) * 0.5f; + nameTextBox.LocalY += 10; + nameLabel.LocalX += (300 - (nameLabel.Width + nameTextBox.Width)) * 0.5f + 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, "Plugins", nameTextBox.Text); + if (Directory.Exists(pluginPath)) + { + nameTextBox.BorderColor = Color.Red; + nameTextBox.BorderSelectedColor = Color.Red; + } + else + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + } + }; + + var gitPathLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + AutoWidth = true, + Text = "Git Path", + HorizontalAlignment = TextAlignment.Near, + }; + gitPathLabel.LocalX += (300 - gitPathLabel.Width) * 0.5f; + gitPathLabel.LocalY += 35; + + var gitPathTextBox = new TextBox + { + Parent = popup, + WatermarkText = "https://github.com/FlaxEngine/ExamplePlugin.git", + AnchorPreset = AnchorPresets.TopLeft, + Size = new Float2(280, TextBox.DefaultHeight), + IsMultiline = false, + }; + gitPathTextBox.LocalY += 60; + gitPathTextBox.LocalX += 10; + + var submitButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Clone", + Width = 70, + }; + submitButton.LocalX += 300 * 0.5f - submitButton.Width - 10; + submitButton.LocalY += 90; + + submitButton.Clicked += () => + { + if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text)) && !string.IsNullOrEmpty(nameTextBox.Text)) + { + Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Cannot create plugin due to name conflict."); + return; + } + OnCloneButtonClicked(nameTextBox.Text, gitPathTextBox.Text); + nameTextBox.Clear(); + gitPathTextBox.Clear(); + popup.Hide(); + }; + + var cancelButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Cancel", + Width = 70, + }; + cancelButton.LocalX += 300 * 0.5f + 10; + cancelButton.LocalY += 90; + + cancelButton.Clicked += () => + { + nameTextBox.Clear(); + gitPathTextBox.Clear(); + popup.Hide(); + }; + } + + private async void OnCloneButtonClicked(string pluginName, string gitPath) + { + if (string.IsNullOrEmpty(gitPath)) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to create plugin project due to no GIT path."); + return; + } + if (string.IsNullOrEmpty(pluginName)) + { + var split = gitPath.Split('/'); + if (string.IsNullOrEmpty(split[^1])) + { + var name = split[^2].Replace(".git", ""); + pluginName = name; + } + else + { + var name = split[^1].Replace(".git", ""); + pluginName = name; + } + } + + var clonePath = Path.Combine(Globals.ProjectFolder, "Plugins", pluginName); + if (!Directory.Exists(clonePath)) + Directory.CreateDirectory(clonePath); + else + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Plugin Name is already used. Pick a different Name."); + return; + } + try + { + // Start git clone + var gitProcess = new Process(); + gitProcess.StartInfo.FileName = "git"; + gitProcess.StartInfo.Arguments = $"clone {gitPath} \"{clonePath}\""; + gitProcess.StartInfo.UseShellExecute = false; + gitProcess.StartInfo.RedirectStandardOutput = true; + gitProcess.StartInfo.RedirectStandardError = true; + + gitProcess.Start(); + await gitProcess.WaitForExitAsync(); + + string output = await gitProcess.StandardOutput.ReadToEndAsync(); + string error = await gitProcess.StandardError.ReadToEndAsync(); + Debug.Logger.LogHandler.LogWrite(LogType.Info, $"{output}"); + Debug.Logger.LogHandler.LogWrite(LogType.Info, $"{error}"); + } + catch (Exception e) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed Gir process. {e}"); + return; + } + + Debug.Logger.LogHandler.LogWrite(LogType.Info, $"Plugin project has been cloned."); + + // Find project config file. Could be different then what the user named the folder. + var files = Directory.GetFiles(clonePath); + string pluginProjectName = ""; + foreach (var file in files) + { + if (file.Contains(".flaxproj", StringComparison.OrdinalIgnoreCase)) + { + pluginProjectName = Path.GetFileNameWithoutExtension(file); + Debug.Log(pluginProjectName); + } + } + + if (string.IsNullOrEmpty(pluginProjectName)) + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to find plugin project file to add to Project config. Please add manually."); + else + { + await AddReferenceToProject(pluginName, pluginProjectName); + MessageBox.Show($"{pluginName} has been successfully cloned. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); + } + } + + private void OnAddButtonClicked() + { + var popup = new ContextMenuBase + { + Size = new Float2(230, 125), + ClipChildren = false, + CullChildren = false, + }; + popup.Show(_addPluginProjectButton, new Float2(_addPluginProjectButton.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 = "Plugin 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, "Plugins", nameTextBox.Text); + if (Directory.Exists(pluginPath)) + { + nameTextBox.BorderColor = Color.Red; + nameTextBox.BorderSelectedColor = Color.Red; + } + else + { + nameTextBox.BorderColor = defaultTextBoxBorderColor; + nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; + } + }; + + var versionLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Version", + HorizontalAlignment = TextAlignment.Near, + }; + versionLabel.LocalX += 10; + versionLabel.LocalY += 35; + + var versionTextBox = new TextBox + { + Parent = popup, + WatermarkText = "1.0.0", + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + versionTextBox.LocalY += 35; + versionTextBox.LocalX += 100; + + var companyLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Company", + HorizontalAlignment = TextAlignment.Near, + }; + companyLabel.LocalX += 10; + companyLabel.LocalY += 60; + + var companyTextBox = new TextBox + { + Parent = popup, + WatermarkText = "Company Name", + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + companyTextBox.LocalY += 60; + companyTextBox.LocalX += 100; + + var submitButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Create", + Width = 70, + }; + submitButton.LocalX += 40; + submitButton.LocalY += 90; + + submitButton.Clicked += () => + { + if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text))) + { + Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Cannot create plugin due to name conflict."); + return; + } + OnCreateButtonClicked(nameTextBox.Text, versionTextBox.Text, companyTextBox.Text); + nameTextBox.Clear(); + versionTextBox.Clear(); + companyTextBox.Clear(); + 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(); + versionTextBox.Clear(); + companyTextBox.Clear(); + popup.Hide(); + }; + } + + private async void OnCreateButtonClicked(string pluginName, string pluginVersion, string companyName) + { + if (string.IsNullOrEmpty(pluginName)) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to create plugin project due to no plugin name."); + return; + } + + var templateUrl = "https://github.com/FlaxEngine/ExamplePlugin/archive/refs/heads/master.zip"; + var localTemplateFolderLocation = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache"); + if (!Directory.Exists(localTemplateFolderLocation)) + Directory.CreateDirectory(localTemplateFolderLocation); + var localTemplatePath = localTemplateFolderLocation + @"\TemplatePlugin.zip"; + + try + { + // Download example plugin + using (HttpClient client = new HttpClient()) + { + byte[] zipBytes = await client.GetByteArrayAsync(templateUrl); + await File.WriteAllBytesAsync(!File.Exists(localTemplatePath) ? Path.Combine(localTemplatePath) : Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip", zipBytes); + + Debug.Logger.LogHandler.LogWrite(LogType.Info, "Template for plugin project has downloaded"); + } + } + catch (Exception e) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to download template project. Trying to use local file. {e}"); + if (!File.Exists(localTemplatePath)) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to use local file. Does not exist."); + return; + } + } + + // Check if any changes in new downloaded file + if (File.Exists(Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip")) + { + var localTemplatePath2 = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip"; + bool areDifferent = false; + using (var zip1 = ZipFile.OpenRead(localTemplatePath)) + { + using (var zip2 = ZipFile.OpenRead(localTemplatePath2)) + { + if (zip1.Entries.Count != zip2.Entries.Count) + { + areDifferent = true; + } + + foreach (ZipArchiveEntry entry1 in zip1.Entries) + { + ZipArchiveEntry entry2 = zip2.GetEntry(entry1.FullName); + if (entry2 == null) + { + areDifferent = true; + break; + } + if (entry1.Length != entry2.Length || entry1.CompressedLength != entry2.CompressedLength || entry1.Crc32 != entry2.Crc32) + { + areDifferent = true; + break; + } + } + } + } + if (areDifferent) + { + File.Delete(localTemplatePath); + File.Move(localTemplatePath2, localTemplatePath); + } + else + { + File.Delete(localTemplatePath2); + } + } + + var extractPath = Path.Combine(Globals.ProjectFolder, "Plugins"); + if (!Directory.Exists(extractPath)) + Directory.CreateDirectory(extractPath); + + try + { + await Task.Run(() => ZipFile.ExtractToDirectory(localTemplatePath, extractPath)); + Debug.Logger.LogHandler.LogWrite(LogType.Info, "Template for plugin project successfully moved to project."); + } + catch (IOException e) + { + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to add plugin to project. {e}"); + } + + var oldpluginPath = Path.Combine(extractPath, "ExamplePlugin-master"); + var newPluginPath = Path.Combine(extractPath , pluginName); + Directory.Move(oldpluginPath, newPluginPath); + + var oldFlaxProjFile = Path.Combine(newPluginPath, "ExamplePlugin.flaxproj"); + var newFlaxProjFile = Path.Combine(newPluginPath, $"{pluginName}.flaxproj"); + File.Move(oldFlaxProjFile, newFlaxProjFile); + + var readme = Path.Combine(newPluginPath, "README.md"); + if (File.Exists(readme)) + File.Delete(readme); + var license = Path.Combine(newPluginPath, "LICENSE"); + if (File.Exists(license)) + File.Delete(license); + + // Flax plugin project file + var flaxPluginProjContents = JsonSerializer.Deserialize(await File.ReadAllTextAsync(newFlaxProjFile)); + flaxPluginProjContents.Name = pluginName; + if (!string.IsNullOrEmpty(pluginVersion)) + flaxPluginProjContents.Version = new Version(pluginVersion); + if (!string.IsNullOrEmpty(companyName)) + flaxPluginProjContents.Company = companyName; + flaxPluginProjContents.GameTarget = $"{pluginName}Target"; + flaxPluginProjContents.EditorTarget = $"{pluginName}EditorTarget"; + await File.WriteAllTextAsync(newFlaxProjFile, JsonSerializer.Serialize(flaxPluginProjContents, typeof(ProjectInfo))); + + // Rename source directories + var sourcePath = Path.Combine(newPluginPath, "Source"); + var sourceDirectories = Directory.GetDirectories(sourcePath); + foreach (var directory in sourceDirectories) + { + var files = Directory.GetFiles(directory); + foreach (var file in files) + { + if (file.Contains("MyPlugin.cs") || file.Contains("MyPluginEditor.cs")) + { + File.Delete(file); + continue; + } + + var fileText = await File.ReadAllTextAsync(file); + await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginName)); + var fileName = Path.GetFileName(file).Replace("ExamplePlugin", pluginName); + File.Move(file, Path.Combine(directory, fileName)); + } + + var newName = directory.Replace("ExamplePlugin", pluginName); + Directory.Move(directory, newName); + } + + // Rename targets + var targetFiles = Directory.GetFiles(sourcePath); + foreach (var file in targetFiles) + { + var fileText = await File.ReadAllTextAsync(file); + await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginName)); + var newName = file.Replace("ExamplePlugin", pluginName); + File.Move(file, newName); + } + Debug.Logger.LogHandler.LogWrite(LogType.Info, $"Plugin project {pluginName} has successfully been created."); + + await AddReferenceToProject(pluginName, pluginName); + MessageBox.Show($"{pluginName} has been successfully created. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); + } + + private async Task AddReferenceToProject(string pluginFolderName, string pluginName) + { + // Project flax config file + var flaxProjPath = Editor.GameProject.ProjectPath; + if (File.Exists(flaxProjPath)) + { + var flaxProjContents = JsonSerializer.Deserialize(await File.ReadAllTextAsync(flaxProjPath)); + var oldReferences = flaxProjContents.References; + var references = new List(oldReferences); + var newPath = $"$(ProjectPath)/Plugins/{pluginFolderName}/{pluginName}.flaxproj"; + if (!references.Exists(x => string.Equals(x.Name, newPath))) + { + var newReference = new ProjectInfo.Reference + { + Name = newPath, + }; + references.Add(newReference); + } + flaxProjContents.References = references.ToArray(); + await File.WriteAllTextAsync(flaxProjPath, JsonSerializer.Serialize(flaxProjContents, typeof(ProjectInfo))); + } + } + + private void OnPluginsChanged() { List toRemove = null; @@ -312,6 +860,10 @@ namespace FlaxEditor.Windows /// public override void OnDestroy() { + if (_addPluginProjectButton != null) + _addPluginProjectButton.Clicked -= OnAddButtonClicked; + if (_cloneProjectButton != null) + _cloneProjectButton.Clicked -= OnCloneProjectButtonClicked; PluginManager.PluginsChanged -= OnPluginsChanged; base.OnDestroy(); From 51664fc236dfc748185dab7ab0ac6323defb1ede Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 24 Aug 2023 13:51:08 -0500 Subject: [PATCH 04/40] Use pltform process. --- Source/Editor/Windows/PluginsWindow.cs | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index 451e58afa..deac0d9ce 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; @@ -376,20 +375,12 @@ namespace FlaxEditor.Windows try { // Start git clone - var gitProcess = new Process(); - gitProcess.StartInfo.FileName = "git"; - gitProcess.StartInfo.Arguments = $"clone {gitPath} \"{clonePath}\""; - gitProcess.StartInfo.UseShellExecute = false; - gitProcess.StartInfo.RedirectStandardOutput = true; - gitProcess.StartInfo.RedirectStandardError = true; - - gitProcess.Start(); - await gitProcess.WaitForExitAsync(); - - string output = await gitProcess.StandardOutput.ReadToEndAsync(); - string error = await gitProcess.StandardError.ReadToEndAsync(); - Debug.Logger.LogHandler.LogWrite(LogType.Info, $"{output}"); - Debug.Logger.LogHandler.LogWrite(LogType.Info, $"{error}"); + var settings = new CreateProcessSettings(); + settings.FileName = "git"; + settings.Arguments = $"clone {gitPath} \"{clonePath}\""; + settings.ShellExecute = false; + settings.LogOutput = true; + Platform.CreateProcess(ref settings); } catch (Exception e) { From 0bbd619e329a355fff606afa4fff6c1bb1f06832 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 24 Aug 2023 13:54:55 -0500 Subject: [PATCH 05/40] Code cleanup --- Source/Editor/Windows/PluginsWindow.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index deac0d9ce..cf68c6ad8 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -384,7 +384,7 @@ namespace FlaxEditor.Windows } catch (Exception e) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed Gir process. {e}"); + Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed Git process. {e}"); return; } @@ -559,7 +559,7 @@ namespace FlaxEditor.Windows var localTemplateFolderLocation = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache"); if (!Directory.Exists(localTemplateFolderLocation)) Directory.CreateDirectory(localTemplateFolderLocation); - var localTemplatePath = localTemplateFolderLocation + @"\TemplatePlugin.zip"; + var localTemplatePath = Path.Combine(localTemplateFolderLocation, "TemplatePlugin.zip"); try { @@ -567,7 +567,7 @@ namespace FlaxEditor.Windows using (HttpClient client = new HttpClient()) { byte[] zipBytes = await client.GetByteArrayAsync(templateUrl); - await File.WriteAllBytesAsync(!File.Exists(localTemplatePath) ? Path.Combine(localTemplatePath) : Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip", zipBytes); + await File.WriteAllBytesAsync(!File.Exists(localTemplatePath) ? Path.Combine(localTemplatePath) : Path.Combine(Editor.LocalCachePath, "TemplatePluginCache" , "TemplatePlugin1.zip"), zipBytes); Debug.Logger.LogHandler.LogWrite(LogType.Info, "Template for plugin project has downloaded"); } @@ -583,9 +583,9 @@ namespace FlaxEditor.Windows } // Check if any changes in new downloaded file - if (File.Exists(Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip")) + if (File.Exists(Path.Combine(Editor.LocalCachePath, "TemplatePluginCache", "TemplatePlugin1.zip"))) { - var localTemplatePath2 = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache") + @"\TemplatePlugin1.zip"; + var localTemplatePath2 = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache", "TemplatePlugin1.zip"); bool areDifferent = false; using (var zip1 = ZipFile.OpenRead(localTemplatePath)) { From ff2f239858a6bc1690d670d9985e44c3ec655890 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 25 Aug 2023 08:17:09 -0500 Subject: [PATCH 06/40] Add system reference. --- Source/Editor/Editor.Build.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Editor.Build.cs b/Source/Editor/Editor.Build.cs index a971dbefa..f497225bb 100644 --- a/Source/Editor/Editor.Build.cs +++ b/Source/Editor/Editor.Build.cs @@ -41,6 +41,7 @@ public class Editor : EditorModule options.ScriptingAPI.SystemReferences.Add("System.Xml.ReaderWriter"); options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions"); options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter"); + options.ScriptingAPI.SystemReferences.Add("System.IO.Compression.ZipFile"); // Enable optimizations for Editor, disable this for debugging the editor if (options.Configuration == TargetConfiguration.Development) From 5b5c298a960a7d096af06ef512ec56d406dfa4ce Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 1 Sep 2023 06:35:52 -0500 Subject: [PATCH 07/40] Add remove tag method to actor. --- Source/Engine/Level/Actor.cpp | 5 +++++ Source/Engine/Level/Actor.h | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 8185f3b9d..a9d136b30 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -476,6 +476,11 @@ void Actor::AddTag(const Tag& tag) Tags.AddUnique(tag); } +void Actor::RemoveTag(const Tag& tag) +{ + Tags.Remove(tag); +} + PRAGMA_DISABLE_DEPRECATION_WARNINGS const String& Actor::GetTag() const diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 510bc9eb8..8643dba05 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -136,6 +136,12 @@ public: /// The tag to add. API_FUNCTION() void AddTag(const Tag& tag); + /// + /// Removes a tag to the actor + /// + /// The tag to remove. + API_FUNCTION() void RemoveTag(const Tag& tag); + /// /// Gets the name of the tag. /// [Deprecated in v1.5] From 53861c479580ba36aa956fb282578be3b5acd924 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 1 Sep 2023 14:28:26 -0500 Subject: [PATCH 08/40] Improve content item in tile view. Decrease margin between list view items. --- Source/Editor/Content/GUI/ContentView.cs | 15 ++--- Source/Editor/Content/Items/ContentItem.cs | 71 ++++++++++++++++++---- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 6065ca9f8..40a06c1b6 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -711,7 +711,7 @@ namespace FlaxEditor.Content.GUI protected override void PerformLayoutBeforeChildren() { float width = GetClientArea().Width; - float x = 0, y = 0; + float x = 0, y = 1; float viewScale = _viewScale * 0.97f; switch (ViewType) @@ -722,21 +722,22 @@ namespace FlaxEditor.Content.GUI int itemsToFit = Mathf.FloorToInt(width / defaultItemsWidth) - 1; if (itemsToFit < 1) itemsToFit = 1; - float itemsWidth = width / Mathf.Max(itemsToFit, 1); + int xSpace = 4; + float itemsWidth = width / Mathf.Max(itemsToFit, 1) - xSpace; float itemsHeight = itemsWidth / defaultItemsWidth * (ContentItem.DefaultHeight * viewScale); var flooredItemsWidth = Mathf.Floor(itemsWidth); var flooredItemsHeight = Mathf.Floor(itemsHeight); - x = itemsToFit == 1 ? 0 : itemsWidth / itemsToFit; + x = itemsToFit == 1 ? 1 : itemsWidth / itemsToFit + xSpace; for (int i = 0; i < _children.Count; i++) { var c = _children[i]; c.Bounds = new Rectangle(Mathf.Floor(x), Mathf.Floor(y), flooredItemsWidth, flooredItemsHeight); - x += itemsWidth + itemsWidth / itemsToFit; + x += (itemsWidth + xSpace) + (itemsWidth + xSpace) / itemsToFit; if (x + itemsWidth > width) { - x = itemsToFit == 1 ? 0 : itemsWidth / itemsToFit; - y += itemsHeight + 5; + x = itemsToFit == 1 ? 1 : itemsWidth / itemsToFit + xSpace; + y += itemsHeight + 7; } } if (x > 0) @@ -751,7 +752,7 @@ namespace FlaxEditor.Content.GUI { var c = _children[i]; c.Bounds = new Rectangle(x, y, width, itemsHeight); - y += itemsHeight + 5; + y += itemsHeight + 1; } y += 40.0f; diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 66825fb42..41f35b4c7 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -483,6 +483,30 @@ namespace FlaxEditor.Content else Render2D.FillRectangle(rectangle, Color.Black); } + + /// + /// Draws the item thumbnail. + /// + /// The thumbnail rectangle. + /// /// Whether or not to draw the shadow. Overrides DrawShadow. + public void DrawThumbnail(ref Rectangle rectangle, bool shadow) + { + // Draw shadow + if (shadow) + { + const float thumbnailInShadowSize = 50.0f; + var shadowRect = rectangle.MakeExpanded((DefaultThumbnailSize - thumbnailInShadowSize) * rectangle.Width / DefaultThumbnailSize * 1.3f); + if (!_shadowIcon.IsValid) + _shadowIcon = Editor.Instance.Icons.AssetShadow128; + Render2D.DrawSprite(_shadowIcon, shadowRect); + } + + // Draw thumbnail + if (_thumbnail.IsValid) + Render2D.DrawSprite(_thumbnail, rectangle); + else + Render2D.FillRectangle(rectangle, Color.Black); + } /// /// Gets the amount of references to that item. @@ -655,9 +679,32 @@ namespace FlaxEditor.Content { case ContentViewType.Tiles: { - var thumbnailSize = size.X - 2 * DefaultMarginSize; - thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize); + var thumbnailSize = size.X; + thumbnailRect = new Rectangle(0, 0, thumbnailSize, thumbnailSize); nameAlignment = TextAlignment.Center; + + // Small shadow + var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1); + var color = Color.Black.AlphaMultiplied(0.2f); + Render2D.FillRectangle(shadowRect, color); + + Render2D.FillRectangle(clientRect, style.LightBackground); + Render2D.FillRectangle(TextRectangle, style.TextBoxBackground); + var accentHeight = 2 * view.ViewScale; + var barRect = new Rectangle(0, thumbnailRect.Height - accentHeight, clientRect.Width, accentHeight); + Render2D.FillRectangle(barRect, Color.DimGray); + + DrawThumbnail(ref thumbnailRect, false); + if (isSelected) + { + Render2D.FillRectangle(textRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + Render2D.DrawRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + } + else if (IsMouseOver) + { + Render2D.FillRectangle(textRect, style.BackgroundHighlighted); + Render2D.DrawRectangle(clientRect, style.BackgroundHighlighted); + } break; } case ContentViewType.List: @@ -665,23 +712,21 @@ namespace FlaxEditor.Content var thumbnailSize = size.Y - 2 * DefaultMarginSize; thumbnailRect = new Rectangle(DefaultMarginSize, DefaultMarginSize, thumbnailSize, thumbnailSize); nameAlignment = TextAlignment.Near; + + if (isSelected) + Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + else if (IsMouseOver) + Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); + + DrawThumbnail(ref thumbnailRect); break; } default: throw new ArgumentOutOfRangeException(); } - - // Draw background - if (isSelected) - Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); - else if (IsMouseOver) - Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); - - // Draw preview - DrawThumbnail(ref thumbnailRect); - + // Draw short name Render2D.PushClip(ref textRect); - Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 0.75f, 0.95f); + Render2D.DrawText(style.FontMedium, ShowFileExtension || view.ShowFileExtensions ? FileName : ShortName, textRect, style.Foreground, nameAlignment, TextAlignment.Center, TextWrapping.WrapWords, 1f, 0.95f); Render2D.PopClip(); } From 67909e33c824068df4f4dd3a19aecb908dfefff2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 1 Sep 2023 16:37:14 -0500 Subject: [PATCH 09/40] Add difference for folders. --- Source/Editor/Content/Items/ContentItem.cs | 55 +++++++++++++++------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 41f35b4c7..b27159aa0 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -683,27 +683,46 @@ namespace FlaxEditor.Content thumbnailRect = new Rectangle(0, 0, thumbnailSize, thumbnailSize); nameAlignment = TextAlignment.Center; - // Small shadow - var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1); - var color = Color.Black.AlphaMultiplied(0.2f); - Render2D.FillRectangle(shadowRect, color); - - Render2D.FillRectangle(clientRect, style.LightBackground); - Render2D.FillRectangle(TextRectangle, style.TextBoxBackground); - var accentHeight = 2 * view.ViewScale; - var barRect = new Rectangle(0, thumbnailRect.Height - accentHeight, clientRect.Width, accentHeight); - Render2D.FillRectangle(barRect, Color.DimGray); - - DrawThumbnail(ref thumbnailRect, false); - if (isSelected) + if (this is ContentFolder) { - Render2D.FillRectangle(textRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); - Render2D.DrawRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + // Small shadow + var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1); + var color = Color.Black.AlphaMultiplied(0.2f); + Render2D.FillRectangle(shadowRect, color); + Render2D.FillRectangle(clientRect, style.Background.RGBMultiplied(1.25f)); + + if (isSelected) + Render2D.FillRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + else if (IsMouseOver) + Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); + + DrawThumbnail(ref thumbnailRect, false); } - else if (IsMouseOver) + else { - Render2D.FillRectangle(textRect, style.BackgroundHighlighted); - Render2D.DrawRectangle(clientRect, style.BackgroundHighlighted); + // Small shadow + var shadowRect = new Rectangle(2, 2, clientRect.Width + 1, clientRect.Height + 1); + var color = Color.Black.AlphaMultiplied(0.2f); + Render2D.FillRectangle(shadowRect, color); + + Render2D.FillRectangle(clientRect, style.Background.RGBMultiplied(1.25f)); + Render2D.FillRectangle(TextRectangle, style.LightBackground); + + var accentHeight = 2 * view.ViewScale; + var barRect = new Rectangle(0, thumbnailRect.Height - accentHeight, clientRect.Width, accentHeight); + Render2D.FillRectangle(barRect, Color.DimGray); + + DrawThumbnail(ref thumbnailRect, false); + if (isSelected) + { + Render2D.FillRectangle(textRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + Render2D.DrawRectangle(clientRect, Parent.ContainsFocus ? style.BackgroundSelected : style.LightBackground); + } + else if (IsMouseOver) + { + Render2D.FillRectangle(textRect, style.BackgroundHighlighted); + Render2D.DrawRectangle(clientRect, style.BackgroundHighlighted); + } } break; } From 92a5432d224f105e5f526a47656dbddd82fc3e60 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 2 Sep 2023 13:13:34 -0500 Subject: [PATCH 10/40] Sort plugin projects in content tree. --- Source/Editor/Windows/ContentWindow.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index b1f04c8f5..9d782b13b 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -931,6 +931,7 @@ namespace FlaxEditor.Windows // Add game project on top, plugins in the middle and engine at bottom _root.AddChild(Editor.ContentDatabase.Game); + Editor.ContentDatabase.Projects.Sort(); foreach (var project in Editor.ContentDatabase.Projects) { project.SortChildrenRecursive(); From afec6afe4c9e9d57c903fd1e363db5258f6bc06d Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 3 Sep 2023 14:04:46 +0300 Subject: [PATCH 11/40] Fix managed UTF-8 string allocation not using correct encoding --- Source/Engine/Engine/NativeInterop.Unmanaged.cs | 4 ++-- Source/Engine/Scripting/Runtime/DotNet.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index 879f804d2..2c1bab744 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -581,9 +581,9 @@ namespace FlaxEngine.Interop } [UnmanagedCallersOnly] - internal static IntPtr NewStringLength(sbyte* text, int length) + internal static IntPtr NewStringUTF8(sbyte* text, int length) { - return ManagedString.ToNativeWeak(new string(text, 0, length)); + return ManagedString.ToNativeWeak(new string(text, 0, length, System.Text.Encoding.UTF8)); } [UnmanagedCallersOnly] diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index f4ae5b1ec..1bcef50b8 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -365,8 +365,8 @@ MString* MCore::String::GetEmpty(MDomain* domain) MString* MCore::String::New(const char* str, int32 length, MDomain* domain) { - static void* NewStringLengthPtr = GetStaticMethodPointer(TEXT("NewStringLength")); - return (MString*)CallStaticMethod(NewStringLengthPtr, str, length); + static void* NewStringUTF8Ptr = GetStaticMethodPointer(TEXT("NewStringUTF8")); + return (MString*)CallStaticMethod(NewStringUTF8Ptr, str, length); } MString* MCore::String::New(const Char* str, int32 length, MDomain* domain) From 924e1c6d81efca81b3aaf3af2b1f863e349c4ec8 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 3 Sep 2023 06:36:29 -0500 Subject: [PATCH 12/40] Fix right click deselecting scene tree nodes. --- Source/Editor/GUI/Tree/TreeNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index ced70a281..1a1ebe5a1 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -762,7 +762,7 @@ namespace FlaxEditor.GUI.Tree // Add/Remove tree.AddOrRemoveSelection(this); } - else + else if (button != MouseButton.Right) { // Select tree.Select(this); From 39ecfe8c242940126c394ac8bd2c2e042beff4a2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 3 Sep 2023 12:11:33 -0500 Subject: [PATCH 13/40] Small bug fix --- Source/Editor/GUI/Tree/TreeNode.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 1a1ebe5a1..7e3af05bf 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -762,7 +762,11 @@ namespace FlaxEditor.GUI.Tree // Add/Remove tree.AddOrRemoveSelection(this); } - else if (button != MouseButton.Right) + else if (button == MouseButton.Right && tree.Selection.Contains(this)) + { + // Do nothing + } + else { // Select tree.Select(this); From 20f35e332f309a13512b2982627fd7736da9ab02 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 4 Sep 2023 20:15:59 -0500 Subject: [PATCH 14/40] Add slider control --- Source/Engine/UI/GUI/Common/Slider.cs | 347 ++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 Source/Engine/UI/GUI/Common/Slider.cs diff --git a/Source/Engine/UI/GUI/Common/Slider.cs b/Source/Engine/UI/GUI/Common/Slider.cs new file mode 100644 index 000000000..06c73f15e --- /dev/null +++ b/Source/Engine/UI/GUI/Common/Slider.cs @@ -0,0 +1,347 @@ +using System; + +namespace FlaxEngine.GUI; + +/// +/// The slider control. +/// +public class Slider : ContainerControl +{ + /// + /// The minimum value. + /// + protected float _minimum; + + /// + /// The maximum value. + /// + protected float _maximum = 100f; + + /// + /// Gets or sets the minimum value. + /// + [EditorOrder(20), Tooltip("The minimum value.")] + public float Minimum + { + get => _minimum; + set + { + if (value > _maximum) + throw new ArgumentOutOfRangeException(); + if (WholeNumbers) + value = Mathf.RoundToInt(value); + _minimum = value; + if (Value < _minimum) + Value = _minimum; + } + } + + /// + /// Gets or sets the maximum value. + /// + [EditorOrder(30), Tooltip("The maximum value.")] + public float Maximum + { + get => _maximum; + set + { + if (value < _minimum || Mathf.IsZero(value)) + throw new ArgumentOutOfRangeException(); + if (WholeNumbers) + value = Mathf.RoundToInt(value); + _maximum = value; + if (Value > _maximum) + Value = _maximum; + } + } + + private float _value = 100f; + private Rectangle _thumbRect; + private float _thumbCenter; + private Float2 _thumbSize = new Float2(16, 16); + private bool _isSliding; + + /// + /// Gets or sets the value (normalized to range 0-100). + /// + [EditorOrder(10), Tooltip("The current value.")] + public float Value + { + get => _value; + set + { + value = Mathf.Clamp(value, Minimum, Maximum); + if (WholeNumbers) + value = Mathf.RoundToInt(value); + if (!Mathf.NearEqual(value, _value)) + { + _value = value; + + // Update + UpdateThumb(); + ValueChanged?.Invoke(); + } + } + } + + /// + /// The height of the track. + /// + [EditorOrder(40), Tooltip("The track height.")] + public int TrackHeight { get; set; } = 2; + + /// + /// The thumb size. + /// + [EditorOrder(41), Tooltip("The size of the thumb.")] + public Float2 ThumbSize { + get => _thumbSize; + set + { + _thumbSize = value; + UpdateThumb(); + } + } + + /// + /// Whether to fill the track. + /// + [EditorOrder(42), Tooltip("Fill the track.")] + public bool FillTrack = true; + + /// + /// Whether to use whole numbers. + /// + [EditorOrder(43), Tooltip("Use whole numbers.")] + public bool WholeNumbers = false; + + /// + /// The color of the slider track line + /// + [EditorDisplay("Track Style"), EditorOrder(2010), Tooltip("The color of the slider track line."), ExpandGroups] + public Color TrackLineColor { get; set; } + + /// + /// The color of the slider fill track line + /// + [EditorDisplay("Track Style"), EditorOrder(2011), VisibleIf(nameof(FillTrack)), Tooltip("The color of the slider fill track line.")] + public Color TrackFillLineColor { get; set; } + + /// + /// Gets the size of the track. + /// + private float TrackWidth => Width; + + /// + /// Gets or sets the brush used for slider track drawing. + /// + [EditorDisplay("Track Style"), EditorOrder(2012), Tooltip("The brush used for slider track drawing.")] + public IBrush TrackBrush { get; set; } + + /// + /// Gets or sets the brush used for slider fill track drawing. + /// + [EditorDisplay("Track Style"), EditorOrder(2013), VisibleIf(nameof(FillTrack)), Tooltip("The brush used for slider fill track drawing.")] + public IBrush FillTrackBrush { get; set; } + + /// + /// The color of the slider thumb when it's not selected + /// + [EditorDisplay("Thumb Style"), EditorOrder(2030), Tooltip("The color of the slider thumb when it's not selected."), ExpandGroups] + public Color ThumbColor { get; set; } + + /// + /// The color of the slider thumb when it's selected + /// + [EditorDisplay("Thumb Style"), EditorOrder(2031), Tooltip("The color of the slider thumb when it's selected.")] + public Color ThumbColorSelected { get; set; } + + /// + /// Gets or sets the brush used for slider thumb drawing. + /// + [EditorDisplay("Thumb Style"), EditorOrder(2032), Tooltip("The brush of the slider thumb.")] + public IBrush ThumbBrush { get; set; } + + /// + /// Gets a value indicating whether user is using a slider. + /// + [HideInEditor] + public bool IsSliding => _isSliding; + + /// + /// Occurs when sliding starts. + /// + public event Action SlidingStart; + + /// + /// Occurs when sliding ends. + /// + public event Action SlidingEnd; + + /// + /// Occurs when value gets changed. + /// + public event Action ValueChanged; + + /// + /// Initializes a new instance of the class. + /// + public Slider() + : this(120, 30) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The width. + /// The height. + public Slider(float width, float height) + : base(0, 0, width, height) + { + var style = Style.Current; + TrackLineColor = style.BackgroundHighlighted; + TrackFillLineColor = style.LightBackground; + ThumbColor = style.BackgroundNormal; + ThumbColorSelected = style.BackgroundSelected; + UpdateThumb(); + } + + private void UpdateThumb() + { + // Cache data + float trackSize = TrackWidth; + float range = Maximum - Minimum; + float pixelRange = trackSize - _thumbSize.X; + float perc = (_value - Minimum) / range; + float thumbPosition = (int)(perc * pixelRange); + _thumbCenter = thumbPosition + _thumbSize.X / 2; + _thumbRect = new Rectangle(thumbPosition, (Height - _thumbSize.Y) / 2, _thumbSize.X, _thumbSize.Y); + } + + private void EndSliding() + { + _isSliding = false; + EndMouseCapture(); + SlidingEnd?.Invoke(); + } + + /// + public override void Draw() + { + base.Draw(); + + // Draw track line + //var lineRect = new Rectangle(4, (Height - TrackHeight) / 2, Width - 8, TrackHeight); + var lineRect = new Rectangle(_thumbSize.X / 2, (Height - TrackHeight) / 2, Width - _thumbSize.X, TrackHeight); + if (TrackBrush != null) + TrackBrush.Draw(lineRect, TrackLineColor); + else + Render2D.FillRectangle(lineRect, TrackLineColor); + + // Draw track fill + if (FillTrack) + { + var fillLineRect = new Rectangle(_thumbSize.X / 2, (Height - TrackHeight - 2) / 2, Width - (Width - _thumbCenter) - _thumbSize.X / 2, TrackHeight + 2); + Render2D.PushClip(ref fillLineRect); + if (FillTrackBrush != null) + FillTrackBrush.Draw(lineRect, TrackFillLineColor); + else + Render2D.FillRectangle(lineRect, TrackFillLineColor); + Render2D.PopClip(); + } + + // Draw thumb + var thumbColor = _isSliding ? ThumbColorSelected : ThumbColor; + if (ThumbBrush != null) + ThumbBrush.Draw(_thumbRect, thumbColor); + else + Render2D.FillRectangle(_thumbRect, thumbColor); + } + + /// + public override void OnLostFocus() + { + if (_isSliding) + { + EndSliding(); + } + + base.OnLostFocus(); + } + + /// + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (button == MouseButton.Left) + { + Focus(); + float mousePosition = location.X; + + if (_thumbRect.Contains(ref location)) + { + // Start sliding + _isSliding = true; + StartMouseCapture(); + SlidingStart?.Invoke(); + return true; + } + else + { + // Click change + Value += (mousePosition < _thumbCenter ? -1 : 1) * 10; + } + } + + return base.OnMouseDown(location, button); + } + + /// + public override void OnMouseMove(Float2 location) + { + if (_isSliding) + { + // Update sliding + var slidePosition = location + Root.TrackingMouseOffset; + Value = Mathf.Remap(slidePosition.X, 4, TrackWidth - 4, Minimum, Maximum); + } + else + { + base.OnMouseMove(location); + } + } + + /// + public override bool OnMouseUp(Float2 location, MouseButton button) + { + if (button == MouseButton.Left && _isSliding) + { + EndSliding(); + return true; + } + + return base.OnMouseUp(location, button); + } + + /// + public override void OnEndMouseCapture() + { + // Check if was sliding + if (_isSliding) + { + EndSliding(); + } + else + { + base.OnEndMouseCapture(); + } + } + + /// + protected override void OnSizeChanged() + { + base.OnSizeChanged(); + + UpdateThumb(); + } +} From 65a02f032fd686bbeebeddc32752a25c95ed5b37 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 5 Sep 2023 08:25:25 -0500 Subject: [PATCH 15/40] Expose helpful slider data. --- Source/Engine/UI/GUI/Common/Slider.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Source/Engine/UI/GUI/Common/Slider.cs b/Source/Engine/UI/GUI/Common/Slider.cs index 06c73f15e..b0344f055 100644 --- a/Source/Engine/UI/GUI/Common/Slider.cs +++ b/Source/Engine/UI/GUI/Common/Slider.cs @@ -83,6 +83,24 @@ public class Slider : ContainerControl } } } + + /// + /// The local position of the thumb center + /// + [HideInEditor] + public float ThumbCenter => _thumbCenter; + + /// + /// The local position of the beginning of the track. + /// + [HideInEditor] + public float TrackBeginning => _thumbSize.X / 2; + + /// + /// The local position of the end of the track. + /// + [HideInEditor] + public float TrackEnd => Width - _thumbSize.X / 2; /// /// The height of the track. From d96f19e5c66bc8f54bb806c515ce564d6b75260a Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 5 Sep 2023 08:44:59 -0500 Subject: [PATCH 16/40] Change exposed positionns to float 2 --- Source/Engine/UI/GUI/Common/Slider.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Slider.cs b/Source/Engine/UI/GUI/Common/Slider.cs index b0344f055..d7324ae0e 100644 --- a/Source/Engine/UI/GUI/Common/Slider.cs +++ b/Source/Engine/UI/GUI/Common/Slider.cs @@ -88,19 +88,19 @@ public class Slider : ContainerControl /// The local position of the thumb center /// [HideInEditor] - public float ThumbCenter => _thumbCenter; + public Float2 ThumbCenter => new(_thumbCenter, Height / 2); /// /// The local position of the beginning of the track. /// [HideInEditor] - public float TrackBeginning => _thumbSize.X / 2; + public Float2 TrackBeginning => new(_thumbSize.X / 2, Height / 2); /// /// The local position of the end of the track. /// [HideInEditor] - public float TrackEnd => Width - _thumbSize.X / 2; + public Float2 TrackEnd => new(Width - _thumbSize.X / 2, Height / 2); /// /// The height of the track. From 5ceafaad77cb70d3ac3ffd3975d2d7a483c46f34 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Wed, 6 Sep 2023 05:23:06 +0300 Subject: [PATCH 17/40] WindowBase::Resized delegate --- Source/Engine/Platform/Base/WindowBase.cpp | 1 + Source/Engine/Platform/Base/WindowBase.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Source/Engine/Platform/Base/WindowBase.cpp b/Source/Engine/Platform/Base/WindowBase.cpp index 5cff76361..49b4cc631 100644 --- a/Source/Engine/Platform/Base/WindowBase.cpp +++ b/Source/Engine/Platform/Base/WindowBase.cpp @@ -405,6 +405,7 @@ void WindowBase::OnResize(int32 width, int32 height) _swapChain->Resize(width, height); if (RenderTask) RenderTask->Resize(width, height); + Resized({ static_cast(width), static_cast(height) }); INVOKE_EVENT_PARAMS_2(OnResize, &width, &height); } diff --git a/Source/Engine/Platform/Base/WindowBase.h b/Source/Engine/Platform/Base/WindowBase.h index e4e7d2080..812359fb4 100644 --- a/Source/Engine/Platform/Base/WindowBase.h +++ b/Source/Engine/Platform/Base/WindowBase.h @@ -315,6 +315,11 @@ public: /// Action Closed; + /// + /// Event fired when window gets resized. + /// + Delegate Resized; + /// /// Event fired when window gets focused. /// From fc7c24f7714bca2781119b3cfdb2bc645f62c5e8 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Thu, 7 Sep 2023 11:33:16 -0400 Subject: [PATCH 18/40] Fix for #1365 --- .../Engine/Physics/Colliders/CharacterController.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 488acd6e4..0d5819422 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -245,12 +245,10 @@ void CharacterController::CreateShape() void CharacterController::UpdateBounds() { - void* actor = _shape ? PhysicsBackend::GetShapeActor(_shape) : nullptr; - if (actor) - PhysicsBackend::GetActorBounds(actor, _box); - else - _box = BoundingBox(_transform.Translation); - BoundingSphere::FromBox(_box, _sphere); + const float radius = Math::Max(_height, _radius); + const Vector3 position = _transform.LocalToWorld(_center); + _sphere = BoundingSphere(position, radius); + _box = BoundingBox::FromSphere(_sphere); } void CharacterController::AddMovement(const Vector3& translation, const Quaternion& rotation) From 317384ed28287473b16b95e6dfb3c35f937992a9 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Fri, 8 Sep 2023 22:19:09 -0400 Subject: [PATCH 19/40] add debugdraw::drawray --- Source/Engine/Debug/DebugDraw.cpp | 5 +++++ Source/Engine/Debug/DebugDraw.h | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 97422b9e2..e8489f5b5 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -920,6 +920,11 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bo } } +void DebugDraw::DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration, bool depthTest) +{ + DrawLine(origin, origin + direction, color, duration, depthTest); +} + void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration, bool depthTest) { const Float3 startF = start - Context->Origin, endF = end - Context->Origin; diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index 8cc5452ed..a4f69678a 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -69,6 +69,16 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// True if draw all debug shapes from scenes too or false if draw just from specified actor list. API_FUNCTION() static void DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes); + /// + /// Draws the line in a direction. + /// + /// The origin of the line. + /// The direction of the line. + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + API_FUNCTION() static void DrawRay(const Vector3& origin, const Vector3& direction, const Color& color, float duration = 0.0f, bool depthTest = true); + /// /// Draws the line. /// @@ -604,6 +614,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color, int32 size = 32, float duration = 0.0f); }; +#define DEBUG_DRAW_RAY(origin, direction, color, duration, depthTest) DebugDraw::DrawRay(origin, direction, color, duration, depthTest) #define DEBUG_DRAW_LINE(start, end, color, duration, depthTest) DebugDraw::DrawLine(start, end, color, duration, depthTest) #define DEBUG_DRAW_LINES(lines, transform, color, duration, depthTest) DebugDraw::DrawLines(lines, transform, color, duration, depthTest) #define DEBUG_DRAW_BEZIER(p1, p2, p3, p4, color, duration, depthTest) DebugDraw::DrawBezier(p1, p2, p3, p4, color, duration, depthTest) From 420e02e3c19f4d109c2a6dbb4e89f333c2c79e99 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Sat, 9 Sep 2023 09:28:06 -0400 Subject: [PATCH 20/40] add empty drawRay --- Source/Engine/Debug/DebugDraw.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Source/Engine/Debug/DebugDraw.cs b/Source/Engine/Debug/DebugDraw.cs index af39ebeea..0fe0d51d2 100644 --- a/Source/Engine/Debug/DebugDraw.cs +++ b/Source/Engine/Debug/DebugDraw.cs @@ -31,6 +31,18 @@ namespace FlaxEngine { } + /// + /// Draws the line in a direction. + /// + /// The start point. + /// The direction of the line. + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + public static void DrawRay(Vector3 origin, Vector3 direction, Color color, float duration = 0.0f, bool depthTest = true) + { + } + /// /// Draws the line. /// From c6ad4d0e19836995a31716563601e954b2717982 Mon Sep 17 00:00:00 2001 From: Ruan Lucas <79365912+RuanLucasGD@users.noreply.github.com> Date: Sat, 9 Sep 2023 13:07:39 -0400 Subject: [PATCH 21/40] small doc fix --- Source/Engine/Debug/DebugDraw.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Debug/DebugDraw.cs b/Source/Engine/Debug/DebugDraw.cs index 0fe0d51d2..bc000e03d 100644 --- a/Source/Engine/Debug/DebugDraw.cs +++ b/Source/Engine/Debug/DebugDraw.cs @@ -34,7 +34,7 @@ namespace FlaxEngine /// /// Draws the line in a direction. /// - /// The start point. + /// The origin of the line. /// The direction of the line. /// The color. /// The duration (in seconds). Use 0 to draw it only once. From 9291295a4d9997e3257037db7135d3a5e52d3962 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 11:25:36 +0200 Subject: [PATCH 22/40] Fix `Dictionary` and `HashSet` iterators to prevent unwanted data copies #1361 --- Source/Engine/Content/Assets/VisualScript.cpp | 6 +-- Source/Engine/Core/Collections/Array.h | 8 ++-- Source/Engine/Core/Collections/ChunkedArray.h | 1 - Source/Engine/Core/Collections/Dictionary.h | 48 +++++++++++-------- Source/Engine/Core/Collections/HashSet.h | 46 +++++++++--------- 5 files changed, 57 insertions(+), 52 deletions(-) diff --git a/Source/Engine/Content/Assets/VisualScript.cpp b/Source/Engine/Content/Assets/VisualScript.cpp index e292a0133..698f1ca8c 100644 --- a/Source/Engine/Content/Assets/VisualScript.cpp +++ b/Source/Engine/Content/Assets/VisualScript.cpp @@ -1251,7 +1251,7 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val boxBase = node->GetBox(3); if (boxBase->HasConnection()) eatBox(node, boxBase->FirstConnection()); - Dictionary::Iterator it(dictionary, iteratorValue.Value.AsInt); + Dictionary::Iterator it(&dictionary, iteratorValue.Value.AsInt); ++it; iteratorValue.Value.AsInt = it.Index(); } @@ -1269,12 +1269,12 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val // Key case 1: if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count()) - value = Dictionary::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Key; + value = Dictionary::Iterator(scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Key; break; // Value case 2: if (iteratorIndex != scope->ReturnedValues.Count() && dictionaryIndex != scope->ReturnedValues.Count()) - value = Dictionary::Iterator(*scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Value; + value = Dictionary::Iterator(scope->ReturnedValues[dictionaryIndex].Value.AsDictionary, scope->ReturnedValues[iteratorIndex].Value.AsInt)->Value; break; // Break case 5: diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h index 3feae4e73..58117cf0a 100644 --- a/Source/Engine/Core/Collections/Array.h +++ b/Source/Engine/Core/Collections/Array.h @@ -938,12 +938,12 @@ public: FORCE_INLINE bool IsEnd() const { - return _index == _array->Count(); + return _index == _array->_count; } FORCE_INLINE bool IsNotEnd() const { - return _index != _array->Count(); + return _index != _array->_count; } FORCE_INLINE T& operator*() const @@ -975,7 +975,7 @@ public: Iterator& operator++() { - if (_index != _array->Count()) + if (_index != _array->_count) _index++; return *this; } @@ -983,7 +983,7 @@ public: Iterator operator++(int) { Iterator temp = *this; - if (_index != _array->Count()) + if (_index != _array->_count) _index++; return temp; } diff --git a/Source/Engine/Core/Collections/ChunkedArray.h b/Source/Engine/Core/Collections/ChunkedArray.h index 86473763b..d01711e38 100644 --- a/Source/Engine/Core/Collections/ChunkedArray.h +++ b/Source/Engine/Core/Collections/ChunkedArray.h @@ -95,7 +95,6 @@ public: struct Iterator { friend ChunkedArray; - private: ChunkedArray* _collection; int32 _chunkIndex; diff --git a/Source/Engine/Core/Collections/Dictionary.h b/Source/Engine/Core/Collections/Dictionary.h index 2327f3c24..01c25a842 100644 --- a/Source/Engine/Core/Collections/Dictionary.h +++ b/Source/Engine/Core/Collections/Dictionary.h @@ -237,22 +237,28 @@ public: { friend Dictionary; private: - Dictionary& _collection; + Dictionary* _collection; int32 _index; public: - Iterator(Dictionary& collection, const int32 index) + Iterator(Dictionary* collection, const int32 index) : _collection(collection) , _index(index) { } - Iterator(Dictionary const& collection, const int32 index) - : _collection((Dictionary&)collection) + Iterator(Dictionary const* collection, const int32 index) + : _collection(const_cast(collection)) , _index(index) { } + Iterator() + : _collection(nullptr) + , _index(-1) + { + } + Iterator(const Iterator& i) : _collection(i._collection) , _index(i._index) @@ -273,27 +279,27 @@ public: FORCE_INLINE bool IsEnd() const { - return _index == _collection._size; + return _index == _collection->_size; } FORCE_INLINE bool IsNotEnd() const { - return _index != _collection._size; + return _index != _collection->_size; } FORCE_INLINE Bucket& operator*() const { - return _collection._allocation.Get()[_index]; + return _collection->_allocation.Get()[_index]; } FORCE_INLINE Bucket* operator->() const { - return &_collection._allocation.Get()[_index]; + return &_collection->_allocation.Get()[_index]; } FORCE_INLINE explicit operator bool() const { - return _index >= 0 && _index < _collection._size; + return _index >= 0 && _index < _collection->_size; } FORCE_INLINE bool operator!() const @@ -320,10 +326,10 @@ public: Iterator& operator++() { - const int32 capacity = _collection.Capacity(); + const int32 capacity = _collection->_size; if (_index != capacity) { - const Bucket* data = _collection._allocation.Get(); + const Bucket* data = _collection->_allocation.Get(); do { _index++; @@ -343,7 +349,7 @@ public: { if (_index > 0) { - const Bucket* data = _collection._allocation.Get(); + const Bucket* data = _collection->_allocation.Get(); do { _index--; @@ -633,7 +639,7 @@ public: /// Iterator with key and value. void Add(const Iterator& i) { - ASSERT(&i._collection != this && i); + ASSERT(i._collection != this && i); const Bucket& bucket = *i; Add(bucket.Key, bucket.Value); } @@ -667,7 +673,7 @@ public: /// True if cannot remove item from the collection because cannot find it, otherwise false. bool Remove(const Iterator& i) { - ASSERT(&i._collection == this); + ASSERT(i._collection == this); if (i) { ASSERT(_allocation.Get()[i._index].IsOccupied()); @@ -711,7 +717,7 @@ public: return End(); FindPositionResult pos; FindPosition(key, pos); - return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End(); + return pos.ObjectIndex != -1 ? Iterator(this, pos.ObjectIndex) : End(); } /// @@ -812,38 +818,38 @@ public: public: Iterator Begin() const { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } Iterator End() const { - return Iterator(*this, _size); + return Iterator(this, _size); } Iterator begin() { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } FORCE_INLINE Iterator end() { - return Iterator(*this, _size); + return Iterator(this, _size); } const Iterator begin() const { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } FORCE_INLINE const Iterator end() const { - return Iterator(*this, _size); + return Iterator(this, _size); } protected: diff --git a/Source/Engine/Core/Collections/HashSet.h b/Source/Engine/Core/Collections/HashSet.h index ad6f8ffc6..107e42e65 100644 --- a/Source/Engine/Core/Collections/HashSet.h +++ b/Source/Engine/Core/Collections/HashSet.h @@ -213,17 +213,17 @@ public: { friend HashSet; private: - HashSet& _collection; + HashSet* _collection; int32 _index; - Iterator(HashSet& collection, const int32 index) + Iterator(HashSet* collection, const int32 index) : _collection(collection) , _index(index) { } - Iterator(HashSet const& collection, const int32 index) - : _collection((HashSet&)collection) + Iterator(HashSet const* collection, const int32 index) + : _collection(const_cast(collection)) , _index(index) { } @@ -244,27 +244,27 @@ public: public: FORCE_INLINE bool IsEnd() const { - return _index == _collection.Capacity(); + return _index == _collection->_size; } FORCE_INLINE bool IsNotEnd() const { - return _index != _collection.Capacity(); + return _index != _collection->_size; } FORCE_INLINE Bucket& operator*() const { - return _collection._allocation.Get()[_index]; + return _collection->_allocation.Get()[_index]; } FORCE_INLINE Bucket* operator->() const { - return &_collection._allocation.Get()[_index]; + return &_collection->_allocation.Get()[_index]; } FORCE_INLINE explicit operator bool() const { - return _index >= 0 && _index < _collection._size; + return _index >= 0 && _index < _collection->_size; } FORCE_INLINE bool operator !() const @@ -274,12 +274,12 @@ public: FORCE_INLINE bool operator==(const Iterator& v) const { - return _index == v._index && &_collection == &v._collection; + return _index == v._index && _collection == v._collection; } FORCE_INLINE bool operator!=(const Iterator& v) const { - return _index != v._index || &_collection != &v._collection; + return _index != v._index || _collection != v._collection; } Iterator& operator=(const Iterator& v) @@ -291,10 +291,10 @@ public: Iterator& operator++() { - const int32 capacity = _collection.Capacity(); + const int32 capacity = _collection->_size; if (_index != capacity) { - const Bucket* data = _collection._allocation.Get(); + const Bucket* data = _collection->_allocation.Get(); do { _index++; @@ -314,7 +314,7 @@ public: { if (_index > 0) { - const Bucket* data = _collection._allocation.Get(); + const Bucket* data = _collection->_allocation.Get(); do { _index--; @@ -464,7 +464,7 @@ public: /// Iterator with item to add void Add(const Iterator& i) { - ASSERT(&i._collection != this && i); + ASSERT(i._collection != this && i); const Bucket& bucket = *i; Add(bucket.Item); } @@ -498,7 +498,7 @@ public: /// True if cannot remove item from the collection because cannot find it, otherwise false. bool Remove(const Iterator& i) { - ASSERT(&i._collection == this); + ASSERT(i._collection == this); if (i) { ASSERT(_allocation.Get()[i._index].IsOccupied()); @@ -523,7 +523,7 @@ public: return End(); FindPositionResult pos; FindPosition(item, pos); - return pos.ObjectIndex != -1 ? Iterator(*this, pos.ObjectIndex) : End(); + return pos.ObjectIndex != -1 ? Iterator(this, pos.ObjectIndex) : End(); } /// @@ -559,38 +559,38 @@ public: public: Iterator Begin() const { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } Iterator End() const { - return Iterator(*this, _size); + return Iterator(this, _size); } Iterator begin() { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } FORCE_INLINE Iterator end() { - return Iterator(*this, _size); + return Iterator(this, _size); } const Iterator begin() const { - Iterator i(*this, -1); + Iterator i(this, -1); ++i; return i; } FORCE_INLINE const Iterator end() const { - return Iterator(*this, _size); + return Iterator(this, _size); } protected: From 53deeff11601b326b4506a5d6747f98ef1426023 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 11:33:08 +0200 Subject: [PATCH 23/40] Missing change from 9291295a4d9997e3257037db7135d3a5e52d3962 --- Source/Engine/Core/Collections/Dictionary.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Collections/Dictionary.h b/Source/Engine/Core/Collections/Dictionary.h index 01c25a842..f5ffb1bfa 100644 --- a/Source/Engine/Core/Collections/Dictionary.h +++ b/Source/Engine/Core/Collections/Dictionary.h @@ -314,7 +314,7 @@ public: FORCE_INLINE bool operator!=(const Iterator& v) const { - return _index != v._index || &_collection != &v._collection; + return _index != v._index || _collection != v._collection; } Iterator& operator=(const Iterator& v) From 9da686cd525c3ff76fc5b8722b697d26c64688a1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 11:58:54 +0200 Subject: [PATCH 24/40] Improve #1369 for #1365 --- .../Physics/Colliders/CharacterController.cpp | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 0d5819422..42455daa4 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -8,6 +8,8 @@ #include "Engine/Serialization/Serialization.h" #include "Engine/Engine/Time.h" +#define CC_MIN_SIZE 0.001f + CharacterController::CharacterController(const SpawnParams& params) : Collider(params) , _controller(nullptr) @@ -100,10 +102,9 @@ void CharacterController::SetStepOffset(float value) { const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float contactOffset = Math::Max(_contactOffset, ZeroTolerance); - const float minSize = 0.001f; - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); - const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, minSize); - PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - minSize)); + const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); + const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, CC_MIN_SIZE); + PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - CC_MIN_SIZE)); } } @@ -175,9 +176,8 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis void CharacterController::DrawPhysicsDebug(RenderView& view) { const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); + const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE); + const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); const Vector3 position = _transform.LocalToWorld(_center); if (view.Mode == ViewMode::PhysicsColliders) DEBUG_DRAW_TUBE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::LightYellow, 0, true); @@ -188,9 +188,8 @@ void CharacterController::DrawPhysicsDebug(RenderView& view) void CharacterController::OnDebugDrawSelected() { const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); + const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE); + const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); const Vector3 position = _transform.LocalToWorld(_center); DEBUG_DRAW_WIRE_TUBE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow, 0, false); @@ -231,9 +230,8 @@ void CharacterController::UpdateSize() const if (_controller) { const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), minSize); - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); + const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), CC_MIN_SIZE); + const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); PhysicsBackend::SetControllerSize(_controller, radius, height); } } @@ -245,10 +243,14 @@ void CharacterController::CreateShape() void CharacterController::UpdateBounds() { - const float radius = Math::Max(_height, _radius); + const float scaling = GetScale().GetAbsolute().MaxValue(); + const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE); + const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE); const Vector3 position = _transform.LocalToWorld(_center); - _sphere = BoundingSphere(position, radius); - _box = BoundingBox::FromSphere(_sphere); + const Vector3 extent(radius, height * 0.5f + radius, radius); + _box = BoundingBox(position - extent, position + extent); + BoundingSphere::FromBox(_box, _sphere); + DEBUG_DRAW_BOX(_box, Color::Red.AlphaMultiplied(0.4f), 2.0f, true); } void CharacterController::AddMovement(const Vector3& translation, const Quaternion& rotation) From a4312590e794b7d1566620ced859dc75cdc90cd0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 13:34:46 +0200 Subject: [PATCH 25/40] Fix debug draw leftover from 9da686cd525c3ff76fc5b8722b697d26c64688a1 #1369 #1365 --- Source/Engine/Physics/Colliders/CharacterController.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 42455daa4..59521e51c 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -250,7 +250,6 @@ void CharacterController::UpdateBounds() const Vector3 extent(radius, height * 0.5f + radius, radius); _box = BoundingBox(position - extent, position + extent); BoundingSphere::FromBox(_box, _sphere); - DEBUG_DRAW_BOX(_box, Color::Red.AlphaMultiplied(0.4f), 2.0f, true); } void CharacterController::AddMovement(const Vector3& translation, const Quaternion& rotation) From ae27d51dc68381eac5162f58990813d68f38c350 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 14:53:58 +0200 Subject: [PATCH 26/40] Fix building for Android with the latest Java version (update to Gradle `8.3`) --- Source/Platforms/Android/Binaries/Project/app/build.gradle | 1 + Source/Platforms/Android/Binaries/Project/build.gradle | 2 +- Source/Platforms/Android/Binaries/Project/gradle.properties | 3 +-- .../Binaries/Project/gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Platforms/Android/Binaries/Project/app/build.gradle b/Source/Platforms/Android/Binaries/Project/app/build.gradle index 0ad5776b5..2f5da0a56 100644 --- a/Source/Platforms/Android/Binaries/Project/app/build.gradle +++ b/Source/Platforms/Android/Binaries/Project/app/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 24 + namespace "${PackageName}" defaultConfig { applicationId "${PackageName}" minSdkVersion 24 diff --git a/Source/Platforms/Android/Binaries/Project/build.gradle b/Source/Platforms/Android/Binaries/Project/build.gradle index 31d9237bc..ff4610757 100644 --- a/Source/Platforms/Android/Binaries/Project/build.gradle +++ b/Source/Platforms/Android/Binaries/Project/build.gradle @@ -6,7 +6,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:8.1.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/Source/Platforms/Android/Binaries/Project/gradle.properties b/Source/Platforms/Android/Binaries/Project/gradle.properties index 7be81848a..f38a936bd 100644 --- a/Source/Platforms/Android/Binaries/Project/gradle.properties +++ b/Source/Platforms/Android/Binaries/Project/gradle.properties @@ -9,5 +9,4 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m -org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 diff --git a/Source/Platforms/Android/Binaries/Project/gradle/wrapper/gradle-wrapper.properties b/Source/Platforms/Android/Binaries/Project/gradle/wrapper/gradle-wrapper.properties index 483089527..ee1048a44 100644 --- a/Source/Platforms/Android/Binaries/Project/gradle/wrapper/gradle-wrapper.properties +++ b/Source/Platforms/Android/Binaries/Project/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip From 716e643f2a609cbc829dc8c117bd3ed4d2cf14e0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 10 Sep 2023 21:07:41 +0200 Subject: [PATCH 27/40] Fix applying prefab changes to nesting prefab instances #1256 --- Source/Editor/Modules/SceneModule.cs | 2 +- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 5 ----- Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs | 2 +- Source/Engine/Level/Prefabs/Prefab.Apply.cpp | 7 +++---- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Modules/SceneModule.cs b/Source/Editor/Modules/SceneModule.cs index 0a51038ac..257262585 100644 --- a/Source/Editor/Modules/SceneModule.cs +++ b/Source/Editor/Modules/SceneModule.cs @@ -591,7 +591,7 @@ namespace FlaxEditor.Modules private void OnActorNameChanged(Actor actor) { ActorNode node = GetActorNode(actor); - node?.TreeNode.OnNameChanged(); + node?.TreeNode.UpdateText(); } private void OnActorActiveChanged(Actor actor) diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 83ea666d9..c4edda65e 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -97,11 +97,6 @@ namespace FlaxEditor.SceneGraph.GUI } } - internal void OnNameChanged() - { - UpdateText(); - } - /// /// Updates the tree node text. /// diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 1b1cfb4ff..5a532e362 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -430,7 +430,7 @@ namespace FlaxEditor.Windows.Assets { if (actorNode.Actor) { - actorNode.TreeNode.OnNameChanged(); + actorNode.TreeNode.UpdateText(); actorNode.TreeNode.OnOrderInParentChanged(); } diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp index 5ea8ad0a4..e0df48fae 100644 --- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp @@ -1255,20 +1255,19 @@ void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, ArrayWaitForLoaded()) { LOG(Warning, "Waiting for prefab asset load failed."); continue; } + // Sync only if prefab is used by this prefab (directly) and it has been captured before const int32 nestedPrefabIndex = nestedPrefab->NestedPrefabs.Find(GetID()); if (nestedPrefabIndex != -1) { - if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[nestedPrefabIndex])) + if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[i])) continue; - nestedPrefab->SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData); - ObjectsRemovalService::Flush(); } } From fb27606541f05333d6fb186458cd2946e120a52d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 13:54:44 +0200 Subject: [PATCH 28/40] Add missing `Particle Scale` getter node to Particle Emitter graph #1343 --- Source/Editor/Surface/Archetypes/Particles.cs | 12 ++++++++++++ .../Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp | 6 ++++++ .../Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp | 4 ++++ Source/Engine/Particles/Graph/ParticleEmitterGraph.h | 9 ++++++++- 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Particles.cs b/Source/Editor/Surface/Archetypes/Particles.cs index dd861d4af..1389a6904 100644 --- a/Source/Editor/Surface/Archetypes/Particles.cs +++ b/Source/Editor/Surface/Archetypes/Particles.cs @@ -553,6 +553,18 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 0), } }, + new NodeArchetype + { + TypeID = 112, + Title = "Particle Scale", + Description = "Particle scale.", + Flags = NodeFlags.ParticleEmitterGraph, + Size = new Float2(200, 30), + Elements = new[] + { + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Float3), 0), + } + }, // Simulation data access nodes new NodeArchetype diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp index 9ef996869..9d91d0013 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp @@ -331,6 +331,12 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node value = GET_PARTICLE_ATTRIBUTE(0, float); break; } + // Particle Scale + case 112: + { + value = GET_PARTICLE_ATTRIBUTE(0, Float3); + break; + } // Effect Position case 200: { diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp index 905d1a11c..de142827c 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp @@ -331,6 +331,10 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va case 111: value = AccessParticleAttribute(node, TEXT("Radius"), ParticleAttribute::ValueTypes::Float, AccessMode::Read); break; + // Particle Scale + case 112: + value = AccessParticleAttribute(node, TEXT("Scale"), ParticleAttribute::ValueTypes::Float3, AccessMode::Read); + break; // Effect Position case 200: value = Value(VariantType::Float3, TEXT("EffectPosition")); diff --git a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h index cfe259a72..f5946c226 100644 --- a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h +++ b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h @@ -269,13 +269,20 @@ public: USE_ATTRIBUTE(Lifetime, Float, 1); break; } - // Particle Mass + // Particle Radius case GRAPH_NODE_MAKE_TYPE(14, 111): { node->UsesParticleData = true; USE_ATTRIBUTE(Radius, Float, 0); break; } + // Particle Scale + case GRAPH_NODE_MAKE_TYPE(14, 112): + { + node->UsesParticleData = true; + USE_ATTRIBUTE(Scale, Float3, 0); + break; + } // Random case GRAPH_NODE_MAKE_TYPE(14, 208): case GRAPH_NODE_MAKE_TYPE(14, 209): From 9afa98e9059dd54525c00b93ee231e57c512e0c3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 13:59:10 +0200 Subject: [PATCH 29/40] Fix `Random Range` nodes in CPU particles/script graphs when using `int` #1341 --- Source/Engine/Visject/VisjectGraph.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index ca4495f67..1cee5d46e 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -1279,16 +1279,16 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value) // Random Float Range case 213: { - auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat; - auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat; + auto a = (float)tryGetValue(node->TryGetBox(1), node->Values[0]); + auto b = (float)tryGetValue(node->TryGetBox(2), node->Values[1]); value = Math::Lerp(a, b, RAND); break; } // Random Vector2 Range case 214: { - auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat2(); - auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat2(); + auto a = (Float2)tryGetValue(node->TryGetBox(1), node->Values[0]); + auto b = (Float2)tryGetValue(node->TryGetBox(2), node->Values[1]); value = Float2( Math::Lerp(a.X, b.X, RAND), Math::Lerp(a.Y, b.Y, RAND) @@ -1298,8 +1298,8 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value) // Random Vector3 Range case 215: { - auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat3(); - auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat3(); + auto a = (Float3)tryGetValue(node->TryGetBox(1), node->Values[0]); + auto b = (Float3)tryGetValue(node->TryGetBox(2), node->Values[1]); value = Float3( Math::Lerp(a.X, b.X, RAND), Math::Lerp(a.Y, b.Y, RAND), @@ -1310,8 +1310,8 @@ void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value) // Random Vector4 Range case 216: { - auto a = tryGetValue(node->TryGetBox(1), node->Values[0]).AsFloat4(); - auto b = tryGetValue(node->TryGetBox(2), node->Values[1]).AsFloat4(); + auto a = (Float4)tryGetValue(node->TryGetBox(1), node->Values[0]); + auto b = (Float4)tryGetValue(node->TryGetBox(2), node->Values[1]); value = Float4( Math::Lerp(a.X, b.X, RAND), Math::Lerp(a.Y, b.Y, RAND), From 1134cfa6a6964837d6391247a899a95573ce6f91 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 15:05:46 +0200 Subject: [PATCH 30/40] Fix using `ManagedDictionary` when item value type is array #1305 --- Source/Engine/Scripting/Scripting.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index 2b49d97d4..68da64bf7 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -205,6 +205,13 @@ namespace FlaxEngine internal static void AddDictionaryItem(IDictionary dictionary, object key, object value) { + // TODO: more generic approach to properly add value that is of custom boxed type? (eg. via NativeInterop.MarshalToManaged) + if (value is ManagedArray managedArray) + { + var managedArrayHandle = ManagedHandle.Alloc(managedArray, GCHandleType.Normal); + value = NativeInterop.MarshalToManaged((IntPtr)managedArrayHandle, managedArray.ArrayType); + managedArrayHandle.Free(); + } dictionary.Add(key, value); } From 48d4a51c1b589ec2b7560812cecc3af5154422fd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 15:06:26 +0200 Subject: [PATCH 31/40] Fix crash when boxing native array to managed value --- Source/Engine/Scripting/ManagedCLR/MUtils.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.h b/Source/Engine/Scripting/ManagedCLR/MUtils.h index 90feecab3..7b340e5c6 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.h +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.h @@ -3,6 +3,7 @@ #pragma once #include "MTypes.h" +#include "MClass.h" #include "MCore.h" #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/DataContainer.h" @@ -354,7 +355,7 @@ struct MConverter> { if (!klass) return nullptr; - MArray* result = MCore::Array::New(klass, data.Count()); + MArray* result = MCore::Array::New(klass->GetElementClass(), data.Count()); MConverter converter; converter.ToManagedArray(result, Span(data.Get(), data.Count())); return (MObject*)result; From 829d3b3642c1ade36c2e74ff37178a5a29df6702 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 15:07:06 +0200 Subject: [PATCH 32/40] Add editor shortcuts usability when toolstrip or main menu controls are focused --- Source/Editor/GUI/MainMenu.cs | 11 ++++++++++ Source/Editor/GUI/ToolStrip.cs | 21 +++++++++++++++++++ Source/Editor/Modules/ContentFindingModule.cs | 2 ++ 3 files changed, 34 insertions(+) diff --git a/Source/Editor/GUI/MainMenu.cs b/Source/Editor/GUI/MainMenu.cs index 66b10f0aa..370ad056b 100644 --- a/Source/Editor/GUI/MainMenu.cs +++ b/Source/Editor/GUI/MainMenu.cs @@ -304,6 +304,17 @@ namespace FlaxEditor.GUI return true; } + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (base.OnKeyDown(key)) + return true; + + // Fallback to the edit window for shortcuts + var editor = Editor.Instance; + return editor.Windows.EditWin.InputActions.Process(editor, this, key); + } + /// protected override void PerformLayoutAfterChildren() { diff --git a/Source/Editor/GUI/ToolStrip.cs b/Source/Editor/GUI/ToolStrip.cs index 0dac241ed..360d63ffe 100644 --- a/Source/Editor/GUI/ToolStrip.cs +++ b/Source/Editor/GUI/ToolStrip.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using FlaxEditor.Windows; using FlaxEngine; using FlaxEngine.GUI; @@ -181,5 +182,25 @@ namespace FlaxEditor.GUI PerformLayout(); } + + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (base.OnKeyDown(key)) + return true; + + // Fallback to the owning window for shortcuts + EditorWindow editorWindow = null; + ContainerControl c = Parent; + while (c != null && editorWindow == null) + { + editorWindow = c as EditorWindow; + c = c.Parent; + } + var editor = Editor.Instance; + if (editorWindow == null) + editorWindow = editor.Windows.EditWin; // Fallback to main editor window + return editorWindow.InputActions.Process(editor, this, key); + } } } diff --git a/Source/Editor/Modules/ContentFindingModule.cs b/Source/Editor/Modules/ContentFindingModule.cs index ccf1cb3e4..9e8002c8a 100644 --- a/Source/Editor/Modules/ContentFindingModule.cs +++ b/Source/Editor/Modules/ContentFindingModule.cs @@ -166,6 +166,8 @@ namespace FlaxEditor.Modules public void ShowFinder(Control control) { var finder = _finder ?? (_finder = new ContentFinder()); + if (control == null) + control = Editor.Instance.Windows.MainWindow.GUI; var position = (control.Size - new Float2(finder.Width, 300.0f)) * 0.5f; finder.Show(control, position); } From 748ff3b8a5a09aa89d2912d33f9a7a108b2121b0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 15:07:37 +0200 Subject: [PATCH 33/40] Optimize `CultureInfoEditor` and `TagEditor` performance when searching entries by text --- Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs | 4 ++-- Source/Editor/CustomEditors/Editors/TagEditor.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs b/Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs index d04b5b6da..e1a2b0195 100644 --- a/Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CultureInfoEditor.cs @@ -123,9 +123,9 @@ namespace FlaxEditor.CustomEditors.Editors { if (tree.IsLayoutLocked) return; - root.LockChildrenRecursive(); + tree.LockChildrenRecursive(); Utilities.Utils.UpdateSearchPopupFilter(root, searchBox.Text); - root.UnlockChildrenRecursive(); + tree.UnlockChildrenRecursive(); menu.PerformLayout(); }; root.ExpandAll(true); diff --git a/Source/Editor/CustomEditors/Editors/TagEditor.cs b/Source/Editor/CustomEditors/Editors/TagEditor.cs index ab5fd5d1a..3d2dd86aa 100644 --- a/Source/Editor/CustomEditors/Editors/TagEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TagEditor.cs @@ -593,9 +593,9 @@ namespace FlaxEditor.CustomEditors.Editors { if (tree.IsLayoutLocked) return; - root.LockChildrenRecursive(); + tree.LockChildrenRecursive(); Utilities.Utils.UpdateSearchPopupFilter(root, searchBox.Text); - root.UnlockChildrenRecursive(); + tree.UnlockChildrenRecursive(); menu.PerformLayout(); }; From 59c460ccbccc4944462b8f978d7fc5393e02efa9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 15:08:13 +0200 Subject: [PATCH 34/40] Format code --- Source/Editor/Managed/ManagedEditor.Internal.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index bed9cfb5c..afcbb740a 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -321,7 +321,7 @@ DEFINE_INTERNAL_CALL(bool) EditorInternal_SaveJsonAsset(MString* outputPathObj, const StringView dataObjChars = MCore::String::GetChars(dataObj); const StringAsANSI<> data(dataObjChars.Get(), dataObjChars.Length()); const StringAnsiView dataAnsi(data.Get(), data.Length()); - + const StringView dataTypeNameObjChars = MCore::String::GetChars(dataTypeNameObj); const StringAsANSI<> dataTypeName(dataTypeNameObjChars.Get(), dataTypeNameObjChars.Length()); const StringAnsiView dataTypeNameAnsi(dataTypeName.Get(), dataTypeName.Length()); @@ -427,7 +427,7 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_GetCollisionWires(CollisionData* colli const int32 linesCount = debugLines.Count() / 2; MCore::GC::WriteRef(triangles, (MObject*)MCore::Array::New(Float3::TypeInitializer.GetClass(), debugLines.Count())); - MCore::GC::WriteRef(indices, (MObject*)MCore::Array::New( MCore::TypeCache::Int32, linesCount * 3)); + MCore::GC::WriteRef(indices, (MObject*)MCore::Array::New(MCore::TypeCache::Int32, linesCount * 3)); // Use one triangle per debug line Platform::MemoryCopy(MCore::Array::GetAddress(*triangles), debugLines.Get(), debugLines.Count() * sizeof(Float3)); @@ -585,7 +585,7 @@ DEFINE_INTERNAL_CALL(MArray*) EditorInternal_GetVisualScriptLocals(int* localsCo const int32 count = stack->Scope->Parameters.Length() + stack->Scope->ReturnedValues.Count(); const MClass* mclass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEditor.Editor+VisualScriptLocal"); ASSERT(mclass); - result = MCore::Array::New( mclass, count); + result = MCore::Array::New(mclass, count); VisualScriptLocalManaged local; local.NodeId = MAX_uint32; if (stack->Scope->Parameters.Length() != 0) @@ -642,7 +642,7 @@ DEFINE_INTERNAL_CALL(MArray*) EditorInternal_GetVisualScriptStackFrames(int* sta } const MClass* mclass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEditor.Editor+VisualScriptStackFrame"); ASSERT(mclass); - result = MCore::Array::New( mclass, count); + result = MCore::Array::New(mclass, count); VisualScriptStackFrameManaged* resultPtr = MCore::Array::GetAddress(result); s = stack; count = 0; From 16fed8927c9657bea85d990a75551ba77ef4a4be Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 20:02:53 +0200 Subject: [PATCH 35/40] Refactor #1266 (code quality and simplicity) --- Source/Editor/Content/ContentFilter.cs | 257 ------------------ ...vigationButton.cs => ContentNavigation.cs} | 68 ++++- .../Content/GUI/ContentNavigationBar.cs | 27 -- .../GUI/ContentNavigationButtonSeparator.cs | 85 ------ .../Content/GUI/ContentSettingsDropdown.cs | 95 ------- Source/Editor/Content/Items/ContentItem.cs | 8 +- .../Content/Tree/RootContentTreeNode.cs | 2 +- Source/Editor/GUI/ComboBox.cs | 27 +- .../GUI/ContextMenu/ContextMenuButton.cs | 2 +- Source/Editor/GUI/NavigationBar.cs | 7 +- .../Editor/Modules/ContentDatabaseModule.cs | 17 ++ .../Windows/ContentWindow.Navigation.cs | 27 +- Source/Editor/Windows/ContentWindow.Search.cs | 33 +-- Source/Editor/Windows/ContentWindow.cs | 170 ++++++++---- 14 files changed, 241 insertions(+), 584 deletions(-) delete mode 100644 Source/Editor/Content/ContentFilter.cs rename Source/Editor/Content/GUI/{ContentNavigationButton.cs => ContentNavigation.cs} (59%) delete mode 100644 Source/Editor/Content/GUI/ContentNavigationBar.cs delete mode 100644 Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs delete mode 100644 Source/Editor/Content/GUI/ContentSettingsDropdown.cs diff --git a/Source/Editor/Content/ContentFilter.cs b/Source/Editor/Content/ContentFilter.cs deleted file mode 100644 index 12cca5c41..000000000 --- a/Source/Editor/Content/ContentFilter.cs +++ /dev/null @@ -1,257 +0,0 @@ -using FlaxEditor.Content; -using FlaxEditor.Content.GUI; -using FlaxEngine; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace FlaxEditor.Content -{ - /// - /// class with is controling visability of items in content window - /// - internal class ContentFilter - { - #region Filters Config - /// - /// all suported files by engine Content Folder - /// - public static readonly List SuportedFileExtencionsInContentFolder = new List() - { - ".flax", - ".json", - ".scene", - ".prefab", - }; - /// - /// all suported files by engine Source Folder - /// - public static readonly List SuportedFileExtencionsInSourceFolder = new List() - { - ".shader", - ".cs", - ".h", - ".cpp", - }; - /// - /// ignores folders in source folder (top layer), - /// for example obj folder is default c# project folder - /// - internal static readonly List HideFoldersInSourceFolder = new List() - { - "obj", // default c# project folder - "Properties", // c# project stuff ? - }; - /// - /// ignores files in source folder (top layer), - /// - internal static readonly List HideFilesInSourceFolder = new List() //dont edit - { - "Game.csproj", //solucion file - "Game.Gen.cs", //auto-generated not be edited - "GameEditorTarget.Build.cs", - "GameTarget.Build.cs", - }; - #endregion - internal static ContentItem gameSettings; - internal static List buildFiles; - - internal static ContentTreeNode settings; - internal static ContentTreeNode shaders; - internal static ProjectTreeNode engine; - internal static List plugins = new(); - internal static List FilterFolder(ContentFolder folder) - { - return FilterFolder(folder, SuportedFileExtencionsInContentFolder.ToArray(), SuportedFileExtencionsInSourceFolder.ToArray()); - } - internal static List FilterFolder(ContentFolder folder, string[] FileExtencionsInContentFolder, string[] ExtencionsInSourceFolder) - { - if (folder.CanHaveAssets) - { - for (int i = 0; i < folder.Children.Count; i++) - { - bool Visible = false; - for (int j = 0; j < FileExtencionsInContentFolder.Length; j++) - { - if ((folder.Children[i].Path.EndsWith(FileExtencionsInContentFolder[j]) || folder.Children[i].IsFolder)) - { - if (folder.Children[i].Visible) - { - Visible = true; - } - break; - } - } - folder.Children[i].Visible = Visible; - } - } - else if (folder.CanHaveScripts) - { - for (int i = 0; i < folder.Children.Count; i++) - { - bool Visible = false; - for (int j = 0; j < ExtencionsInSourceFolder.Length; j++) - { - if ((folder.Children[i].Path.EndsWith(ExtencionsInSourceFolder[j]) || folder.Children[i].IsFolder)) - { - if (folder.Children[i].Visible) - { - Visible = true; - } - break; - } - } - folder.Children[i].Visible = Visible; - } - } - return folder.Children; - } - internal static ProjectTreeNode Filter(ProjectTreeNode tree) - { - var content = tree.Children[0] as ContentTreeNode; - var source = tree.Children[1] as ContentTreeNode; - //filter content folder (top layer) - buildFiles = new(); - for (int i = 0; i < content.Folder.Children.Count; i++) - { - if (content.Folder.Children[i].FileName == "GameSettings.json") - { - gameSettings = content.Folder.Children[i]; - content.Folder.Children[i].Visible = false; - break; - } - } - - int hindenCount = 0; - - for (int i = content.Children.Count - 1; i >= 0; i--)//we are starting from back it faster - { - var node = content.Children[i] as ContentTreeNode; - if (node.Folder.FileName == "Settings") - { - settings = node; - hindenCount++; - node.Visible = false; - node.Folder.Visible = false; - } - if (node.Folder.FileName == "Shaders") - { - shaders = node; - hindenCount++; - node.Visible = false; - node.Folder.Visible = false; - - } - if (hindenCount == 2) - break; - } - - - //----------------------------------------------------------------------------------------------------- - - //filter source folder (top layer) - hindenCount = 0; - for (int i = 0; i < source.Folder.Children.Count; i++) - { - for (int j = 0; j < HideFilesInSourceFolder.Count; j++) - { - if (source.Folder.Children[i].FileName == HideFilesInSourceFolder[j]) - { - source.Folder.Children[i].Visible = false; - hindenCount++; - if(i > 1) - { - buildFiles.Add(source.Folder.Children[i]); - } - if (HideFilesInSourceFolder.Count == hindenCount) goto HideFilesInSourceFolderComplited; - break; - } - } - } - HideFilesInSourceFolderComplited: - hindenCount = 0; - for (int i = source.Children.Count - 1; i >= 0; i--) - { - var node = source.Children[i] as ContentTreeNode; - for (int j = 0; j < HideFoldersInSourceFolder.Count; j++) - { - if (node.Folder.FileName == HideFoldersInSourceFolder[j]) - { - node.Visible = false; - node.Folder.Visible = false; - hindenCount++; - if (HideFoldersInSourceFolder.Count == hindenCount) goto HideFoldersInSourceFolderComplited; - break; - } - } - } - HideFoldersInSourceFolderComplited: - //content - return tree; - } - internal static void UpdateFilterVisability(ContentSettingsDropdown dropdown) - { - engine.Visible = false; - engine.Folder.Visible = false; - - foreach (var item in plugins) - { - item.Visible = false; - item.Folder.Visible = false; - } - foreach (var item in buildFiles) - { - item.Visible = false; - } - gameSettings.Visible = false; - settings.Visible = false; - settings.Folder.Visible = false; - - shaders.Visible = false; - shaders.Folder.Visible = false; - - for (int i = 0; i < dropdown.Selection.Count; i++) - { - if (dropdown.Selection[i] == 0) //Show Engine Content - { - engine.Visible = true; - engine.Folder.Visible = true; - } - - if (dropdown.Selection[i] == 1)//Show Plugin Content - { - foreach (var item in plugins) - { - item.Visible = true; - item.Folder.Visible = true; - } - } - - if (dropdown.Selection[i] == 2)//Show Build Files - { - foreach (var item in buildFiles) - { - item.Visible = true; - } - } - - if (dropdown.Selection[i] == 3)//Show Game Settings - { - gameSettings.Visible = true; - settings.Visible = true; - settings.Folder.Visible = true; - } - - if (dropdown.Selection[i] == 4)//"Show Shader Source" - { - shaders.Visible = true; - shaders.Folder.Visible = true; - } - } - engine.ParentTree.PerformLayout(); - Editor.Instance.Windows.ContentWin.RefreshView(); - } - } -} diff --git a/Source/Editor/Content/GUI/ContentNavigationButton.cs b/Source/Editor/Content/GUI/ContentNavigation.cs similarity index 59% rename from Source/Editor/Content/GUI/ContentNavigationButton.cs rename to Source/Editor/Content/GUI/ContentNavigation.cs index beec247da..7adf6aa62 100644 --- a/Source/Editor/Content/GUI/ContentNavigationButton.cs +++ b/Source/Editor/Content/GUI/ContentNavigation.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEditor.GUI; +using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Drag; using FlaxEngine; using FlaxEngine.GUI; @@ -72,7 +73,6 @@ namespace FlaxEditor.Content.GUI if (_dragOverItems == null) _dragOverItems = new DragItems(ValidateDragItem); - _dragOverItems.OnDragEnter(data); var result = GetDragEffect(data); _validDragOver = result != DragDropEffect.None; @@ -122,4 +122,70 @@ namespace FlaxEditor.Content.GUI return result; } } + + sealed class ContentNavigationSeparator : ComboBox + { + public ContentNavigationButton Target; + + public ContentNavigationSeparator(ContentNavigationButton target, float x, float y, float height) + { + Target = target; + Bounds = new Rectangle(x, y, 16, height); + Offsets = new Margin(Bounds.X, Bounds.Width, Bounds.Y, Bounds.Height); + UpdateTransform(); + + MaximumItemsInViewCount = 20; + var style = Style.Current; + BackgroundColor = style.BackgroundNormal; + BackgroundColorHighlighted = BackgroundColor; + BackgroundColorSelected = BackgroundColor; + } + + protected override ContextMenu OnCreatePopup() + { + // Update items + ClearItems(); + foreach (var child in Target.TargetNode.Children) + { + if (child is ContentTreeNode node) + { + if (node.Folder.VisibleInHierarchy) // Respect the filter set by ContentFilterConfig.Filter(...) + AddItem(node.Folder.ShortName); + } + } + + return base.OnCreatePopup(); + } + + public override void Draw() + { + var style = Style.Current; + var rect = new Rectangle(Float2.Zero, Size); + var color = IsDragOver ? style.BackgroundSelected * 0.6f : (_mouseDown ? style.BackgroundSelected : (IsMouseOver ? style.BackgroundHighlighted : Color.Transparent)); + Render2D.FillRectangle(rect, color); + Render2D.DrawSprite(Editor.Instance.Icons.ArrowRight12, new Rectangle(rect.Location.X, rect.Y + rect.Size.Y * 0.25f, rect.Size.X, rect.Size.X), EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled); + } + + protected override void OnLayoutMenuButton(ContextMenuButton button, int index, bool construct = false) + { + button.Icon = Editor.Instance.Icons.FolderClosed32; + if (_tooltips != null && _tooltips.Length > index) + button.TooltipText = _tooltips[index]; + } + + protected override void OnItemClicked(int index) + { + base.OnItemClicked(index); + + var item = _items[index]; + foreach (var child in Target.TargetNode.Children) + { + if (child is ContentTreeNode node && node.Folder.ShortName == item) + { + Editor.Instance.Windows.ContentWin.Navigate(node); + return; + } + } + } + } } diff --git a/Source/Editor/Content/GUI/ContentNavigationBar.cs b/Source/Editor/Content/GUI/ContentNavigationBar.cs deleted file mode 100644 index 673ac806e..000000000 --- a/Source/Editor/Content/GUI/ContentNavigationBar.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FlaxEditor.GUI; -using FlaxEngine; - -namespace FlaxEditor.Content.GUI -{ - internal class ContentNavigationBar : NavigationBar - { - ToolStrip _toolstrip; - internal float ofssetFromRightEdge = 80; - internal ContentNavigationBar(ToolStrip toolstrip) : base() - { - _toolstrip = toolstrip; - } - /// - protected override void Arrange() - { - base.Arrange(); - var lastToolstripButton = _toolstrip.LastButton; - var parentSize = Parent.Size; - Bounds = new Rectangle - ( - new Float2(lastToolstripButton.Right, 0), - new Float2(parentSize.X - X - ofssetFromRightEdge, _toolstrip.Height) - ); - } - } -} diff --git a/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs b/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs deleted file mode 100644 index cad63066d..000000000 --- a/Source/Editor/Content/GUI/ContentNavigationButtonSeparator.cs +++ /dev/null @@ -1,85 +0,0 @@ -using FlaxEditor; -using FlaxEditor.GUI; -using FlaxEditor.GUI.ContextMenu; -using FlaxEngine; -using FlaxEngine.GUI; -using System.Collections.Generic; - -namespace FlaxEditor.Content.GUI -{ - internal class ContentNavigationButtonSeparator : ComboBox - { - public ContentNavigationButton Target { get; } - public ContentNavigationButtonSeparator(ContentNavigationButton target, float x, float y,float height) - { - Target = target; - Bounds = new Rectangle(x, y, 16, height); - Offsets = new Margin(Bounds.X, Bounds.Width, Bounds.Y, Bounds.Height); - UpdateTransform(); - - MaximumItemsInViewCount = 20; - var style = Style.Current; - BackgroundColor = style.BackgroundNormal; - BackgroundColorHighlighted = BackgroundColor; - BackgroundColorSelected = BackgroundColor; - } - protected override ContextMenu OnCreatePopup() - { - UpdateDropDownItems(); - return base.OnCreatePopup(); - } - internal void UpdateDropDownItems() - { - ClearItems(); - _items = new(); - for (int i = 0; i < Target.TargetNode.Children.Count; i++) - { - if (Target.TargetNode.Children[i] is ContentTreeNode node) - { - if (node.Folder.VisibleInHierarchy) // respect the filter set by ContentFilterConfig.Filter(...) - AddItem(node.Folder.ShortName); - } - } - } - /// - public override void Draw() - { - // Cache data - var style = Style.Current; - var clientRect = new Rectangle(Float2.Zero, Size); - // Draw background - if (IsDragOver) - { - Render2D.FillRectangle(clientRect, Style.Current.BackgroundSelected * 0.6f); - } - else if (_mouseDown) - { - Render2D.FillRectangle(clientRect, style.BackgroundSelected); - } - else if (IsMouseOver) - { - Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); - } - - Render2D.DrawSprite(Editor.Instance.Icons.ArrowRight12, new Rectangle(clientRect.Location.X, clientRect.Y + ((Size.Y / 2f)/2f), Size.X, Size.X), EnabledInHierarchy ? Style.Current.Foreground : Style.Current.ForegroundDisabled); - } - protected override void OnLayoutMenuButton(ref ContextMenuButton button, int index, bool construct = false) - { - var style = Style.Current; - button.Icon = Editor.Instance.Icons.FolderClosed32; - if (_tooltips != null && _tooltips.Length > index) - { - button.TooltipText = _tooltips[index]; - } - } - protected override void OnItemClicked(int index) - { - base.OnItemClicked(index); - if (Target.TargetNode.Children[index] is ContentTreeNode node) - { - Editor.Instance.Windows.ContentWin.Navigate(node); - } - // Navigate calls the OnDestroy at some point dont place code below or editor will crash - } - } -} diff --git a/Source/Editor/Content/GUI/ContentSettingsDropdown.cs b/Source/Editor/Content/GUI/ContentSettingsDropdown.cs deleted file mode 100644 index 04722a674..000000000 --- a/Source/Editor/Content/GUI/ContentSettingsDropdown.cs +++ /dev/null @@ -1,95 +0,0 @@ -using FlaxEditor.Content; -using FlaxEditor.CustomEditors.Editors; -using FlaxEditor.GUI; -using FlaxEditor.GUI.ContextMenu; -using FlaxEditor.States; -using FlaxEditor.Utilities; -using FlaxEngine; -using FlaxEngine.GUI; -using FlaxEngine.Json; -using System.Collections.Generic; -using System.Linq; - -namespace FlaxEditor.Content.GUI -{ - class ContentSettingsDropdown : ComboBox - { - internal readonly List Settings = new() - { - "Show Engine Content", - "Show Plugin Content", - "Show Build Files", - "Show Game Settings", - "Show Shader Source" - }; - public ContentSettingsDropdown() - { - SupportMultiSelect = true; - MaximumItemsInViewCount = 20; - var style = Style.Current; - BackgroundColor = style.BackgroundNormal; - BackgroundColorHighlighted = BackgroundColor; - BackgroundColorSelected = BackgroundColor; - - } - protected override ContextMenu OnCreatePopup() - { - UpdateDropDownItems(); - return base.OnCreatePopup(); - } - internal void UpdateDropDownItems() - { - ClearItems(); - - for (int i = 0; i < Settings.Count; i++) - { - AddItem(Settings[i]); - } - } - /// - public override void Draw() - { - // Cache data - var style = Style.Current; - var clientRect = new Rectangle(Float2.Zero, Size); - // Draw background - if (IsDragOver) - { - Render2D.FillRectangle(clientRect, Style.Current.BackgroundSelected * 0.6f); - } - else if (_mouseDown) - { - Render2D.FillRectangle(clientRect, style.BackgroundSelected); - } - else if (IsMouseOver) - { - Render2D.FillRectangle(clientRect, style.BackgroundHighlighted); - } - float size = (Size.Y / 1.5f); //Icon size - // Draw text - Render2D.DrawText(Font.GetFont(), "Settings", new Rectangle(size, 0, Size.X - Size.Y, Size.Y), TextColor, TextAlignment.Center, TextAlignment.Center); - Render2D.DrawSprite(Editor.Instance.Icons.Settings12, new Rectangle((Size.Y - size) / 2.0f, (Size.Y - size) / 2.0f, size, size)); - } - protected override void OnLayoutMenuButton(ref ContextMenuButton button, int index, bool construct = false) - { - var style = Style.Current; - if (_selectedIndices.Contains(index)) - { - button.Icon = style.CheckBoxTick; - } - else - { - button.Icon = SpriteHandle.Default; - } - if (_tooltips != null && _tooltips.Length > index) - { - button.TooltipText = _tooltips[index]; - } - } - protected override void OnSelectedIndexChanged() - { - ContentFilter.UpdateFilterVisability(this); - base.OnSelectedIndexChanged(); - } - } -} diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index e6a8c309c..94db3a5b9 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -441,11 +441,9 @@ namespace FlaxEditor.Content { get { + // Skip when hidden if (!Visible) - { return Rectangle.Empty; - } - var view = Parent as ContentView; var size = Size; switch (view?.ViewType ?? ContentViewType.Tiles) @@ -453,7 +451,7 @@ namespace FlaxEditor.Content case ContentViewType.Tiles: { var textHeight = DefaultTextHeight * size.X / DefaultWidth; - return new Rectangle(0, size.Y - textHeight, size.X - 2, textHeight); + return new Rectangle(0, size.Y - textHeight, size.X, textHeight); } case ContentViewType.List: { @@ -671,8 +669,6 @@ namespace FlaxEditor.Content /// public override void Draw() { - - // Cache data var size = Size; var style = Style.Current; var view = Parent as ContentView; diff --git a/Source/Editor/Content/Tree/RootContentTreeNode.cs b/Source/Editor/Content/Tree/RootContentTreeNode.cs index 6dde4cbb2..328c51874 100644 --- a/Source/Editor/Content/Tree/RootContentTreeNode.cs +++ b/Source/Editor/Content/Tree/RootContentTreeNode.cs @@ -17,6 +17,6 @@ namespace FlaxEditor.Content } /// - public override string NavButtonLabel => string.Empty; + public override string NavButtonLabel => " /"; } } diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index da38268b2..da2106450 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using FlaxEngine.GUI; -using FlaxEditor.GUI.ContextMenu; namespace FlaxEditor.GUI { @@ -399,13 +399,14 @@ namespace FlaxEditor.GUI OnItemClicked((int)btn.Tag); if (SupportMultiSelect) { + // Don't hide in multi-select, so user can edit multiple elements instead of just one UpdateButtons(); _popupMenu?.PerformLayout(); } else { _popupMenu?.Hide(); - }//[nori_sc] don't hide it Support MultiSelect is on actions per min is important for UX, if some one wont to set more then 5 elements in multi select menu let them do it + } }; } @@ -423,13 +424,15 @@ namespace FlaxEditor.GUI if (_items.Count > 0) { UpdateButtons(); + // Show dropdown list _popupMenu.MinimumWidth = Width; _popupMenu.Show(this, new Float2(1, Height)); } } + /// - /// update buttons layout and repains + /// Updates buttons layout. /// private void UpdateButtons() { @@ -443,7 +446,7 @@ namespace FlaxEditor.GUI for (int i = 0; i < _items.Count; i++) { var btn = _popupMenu.AddButton(_items[i]); - OnLayoutMenuButton(ref btn, i, true); + OnLayoutMenuButton(btn, i, true); btn.Tag = i; } } @@ -457,25 +460,23 @@ namespace FlaxEditor.GUI if (itemControls[i] is ContextMenuButton btn) { btn.Text = _items[i]; - OnLayoutMenuButton(ref btn, i, true); + OnLayoutMenuButton(btn, i, true); } } } } + /// - /// caled when button is created or repainted u can overite it to give the button custom look + /// Called when button is created or updated. Can be used to customize the visuals. /// - /// button refrance - /// curent item index + /// The button. + /// The item index. /// true if button is created else it is repainting the button - protected virtual void OnLayoutMenuButton(ref FlaxEditor.GUI.ContextMenu.ContextMenuButton button,int index, bool construct = false) + protected virtual void OnLayoutMenuButton(ContextMenuButton button, int index, bool construct = false) { - var style = Style.Current; button.Checked = _selectedIndices.Contains(index); if (_tooltips != null && _tooltips.Length > index) - { button.TooltipText = _tooltips[index]; - } } /// @@ -498,6 +499,8 @@ namespace FlaxEditor.GUI _popupMenu = null; } + if (IsDisposing) + return; _selectedIndices.Clear(); _selectedIndices = null; _items.Clear(); diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs index c2b130e4e..e371f7c4b 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuButton.cs @@ -138,7 +138,7 @@ namespace FlaxEditor.GUI.ContextMenu // Draw icon const float iconSize = 14; - var icon = Checked ? Style.Current.CheckBoxTick : Icon; + var icon = Checked ? style.CheckBoxTick : Icon; if (icon.IsValid) Render2D.DrawSprite(icon, new Rectangle(-iconSize - 1, (Height - iconSize) / 2, iconSize, iconSize), textColor); } diff --git a/Source/Editor/GUI/NavigationBar.cs b/Source/Editor/GUI/NavigationBar.cs index 3c717423b..b3811b4a9 100644 --- a/Source/Editor/GUI/NavigationBar.cs +++ b/Source/Editor/GUI/NavigationBar.cs @@ -50,14 +50,9 @@ namespace FlaxEditor.GUI { if (toolstrip == null) return; - var lastToolstripButton = toolstrip.LastButton; var parentSize = Parent.Size; - Bounds = new Rectangle - ( - new Float2(lastToolstripButton.Right + 8.0f, 0), - new Float2(parentSize.X - X - 8.0f, toolstrip.Height) - ); + Bounds = new Rectangle(lastToolstripButton.Right + 8.0f, 0, parentSize.X - X - 8.0f, toolstrip.Height); } } } diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index ecafbf5d4..29d6b2c00 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -902,6 +902,23 @@ namespace FlaxEditor.Modules } if (sortChildren) node.SortChildren(); + + // Ignore some special folders + if (node is MainContentTreeNode mainNode && mainNode.Folder.ShortName == "Source") + { + var mainNodeChild = mainNode.Folder.Find(StringUtils.CombinePaths(mainNode.Path, "obj")) as ContentFolder; + if (mainNodeChild != null) + { + mainNodeChild.Visible = false; + mainNodeChild.Node.Visible = false; + } + mainNodeChild = mainNode.Folder.Find(StringUtils.CombinePaths(mainNode.Path, "Properties")) as ContentFolder; + if (mainNodeChild != null) + { + mainNodeChild.Visible = false; + mainNodeChild.Node.Visible = false; + } + } } private void LoadScripts(ContentTreeNode parent, string[] files) diff --git a/Source/Editor/Windows/ContentWindow.Navigation.cs b/Source/Editor/Windows/ContentWindow.Navigation.cs index ca3de992d..b1def7ae8 100644 --- a/Source/Editor/Windows/ContentWindow.Navigation.cs +++ b/Source/Editor/Windows/ContentWindow.Navigation.cs @@ -196,18 +196,19 @@ namespace FlaxEditor.Windows } float x = NavigationBar.DefaultButtonsMargin; float h = _toolStrip.ItemsHeight - 2 * ToolStrip.DefaultMarginV; - for (int i = nodes.Count - 2; i >= 0; i--) + for (int i = nodes.Count - 1; i >= 0; i--) { - var button = new ContentNavigationButton(nodes[i], x - 100, ToolStrip.DefaultMarginV, h); + var button = new ContentNavigationButton(nodes[i], x, ToolStrip.DefaultMarginV, h); button.PerformLayout(); x += button.Width + NavigationBar.DefaultButtonsMargin; _navigationBar.AddChild(button); - if (i == 0) - continue; - var buttonSeparator = new ContentNavigationButtonSeparator(button, x, ToolStrip.DefaultMarginV, h); - buttonSeparator.PerformLayout(); - x += buttonSeparator.Width + NavigationBar.DefaultButtonsMargin; - _navigationBar.AddChild(buttonSeparator); + if (i > 0) + { + var separator = new ContentNavigationSeparator(button, x, ToolStrip.DefaultMarginV, h); + separator.PerformLayout(); + x += separator.Width + NavigationBar.DefaultButtonsMargin; + _navigationBar.AddChild(separator); + } } nodes.Clear(); @@ -224,21 +225,13 @@ namespace FlaxEditor.Windows /// /// Gets the current view folder. /// - public ContentFolder CurrentViewFolder - { - get - { - var node = SelectedNode; - return node?.Folder; - } - } + public ContentFolder CurrentViewFolder => SelectedNode?.Folder; /// /// Shows the root folder. /// public void ShowRoot() { - // Show root folder _tree.Select(_root); } } diff --git a/Source/Editor/Windows/ContentWindow.Search.cs b/Source/Editor/Windows/ContentWindow.Search.cs index a6fd2c81e..a1072d158 100644 --- a/Source/Editor/Windows/ContentWindow.Search.cs +++ b/Source/Editor/Windows/ContentWindow.Search.cs @@ -191,6 +191,7 @@ namespace FlaxEditor.Windows } // Search by filter only + bool showAllFiles = _showAllFiles; if (string.IsNullOrWhiteSpace(query)) { if (SelectedNode == _root) @@ -199,12 +200,12 @@ namespace FlaxEditor.Windows for (int i = 0; i < _root.ChildrenCount; i++) { if (_root.GetChild(i) is ContentTreeNode node) - UpdateItemsSearchFilter(node.Folder, items, filters); + UpdateItemsSearchFilter(node.Folder, items, filters, showAllFiles); } } else { - UpdateItemsSearchFilter(CurrentViewFolder, items, filters); + UpdateItemsSearchFilter(CurrentViewFolder, items, filters, showAllFiles); } } // Search by asset ID @@ -221,12 +222,12 @@ namespace FlaxEditor.Windows for (int i = 0; i < _root.ChildrenCount; i++) { if (_root.GetChild(i) is ContentTreeNode node) - UpdateItemsSearchFilter(node.Folder, items, filters, query); + UpdateItemsSearchFilter(node.Folder, items, filters, showAllFiles, query); } } else { - UpdateItemsSearchFilter(CurrentViewFolder, items, filters, query); + UpdateItemsSearchFilter(CurrentViewFolder, items, filters, showAllFiles, query); } } @@ -234,42 +235,34 @@ namespace FlaxEditor.Windows _view.ShowItems(items, _sortType); } - private void UpdateItemsSearchFilter(ContentFolder folder, List items, bool[] filters) + private void UpdateItemsSearchFilter(ContentFolder folder, List items, bool[] filters, bool showAllFiles) { for (int i = 0; i < folder.Children.Count; i++) { var child = folder.Children[i]; - if (child is ContentFolder childFolder) { - UpdateItemsSearchFilter(childFolder, items, filters); + UpdateItemsSearchFilter(childFolder, items, filters, showAllFiles); } - else + else if (filters[(int)child.SearchFilter] && (showAllFiles || !(child is FileItem))) { - if (filters[(int)child.SearchFilter]) - { - items.Add(child); - } + items.Add(child); } } } - private void UpdateItemsSearchFilter(ContentFolder folder, List items, bool[] filters, string filterText) + private void UpdateItemsSearchFilter(ContentFolder folder, List items, bool[] filters, bool showAllFiles, string filterText) { for (int i = 0; i < folder.Children.Count; i++) { var child = folder.Children[i]; - if (child is ContentFolder childFolder) { - UpdateItemsSearchFilter(childFolder, items, filters, filterText); + UpdateItemsSearchFilter(childFolder, items, filters, showAllFiles, filterText); } - else if (filters[(int)child.SearchFilter]) + else if (filters[(int)child.SearchFilter] && (showAllFiles || !(child is FileItem)) && QueryFilterHelper.Match(filterText, child.ShortName)) { - if (filters[(int)child.SearchFilter] && QueryFilterHelper.Match(filterText, child.ShortName)) - { - items.Add(child); - } + items.Add(child); } } } diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 2a779ec01..f4fb4afdc 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Xml; using FlaxEditor.Content; using FlaxEditor.Content.GUI; @@ -39,14 +40,13 @@ namespace FlaxEditor.Windows private readonly ToolStripButton _navigateForwardButton; private readonly ToolStripButton _navigateUpButton; - private ContentNavigationBar _navigationBar; + private NavigationBar _navigationBar; private Tree _tree; private TextBox _foldersSearchBox; private TextBox _itemsSearchBox; private ViewDropdown _viewDropdown; - private ContentSettingsDropdown _ContentSettingDropdown; - private const float _ContentDropdownSizeX = 100; private SortType _sortType; + private bool _showEngineFiles = true, _showPluginsFiles = true, _showAllFiles = true; private RootContentTreeNode _root; @@ -66,6 +66,57 @@ namespace FlaxEditor.Windows /// public ContentView View => _view; + internal bool ShowEngineFiles + { + get => _showEngineFiles; + set + { + if (_showEngineFiles != value) + { + _showEngineFiles = value; + if (Editor.ContentDatabase.Engine != null) + { + Editor.ContentDatabase.Engine.Visible = value; + Editor.ContentDatabase.Engine.Folder.Visible = value; + RefreshView(); + } + } + } + } + + internal bool ShowPluginsFiles + { + get => _showPluginsFiles; + set + { + if (_showPluginsFiles != value) + { + _showPluginsFiles = value; + foreach (var project in Editor.ContentDatabase.Projects) + { + if (project == Editor.ContentDatabase.Game || project == Editor.ContentDatabase.Engine) + continue; + project.Visible = value; + project.Folder.Visible = value; + RefreshView(); + } + } + } + } + + internal bool ShowAllFiles + { + get => _showAllFiles; + set + { + if (_showAllFiles != value) + { + _showAllFiles = value; + RefreshView(); + } + } + } + /// /// Initializes a new instance of the class. /// @@ -75,7 +126,7 @@ namespace FlaxEditor.Windows { Title = "Content"; Icon = editor.Icons.Folder32; - + // Content database events editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true; editor.ContentDatabase.ItemRemoved += OnContentDatabaseItemRemoved; @@ -110,24 +161,11 @@ namespace FlaxEditor.Windows _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 - - _navigationBar = new ContentNavigationBar(_toolStrip) + _navigationBar = new NavigationBar { Parent = _toolStrip, - ofssetFromRightEdge = _ContentDropdownSizeX - }; - var DropdownSettingsPanel = new Panel(ScrollBars.None) - { - Parent = _toolStrip, - Size = new Float2(_ContentDropdownSizeX, _toolStrip.Height) - }; - //setings Dropdown - _ContentSettingDropdown = new ContentSettingsDropdown - { - AnchorPreset = AnchorPresets.StretchAll, - Parent = DropdownSettingsPanel, - Offsets = new Margin(2, 4, 4, 4) }; // Split panel @@ -231,10 +269,6 @@ namespace FlaxEditor.Windows private ContextMenu OnViewDropdownPopupCreate(ComboBox comboBox) { var menu = new ContextMenu(); - - var showFileExtensionsButton = menu.AddButton("Show file extensions", () => View.ShowFileExtensions = !View.ShowFileExtensions); - showFileExtensionsButton.Checked = View.ShowFileExtensions; - showFileExtensionsButton.AutoCheck = true; var viewScale = menu.AddButton("View Scale"); viewScale.CloseMenuOnClick = false; @@ -259,6 +293,33 @@ namespace FlaxEditor.Windows } }; + var show = menu.AddChildMenu("Show"); + { + var b = show.ContextMenu.AddButton("File extensions", () => View.ShowFileExtensions = !View.ShowFileExtensions); + b.TooltipText = "Shows all files with extensions"; + b.Checked = View.ShowFileExtensions; + b.CloseMenuOnClick = false; + b.AutoCheck = true; + + b = show.ContextMenu.AddButton("Engine files", () => ShowEngineFiles = !ShowEngineFiles); + b.TooltipText = "Shows in-built engine content"; + b.Checked = ShowEngineFiles; + b.CloseMenuOnClick = false; + b.AutoCheck = true; + + b = show.ContextMenu.AddButton("Plugins files", () => ShowPluginsFiles = !ShowPluginsFiles); + b.TooltipText = "Shows plugin projects content"; + b.Checked = ShowPluginsFiles; + b.CloseMenuOnClick = false; + b.AutoCheck = true; + + b = show.ContextMenu.AddButton("All files", () => ShowAllFiles = !ShowAllFiles); + b.TooltipText = "Shows all files including other than assets and source code"; + b.Checked = ShowAllFiles; + b.CloseMenuOnClick = false; + b.AutoCheck = true; + } + var filters = menu.AddChildMenu("Filters"); for (int i = 0; i < _viewDropdown.Items.Count; i++) { @@ -367,7 +428,7 @@ namespace FlaxEditor.Windows { if (!item.CanRename) return; - + // Show element in the view Select(item, true); @@ -444,10 +505,7 @@ namespace FlaxEditor.Windows if (!Editor.ContentEditing.IsValidAssetName(item, newShortName, out string hint)) { // Invalid name - MessageBox.Show("Given asset name is invalid. " + hint, - "Invalid name", - MessageBoxButtons.OK, - MessageBoxIcon.Error); + MessageBox.Show("Given asset name is invalid. " + hint, "Invalid name", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } @@ -893,7 +951,7 @@ namespace FlaxEditor.Windows if (target == _root) { // Special case for root folder - List items = new List(8); + var items = new List(8); for (int i = 0; i < _root.ChildrenCount; i++) { if (_root.GetChild(i) is ContentTreeNode node) @@ -905,9 +963,11 @@ namespace FlaxEditor.Windows } else { - // Show folder contents - _view.ShowItems(ContentFilter.FilterFolder(target.Folder), _sortType, false, true); + var items = target.Folder.Children; + if (!_showAllFiles) + items = items.Where(x => !(x is FileItem)).ToList(); + _view.ShowItems(items, _sortType, false, true); } } @@ -930,12 +990,6 @@ namespace FlaxEditor.Windows _navigateUpButton.Enabled = folder != null && _tree.SelectedNode != _root; } - private void RemoveFolder2Root(ContentTreeNode node) - { - // Remove from the root - _root.RemoveChild(node); - } - /// public override void OnInit() { @@ -947,18 +1001,20 @@ namespace FlaxEditor.Windows _root.Expand(true); // Add game project on top, plugins in the middle and engine at bottom - _root.AddChild(ContentFilter.Filter(Editor.ContentDatabase.Game)); + _root.AddChild(Editor.ContentDatabase.Game); Editor.ContentDatabase.Projects.Sort(); foreach (var project in Editor.ContentDatabase.Projects) { project.SortChildrenRecursive(); if (project == Editor.ContentDatabase.Game || project == Editor.ContentDatabase.Engine) continue; + project.Visible = _showPluginsFiles; + project.Folder.Visible = _showPluginsFiles; _root.AddChild(project); - ContentFilter.plugins.Add(project); } + Editor.ContentDatabase.Engine.Visible = _showEngineFiles; + Editor.ContentDatabase.Engine.Folder.Visible = _showEngineFiles; _root.AddChild(Editor.ContentDatabase.Engine); - ContentFilter.engine = Editor.ContentDatabase.Engine; Editor.ContentDatabase.Game?.Expand(true); _tree.Margin = new Margin(0.0f, 0.0f, -16.0f, 2.0f); // Hide root node @@ -1029,7 +1085,6 @@ namespace FlaxEditor.Windows /// public override bool OnMouseUp(Float2 location, MouseButton button) { - // Check if it's a right mouse button if (button == MouseButton.Right) { // Find control that is under the mouse @@ -1067,6 +1122,15 @@ namespace FlaxEditor.Windows return base.OnMouseUp(location, button); } + + /// + protected override void PerformLayoutBeforeChildren() + { + base.PerformLayoutBeforeChildren(); + + _navigationBar?.UpdateBounds(_toolStrip); + } + /// public override bool UseLayoutData => true; @@ -1076,13 +1140,10 @@ namespace FlaxEditor.Windows LayoutSerializeSplitter(writer, "Split", _split); writer.WriteAttributeString("Scale", _view.ViewScale.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString("ShowFileExtensions", _view.ShowFileExtensions.ToString()); + writer.WriteAttributeString("ShowEngineFiles", ShowEngineFiles.ToString()); + writer.WriteAttributeString("ShowPluginsFiles", ShowPluginsFiles.ToString()); + writer.WriteAttributeString("ShowAllFiles", ShowAllFiles.ToString()); writer.WriteAttributeString("ViewType", _view.ViewType.ToString()); - for (int i = 0; i < _ContentSettingDropdown.Selection.Count; i++) - { - if(_ContentSettingDropdown.Selection.Count != 0) - writer.WriteAttributeString(_ContentSettingDropdown.Settings[_ContentSettingDropdown.Selection[i]].Replace(' ', '_'), _ContentSettingDropdown.Selection[i].ToString()); - } - } /// @@ -1093,17 +1154,14 @@ namespace FlaxEditor.Windows _view.ViewScale = value1; if (bool.TryParse(node.GetAttribute("ShowFileExtensions"), out bool value2)) _view.ShowFileExtensions = value2; + if (bool.TryParse(node.GetAttribute("ShowEngineFiles"), out value2)) + ShowEngineFiles = value2; + if (bool.TryParse(node.GetAttribute("ShowPluginsFiles"), out value2)) + ShowPluginsFiles = value2; + if (bool.TryParse(node.GetAttribute("ShowAllFiles"), out value2)) + ShowAllFiles = value2; if (Enum.TryParse(node.GetAttribute("ViewType"), out ContentViewType viewType)) _view.ViewType = viewType; - - for (int i = 0; i < _ContentSettingDropdown.Settings.Count; i++) - { - int flag; - if (int.TryParse(node.GetAttribute(_ContentSettingDropdown.Settings[i].Replace(' ', '_')), out flag)) - _ContentSettingDropdown.Selection.Add(flag); - - } - ContentFilter.UpdateFilterVisability(_ContentSettingDropdown); } /// From 0a5f983ff4eff7fc2b9bf23ad3f75423e77761ac Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 21:36:36 +0200 Subject: [PATCH 36/40] Fix error when model is missing in Editor for prefab editor #1354 --- Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index 277f9ecb0..9607680f2 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -39,6 +39,8 @@ namespace FlaxEditor.CustomEditors.Editors _entryIndex = entryIndex; _modelInstance = modelInstance; var slots = modelInstance.MaterialSlots; + if (slots == null || entryIndex >= slots.Length) + return; if (entry.Material == slots[entryIndex].Material) { // Ensure that entry with default material set is set back to null From 776fbe2a7b8a44b7c008c7337827c17031d226a6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 22:30:51 +0200 Subject: [PATCH 37/40] Fix creating plugin with whitespace or non-ASCII character in the name #1335 --- Source/Editor/Windows/PluginsWindow.cs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index cf68c6ad8..f4acfffc8 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -636,11 +636,17 @@ namespace FlaxEditor.Windows { Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to add plugin to project. {e}"); } - var oldpluginPath = Path.Combine(extractPath, "ExamplePlugin-master"); var newPluginPath = Path.Combine(extractPath , pluginName); Directory.Move(oldpluginPath, newPluginPath); + // Format plugin name into a valid name for code (C#/C++ typename) + var pluginCodeName = Content.ScriptItem.FilterScriptName(pluginName); + if (string.IsNullOrEmpty(pluginCodeName)) + pluginCodeName = "MyPlugin"; + Editor.Log($"Using plugin code type name: {pluginCodeName}"); + + var oldFlaxProjFile = Path.Combine(newPluginPath, "ExamplePlugin.flaxproj"); var newFlaxProjFile = Path.Combine(newPluginPath, $"{pluginName}.flaxproj"); File.Move(oldFlaxProjFile, newFlaxProjFile); @@ -659,8 +665,8 @@ namespace FlaxEditor.Windows flaxPluginProjContents.Version = new Version(pluginVersion); if (!string.IsNullOrEmpty(companyName)) flaxPluginProjContents.Company = companyName; - flaxPluginProjContents.GameTarget = $"{pluginName}Target"; - flaxPluginProjContents.EditorTarget = $"{pluginName}EditorTarget"; + flaxPluginProjContents.GameTarget = $"{pluginCodeName}Target"; + flaxPluginProjContents.EditorTarget = $"{pluginCodeName}EditorTarget"; await File.WriteAllTextAsync(newFlaxProjFile, JsonSerializer.Serialize(flaxPluginProjContents, typeof(ProjectInfo))); // Rename source directories @@ -676,14 +682,14 @@ namespace FlaxEditor.Windows File.Delete(file); continue; } - + var fileText = await File.ReadAllTextAsync(file); - await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginName)); - var fileName = Path.GetFileName(file).Replace("ExamplePlugin", pluginName); + await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginCodeName)); + var fileName = Path.GetFileName(file).Replace("ExamplePlugin", pluginCodeName); File.Move(file, Path.Combine(directory, fileName)); } - - var newName = directory.Replace("ExamplePlugin", pluginName); + + var newName = directory.Replace("ExamplePlugin", pluginCodeName); Directory.Move(directory, newName); } @@ -692,8 +698,8 @@ namespace FlaxEditor.Windows foreach (var file in targetFiles) { var fileText = await File.ReadAllTextAsync(file); - await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginName)); - var newName = file.Replace("ExamplePlugin", pluginName); + await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginCodeName)); + var newName = file.Replace("ExamplePlugin", pluginCodeName); File.Move(file, newName); } Debug.Logger.LogHandler.LogWrite(LogType.Info, $"Plugin project {pluginName} has successfully been created."); From 99c2adc1f3f91ee9f6240b35b38858fb57747393 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 22:32:17 +0200 Subject: [PATCH 38/40] Add game settings formatting for created plugin ProjectInfo #1335 --- Source/Editor/Windows/PluginsWindow.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index f4acfffc8..fa2547797 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -669,6 +669,17 @@ namespace FlaxEditor.Windows flaxPluginProjContents.EditorTarget = $"{pluginCodeName}EditorTarget"; await File.WriteAllTextAsync(newFlaxProjFile, JsonSerializer.Serialize(flaxPluginProjContents, typeof(ProjectInfo))); + // Format game settings + var gameSettingsPath = Path.Combine(newPluginPath, "Content", "GameSettings.json"); + if (File.Exists(gameSettingsPath)) + { + var contents = await File.ReadAllTextAsync(gameSettingsPath); + contents = contents.Replace("Example Plugin", pluginName); + contents = contents.Replace("\"CompanyName\": \"Flax\"", $"\"CompanyName\": \"{companyName}\""); + contents = contents.Replace("1.0", pluginVersion); + await File.WriteAllTextAsync(gameSettingsPath, contents, Encoding.UTF8); + } + // Rename source directories var sourcePath = Path.Combine(newPluginPath, "Source"); var sourceDirectories = Directory.GetDirectories(sourcePath); From 985d6cc811134f5321b71fd81d16680efb941bef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 22:32:37 +0200 Subject: [PATCH 39/40] Add UTF8 format for created plugin files and format code #1335 --- Source/Editor/Content/Items/ScriptItem.cs | 2 +- Source/Editor/Windows/PluginsWindow.cs | 87 ++++++++++++----------- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/Source/Editor/Content/Items/ScriptItem.cs b/Source/Editor/Content/Items/ScriptItem.cs index 811afc179..95b879b79 100644 --- a/Source/Editor/Content/Items/ScriptItem.cs +++ b/Source/Editor/Content/Items/ScriptItem.cs @@ -30,7 +30,7 @@ namespace FlaxEditor.Content ShowFileExtension = true; } - private static string FilterScriptName(string input) + internal static string FilterScriptName(string input) { var length = input.Length; var sb = new StringBuilder(length); diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index fa2547797..d53b64e22 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -6,6 +6,7 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Net.Http; +using System.Text; using System.Threading.Tasks; using FlaxEditor.GUI; using FlaxEditor.GUI.ContextMenu; @@ -13,7 +14,6 @@ using FlaxEditor.GUI.Tabs; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; -using Debug = FlaxEngine.Debug; namespace FlaxEditor.Windows { @@ -193,7 +193,7 @@ namespace FlaxEditor.Windows Text = "Create Plugin Project", TooltipText = "Add new plugin project.", AnchorPreset = AnchorPresets.TopLeft, - LocalLocation = new Float2(70,18), + LocalLocation = new Float2(70, 18), Size = new Float2(150, 25), Parent = vp, }; @@ -209,7 +209,7 @@ namespace FlaxEditor.Windows Parent = vp, }; _cloneProjectButton.Clicked += OnCloneProjectButtonClicked; - + _tabs = new Tabs { Orientation = Orientation.Vertical, @@ -254,7 +254,7 @@ namespace FlaxEditor.Windows nameTextBox.LocalX += (300 - (10)) * 0.5f; nameTextBox.LocalY += 10; nameLabel.LocalX += (300 - (nameLabel.Width + nameTextBox.Width)) * 0.5f + 10; - + var defaultTextBoxBorderColor = nameTextBox.BorderColor; var defaultTextBoxBorderSelectedColor = nameTextBox.BorderSelectedColor; nameTextBox.TextChanged += () => @@ -265,7 +265,7 @@ namespace FlaxEditor.Windows nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; return; } - + var pluginPath = Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text); if (Directory.Exists(pluginPath)) { @@ -315,7 +315,7 @@ namespace FlaxEditor.Windows { if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text)) && !string.IsNullOrEmpty(nameTextBox.Text)) { - Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Cannot create plugin due to name conflict."); + Editor.LogWarning("Cannot create plugin due to name conflict."); return; } OnCloneButtonClicked(nameTextBox.Text, gitPathTextBox.Text); @@ -346,7 +346,7 @@ namespace FlaxEditor.Windows { if (string.IsNullOrEmpty(gitPath)) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to create plugin project due to no GIT path."); + Editor.LogError($"Failed to create plugin project due to no GIT path."); return; } if (string.IsNullOrEmpty(pluginName)) @@ -363,32 +363,34 @@ namespace FlaxEditor.Windows pluginName = name; } } - + var clonePath = Path.Combine(Globals.ProjectFolder, "Plugins", pluginName); if (!Directory.Exists(clonePath)) Directory.CreateDirectory(clonePath); else { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Plugin Name is already used. Pick a different Name."); + Editor.LogError($"Plugin Name is already used. Pick a different Name."); return; } try { // Start git clone - var settings = new CreateProcessSettings(); - settings.FileName = "git"; - settings.Arguments = $"clone {gitPath} \"{clonePath}\""; - settings.ShellExecute = false; - settings.LogOutput = true; + var settings = new CreateProcessSettings + { + FileName = "git", + Arguments = $"clone {gitPath} \"{clonePath}\"", + ShellExecute = false, + LogOutput = true, + }; Platform.CreateProcess(ref settings); } catch (Exception e) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed Git process. {e}"); + Editor.LogError($"Failed Git process. {e}"); return; } - - Debug.Logger.LogHandler.LogWrite(LogType.Info, $"Plugin project has been cloned."); + + Editor.Log($"Plugin project has been cloned."); // Find project config file. Could be different then what the user named the folder. var files = Directory.GetFiles(clonePath); @@ -401,9 +403,9 @@ namespace FlaxEditor.Windows Debug.Log(pluginProjectName); } } - + if (string.IsNullOrEmpty(pluginProjectName)) - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to find plugin project file to add to Project config. Please add manually."); + Editor.LogError($"Failed to find plugin project file to add to Project config. Please add manually."); else { await AddReferenceToProject(pluginName, pluginProjectName); @@ -450,7 +452,7 @@ namespace FlaxEditor.Windows nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor; return; } - + var pluginPath = Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text); if (Directory.Exists(pluginPath)) { @@ -518,7 +520,7 @@ namespace FlaxEditor.Windows { if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Plugins", nameTextBox.Text))) { - Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Cannot create plugin due to name conflict."); + Editor.LogWarning("Cannot create plugin due to name conflict."); return; } OnCreateButtonClicked(nameTextBox.Text, versionTextBox.Text, companyTextBox.Text); @@ -551,10 +553,10 @@ namespace FlaxEditor.Windows { if (string.IsNullOrEmpty(pluginName)) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to create plugin project due to no plugin name."); + Editor.LogError($"Failed to create plugin project due to no plugin name."); return; } - + var templateUrl = "https://github.com/FlaxEngine/ExamplePlugin/archive/refs/heads/master.zip"; var localTemplateFolderLocation = Path.Combine(Editor.LocalCachePath, "TemplatePluginCache"); if (!Directory.Exists(localTemplateFolderLocation)) @@ -564,24 +566,24 @@ namespace FlaxEditor.Windows try { // Download example plugin - using (HttpClient client = new HttpClient()) + using (var client = new HttpClient()) { byte[] zipBytes = await client.GetByteArrayAsync(templateUrl); - await File.WriteAllBytesAsync(!File.Exists(localTemplatePath) ? Path.Combine(localTemplatePath) : Path.Combine(Editor.LocalCachePath, "TemplatePluginCache" , "TemplatePlugin1.zip"), zipBytes); - - Debug.Logger.LogHandler.LogWrite(LogType.Info, "Template for plugin project has downloaded"); + await File.WriteAllBytesAsync(!File.Exists(localTemplatePath) ? Path.Combine(localTemplatePath) : Path.Combine(Editor.LocalCachePath, "TemplatePluginCache", "TemplatePlugin1.zip"), zipBytes); + + Editor.Log("Template for plugin project has downloaded"); } } catch (Exception e) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to download template project. Trying to use local file. {e}"); + Editor.LogError($"Failed to download template project. Trying to use local file. {e}"); if (!File.Exists(localTemplatePath)) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to use local file. Does not exist."); + Editor.LogError($"Failed to use local file. Does not exist."); return; } } - + // Check if any changes in new downloaded file if (File.Exists(Path.Combine(Editor.LocalCachePath, "TemplatePluginCache", "TemplatePlugin1.zip"))) { @@ -622,7 +624,7 @@ namespace FlaxEditor.Windows File.Delete(localTemplatePath2); } } - + var extractPath = Path.Combine(Globals.ProjectFolder, "Plugins"); if (!Directory.Exists(extractPath)) Directory.CreateDirectory(extractPath); @@ -630,15 +632,12 @@ namespace FlaxEditor.Windows try { await Task.Run(() => ZipFile.ExtractToDirectory(localTemplatePath, extractPath)); - Debug.Logger.LogHandler.LogWrite(LogType.Info, "Template for plugin project successfully moved to project."); + Editor.Log("Template for plugin project successfully moved to project."); } catch (IOException e) { - Debug.Logger.LogHandler.LogWrite(LogType.Error, $"Failed to add plugin to project. {e}"); + Editor.LogError($"Failed to add plugin to project. {e}"); } - var oldpluginPath = Path.Combine(extractPath, "ExamplePlugin-master"); - var newPluginPath = Path.Combine(extractPath , pluginName); - Directory.Move(oldpluginPath, newPluginPath); // Format plugin name into a valid name for code (C#/C++ typename) var pluginCodeName = Content.ScriptItem.FilterScriptName(pluginName); @@ -646,11 +645,14 @@ namespace FlaxEditor.Windows pluginCodeName = "MyPlugin"; Editor.Log($"Using plugin code type name: {pluginCodeName}"); + var oldPluginPath = Path.Combine(extractPath, "ExamplePlugin-master"); + var newPluginPath = Path.Combine(extractPath, pluginName); + Directory.Move(oldPluginPath, newPluginPath); var oldFlaxProjFile = Path.Combine(newPluginPath, "ExamplePlugin.flaxproj"); var newFlaxProjFile = Path.Combine(newPluginPath, $"{pluginName}.flaxproj"); File.Move(oldFlaxProjFile, newFlaxProjFile); - + var readme = Path.Combine(newPluginPath, "README.md"); if (File.Exists(readme)) File.Delete(readme); @@ -667,7 +669,7 @@ namespace FlaxEditor.Windows flaxPluginProjContents.Company = companyName; flaxPluginProjContents.GameTarget = $"{pluginCodeName}Target"; flaxPluginProjContents.EditorTarget = $"{pluginCodeName}EditorTarget"; - await File.WriteAllTextAsync(newFlaxProjFile, JsonSerializer.Serialize(flaxPluginProjContents, typeof(ProjectInfo))); + await File.WriteAllTextAsync(newFlaxProjFile, JsonSerializer.Serialize(flaxPluginProjContents, typeof(ProjectInfo)), Encoding.UTF8); // Format game settings var gameSettingsPath = Path.Combine(newPluginPath, "Content", "GameSettings.json"); @@ -703,17 +705,17 @@ namespace FlaxEditor.Windows var newName = directory.Replace("ExamplePlugin", pluginCodeName); Directory.Move(directory, newName); } - + // Rename targets var targetFiles = Directory.GetFiles(sourcePath); foreach (var file in targetFiles) { var fileText = await File.ReadAllTextAsync(file); - await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginCodeName)); + await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginCodeName), Encoding.UTF8); var newName = file.Replace("ExamplePlugin", pluginCodeName); File.Move(file, newName); } - Debug.Logger.LogHandler.LogWrite(LogType.Info, $"Plugin project {pluginName} has successfully been created."); + Editor.Log($"Plugin project {pluginName} has successfully been created."); await AddReferenceToProject(pluginName, pluginName); MessageBox.Show($"{pluginName} has been successfully created. Restart editor for changes to take effect.", "Plugin Project Created", MessageBoxButtons.OK); @@ -738,11 +740,10 @@ namespace FlaxEditor.Windows references.Add(newReference); } flaxProjContents.References = references.ToArray(); - await File.WriteAllTextAsync(flaxProjPath, JsonSerializer.Serialize(flaxProjContents, typeof(ProjectInfo))); + await File.WriteAllTextAsync(flaxProjPath, JsonSerializer.Serialize(flaxProjContents, typeof(ProjectInfo)), Encoding.UTF8); } } - private void OnPluginsChanged() { List toRemove = null; From 9d494679ac3cb06baf5f414bb643abe620f29e9f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Sep 2023 22:42:48 +0200 Subject: [PATCH 40/40] Add formatting `MyPlugin.cs` for new plugin setup #1335 --- Source/Editor/Windows/PluginsWindow.cs | 29 ++++++++++++++++---------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Windows/PluginsWindow.cs b/Source/Editor/Windows/PluginsWindow.cs index d53b64e22..c5ec02b31 100644 --- a/Source/Editor/Windows/PluginsWindow.cs +++ b/Source/Editor/Windows/PluginsWindow.cs @@ -346,7 +346,7 @@ namespace FlaxEditor.Windows { if (string.IsNullOrEmpty(gitPath)) { - Editor.LogError($"Failed to create plugin project due to no GIT path."); + Editor.LogError("Failed to create plugin project due to no GIT path."); return; } if (string.IsNullOrEmpty(pluginName)) @@ -369,7 +369,7 @@ namespace FlaxEditor.Windows Directory.CreateDirectory(clonePath); else { - Editor.LogError($"Plugin Name is already used. Pick a different Name."); + Editor.LogError("Plugin Name is already used. Pick a different Name."); return; } try @@ -390,7 +390,7 @@ namespace FlaxEditor.Windows return; } - Editor.Log($"Plugin project has been cloned."); + Editor.Log("Plugin project has been cloned."); // Find project config file. Could be different then what the user named the folder. var files = Directory.GetFiles(clonePath); @@ -405,7 +405,7 @@ namespace FlaxEditor.Windows } if (string.IsNullOrEmpty(pluginProjectName)) - Editor.LogError($"Failed to find plugin project file to add to Project config. Please add manually."); + Editor.LogError("Failed to find plugin project file to add to Project config. Please add manually."); else { await AddReferenceToProject(pluginName, pluginProjectName); @@ -553,7 +553,7 @@ namespace FlaxEditor.Windows { if (string.IsNullOrEmpty(pluginName)) { - Editor.LogError($"Failed to create plugin project due to no plugin name."); + Editor.LogError("Failed to create plugin project due to no plugin name."); return; } @@ -579,7 +579,7 @@ namespace FlaxEditor.Windows Editor.LogError($"Failed to download template project. Trying to use local file. {e}"); if (!File.Exists(localTemplatePath)) { - Editor.LogError($"Failed to use local file. Does not exist."); + Editor.LogError("Failed to use local file. Does not exist."); return; } } @@ -690,15 +690,24 @@ namespace FlaxEditor.Windows var files = Directory.GetFiles(directory); foreach (var file in files) { - if (file.Contains("MyPlugin.cs") || file.Contains("MyPluginEditor.cs")) + if (file.Contains("MyPluginEditor.cs")) { File.Delete(file); continue; } - var fileText = await File.ReadAllTextAsync(file); - await File.WriteAllTextAsync(file, fileText.Replace("ExamplePlugin", pluginCodeName)); var fileName = Path.GetFileName(file).Replace("ExamplePlugin", pluginCodeName); + var fileText = await File.ReadAllTextAsync(file); + fileText = fileText.Replace("ExamplePlugin", pluginCodeName); + if (file.Contains("MyPlugin.cs")) + { + fileName = "ExamplePlugin.cs"; + fileText = fileText.Replace("MyPlugin", pluginCodeName); + fileText = fileText.Replace("My Plugin", pluginName); + fileText = fileText.Replace("Flax Engine", companyName); + fileText = fileText.Replace("new Version(1, 0)", $"new Version({pluginVersion.Trim().Replace(".", ", ")})"); + } + await File.WriteAllTextAsync(file, fileText); File.Move(file, Path.Combine(directory, fileName)); } @@ -856,13 +865,11 @@ namespace FlaxEditor.Windows { if (pluginType == null) return null; - foreach (var e in _entries.Keys) { if (e.GetType() == pluginType && _entries.ContainsKey(e)) return _entries[e]; } - return null; }