From 1b603c762a76c9c6f10772a4b0845fd9989edbd1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 19 Apr 2026 18:05:09 +0200 Subject: [PATCH] Add main menu bare bones --- ILSpy.Tests/MainMenuTests.cs | 49 +++++++++++++++++++++++++ ILSpy/App.axaml.cs | 5 +-- ILSpy/ViewModels/MainWindowViewModel.cs | 12 +++++- ILSpy/Views/MainMenu.axaml | 18 ++++++--- ILSpy/Views/MainMenu.axaml.cs | 4 +- ILSpy/Views/MainWindow.axaml | 30 ++++++++++----- ILSpy/Views/MainWindow.axaml.cs | 14 ++++++- 7 files changed, 108 insertions(+), 24 deletions(-) create mode 100644 ILSpy.Tests/MainMenuTests.cs diff --git a/ILSpy.Tests/MainMenuTests.cs b/ILSpy.Tests/MainMenuTests.cs new file mode 100644 index 000000000..08301881a --- /dev/null +++ b/ILSpy.Tests/MainMenuTests.cs @@ -0,0 +1,49 @@ +// Copyright (c) 2026 AlphaSierraPapa 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.Linq; + +using Avalonia.Controls; +using Avalonia.Headless.NUnit; + +using AwesomeAssertions; + +using ILSpy; + +using NUnit.Framework; + +namespace ICSharpCode.ILSpy.Tests; + +// MainMenu's top-level structure (File / View / Window with mnemonic underscores) is the +// scaffolding every later commit hangs items onto via MEF. If a future commit accidentally +// drops one of these top-levels or shuffles the order, the [ExportMainMenuCommand] entries +// that target them by header would silently land in the wrong menu. +[TestFixture] +public class MainMenuTests +{ + [AvaloniaTest] + public void MainMenu_top_level_items_are_File_View_Window_in_order() + { + var mainMenu = new MainMenu(); + var menu = mainMenu.FindControl("Menu"); + menu.Should().NotBeNull(); + + var headers = menu!.Items.OfType().Select(m => m.Header as string).ToList(); + headers.Should().Equal("_File", "_View", "_Window"); + } +} diff --git a/ILSpy/App.axaml.cs b/ILSpy/App.axaml.cs index 8c75e5604..8a124ee13 100644 --- a/ILSpy/App.axaml.cs +++ b/ILSpy/App.axaml.cs @@ -56,9 +56,8 @@ namespace ILSpy if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - desktop.MainWindow = new MainWindow { - DataContext = new MainWindowViewModel(), - }; + desktop.MainWindow = Composition?.GetExport() + ?? new MainWindow(); desktop.Exit += (_, _) => Composition?.Dispose(); } diff --git a/ILSpy/ViewModels/MainWindowViewModel.cs b/ILSpy/ViewModels/MainWindowViewModel.cs index ada082756..dcec7ae68 100644 --- a/ILSpy/ViewModels/MainWindowViewModel.cs +++ b/ILSpy/ViewModels/MainWindowViewModel.cs @@ -16,10 +16,18 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Composition; + namespace ILSpy.ViewModels { + [Export] + [Shared] public partial class MainWindowViewModel : ViewModelBase { - public string Greeting { get; } = "Welcome to Avalonia!"; + public MainWindowViewModel() + { + } + + public string Title => "ILSpy"; } -} \ No newline at end of file +} diff --git a/ILSpy/Views/MainMenu.axaml b/ILSpy/Views/MainMenu.axaml index dbe938324..f97224195 100644 --- a/ILSpy/Views/MainMenu.axaml +++ b/ILSpy/Views/MainMenu.axaml @@ -2,11 +2,17 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="23" x:Class="ILSpy.MainMenu"> - - - - - + + + + + + + + + + + diff --git a/ILSpy/Views/MainMenu.axaml.cs b/ILSpy/Views/MainMenu.axaml.cs index b68e479c5..e5f0f6e6d 100644 --- a/ILSpy/Views/MainMenu.axaml.cs +++ b/ILSpy/Views/MainMenu.axaml.cs @@ -16,9 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -using Avalonia; using Avalonia.Controls; -using Avalonia.Markup.Xaml; namespace ILSpy; @@ -28,4 +26,4 @@ public partial class MainMenu : UserControl { InitializeComponent(); } -} \ No newline at end of file +} diff --git a/ILSpy/Views/MainWindow.axaml b/ILSpy/Views/MainWindow.axaml index 68dd5b91b..a5201c12c 100644 --- a/ILSpy/Views/MainWindow.axaml +++ b/ILSpy/Views/MainWindow.axaml @@ -1,24 +1,36 @@ + MinWidth="250" MinHeight="200" + UseLayoutRounding="True" + Title="{Binding Title}"> - - - - + + + + - + + + + + + + + + diff --git a/ILSpy/Views/MainWindow.axaml.cs b/ILSpy/Views/MainWindow.axaml.cs index 54d92925c..0ae437301 100644 --- a/ILSpy/Views/MainWindow.axaml.cs +++ b/ILSpy/Views/MainWindow.axaml.cs @@ -16,15 +16,27 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Composition; + using Avalonia.Controls; +using ILSpy.ViewModels; + namespace ILSpy.Views { + [Export] + [Shared] public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } + + [ImportingConstructor] + public MainWindow(MainWindowViewModel viewModel) : this() + { + DataContext = viewModel; + } } -} \ No newline at end of file +}