From 03c38c5e17819661acc067dcd02ae33fe0a4101d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 7 Jan 2022 14:41:42 +0100 Subject: [PATCH] Improved Window menu. --- ILSpy/Commands/ShowPane.cs | 29 ++-- ILSpy/MainWindow.xaml.cs | 192 +++++++++++++++++++++- ILSpy/Search/SearchPane.cs | 1 - ILSpy/ViewModels/AnalyzerPaneModel.cs | 2 + ILSpy/ViewModels/AssemblyListPaneModel.cs | 2 + ILSpy/ViewModels/SearchPaneModel.cs | 3 + ILSpy/ViewModels/ToolPaneModel.cs | 5 + 7 files changed, 217 insertions(+), 17 deletions(-) diff --git a/ILSpy/Commands/ShowPane.cs b/ILSpy/Commands/ShowPane.cs index 2a5b14030..372081e0b 100644 --- a/ILSpy/Commands/ShowPane.cs +++ b/ILSpy/Commands/ShowPane.cs @@ -4,32 +4,33 @@ using ICSharpCode.ILSpy.ViewModels; namespace ICSharpCode.ILSpy.Commands { - [ExportMainMenuCommand(Menu = nameof(Resources._Window), Header = nameof(Resources._Assemblies), MenuCategory = "pane", MenuOrder = 5000)] - class ShowAssemblies : SimpleCommand + class ToolPaneCommand : SimpleCommand { - public override void Execute(object parameter) + readonly string contentId; + + public ToolPaneCommand(string contentId) { - DockWorkspace.Instance.ShowToolPane(AssemblyListPaneModel.PaneContentId); + this.contentId = contentId; } - } - [ExportMainMenuCommand(Menu = nameof(Resources._Window), Header = nameof(Resources._Analyzer), MenuCategory = "pane", MenuOrder = 5000)] - class ShowAnalyzer : SimpleCommand - { public override void Execute(object parameter) { - DockWorkspace.Instance.ShowToolPane(AnalyzerPaneModel.PaneContentId); + DockWorkspace.Instance.ShowToolPane(contentId); } } -#if DEBUG - [ExportMainMenuCommand(Menu = nameof(Resources._Window), Header = nameof(Resources._ShowDebugSteps), MenuCategory = "pane", MenuOrder = 5000)] - class ShowDebugSteps : SimpleCommand + class TabPageCommand : SimpleCommand { + readonly TabPageModel model; + + public TabPageCommand(TabPageModel model) + { + this.model = model; + } + public override void Execute(object parameter) { - DockWorkspace.Instance.ShowToolPane(DebugStepsPaneModel.PaneContentId); + DockWorkspace.Instance.ActiveTabPage = model; } } -#endif } \ No newline at end of file diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index c2e11b004..e3a616cf2 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -43,6 +43,7 @@ using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.ILSpy.Analyzers; +using ICSharpCode.ILSpy.Commands; using ICSharpCode.ILSpy.Docking; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.Themes; @@ -128,6 +129,7 @@ namespace ICSharpCode.ILSpy filterSettings.PropertyChanged += filterSettings_PropertyChanged; DockWorkspace.Instance.PropertyChanged += DockWorkspace_PropertyChanged; InitMainMenu(); + InitWindowMenu(); InitToolbar(); ContextMenuProvider.Add(AssemblyTreeView); @@ -139,9 +141,19 @@ namespace ICSharpCode.ILSpy switch (e.PropertyName) { case nameof(DockWorkspace.Instance.ActiveTabPage): + DockWorkspace dock = DockWorkspace.Instance; filterSettings.PropertyChanged -= filterSettings_PropertyChanged; - filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings; + filterSettings = dock.ActiveTabPage.FilterSettings; filterSettings.PropertyChanged += filterSettings_PropertyChanged; + + var windowMenuItem = mainMenu.Items.OfType().First(m => GetResourceString(m.Header as string) == Properties.Resources._Window); + foreach (MenuItem menuItem in windowMenuItem.Items.OfType()) + { + if (menuItem.IsCheckable && menuItem.Tag is TabPageModel) + { + menuItem.IsChecked = menuItem.Tag == dock.ActiveTabPage; + } + } break; } } @@ -231,7 +243,7 @@ namespace ICSharpCode.ILSpy var mainMenuCommands = App.ExportProvider.GetExports("MainMenuCommand"); foreach (var topLevelMenu in mainMenuCommands.OrderBy(c => c.Metadata.MenuOrder).GroupBy(c => GetResourceString(c.Metadata.Menu))) { - var topLevelMenuItem = mainMenu.Items.OfType().FirstOrDefault(m => (GetResourceString(m.Header as string)) == topLevelMenu.Key); + var topLevelMenuItem = mainMenu.Items.OfType().FirstOrDefault(m => GetResourceString(m.Header as string) == topLevelMenu.Key); foreach (var category in topLevelMenu.GroupBy(c => GetResourceString(c.Metadata.MenuCategory))) { if (topLevelMenuItem == null) @@ -296,6 +308,182 @@ namespace ICSharpCode.ILSpy DockManager.LayoutItemTemplateSelector = templateSelector; } + private void InitWindowMenu() + { + var windowMenuItem = mainMenu.Items.OfType().First(m => GetResourceString(m.Header as string) == Properties.Resources._Window); + Separator separatorBeforeTools, separatorBeforeDocuments; + windowMenuItem.Items.Add(separatorBeforeTools = new Separator()); + windowMenuItem.Items.Add(separatorBeforeDocuments = new Separator()); + + var dock = DockWorkspace.Instance; + dock.ToolPanes.CollectionChanged += ToolsChanged; + dock.TabPages.CollectionChanged += TabsChanged; + + ToolsChanged(dock.ToolPanes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + TabsChanged(dock.TabPages, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + + void ToolsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + int endIndex = windowMenuItem.Items.IndexOf(separatorBeforeDocuments); + int startIndex = windowMenuItem.Items.IndexOf(separatorBeforeTools) + 1; + int insertionIndex; + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + insertionIndex = Math.Min(endIndex, startIndex + e.NewStartingIndex); + foreach (ToolPaneModel pane in e.NewItems) + { + MenuItem menuItem = CreateMenuItem(pane); + windowMenuItem.Items.Insert(insertionIndex, menuItem); + insertionIndex++; + } + break; + case NotifyCollectionChangedAction.Remove: + foreach (ToolPaneModel pane in e.OldItems) + { + for (int i = endIndex - 1; i >= startIndex; i--) + { + MenuItem item = (MenuItem)windowMenuItem.Items[i]; + if (pane == item.Tag) + { + windowMenuItem.Items.RemoveAt(i); + item.Tag = null; + endIndex--; + break; + } + } + } + break; + case NotifyCollectionChangedAction.Replace: + break; + case NotifyCollectionChangedAction.Move: + break; + case NotifyCollectionChangedAction.Reset: + for (int i = endIndex - 1; i >= startIndex; i--) + { + MenuItem item = (MenuItem)windowMenuItem.Items[0]; + item.Tag = null; + windowMenuItem.Items.RemoveAt(i); + endIndex--; + } + insertionIndex = endIndex; + foreach (ToolPaneModel pane in dock.ToolPanes) + { + MenuItem menuItem = CreateMenuItem(pane); + windowMenuItem.Items.Insert(insertionIndex, menuItem); + insertionIndex++; + } + break; + } + + MenuItem CreateMenuItem(ToolPaneModel pane) + { + MenuItem menuItem = new MenuItem(); + menuItem.Command = new ToolPaneCommand(pane.ContentId); + menuItem.Header = pane.Title; + menuItem.Tag = pane; + var shortcutKey = pane.ShortcutKey; + if (shortcutKey != null) + { + InputBindings.Add(new InputBinding(menuItem.Command, shortcutKey)); + menuItem.InputGestureText = shortcutKey.GetDisplayStringForCulture(System.Globalization.CultureInfo.CurrentUICulture); + } + if (!string.IsNullOrEmpty(pane.Icon)) + { + menuItem.Icon = new Image { + Width = 16, + Height = 16, + Source = Images.Load(pane, pane.Icon) + }; + } + + return menuItem; + } + } + + void TabsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + int endIndex = windowMenuItem.Items.Count; + int startIndex = windowMenuItem.Items.IndexOf(separatorBeforeDocuments) + 1; + int insertionIndex; + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + insertionIndex = Math.Min(endIndex, startIndex + e.NewStartingIndex); + foreach (TabPageModel pane in e.NewItems) + { + MenuItem menuItem = CreateMenuItem(pane); + pane.PropertyChanged += TabPageChanged; + windowMenuItem.Items.Insert(insertionIndex, menuItem); + insertionIndex++; + } + break; + case NotifyCollectionChangedAction.Remove: + foreach (TabPageModel pane in e.OldItems) + { + for (int i = endIndex - 1; i >= startIndex; i--) + { + MenuItem item = (MenuItem)windowMenuItem.Items[i]; + if (pane == item.Tag) + { + windowMenuItem.Items.RemoveAt(i); + pane.PropertyChanged -= TabPageChanged; + item.Tag = null; + endIndex--; + break; + } + } + } + break; + case NotifyCollectionChangedAction.Replace: + break; + case NotifyCollectionChangedAction.Move: + break; + case NotifyCollectionChangedAction.Reset: + for (int i = endIndex - 1; i >= startIndex; i--) + { + MenuItem item = (MenuItem)windowMenuItem.Items[i]; + windowMenuItem.Items.RemoveAt(i); + ((TabPageModel)item.Tag).PropertyChanged -= TabPageChanged; + endIndex--; + } + insertionIndex = endIndex; + foreach (TabPageModel pane in dock.TabPages) + { + MenuItem menuItem = CreateMenuItem(pane); + pane.PropertyChanged += TabPageChanged; + windowMenuItem.Items.Insert(insertionIndex, menuItem); + insertionIndex++; + } + break; + } + + MenuItem CreateMenuItem(TabPageModel pane) + { + MenuItem menuItem = new MenuItem(); + menuItem.Command = new TabPageCommand(pane); + menuItem.Header = pane.Title.Length > 20 ? pane.Title.Substring(20) + "..." : pane.Title; + menuItem.Tag = pane; + menuItem.IsCheckable = true; + + return menuItem; + } + } + + static void TabPageChanged(object sender, PropertyChangedEventArgs e) + { + var windowMenuItem = Instance.mainMenu.Items.OfType().First(m => GetResourceString(m.Header as string) == Properties.Resources._Window); + foreach (MenuItem menuItem in windowMenuItem.Items.OfType()) + { + if (menuItem.IsCheckable && menuItem.Tag == sender) + { + string title = ((TabPageModel)sender).Title; + menuItem.Header = title.Length > 20 ? title.Substring(0, 20) + "..." : title; + } + } + } + } + public void ShowInTopPane(string title, object content) { var model = DockWorkspace.Instance.ToolPanes.OfType().FirstOrDefault(p => p.Content == content); diff --git a/ILSpy/Search/SearchPane.cs b/ILSpy/Search/SearchPane.cs index 20e5f1a9d..3fb677d35 100644 --- a/ILSpy/Search/SearchPane.cs +++ b/ILSpy/Search/SearchPane.cs @@ -523,7 +523,6 @@ namespace ICSharpCode.ILSpy } } - [ExportMainMenuCommand(Menu = nameof(Properties.Resources._Window), Header = nameof(Properties.Resources.Search), MenuIcon = "Images/Search", MenuCategory = "pane", MenuOrder = 100)] [ExportToolbarCommand(ToolTip = nameof(Properties.Resources.SearchCtrlShiftFOrCtrlE), ToolbarIcon = "Images/Search", ToolbarCategory = nameof(Properties.Resources.View), ToolbarOrder = 100)] sealed class ShowSearchCommand : CommandWrapper { diff --git a/ILSpy/ViewModels/AnalyzerPaneModel.cs b/ILSpy/ViewModels/AnalyzerPaneModel.cs index 28b07734c..d86558ee6 100644 --- a/ILSpy/ViewModels/AnalyzerPaneModel.cs +++ b/ILSpy/ViewModels/AnalyzerPaneModel.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Windows; +using System.Windows.Input; namespace ICSharpCode.ILSpy.ViewModels { @@ -29,6 +30,7 @@ namespace ICSharpCode.ILSpy.ViewModels { ContentId = PaneContentId; Title = Properties.Resources.Analyze; + ShortcutKey = new KeyGesture(Key.R, ModifierKeys.Control); } public override DataTemplate Template => (DataTemplate)MainWindow.Instance.FindResource("AnalyzerPaneTemplate"); diff --git a/ILSpy/ViewModels/AssemblyListPaneModel.cs b/ILSpy/ViewModels/AssemblyListPaneModel.cs index 9d5a39295..38439467a 100644 --- a/ILSpy/ViewModels/AssemblyListPaneModel.cs +++ b/ILSpy/ViewModels/AssemblyListPaneModel.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Windows; +using System.Windows.Input; using ICSharpCode.ILSpy.Properties; @@ -32,6 +33,7 @@ namespace ICSharpCode.ILSpy.ViewModels Title = Resources.Assemblies; ContentId = PaneContentId; IsCloseable = false; + ShortcutKey = new KeyGesture(Key.F6); } public override DataTemplate Template => (DataTemplate)MainWindow.Instance.FindResource("AssemblyListPaneTemplate"); diff --git a/ILSpy/ViewModels/SearchPaneModel.cs b/ILSpy/ViewModels/SearchPaneModel.cs index 66a156825..04cacb2c1 100644 --- a/ILSpy/ViewModels/SearchPaneModel.cs +++ b/ILSpy/ViewModels/SearchPaneModel.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Windows; +using System.Windows.Input; namespace ICSharpCode.ILSpy.ViewModels { @@ -29,6 +30,8 @@ namespace ICSharpCode.ILSpy.ViewModels { ContentId = PaneContentId; Title = Properties.Resources.SearchPane_Search; + Icon = "Images/Search"; + ShortcutKey = new KeyGesture(Key.F, ModifierKeys.Control | ModifierKeys.Shift); IsCloseable = true; } diff --git a/ILSpy/ViewModels/ToolPaneModel.cs b/ILSpy/ViewModels/ToolPaneModel.cs index 672273674..547385b1b 100644 --- a/ILSpy/ViewModels/ToolPaneModel.cs +++ b/ILSpy/ViewModels/ToolPaneModel.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Windows; +using System.Windows.Input; namespace ICSharpCode.ILSpy.ViewModels { @@ -29,5 +30,9 @@ namespace ICSharpCode.ILSpy.ViewModels } public abstract DataTemplate Template { get; } + + public KeyGesture ShortcutKey { get; protected set; } + + public string Icon { get; protected set; } } }