diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index 23f493c65..8eeccf288 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -248,26 +248,30 @@ namespace FlaxEditor.GUI /// /// The control width. /// The control height. - public ItemsListContextMenu(float width = 320, float height = 220) + /// Enables search field. + public ItemsListContextMenu(float width = 320, float height = 220, bool withSearch = true) { // Context menu dimensions Size = new Float2(width, height); - // Search box - _searchBox = new SearchBox(false, 1, 1) + if (withSearch) { - Parent = this, - Width = Width - 3, - }; - _searchBox.TextChanged += OnSearchFilterChanged; - _searchBox.ClearSearchButton.Clicked += () => PerformLayout(); + // Search box + _searchBox = new SearchBox(false, 1, 1) + { + Parent = this, + Width = Width - 3, + }; + _searchBox.TextChanged += OnSearchFilterChanged; + _searchBox.ClearSearchButton.Clicked += () => PerformLayout(); + } // Panel with scrollbar _scrollPanel = new Panel(ScrollBars.Vertical) { Parent = this, AnchorPreset = AnchorPresets.StretchAll, - Bounds = new Rectangle(0, _searchBox.Bottom + 1, Width, Height - _searchBox.Bottom - 2), + Bounds = withSearch ? new Rectangle(0, _searchBox.Bottom + 1, Width, Height - _searchBox.Bottom - 2) : new Rectangle(Float2.Zero, Size), }; // Items list panel @@ -446,7 +450,7 @@ namespace FlaxEditor.GUI } } - _searchBox.Clear(); + _searchBox?.Clear(); UnlockChildrenRecursive(); PerformLayout(true); } @@ -510,7 +514,7 @@ namespace FlaxEditor.GUI if (RootWindow.FocusedControl == null) { // Focus search box if nothing is focused - _searchBox.Focus(); + _searchBox?.Focus(); return true; } @@ -536,7 +540,7 @@ namespace FlaxEditor.GUI var focusedIndex = items.IndexOf(focusedItem); if (focusedIndex == 0) { - _searchBox.Focus(); + _searchBox?.Focus(); } else if (focusedIndex > 0) { @@ -556,7 +560,7 @@ namespace FlaxEditor.GUI break; } - if (_waitingForInput) + if (_waitingForInput && _searchBox != null) { _waitingForInput = false; _searchBox.Focus(); diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 86d0f060d..361f2e4b1 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -3,10 +3,12 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Xml; +using FlaxEditor.GUI; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEditor.Options; @@ -127,10 +129,13 @@ namespace FlaxEditor.Windows /// private class CommandLineBox : TextBox { - public CommandLineBox(float x, float y, float width) + private OutputLogWindow _window; + + public CommandLineBox(float x, float y, float width, OutputLogWindow window) : base(false, x, y, width) { WatermarkText = ">"; + _window = window; } /// @@ -141,8 +146,22 @@ namespace FlaxEditor.Windows case KeyboardKeys.Return: { // Run command - DebugCommands.Execute(Text); + var command = Text.Trim(); + if (command.Length == 0) + return true; + DebugCommands.Execute(command); SetText(string.Empty); + + // Update history buffer + if (_window._commandHistory == null) + _window._commandHistory = new List(); + else if (_window._commandHistory.Count != 0 && _window._commandHistory.Last() == command) + _window._commandHistory.RemoveAt(_window._commandHistory.Count - 1); + _window._commandHistory.Add(command); + if (_window._commandHistory.Count > CommandHistoryLimit) + _window._commandHistory.RemoveAt(0); + _window.SaveHistory(); + return true; } case KeyboardKeys.Tab: @@ -189,6 +208,48 @@ namespace FlaxEditor.Windows } return true; } + case KeyboardKeys.ArrowUp: + { + if (TextLength == 0) + { + if (_window._commandHistory != null && _window._commandHistory.Count != 0) + { + // Show command history popup + var cm = new ItemsListContextMenu(180, 220, false); + ItemsListContextMenu.Item lastItem = null; + var count = _window._commandHistory.Count; + for (int i = 0; i < count; i++) + { + var command = _window._commandHistory[i]; + cm.AddItem(lastItem = new ItemsListContextMenu.Item + { + Name = command, + }); + } + cm.ItemClicked += item => + { + SetText(item.Name); + SetSelection(Text.Length); + }; + var totalHeight = count * lastItem.Height + cm.ItemsPanel.Margin.Height + cm.ItemsPanel.Spacing * (count - 1); + if (cm.Height > totalHeight) + cm.Height = totalHeight; // Limit popup height if history is small + cm.Show(this, Float2.Zero, ContextMenuDirection.RightUp); + lastItem.Focus(); + cm.ScrollViewTo(lastItem); + } + } + else + { + // TODO: focus similar commands (via popup) + } + return true; + } + case KeyboardKeys.ArrowDown: + { + // Ignore + return true; + } } return base.OnKeyDown(key); } @@ -210,6 +271,9 @@ namespace FlaxEditor.Windows private List _textBlocks = new List(); private DateTime _startupTime; private Regex _compileRegex = new Regex("(?^(?:[a-zA-Z]\\:|\\\\\\\\[ \\-\\.\\w\\.]+\\\\[ \\-\\.\\w.$]+)\\\\(?:[ \\-\\.\\w]+\\\\)*\\w([ \\w.])+)\\((?\\d{1,}),\\d{1,},\\d{1,},\\d{1,}\\): (?error|warning) (?.*)", RegexOptions.Compiled); + private List _commandHistory; + private const string CommandHistoryKey = "CommandHistory"; + private const int CommandHistoryLimit = 30; private Button _viewDropdown; private TextBox _searchBox; @@ -267,7 +331,7 @@ namespace FlaxEditor.Windows }; _output.TargetViewOffsetChanged += OnOutputTargetViewOffsetChanged; _output.TextChanged += OnOutputTextChanged; - _commandLineBox = new CommandLineBox(2, Height - 2 - TextBox.DefaultHeight, Width - 4) + _commandLineBox = new CommandLineBox(2, Height - 2 - TextBox.DefaultHeight, Width - 4, this) { Parent = this, }; @@ -394,6 +458,14 @@ namespace FlaxEditor.Windows FocusOrShow(); } + private void SaveHistory() + { + if (_commandHistory == null || _commandHistory.Count == 0) + Editor.ProjectCache.RemoveCustomData(CommandHistoryKey); + else + Editor.ProjectCache.SetCustomData(CommandHistoryKey, FlaxEngine.Json.JsonSerializer.Serialize(_commandHistory)); + } + /// /// Refreshes the log output. /// @@ -710,6 +782,25 @@ namespace FlaxEditor.Windows public override void OnInit() { _startupTime = Time.StartupTime; + + // Load debug commands history + if (Editor.ProjectCache.TryGetCustomData(CommandHistoryKey, out string history)) + { + try + { + _commandHistory = (List)FlaxEngine.Json.JsonSerializer.Deserialize(history, typeof(List)); + for (int i = _commandHistory.Count - 1; i >= 0; i--) + { + if (string.IsNullOrEmpty(_commandHistory[i])) + _commandHistory.RemoveAt(i); + } + } + catch + { + // Ignore errors + _commandHistory = null; + } + } } /// @@ -750,6 +841,7 @@ namespace FlaxEditor.Windows _outLogTypes = null; _outLogTimes = null; _compileRegex = null; + _commandHistory = null; // Unlink controls _viewDropdown = null;