From 35db05ea158b5ce5e7fcb8e7c5f6c7bdee403cd0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 7 Jun 2026 23:12:27 +0200 Subject: [PATCH] Add the theme picker to the Display options panel The previous version let you pick the theme from a dropdown on the Display options page; the Avalonia port only exposed theme switching via the menu. Bind a ComboBox to SessionSettings.Theme -- the same property the menu's SetThemeCommand sets and ThemeManager applies live -- so the page switches the theme too, and resetting Display defaults restores the default theme. Assisted-by: Claude:claude-opus-4-8:Claude Code --- ILSpy.Tests/Options/ThemePickerTests.cs | 69 +++++++++++++++++++++++ ILSpy/Options/DisplaySettingsPanel.axaml | 5 ++ ILSpy/Options/DisplaySettingsViewModel.cs | 9 +++ 3 files changed, 83 insertions(+) create mode 100644 ILSpy.Tests/Options/ThemePickerTests.cs diff --git a/ILSpy.Tests/Options/ThemePickerTests.cs b/ILSpy.Tests/Options/ThemePickerTests.cs new file mode 100644 index 000000000..4d405ceac --- /dev/null +++ b/ILSpy.Tests/Options/ThemePickerTests.cs @@ -0,0 +1,69 @@ +// 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.Threading.Tasks; + +using Avalonia.Headless.NUnit; + +using AwesomeAssertions; + +using ILSpy; +using ILSpy.AppEnv; +using ILSpy.Options; +using ILSpy.Themes; + +using NUnit.Framework; + +namespace ICSharpCode.ILSpy.Tests; + +/// +/// The Display options page exposes a theme picker bound to SessionSettings.Theme, which switches the +/// theme live (ThemeManager applies it). (Regression: the picker was dropped from the Avalonia panel.) +/// +[TestFixture] +public class ThemePickerTests +{ + [AvaloniaTest] + public async Task Display_Options_Theme_Picker_Lists_Themes_And_Switches_Live() + { + await TestHarness.BootAsync(1); + var settingsService = AppComposition.Current.GetExport(); + var original = settingsService.SessionSettings.Theme; + try + { + var vm = new DisplaySettingsViewModel(); + vm.Load(settingsService); + + vm.AllThemes.Should().Contain("Light").And.Contain("Dark"); + + // The picker binds two-way to the live SessionSettings.Theme -- the exact property + // SetThemeCommand sets and ThemeManager applies, so the picker switches the theme. + vm.SessionSettings.Should().BeSameAs(settingsService.SessionSettings); + vm.SessionSettings.Theme = "Dark"; + settingsService.SessionSettings.Theme.Should().Be("Dark"); + + vm.LoadDefaults(); + vm.SessionSettings.Theme.Should().Be(ThemeManager.Current.DefaultTheme, + "resetting Display defaults restores the default theme"); + } + finally + { + settingsService.SessionSettings.Theme = original; + } + } +} diff --git a/ILSpy/Options/DisplaySettingsPanel.axaml b/ILSpy/Options/DisplaySettingsPanel.axaml index 8de454662..ff4e624a1 100644 --- a/ILSpy/Options/DisplaySettingsPanel.axaml +++ b/ILSpy/Options/DisplaySettingsPanel.axaml @@ -15,6 +15,11 @@ + + + + diff --git a/ILSpy/Options/DisplaySettingsViewModel.cs b/ILSpy/Options/DisplaySettingsViewModel.cs index e5b587834..d8c7508d7 100644 --- a/ILSpy/Options/DisplaySettingsViewModel.cs +++ b/ILSpy/Options/DisplaySettingsViewModel.cs @@ -40,6 +40,13 @@ namespace ILSpy.Options [ObservableProperty] DisplaySettings settings = null!; + // Session settings carry the active Theme. The theme ComboBox binds two-way to + // SessionSettings.Theme; ThemeManager already applies the change live on that property. + [ObservableProperty] + SessionSettings sessionSettings = null!; + + public System.Collections.Generic.IReadOnlyCollection AllThemes => Themes.ThemeManager.AllThemes; + // Avalonia equivalent of WPF's Fonts.SystemFontFamilies. FontManager.Current populates // from the platform font enumerator (DirectWrite on Windows, FontConfig on Linux, // CoreText on macOS), so the list is realistic on every supported target. @@ -66,6 +73,7 @@ namespace ILSpy.Options Settings.PropertyChanged -= OnSettingsPropertyChanged; Settings = service.DisplaySettings; Settings.PropertyChanged += OnSettingsPropertyChanged; + SessionSettings = service.SessionSettings; OnPropertyChanged(nameof(CurrentFontFamily)); } @@ -80,6 +88,7 @@ namespace ILSpy.Options // Reset by replaying LoadFromXml against an empty element — every attribute is // absent so each property falls back to its built-in default. Settings.LoadFromXml(new XElement("DisplaySettings")); + SessionSettings.Theme = Themes.ThemeManager.Current.DefaultTheme; } } }