diff --git a/Build/Updater.ini b/Build/Updater.ini
index 1862b51a..a93329d2 100755
--- a/Build/Updater.ini
+++ b/Build/Updater.ini
@@ -1,5 +1,5 @@
URL http://devbuilds.drdteam.org/ultimatedoombuilder/
FileName Builder.exe
-UpdateName UltimateDoomBuilder-r[REVNUM]-x64.7z
-InstallerName UltimateDoomBuilder-Setup-R[REVNUM]-x64.exe
-UpdaterName UDB_Updater-x64.7z
\ No newline at end of file
+UpdateName UltimateDoomBuilder-r[REVNUM]-x86.7z
+InstallerName UltimateDoomBuilder-Setup-R[REVNUM]-x86.exe
+UpdaterName UDB_Updater-x86.7z
\ No newline at end of file
diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj
index 2a8ab46f..984196e8 100644
--- a/Source/Core/Builder.csproj
+++ b/Source/Core/Builder.csproj
@@ -180,6 +180,12 @@
ArgumentBox.cs
+
+ UserControl
+
+
+ CommandPaletteControl.cs
+
UserControl
@@ -669,6 +675,9 @@
+
+ CommandPaletteControl.cs
+
ExternalCommandControl.cs
diff --git a/Source/Core/BuilderMono.csproj b/Source/Core/BuilderMono.csproj
index 980b8d4e..5bb51403 100644
--- a/Source/Core/BuilderMono.csproj
+++ b/Source/Core/BuilderMono.csproj
@@ -177,6 +177,12 @@
ArgumentBox.cs
+
+ UserControl
+
+
+ CommandPaletteControl.cs
+
UserControl
@@ -661,6 +667,9 @@
+
+ CommandPaletteControl.cs
+
ExternalCommandControl.cs
diff --git a/Source/Core/Controls/CommandPaletteControl.Designer.cs b/Source/Core/Controls/CommandPaletteControl.Designer.cs
new file mode 100644
index 00000000..7b7be86d
--- /dev/null
+++ b/Source/Core/Controls/CommandPaletteControl.Designer.cs
@@ -0,0 +1,136 @@
+
+namespace CodeImp.DoomBuilder.Controls
+{
+ partial class CommandPaletteControl
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.Windows.Forms.ListViewGroup listViewGroup1 = new System.Windows.Forms.ListViewGroup("Recent", System.Windows.Forms.HorizontalAlignment.Left);
+ System.Windows.Forms.ListViewGroup listViewGroup2 = new System.Windows.Forms.ListViewGroup("Usable actions", System.Windows.Forms.HorizontalAlignment.Left);
+ System.Windows.Forms.ListViewGroup listViewGroup3 = new System.Windows.Forms.ListViewGroup("Not usable in this context", System.Windows.Forms.HorizontalAlignment.Left);
+ this.commandsearch = new System.Windows.Forms.TextBox();
+ this.noresults = new System.Windows.Forms.Label();
+ this.commandlist = new CodeImp.DoomBuilder.Controls.OptimizedListView();
+ this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.SuspendLayout();
+ //
+ // commandsearch
+ //
+ this.commandsearch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.commandsearch.Location = new System.Drawing.Point(3, 2);
+ this.commandsearch.Name = "commandsearch";
+ this.commandsearch.Size = new System.Drawing.Size(864, 20);
+ this.commandsearch.TabIndex = 2;
+ this.commandsearch.TextChanged += new System.EventHandler(this.commandsearch_TextChanged);
+ this.commandsearch.KeyDown += new System.Windows.Forms.KeyEventHandler(this.commandsearch_KeyDown);
+ //
+ // noresults
+ //
+ this.noresults.AutoSize = true;
+ this.noresults.Location = new System.Drawing.Point(6, 28);
+ this.noresults.Name = "noresults";
+ this.noresults.Size = new System.Drawing.Size(84, 13);
+ this.noresults.TabIndex = 4;
+ this.noresults.Text = "No results found";
+ this.noresults.Visible = false;
+ //
+ // commandlist
+ //
+ this.commandlist.Activation = System.Windows.Forms.ItemActivation.OneClick;
+ this.commandlist.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.commandlist.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+ this.columnHeader1,
+ this.columnHeader3,
+ this.columnHeader2});
+ this.commandlist.FullRowSelect = true;
+ listViewGroup1.Header = "Recent";
+ listViewGroup1.Name = "recent";
+ listViewGroup2.Header = "Usable actions";
+ listViewGroup2.Name = "usableactions";
+ listViewGroup3.Header = "Not usable in this context";
+ listViewGroup3.Name = "notusableactions";
+ this.commandlist.Groups.AddRange(new System.Windows.Forms.ListViewGroup[] {
+ listViewGroup1,
+ listViewGroup2,
+ listViewGroup3});
+ this.commandlist.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
+ this.commandlist.Location = new System.Drawing.Point(3, 25);
+ this.commandlist.MultiSelect = false;
+ this.commandlist.Name = "commandlist";
+ this.commandlist.Size = new System.Drawing.Size(864, 173);
+ this.commandlist.TabIndex = 3;
+ this.commandlist.TabStop = false;
+ this.commandlist.UseCompatibleStateImageBehavior = false;
+ this.commandlist.View = System.Windows.Forms.View.Details;
+ this.commandlist.ItemActivate += new System.EventHandler(this.commandlist_ItemActivate);
+ //
+ // columnHeader1
+ //
+ this.columnHeader1.Text = "Action";
+ this.columnHeader1.Width = 275;
+ //
+ // columnHeader3
+ //
+ this.columnHeader3.Text = "Section";
+ this.columnHeader3.Width = 196;
+ //
+ // columnHeader2
+ //
+ this.columnHeader2.Text = "Key";
+ this.columnHeader2.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
+ this.columnHeader2.Width = 117;
+ //
+ // CommandPaletteControl
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.noresults);
+ this.Controls.Add(this.commandlist);
+ this.Controls.Add(this.commandsearch);
+ this.DoubleBuffered = true;
+ this.Name = "CommandPaletteControl";
+ this.Size = new System.Drawing.Size(870, 201);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private OptimizedListView commandlist;
+ private System.Windows.Forms.ColumnHeader columnHeader1;
+ private System.Windows.Forms.ColumnHeader columnHeader2;
+ private System.Windows.Forms.TextBox commandsearch;
+ private System.Windows.Forms.ColumnHeader columnHeader3;
+ private System.Windows.Forms.Label noresults;
+ }
+}
diff --git a/Source/Core/Controls/CommandPaletteControl.cs b/Source/Core/Controls/CommandPaletteControl.cs
new file mode 100644
index 00000000..6f680309
--- /dev/null
+++ b/Source/Core/Controls/CommandPaletteControl.cs
@@ -0,0 +1,461 @@
+#region ================== Copyright (c) 2022 Boris Iwanski
+
+/*
+ * This program is free software: you can redistribute it and/or modify
+ *
+ * it under the terms of the GNU General Public License as published by
+ *
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.If not, see.
+ */
+
+#endregion
+
+#region ================== Namespaces
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+using CodeImp.DoomBuilder.Windows;
+
+#endregion
+
+namespace CodeImp.DoomBuilder.Controls
+{
+ public partial class CommandPaletteControl : UserControl
+ {
+ #region ================== Constants
+
+ private const int MAX_ITEMS = 20;
+ private const int MAX_RECENT_ACTIONS = 5;
+ private const int GROUP_RECENT = 0;
+ private const int GROUP_USABLE = 1;
+ private const int GROUP_UNUSABLE = 2;
+
+
+ #endregion
+
+ #region ================== Variables
+
+ private readonly List recentactions;
+
+ #endregion
+
+ #region ================== Constructor
+
+ public CommandPaletteControl()
+ {
+ InitializeComponent();
+
+ recentactions = new List();
+
+ Enabled = false;
+ }
+
+ #endregion
+
+ #region ================== Methods
+
+ ///
+ /// Hides the palette. Disabled it and sends it to the background.
+ ///
+ /// The sender
+ /// The event args
+ private void HidePalette(object sender, EventArgs e)
+ {
+ commandsearch.LostFocus -= HidePalette;
+ Enabled = false;
+
+ if (Parent is MainForm mf)
+ {
+ mf.Resize -= Reposition;
+
+ mf.Controls.SetChildIndex(this, 0xffff);
+ mf.ActiveControl = null;
+ mf.Focus();
+ }
+ }
+
+ ///
+ /// Sets the color of the currently selected item.
+ ///
+ private void HighlightSelectedItem()
+ {
+ if (commandlist.SelectedItems.Count > 0)
+ {
+ commandlist.SelectedItems[0].BackColor = SystemColors.Highlight;
+ commandlist.SelectedItems[0].ForeColor = SystemColors.HighlightText;
+ }
+ }
+
+ ///
+ /// Shows the palette
+ ///
+ public void MakeVisible()
+ {
+ if (Parent is MainForm mf)
+ {
+ // Reset everything to a blank slate
+ commandsearch.Text = string.Empty;
+ // commandsearch_TextChanged(this, EventArgs.Empty);
+ FillCommandList(withrecent: true);
+ HighlightSelectedItem();
+
+ // Set the width of each column to the max width of its fields
+ commandlist.Columns[0].Width = -1;
+ commandlist.Columns[1].Width = -1;
+ commandlist.Columns[2].Width = -1;
+
+ // Compute the new width. It's the width of the columns, the vertical scroll bar and some buffer
+ Width = commandlist.Columns[0].Width + commandlist.Columns[1].Width + commandlist.Columns[2].Width + SystemInformation.VerticalScrollBarWidth + commandlist.Location.X * 4;
+
+ // Center the control at the top middle
+ Location = new Point(mf.Display.Width / 2 - Width / 2, mf.Display.Location.Y + 5);
+
+ Enabled = true;
+
+ commandsearch.Focus();
+
+ // We want to hide the control when the focus is lost
+ commandsearch.LostFocus += HidePalette;
+
+ // Bring it to the foreground
+ mf.Controls.SetChildIndex(this, 0);
+
+ // Always keep the control in the center
+ mf.Resize += Reposition;
+ }
+ }
+
+ ///
+ /// Keeps the control positioned in the top middle of the window when it is rezied.
+ ///
+ /// The sender
+ /// The event args
+ private void Reposition(object sender, EventArgs e)
+ {
+ // Center the control at the top middle
+ if (Parent is MainForm mf)
+ Location = new Point(mf.Display.Width / 2 - Width / 2, mf.Display.Location.Y + 5);
+ }
+
+ ///
+ /// Selects the item before or after the current item in the command list.
+ ///
+ /// By how much the index should be changed. Positive numbers mean that it will scroll up, negative numbers will scroll down.
+ /// If the selection should wrap around to the opposite side if the top or bottom of the list is reached
+ private void SetSelectedItem(int changeindexby, bool wraparound)
+ {
+ if (commandlist.Items.Count > 1)
+ {
+ int newindex = commandlist.SelectedIndices[0] + changeindexby;
+
+ if (newindex >= commandlist.Items.Count)
+ {
+ if (wraparound)
+ newindex = 0;
+ else
+ newindex = commandlist.Items.Count - 1;
+ }
+ else if (newindex < 0)
+ {
+ if (wraparound)
+ newindex = commandlist.Items.Count - 1;
+ else
+ newindex = 0;
+ }
+
+ // Reset the colors of the currently selected item to the defaults
+ commandlist.SelectedItems[0].BackColor = SystemColors.Window;
+ commandlist.SelectedItems[0].ForeColor = SystemColors.WindowText;
+
+ // Set the new item, scroll the list to it, and set the highlight color
+ commandlist.Items[newindex].Selected = true;
+ commandlist.EnsureVisible(newindex);
+ HighlightSelectedItem();
+ }
+ }
+
+ ///
+ /// Checks if a search string matches a text. It replicates the behavior of Visual Stuido Code.
+ /// At first it tries to match the whole search string. If that didn't produce a result it'll try to match as much of the search
+ /// string at the *beginning* of a word in the text. If that worked the matching characters are removed from the search text and
+ /// all words in the text up to (including) the found word are removed. This is repeated until all characters in the search string
+ /// are gone. This means:
+ /// "le cl" matches "Toggle classic rendering"
+ /// ^^^^^
+ /// "tore" matches "Toggle classic rendering"
+ /// ^^ ^^
+ /// "tcl" matches "Toggle classic rendering"
+ /// ^ ^ ^
+ /// "tof" matches "Toggle Full Brightness"
+ /// ^^ ^
+ /// "Align Floor Textures to Front Side"
+ /// ^^ ^
+ /// "Reset Texture Offsets"
+ /// ^ ^^
+ /// (and a couple other)
+ ///
+ /// The string to search in
+ /// The string to search for
+ ///
+ private bool MatchText(string text, string search)
+ {
+ text = text.ToLowerInvariant().Trim();
+ text = Regex.Replace(text, @"\s+", " ");
+
+ search = search.ToLowerInvariant().Trim();
+ search = Regex.Replace(search, @"\s+", " ");
+
+ // Check if the search string is empty or the whole search string is in the text to search
+ if (string.IsNullOrWhiteSpace(search) || text.Contains(search))
+ return true;
+
+ // No match yet, so let's check if all search tokens are at the beginning of a text token. This is the same(ish?) behavior as Visual Studio Code.
+ // This means that searching for "op ma" will match "Open Map", but not "Open Command Palette", because the "ma" in "Command" is not in the beginning.
+ List textitems = text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList();
+ string[] searchitems = search.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+
+ for(int i=0; i < searchitems.Length; i++)
+ {
+ string si = searchitems[i];
+
+ // If the search item is empty it means we processed all its characters, so go to the next search item
+ if (string.IsNullOrEmpty(si))
+ continue;
+
+ string result = null;
+
+ // Search token not found, so try to match parts of the search token
+ while (si.Length > 0)
+ {
+ // Try to find the first text token that starts with the search token
+ result = textitems.FirstOrDefault(ti => ti.StartsWith(si));
+
+ // We found something, so remove the matching part of the search token and prepare processing this search token again
+ if (result != null)
+ {
+ searchitems[i] = searchitems[i].Remove(0, si.Length);
+ i--;
+ break;
+ }
+
+ // Nothing found, so remove the last character and keep going
+ si = si.Remove(si.Length - 1);
+ }
+
+ // Nothing found, so abort
+ if (result == null)
+ return false;
+
+ // We found a search token (or part of it), so remove all text tokens up to including the found text token
+ int index = textitems.IndexOf(result);
+ textitems.RemoveRange(0, index + 1);
+ }
+
+ // We didn't return yet, so we must have found everything
+ return true;
+ }
+
+ ///
+ /// Adds an action to the command list, either in the "usable" or "unsuable" group.
+ ///
+ /// The action to add
+ private void AddActionToList(Actions.Action action, bool isrecent = false)
+ {
+ string actiontitle = action.Title;
+ string catname = string.Empty;
+ bool isbound = action.BeginBound || action.EndBound;
+
+ if (General.Actions.Categories.ContainsKey(action.Category))
+ catname = General.Actions.Categories[action.Category];
+
+ ListViewItem item = commandlist.Items.Add(action.Name, actiontitle, 0);
+
+ // Store the action in the tag, so we can invoke the action later
+ item.Tag = action;
+
+ // Add the item to the appropriate group, either the "usable" (0) or "unusable" (1) one
+ if (isrecent)
+ item.Group = commandlist.Groups[GROUP_RECENT];
+ else
+ item.Group = commandlist.Groups[isbound ? GROUP_USABLE : GROUP_UNUSABLE];
+
+ item.SubItems.Add(catname);
+ item.SubItems.Add(Actions.Action.GetShortcutKeyDesc(action.ShortcutKey));
+ }
+
+ ///
+ /// Runs an action and adds it to the list of recent actions
+ ///
+ ///
+ private void RunAction(Actions.Action action)
+ {
+ // Remove the action (if it's in the list) and then insert it at the beginning
+ recentactions.Remove(action);
+ recentactions.Insert(0, action);
+
+ // Remove all actions that exceed the limit of the max number of recent actions
+ if (recentactions.Count > MAX_RECENT_ACTIONS)
+ recentactions.RemoveRange(4, recentactions.Count - MAX_RECENT_ACTIONS);
+
+ General.Actions.InvokeAction(action.Name);
+ }
+
+ ///
+ /// Fills the control, filtering it so that only the actions that match the search string are shown.
+ ///
+ /// Text to search for in the action name
+ /// If recently shown actions should be shown or not
+ private void FillCommandList(string searchtext = "", bool withrecent = false)
+ {
+ List usableactions = new List();
+ List unusableactions = new List();
+
+ commandlist.BeginUpdate();
+ commandlist.Items.Clear();
+
+ Actions.Action[] actions = General.Actions.GetAllActions();
+
+ // Crawl through all actions and check if they are usable or not in the current context
+ foreach (Actions.Action a in actions)
+ {
+ if (MatchText(a.Title, searchtext))
+ {
+ if (a.BeginBound || a.EndBound)
+ usableactions.Add(a);
+ else
+ unusableactions.Add(a);
+ }
+ }
+
+ // If there are matching actions we have to change the control's height and set the default selection
+ if (usableactions.Count + unusableactions.Count > 0)
+ {
+ noresults.Visible = false;
+ commandlist.Visible = true;
+
+ if (withrecent)
+ foreach (Actions.Action a in recentactions) if (a != null) AddActionToList(a, true);
+
+ // We have to do the sorting on our own, because otherwise the groups will screw with the selection logic when pressing the up/down keys
+ foreach (Actions.Action a in usableactions.OrderBy(o => o.Title)) AddActionToList(a);
+ foreach (Actions.Action a in unusableactions.OrderBy(o => o.Title)) AddActionToList(a);
+
+ // We want to show at most MAX_ITEMS items before having a scroll bar
+ int numitems = commandlist.Items.Count > MAX_ITEMS ? MAX_ITEMS : commandlist.Items.Count;
+
+ // Get the height of a row
+ int itemheight = commandlist.Items[0].GetBounds(ItemBoundsPortion.Entire).Height;
+
+ // Get the number of shown groups
+ int numgroups = (usableactions.Count == 0 ? 0 : 1) + (unusableactions.Count == 0 ? 0 : 1);
+
+ // Set the new height, which is the number of items times the row height, the groups, the search textbox and some buffer
+ Height = itemheight * numitems + commandsearch.Height + numgroups * (int)(itemheight * 1.4) + commandlist.Location.X * 5;
+
+ // Select the topmost item and highlight it
+ commandlist.Items[0].Selected = true;
+ HighlightSelectedItem();
+
+ noresults.Visible = false;
+ }
+ else // No matching actions, hide line command list and tell the user that there are no matches
+ {
+ commandlist.Visible = false;
+ noresults.Visible = true;
+
+ Height = noresults.Location.Y + noresults.Height + noresults.Margin.Left * 2;
+ }
+
+ commandlist.EndUpdate();
+ }
+
+ #endregion
+
+ #region ================== Events
+
+ private void commandsearch_TextChanged(object sender, EventArgs e)
+ {
+ string searchtext = commandsearch.Text.Trim();
+
+ if (string.IsNullOrWhiteSpace(searchtext))
+ FillCommandList(withrecent: true);
+ else
+ FillCommandList(searchtext);
+ }
+
+ ///
+ /// Handles certain special keys. Esc will close the palette, the Up and Down keys will change the selection, and Enter will start the command.
+ ///
+ /// The sender
+ /// The event args
+ private void commandsearch_KeyDown(object sender, KeyEventArgs e)
+ {
+ switch(e.KeyCode)
+ {
+ case Keys.Escape:
+ case Keys.Down:
+ case Keys.Up:
+ case Keys.PageDown:
+ case Keys.PageUp:
+ //case Keys.End:
+ //case Keys.Home:
+ case Keys.Enter:
+ e.Handled = true;
+ e.SuppressKeyPress = true;
+ break;
+ }
+
+ if (e.KeyCode == Keys.Escape)
+ HidePalette(this, EventArgs.Empty);
+ else if (e.KeyCode == Keys.Down)
+ SetSelectedItem(1, true);
+ else if (e.KeyCode == Keys.Up)
+ SetSelectedItem(-1, true);
+ else if (e.KeyCode == Keys.PageDown)
+ SetSelectedItem(MAX_ITEMS - 1, false);
+ else if (e.KeyCode == Keys.PageUp)
+ SetSelectedItem(-MAX_ITEMS + 1, false);
+ //else if (e.KeyCode == Keys.End)
+ // SetSelectedItem(commandlist.Items.Count, false);
+ //else if (e.KeyCode == Keys.Home)
+ // SetSelectedItem(0, false);
+ else if (e.KeyCode == Keys.Enter)
+ {
+ if (commandlist.Items.Count > 0)
+ {
+ HidePalette(this, EventArgs.Empty);
+ RunAction((Actions.Action)commandlist.SelectedItems[0].Tag);
+ }
+ }
+ }
+
+ ///
+ /// Run the command that was clicked on
+ ///
+ /// The sender
+ /// The event args
+ private void commandlist_ItemActivate(object sender, EventArgs e)
+ {
+ HidePalette(this, EventArgs.Empty);
+
+ RunAction((Actions.Action)commandlist.SelectedItems[0].Tag);
+ }
+
+ #endregion
+ }
+}
diff --git a/Source/Core/Controls/CommandPaletteControl.resx b/Source/Core/Controls/CommandPaletteControl.resx
new file mode 100644
index 00000000..1af7de15
--- /dev/null
+++ b/Source/Core/Controls/CommandPaletteControl.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Source/Core/Resources/Actions.cfg b/Source/Core/Resources/Actions.cfg
index 71d7b657..42816c23 100755
--- a/Source/Core/Resources/Actions.cfg
+++ b/Source/Core/Resources/Actions.cfg
@@ -1443,3 +1443,13 @@ placethingatcursor
allowscroll = false;
default = 131076;
}
+
+opencommandpalette
+{
+ title = "Open Command Palette";
+ category = "tools";
+ description = "Opens the command palette.";
+ allowkeys = true;
+ allowmouse = true;
+ allowscroll = true;
+}
\ No newline at end of file
diff --git a/Source/Core/Windows/MainForm.cs b/Source/Core/Windows/MainForm.cs
index 65fd9762..cc29f7eb 100755
--- a/Source/Core/Windows/MainForm.cs
+++ b/Source/Core/Windows/MainForm.cs
@@ -159,6 +159,8 @@ namespace CodeImp.DoomBuilder.Windows
//mxd. Misc drawing
private Graphics graphics;
+
+ private CommandPaletteControl commandpalette;
#endregion
@@ -465,11 +467,28 @@ namespace CodeImp.DoomBuilder.Windows
this.Update();
}
-
+
+ // We're doing it in EndAction because it'll otherwise screw with the stored keys
+ [EndAction("opencommandpalette")]
+ public void OpenCommandPalette()
+ {
+ if (commandpalette == null)
+ {
+ // We have to add the command palette control manually because trying to use the designer will make the form explode
+ commandpalette = new CommandPaletteControl();
+ Controls.Add(commandpalette);
+
+ // Send it somewhere to the background
+ Controls.SetChildIndex(commandpalette, 0xffff);
+ }
+
+ commandpalette.MakeVisible();
+ }
+
#endregion
-
+
#region ================== Window
-
+
// This locks the window for updating
internal void LockUpdate()
{
@@ -1162,7 +1181,7 @@ namespace CodeImp.DoomBuilder.Windows
{
General.Plugins.OnEditMouseEnter(e);
General.Editing.Mode.OnMouseEnter(e);
- if(Application.OpenForms.Count == 1 || editformopen) display.Focus(); //mxd
+ if((Application.OpenForms.Count == 1 || editformopen) && (commandpalette == null ? true : !commandpalette.Visible)) display.Focus(); //mxd
}
}
@@ -1424,7 +1443,7 @@ namespace CodeImp.DoomBuilder.Windows
if(alt) mod |= (int)Keys.Alt;
if(shift) mod |= (int)Keys.Shift;
if(ctrl) mod |= (int)Keys.Control;
-
+
// Don't process any keys when they are meant for other input controls
if((e.KeyData != Keys.None) && ((ActiveControl == null) || (ActiveControl == display)))
{
@@ -1480,7 +1499,7 @@ namespace CodeImp.DoomBuilder.Windows
private void MainForm_KeyUp(object sender, KeyEventArgs e)
{
int mod = 0;
-
+
// Keep key modifiers
alt = e.Alt;
shift = e.Shift;