Browse Source

Toolbar icons for Refresh/Sort/CollapseAll + test

Imports the three SVG assets that were missing from `ILSpy.Avalonia/Assets/Icons/`
and registers them in `Images/Images.cs`, so the MEF-injected toolbar buttons
for "Reload all assemblies", "Sort assembly list by name", and "Collapse all
tree nodes" now render their icons instead of falling back to the tooltip text
(`MainToolBar.axaml.cs:226` `ResolveIcon` returned null for these). Adds a
regression test asserting every `[ExportToolbarCommand]`'s `ToolbarIcon`
resolves to a real `IImage` field on the registry.
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
7dfbf6cd28
  1. 26
      ILSpy.Tests/MainWindow/MainToolBarLayoutTests.cs
  2. 1
      ILSpy/Assets/Icons/CollapseAll.svg
  3. 1
      ILSpy/Assets/Icons/Refresh.svg
  4. 1
      ILSpy/Assets/Icons/Sort.svg
  5. 3
      ILSpy/Images.cs

26
ILSpy.Tests/MainWindow/MainToolBarLayoutTests.cs

@ -30,6 +30,7 @@ using ICSharpCode.ILSpy.Properties; @@ -30,6 +30,7 @@ using ICSharpCode.ILSpy.Properties;
using ILSpy;
using ILSpy.AppEnv;
using ILSpy.Commands;
using ILSpy.Search;
using ILSpy.ViewModels;
using ILSpy.Views;
@ -115,6 +116,31 @@ public class MainToolBarLayoutTests @@ -115,6 +116,31 @@ public class MainToolBarLayoutTests
"clicking the Show-Search toolbar button must activate the search tool pane");
}
[AvaloniaTest]
public void Every_ExportToolbarCommand_Resolves_An_Icon()
{
// `MainToolBar.BuildButton` falls back to a text label when `ResolveIcon` can't find
// the `Images/<Name>` asset — that's a UX regression (and a silent one, since the
// button still works). Pin every MEF-exported toolbar command to a real IImage so the
// next "we forgot to import the SVG" lands as a test failure, not a user report.
var registry = AppComposition.Current.GetExport<ToolbarCommandRegistry>();
foreach (var entry in registry.Commands)
{
var iconPath = entry.Metadata.ToolbarIcon;
iconPath.Should().NotBeNullOrEmpty(
"every toolbar command must declare a ToolbarIcon");
var name = iconPath!.StartsWith("Images/", System.StringComparison.Ordinal)
? iconPath["Images/".Length..]
: iconPath;
var field = typeof(global::ILSpy.Images.Images)
.GetField(name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
field.Should().NotBeNull(
$"toolbar command with ToolTip='{entry.Metadata.ToolTip}' references Images/{name} but no static field with that name exists in ILSpy.Images.Images");
field!.GetValue(null).Should().NotBeNull(
$"the Images.{name} field is declared but null at runtime");
}
}
[AvaloniaTest]
public async Task MainToolBar_Button_Order_Mirrors_WPF()
{

1
ILSpy/Assets/Icons/CollapseAll.svg

@ -0,0 +1 @@ @@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-bg{fill:#424242;} .icon-vs-fg{fill:#F0EFF1;} .icon-vs-action-blue{fill:#00539C;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M15 10h-2v2h-2v2h-9v-9h2v-2h2v-2h9v9z" id="outline"/><path class="icon-vs-bg" d="M14 2v7h-1v-6h-6v-1h7zm-9 2v1h6v6h1v-7h-7zm5 2v7h-7v-7h7zm-1 1h-5v5h5v-5z" id="iconBg"/><path class="icon-vs-fg" d="M4 7v5h5v-5h-5zm4 3h-3v-1h3v1z" id="iconFg"/><path class="icon-vs-action-blue" d="M8 10h-3v-1.031l3 .031v1z" id="colorAction"/></svg>

After

Width:  |  Height:  |  Size: 697 B

1
ILSpy/Assets/Icons/Refresh.svg

@ -0,0 +1 @@ @@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 0v16H0V0h16z" id="canvas"/><path class="icon-vs-out" d="M16 8c0 4.411-3.589 8-8 8s-8-3.589-8-8a7.99 7.99 0 0 1 1.088-4H0V0h8v8H4c0 2.206 1.794 4 4 4s4-1.794 4-4c0-1.344-.669-2.59-1.79-3.334l-.833-.554L11.592.78l.833.553A7.992 7.992 0 0 1 16 8z" id="outline"/><g id="iconBg"><path class="icon-vs-action-blue" d="M15 8c0 3.859-3.141 7-7 7-3.86 0-7-3.141-7-7a7 7 0 0 1 2.12-5H1V1h6v6H5V4.002A5.01 5.01 0 0 0 3 8c0 2.757 2.243 5 5 5s5-2.243 5-5a4.993 4.993 0 0 0-2.236-4.167l1.107-1.666A6.989 6.989 0 0 1 15 8z"/></g></svg>

After

Width:  |  Height:  |  Size: 747 B

1
ILSpy/Assets/Icons/Sort.svg

@ -0,0 +1 @@ @@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-bg{fill:#424242;} .icon-vs-fg{fill:#F0EFF1;} .icon-vs-action-blue{fill:#00539C;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 5v6l-4 4-4-4v1l-1.552 1h1.552v3h-7v-2.414l1.586-1.586h-1.586v-3h-1v-9h9v6l1 1v-4h4v4l2-2z" id="outline"/><path class="icon-vs-bg" d="M2 11h3l-3 3v1h5v-1h-2.987l2.987-2.949-.048-.051h.048v-1h-5v1zm2.5-7.75l.7 1.75h-1.4l.7-1.75zm3.5-2.25v7h-7v-7h7zm-1 6l-2-5h-1l-2 5h1l.399-1h2.201l.4 1h1z" id="iconBg"/><path class="icon-vs-fg" d="M6 7h1l-2-5h-1l-2 5h1l.399-1h2.201l.4 1zm-2.2-2l.7-1.75.7 1.75h-1.4z" id="iconFg"/><path class="icon-vs-action-blue" d="M15 8v2l-3 3-3-3v-2l2 2v-6h2v6l2-2z" id="colorAction"/></svg>

After

Width:  |  Height:  |  Size: 882 B

3
ILSpy/Images.cs

@ -76,6 +76,9 @@ namespace ILSpy.Images @@ -76,6 +76,9 @@ namespace ILSpy.Images
public static readonly IImage Forward = LoadSvg(nameof(Forward));
public static readonly IImage Open = LoadSvg(nameof(Open));
public static readonly IImage Save = LoadSvg(nameof(Save));
public static readonly IImage Refresh = LoadSvg(nameof(Refresh));
public static readonly IImage Sort = LoadSvg(nameof(Sort));
public static readonly IImage CollapseAll = LoadSvg(nameof(CollapseAll));
public static readonly IImage ShowPublicOnly = LoadSvg(nameof(ShowPublicOnly));
public static readonly IImage ShowPrivateInternal = LoadSvg(nameof(ShowPrivateInternal));
public static readonly IImage ShowAll = LoadSvg(nameof(ShowAll));

Loading…
Cancel
Save