diff --git a/.editorconfig b/.editorconfig index 1ea262ade4..a4b3fdb1f8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,6 +5,8 @@ root = true indent_style = tab indent_size = 4 guidelines = 110 +tab_width = 4 +end_of_line = crlf [*.il] indent_style = space @@ -44,7 +46,7 @@ csharp_indent_braces = false csharp_indent_case_contents = true csharp_indent_case_contents_when_block = false csharp_indent_switch_labels = true -csharp_indent_labels = one_less +csharp_indent_labels = no_change # Avoid 'this.' in generated code unless absolutely necessary, but allow developers to use it dotnet_style_qualification_for_field = false:silent @@ -54,7 +56,7 @@ dotnet_style_qualification_for_event = false:silent # Do not use 'var' when generating code, but allow developers to use it csharp_style_var_for_built_in_types = false:silent -csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_when_type_is_apparent = true:suggestion csharp_style_var_elsewhere = false:silent # Use language keywords instead of BCL types when generating code, but allow developers to use either @@ -143,6 +145,72 @@ dotnet_naming_rule.private_fields_rule.severity = warning dotnet_naming_rule.private_fields_rule.style = lower_camel_case_style dotnet_naming_rule.private_fields_rule.symbols = private_fields_symbols +# General settings: +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_static_local_function = true:suggestion +csharp_prefer_static_anonymous_function = true:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion + +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_require_accessibility_modifiers = omit_if_default:suggestion +dotnet_style_allow_multiple_blank_lines_experimental = false:warning +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_code_quality_unused_parameters = all:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent + # Errors and warnings # MEF006: No importing constructor diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000000..8281d7abdb --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + IDE2000 + + \ No newline at end of file diff --git a/ILSpy.sln b/ILSpy.sln index ce506f4d2c..099c49504c 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -39,6 +39,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D0858E90-DCD5-4FD9-AA53-7262FAB8BEDB}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + Directory.Build.props = Directory.Build.props Directory.Packages.props = Directory.Packages.props EndProjectSection EndProject diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs index 4d7b666397..298996b5b9 100644 --- a/ILSpy/AboutPage.cs +++ b/ILSpy/AboutPage.cs @@ -26,6 +26,7 @@ using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; +using System.Windows.Navigation; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.Decompiler; @@ -53,7 +54,7 @@ public AboutPage(SettingsService settingsService, IEnumerable + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ILSpy/Util/MenuService.cs b/ILSpy/Controls/MainMenu.xaml.cs similarity index 67% rename from ILSpy/Util/MenuService.cs rename to ILSpy/Controls/MainMenu.xaml.cs index e5ae79f8b7..a5bae1dd9a 100644 --- a/ILSpy/Util/MenuService.cs +++ b/ILSpy/Controls/MainMenu.xaml.cs @@ -22,33 +22,43 @@ using System.Linq; using System.Windows; using System.Windows.Controls; -using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using ICSharpCode.ILSpy.Commands; + using ICSharpCode.ILSpy.Docking; -using ICSharpCode.ILSpy.Themes; using ICSharpCode.ILSpy.ViewModels; using TomsToolbox.Composition; using TomsToolbox.ObservableCollections; +using TomsToolbox.Wpf; using TomsToolbox.Wpf.Converters; -namespace ICSharpCode.ILSpy.Util +namespace ICSharpCode.ILSpy.Controls { + /// + /// Interaction logic for MainMenu.xaml + /// [Export] - [Shared] - public class MenuService(IExportProvider exportProvider, DockWorkspace dockWorkspace) + [NonShared] + public partial class MainMenu { - public void Init(Menu mainMenu, ToolBar toolBar, InputBindingCollection inputBindings) + public MainMenu(SettingsService settingsService, IExportProvider exportProvider, DockWorkspace dockWorkspace) { - InitMainMenu(mainMenu); - InitWindowMenu(mainMenu, inputBindings); - InitToolbar(toolBar); + SessionSettings = settingsService.SessionSettings; + + InitializeComponent(); + + this.BeginInvoke(() => { + InitMainMenu(Menu, exportProvider); + InitWindowMenu(WindowMenuItem, Window.GetWindow(this)!.InputBindings, dockWorkspace); + }); } - void InitMainMenu(Menu mainMenu) + public SessionSettings SessionSettings { get; } + + static void InitMainMenu(Menu mainMenu, IExportProvider exportProvider) { var mainMenuCommands = exportProvider.GetExports("MainMenuCommand"); // Start by constructing the individual flat menus @@ -106,12 +116,9 @@ void InitMainMenu(Menu mainMenu) } } - foreach (var item in parentMenuItems.Values) + foreach (var item in parentMenuItems.Values.Where(item => item.Parent == null)) { - if (item.Parent == null) - { - mainMenu.Items.Add(item); - } + mainMenu.Items.Add(item); } MenuItem GetOrAddParentMenuItem(string menuId, string resourceKey) @@ -137,59 +144,21 @@ MenuItem GetOrAddParentMenuItem(string menuId, string resourceKey) } } - void InitWindowMenu(Menu mainMenu, InputBindingCollection inputBindings) + static void InitWindowMenu(MenuItem windowMenuItem, InputBindingCollection inputBindings, DockWorkspace dockWorkspace) { - var windowMenuItem = mainMenu.Items.OfType().First(m => (string)m.Tag == nameof(Properties.Resources._Window)); - var defaultItems = windowMenuItem.Items.Cast().ToArray(); windowMenuItem.Items.Clear(); - var toolItems = dockWorkspace.ToolPanes.Select(toolPane => CreateMenuItem(toolPane, inputBindings)).ToArray(); - var tabItems = dockWorkspace.TabPages.ObservableSelect(tabPage => CreateMenuItem(tabPage)); + var toolItems = dockWorkspace.ToolPanes.Select(toolPane => CreateMenuItem(toolPane, inputBindings, dockWorkspace)).ToArray(); + var tabItems = dockWorkspace.TabPages.ObservableSelect(tabPage => CreateMenuItem(tabPage, dockWorkspace)); var allItems = new ObservableCompositeCollection(defaultItems, [new Separator()], toolItems, [new Separator()], tabItems); windowMenuItem.ItemsSource = allItems; } - void InitToolbar(ToolBar toolBar) - { - int navigationPos = 0; - int openPos = 1; - var toolbarCommandsByCategory = exportProvider.GetExports("ToolbarCommand") - .OrderBy(c => c.Metadata?.ToolbarOrder) - .GroupBy(c => c.Metadata?.ToolbarCategory); - - foreach (var commandCategory in toolbarCommandsByCategory) - { - if (commandCategory.Key == nameof(Properties.Resources.Navigation)) - { - foreach (var command in commandCategory) - { - toolBar.Items.Insert(navigationPos++, CreateToolbarItem(command)); - openPos++; - } - } - else if (commandCategory.Key == nameof(Properties.Resources.Open)) - { - foreach (var command in commandCategory) - { - toolBar.Items.Insert(openPos++, CreateToolbarItem(command)); - } - } - else - { - toolBar.Items.Add(new Separator()); - foreach (var command in commandCategory) - { - toolBar.Items.Add(CreateToolbarItem(command)); - } - } - } - } - - Control CreateMenuItem(TabPageModel pane) + static Control CreateMenuItem(TabPageModel pane, DockWorkspace dockWorkspace) { var header = new TextBlock { MaxWidth = 200, @@ -200,7 +169,7 @@ Control CreateMenuItem(TabPageModel pane) Source = pane }); - MenuItem menuItem = new() { + var menuItem = new MenuItem { Command = new TabPageCommand(pane, dockWorkspace), Header = header, IsCheckable = true @@ -216,16 +185,16 @@ Control CreateMenuItem(TabPageModel pane) return menuItem; } - Control CreateMenuItem(ToolPaneModel pane, InputBindingCollection inputBindings) + static Control CreateMenuItem(ToolPaneModel pane, InputBindingCollection inputBindings, DockWorkspace dockWorkspace) { - MenuItem menuItem = new() { + var menuItem = new MenuItem { Command = pane.AssociatedCommand ?? new ToolPaneCommand(pane.ContentId, dockWorkspace), Header = pane.Title }; var shortcutKey = pane.ShortcutKey; if (shortcutKey != null) { - inputBindings.Add(new(menuItem.Command, shortcutKey)); + inputBindings.Add(new InputBinding(menuItem.Command, shortcutKey)); menuItem.InputGestureText = shortcutKey.GetDisplayStringForCulture(CultureInfo.CurrentUICulture); } if (!string.IsNullOrEmpty(pane.Icon)) @@ -239,29 +208,5 @@ Control CreateMenuItem(ToolPaneModel pane, InputBindingCollection inputBindings) return menuItem; } - - static Button CreateToolbarItem(IExport commandExport) - { - var command = commandExport.Value; - - Button toolbarItem = new() { - Style = ThemeManager.Current.CreateToolBarButtonStyle(), - Command = CommandWrapper.Unwrap(command), - ToolTip = Properties.Resources.ResourceManager.GetString(commandExport.Metadata?.ToolTip ?? string.Empty), - Tag = commandExport.Metadata?.Tag, - Content = new Image { - Width = 16, - Height = 16, - Source = Images.Load(command, commandExport.Metadata?.ToolbarIcon) - } - }; - - if (command is IProvideParameterBinding parameterBinding) - { - BindingOperations.SetBinding(toolbarItem, ButtonBase.CommandParameterProperty, parameterBinding.ParameterBinding); - } - - return toolbarItem; - } } } diff --git a/ILSpy/Controls/MainToolBar.xaml b/ILSpy/Controls/MainToolBar.xaml new file mode 100644 index 0000000000..867c8119d8 --- /dev/null +++ b/ILSpy/Controls/MainToolBar.xaml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ILSpy/Controls/MainToolBar.xaml.cs b/ILSpy/Controls/MainToolBar.xaml.cs new file mode 100644 index 0000000000..24f6e7a158 --- /dev/null +++ b/ILSpy/Controls/MainToolBar.xaml.cs @@ -0,0 +1,136 @@ +// Copyright (c) 2024 Tom Englert for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Composition; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Threading; + +using ICSharpCode.ILSpy.Themes; + +using TomsToolbox.Composition; + +namespace ICSharpCode.ILSpy.Controls +{ + /// + /// Interaction logic for MainToolBar.xaml + /// + [Export] + [NonShared] + public partial class MainToolBar + { + public MainToolBar(IExportProvider exportProvider) + { + InitializeComponent(); + + this.Dispatcher.BeginInvoke(DispatcherPriority.Background, () => { + InitToolbar(ToolBar, exportProvider); + Window.GetWindow(this)!.KeyDown += MainWindow_KeyDown; + }); + } + + static void InitToolbar(ToolBar toolBar, IExportProvider exportProvider) + { + int navigationPos = 0; + int openPos = 1; + var toolbarCommandsByCategory = exportProvider + .GetExports("ToolbarCommand") + .OrderBy(c => c.Metadata?.ToolbarOrder) + .GroupBy(c => c.Metadata?.ToolbarCategory); + + foreach (var commandCategory in toolbarCommandsByCategory) + { + if (commandCategory.Key == nameof(Properties.Resources.Navigation)) + { + foreach (var command in commandCategory) + { + toolBar.Items.Insert(navigationPos++, CreateToolbarItem(command)); + openPos++; + } + } + else if (commandCategory.Key == nameof(Properties.Resources.Open)) + { + foreach (var command in commandCategory) + { + toolBar.Items.Insert(openPos++, CreateToolbarItem(command)); + } + } + else + { + toolBar.Items.Add(new Separator()); + foreach (var command in commandCategory) + { + toolBar.Items.Add(CreateToolbarItem(command)); + } + } + } + } + + static Button CreateToolbarItem(IExport commandExport) + { + var command = commandExport.Value; + + Button toolbarItem = new() { + Style = ThemeManager.Current.CreateToolBarButtonStyle(), + Command = CommandWrapper.Unwrap(command), + ToolTip = Properties.Resources.ResourceManager.GetString( + commandExport.Metadata?.ToolTip ?? string.Empty), + Tag = commandExport.Metadata?.Tag, + Content = new Image { + Width = 16, + Height = 16, + Source = Images.Load(command, commandExport.Metadata?.ToolbarIcon) + } + }; + + if (command is IProvideParameterBinding parameterBinding) + { + BindingOperations.SetBinding(toolbarItem, ButtonBase.CommandParameterProperty, + parameterBinding.ParameterBinding); + } + + return toolbarItem; + } + + void MainWindow_KeyDown(object sender, KeyEventArgs e) + { + if (e.Handled || e.KeyboardDevice.Modifiers != ModifierKeys.Alt || e.Key != Key.System) + return; + + switch (e.SystemKey) + { + case Key.A: + assemblyListComboBox.Focus(); + e.Handled = true; + break; + case Key.L: + languageComboBox.Focus(); + e.Handled = true; + break; + case Key.E: // Alt+V was already taken by _View menu + languageVersionComboBox.Focus(); + e.Handled = true; + break; + } + } + } +} \ No newline at end of file diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 177dbe951b..99c5ee08f1 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -49,127 +49,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 510f262d24..e78c4f7f80 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -22,7 +22,6 @@ using System.Drawing; using System.Linq; using System.Windows; -using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; @@ -40,7 +39,7 @@ partial class MainWindow { private readonly SettingsService settingsService; - public MainWindow(MainWindowViewModel mainWindowViewModel, MenuService menuService, SettingsService settingsService) + public MainWindow(MainWindowViewModel mainWindowViewModel, SettingsService settingsService) { this.settingsService = settingsService; @@ -53,7 +52,6 @@ public MainWindow(MainWindowViewModel mainWindowViewModel, MenuService menuServi Dispatcher.BeginInvoke(DispatcherPriority.Background, () => { mainWindowViewModel.Workspace.InitializeLayout(); - menuService.Init(mainMenu, toolBar, InputBindings); MessageBus.Send(this, new MainWindowLoadedEventArgs()); }); } @@ -85,29 +83,6 @@ protected override void OnSourceInitialized(EventArgs e) this.WindowState = sessionSettings.WindowState; } - protected override void OnKeyDown(KeyEventArgs e) - { - base.OnKeyDown(e); - if (!e.Handled && e.KeyboardDevice.Modifiers == ModifierKeys.Alt && e.Key == Key.System) - { - switch (e.SystemKey) - { - case Key.A: - assemblyListComboBox.Focus(); - e.Handled = true; - break; - case Key.L: - languageComboBox.Focus(); - e.Handled = true; - break; - case Key.E: // Alt+V was already taken by _View menu - languageVersionComboBox.Focus(); - e.Handled = true; - break; - } - } - } - protected override void OnStateChanged(EventArgs e) { base.OnStateChanged(e);