Browse Source

Assembly tree expander, icons, and load-error visuals

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
318d8776ff
  1. 1
      Directory.Packages.props
  2. 17
      ILSpy/AssemblyTree/AssemblyListPane.axaml
  3. 10
      ILSpy/AssemblyTree/AssemblyListPane.axaml.cs
  4. 1
      ILSpy/Assets/Icons/Assembly.svg
  5. 1
      ILSpy/Assets/Icons/AssemblyWarning.svg
  6. 1
      ILSpy/Assets/Icons/Class.svg
  7. 70
      ILSpy/Assets/Icons/Constructor.svg
  8. 1
      ILSpy/Assets/Icons/Delegate.svg
  9. 1
      ILSpy/Assets/Icons/Enum.svg
  10. 1
      ILSpy/Assets/Icons/Event.svg
  11. 1
      ILSpy/Assets/Icons/Field.svg
  12. 1
      ILSpy/Assets/Icons/Interface.svg
  13. 1
      ILSpy/Assets/Icons/Method.svg
  14. 1
      ILSpy/Assets/Icons/Namespace.svg
  15. 1
      ILSpy/Assets/Icons/Operator.svg
  16. 1
      ILSpy/Assets/Icons/Property.svg
  17. 1
      ILSpy/Assets/Icons/Struct.svg
  18. 1
      ILSpy/ILSpy.csproj
  19. 48
      ILSpy/Images.cs
  20. 39
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  21. 7
      ILSpy/TreeNodes/MemberTreeNode.cs
  22. 4
      ILSpy/TreeNodes/NamespaceTreeNode.cs
  23. 29
      ILSpy/TreeNodes/TypeTreeNode.cs

1
Directory.Packages.props

@ -20,6 +20,7 @@ @@ -20,6 +20,7 @@
<PackageVersion Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Avalonia.Headless.NUnit" Version="$(AvaloniaVersion)" />
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="12.0.0.5" />
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="12.0.0" />
<PackageVersion Include="AvaloniaEdit.TextMate" Version="12.0.0" />
<PackageVersion Include="Avalonia.Xaml.Behaviors" Version="11.3.0.6" />

17
ILSpy/AssemblyTree/AssemblyListPane.axaml

@ -15,14 +15,21 @@ @@ -15,14 +15,21 @@
CanUserResizeColumns="False"
SelectionMode="Single">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataGridHierarchicalColumn Header="Name" Width="*">
<DataGridHierarchicalColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Spacing="4"
VerticalAlignment="Center" Margin="4,0"
ToolTip.Tip="{ReflectionBinding Item.ToolTip}">
<Image Source="{ReflectionBinding Item.Icon}"
Width="16" Height="16"
VerticalAlignment="Center" />
<TextBlock Text="{ReflectionBinding Item.Text}"
VerticalAlignment="Center" Margin="4,0" />
VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGridHierarchicalColumn.CellTemplate>
</DataGridHierarchicalColumn>
</DataGrid.Columns>
</DataGrid>
</UserControl>

10
ILSpy/AssemblyTree/AssemblyListPane.axaml.cs

@ -54,7 +54,15 @@ namespace ILSpy.AssemblyTree @@ -54,7 +54,15 @@ namespace ILSpy.AssemblyTree
void BindTree(SharpTreeNode root)
{
var options = new HierarchicalOptions<SharpTreeNode> {
ChildrenSelector = node => node.Children,
// Force lazy children to load BEFORE returning the collection. ProDataGrid
// queries ChildrenSelector while expanding, before propagating IsExpanded to
// the source via IsExpandedSetter, so SharpTreeNode.LazyLoading wouldn't have
// triggered yet otherwise -- and an empty Children collection causes the grid
// to revert the expansion immediately.
ChildrenSelector = node => {
node.EnsureLazyChildren();
return node.Children;
},
IsLeafSelector = node => !node.ShowExpander,
IsExpandedSelector = node => node.IsExpanded,
IsExpandedSetter = (node, val) => node.IsExpanded = val,

1
ILSpy/Assets/Icons/Assembly.svg

@ -0,0 +1 @@ @@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.st0{opacity:0}.st0,.st1{fill:#f6f6f6}.st2{fill:#424242}</style><g id="outline"><path class="st0" d="M0 0h16v16H0z"/><path class="st1" d="M16 3v9H8V9H6v2H0V4h6v2h2V3h8z"/></g><g id="icon_x5F_bg"><path class="st2" d="M1 5h4v5H1zM9 4h6v7H9zM6 7h2v1H6z"/></g></svg>

After

Width:  |  Height:  |  Size: 329 B

1
ILSpy/Assets/Icons/AssemblyWarning.svg

@ -0,0 +1 @@ @@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.st0{opacity:0}.st0,.st1{fill:#f6f6f6}.st2{fill:#424242}.st3{fill:#fc0}</style><g id="outline"><path class="st0" d="M0 0h16v16H0z"/><path class="st1" d="M16 2H8v3H6V3H0v7h6V8h2v3h.5L7 14l1 2h7l1-2-1.5-3H16z"/></g><path class="st2" d="M5 9H1V4h4v5zm3-3H6v1h2V6zm4.5 1l1.5 3h1V3H9v7l1.5-3h2z" id="icon_x5F_bg"/><path class="st3" d="M11.891 8.016h-.781l-3.016 6 .515.984h5.766l.484-1-2.968-5.984zM12 14h-1v-1h1v1zm0-2h-1v-2h1v2z" id="not_x5F_bg"/><path d="M12 12h-1v-2h1v2zm0 1h-1v1h1v-1z" id="not_x5F_fg"/></svg>

After

Width:  |  Height:  |  Size: 577 B

1
ILSpy/Assets/Icons/Class.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-orange{fill:#c27d1a}</style><g id="canvas"><path id="XMLID_1_" class="icon-canvas-transparent" d="M16 16H0V0h16v16z"/></g><path class="icon-vs-out" d="M16 6.586l-3-3L11.586 5H9.414l1-1-4-4h-.828L0 5.586v.828l4 4L6.414 8H7v5h1.586l3 3h.828L16 12.414v-.828L13.914 9.5 16 7.414v-.828z" id="outline"/><g id="iconBg"><path class="icon-vs-action-orange" d="M13 10l2 2-3 3-2-2 1-1H8V7H6L4 9 1 6l5-5 3 3-2 2h5l1-1 2 2-3 3-2-2 1-1H9v4l2.999.002L13 10z"/></g></svg>

After

Width:  |  Height:  |  Size: 612 B

70
ILSpy/Assets/Icons/Constructor.svg

@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 16 16"
enable-background="new 0 0 16 16"
version="1.1"
id="svg8"
sodipodi:docname="Constructor.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata14">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs12" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview10"
showgrid="false"
inkscape:zoom="5.2149125"
inkscape:cx="34.571566"
inkscape:cy="-20.671176"
inkscape:window-x="-8"
inkscape:window-y="340"
inkscape:window-maximized="1"
inkscape:current-layer="svg8" />
<style
type="text/css"
id="style2">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-fg{fill:#F0EFF1;} .icon-vs-action-purple{fill:#652D90;}</style>
<path
class="icon-canvas-transparent"
d="M16 16h-16v-16h16v16z"
id="canvas" />
<path
class="icon-vs-out"
d="M15 3.349v8.403l-6.025 4.248h-.905l-7.07-4.418v-8.255l6.595-3.327h1.118l6.287 3.349z"
id="outline" />
<path
class="icon-vs-fg"
d="M12.715 4.398l-4.228 2.622-4.922-2.748 4.578-2.309 4.572 2.435zm-9.715.704l5 2.792v5.705l-5-3.125v-5.372zm6 8.434v-5.658l4-2.48v5.317l-4 2.821z"
id="iconFg" />
<path
class="icon-vs-action-purple"
d="M8.156.837l-6.156 3.105v7.085l6.517 4.073 5.483-3.867v-7.283l-5.844-3.113zm4.559 3.561l-4.228 2.622-4.922-2.748 4.578-2.309 4.572 2.435zm-9.715.704l5 2.792v5.705l-5-3.125v-5.372zm6 8.434v-5.658l4-2.48v5.317l-4 2.821z"
id="iconBg"
style="fill:#218022;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

1
ILSpy/Assets/Icons/Delegate.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-fg{fill:#f0eff1}.icon-vs-action-purple{fill:#652d90}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M2.75 16A2.752 2.752 0 0 1 0 13.25v-7.5A2.752 2.752 0 0 1 2.75 3H4v-.75A2.252 2.252 0 0 1 6.25 0h3.5C10.99 0 12 1.009 12 2.25V3h1.25A2.752 2.752 0 0 1 16 5.75v7.5A2.752 2.752 0 0 1 13.25 16H2.75z" id="outline"/><path class="icon-vs-action-purple" d="M13.25 4H11V2.25C11 1.56 10.44 1 9.75 1h-3.5C5.56 1 5 1.56 5 2.25V4H2.75C1.785 4 1 4.785 1 5.75v7.5c0 .965.785 1.75 1.75 1.75h10.5c.965 0 1.75-.785 1.75-1.75v-7.5C15 4.785 14.215 4 13.25 4zM4 13H3V6h1v7zm6-9H6V2.25A.25.25 0 0 1 6.25 2h3.5a.25.25 0 0 1 .25.25V4zm3 9h-1V6h1v7z" id="iconBg"/><g id="iconFg"><path class="icon-vs-fg" d="M4 13H3V6h1v7zm9 0h-1V6h1v7z"/></g></svg>

After

Width:  |  Height:  |  Size: 936 B

1
ILSpy/Assets/Icons/Enum.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-fg{fill:#f0eff1}.icon-vs-action-orange{fill:#c27d1a}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M14.414 1L16 2.586v5.828L14.414 10H10v3.416L8.414 15H1.586L0 13.416v-5.83L1.586 6H6V2.586L7.586 1h6.828z" id="outline"/><path class="icon-vs-fg" d="M2 13h6V8H2v5zm1-4h4v1H3V9zm0 2h4v1H3v-1zm11-5V3H8v3h.414L9 6.586V6h4v1H9.414l.586.586V8h4V6zm-1-1H9V4h4v1z" id="iconFg"/><g id="iconBg"><path class="icon-vs-action-orange" d="M3 11h4.001v1H3v-1zm0-1h4.001V9H3v1zm6-2v5l-1 1H2l-1-1V8l1-1h6l1 1zM8 8H2v5h6V8zm1-2l1 1h3V6H9zm0-1h4V4H9v1zm5-3H8L7 3v3h1V3h6v5h-4v1h4l1-1V3l-1-1z"/></g></svg>

After

Width:  |  Height:  |  Size: 796 B

1
ILSpy/Assets/Icons/Event.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-orange{fill:#c27d1a}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M14 1.414L9.414 6H14v1.414L5.414 16H3v-1.234L5.371 10H2V8.764L6.382 0H14v1.414z" id="outline"/><path class="icon-vs-action-orange" d="M7 7h6l-8 8H4l2.985-6H3l4-8h6L7 7z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 476 B

1
ILSpy/Assets/Icons/Field.svg

@ -0,0 +1 @@ @@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .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="M0 10.736v-6.236l9-4.5 7 3.5v6.236l-9 4.5-7-3.5z" id="outline"/><path class="icon-vs-action-blue" d="M9 1l-8 4v5l6 3 8-4v-5l-6-3zm-2 5.882l-3.764-1.882 5.764-2.882 3.764 1.882-5.764 2.882z" id="iconBg"/><path class="icon-vs-fg" d="M9 2.118l3.764 1.882-5.764 2.882-3.764-1.882 5.764-2.882z" id="iconFg"/></svg>

After

Width:  |  Height:  |  Size: 680 B

1
ILSpy/Assets/Icons/Interface.svg

@ -0,0 +1 @@ @@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .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="M11.5 12c-1.915 0-3.602-1.241-4.228-3h-1.41c-.536.985-1.572 1.625-2.737 1.625-1.723 0-3.125-1.402-3.125-3.125s1.402-3.125 3.125-3.125c1.165 0 2.201.639 2.737 1.625h1.41c.626-1.759 2.313-3 4.228-3 2.481 0 4.5 2.019 4.5 4.5s-2.019 4.5-4.5 4.5z" id="outline"/><path class="icon-vs-fg" d="M11.5 9c-.827 0-1.5-.674-1.5-1.5 0-.828.673-1.5 1.5-1.5s1.5.672 1.5 1.5c0 .826-.673 1.5-1.5 1.5z" id="iconFg"/><path class="icon-vs-action-blue" d="M11.5 4c-1.762 0-3.205 1.306-3.45 3h-2.865c-.226-.931-1.059-1.625-2.06-1.625-1.174 0-2.125.951-2.125 2.125s.951 2.125 2.125 2.125c1 0 1.834-.694 2.06-1.625h2.865c.245 1.694 1.688 3 3.45 3 1.933 0 3.5-1.567 3.5-3.5s-1.567-3.5-3.5-3.5zm0 5c-.827 0-1.5-.673-1.5-1.5s.673-1.5 1.5-1.5 1.5.673 1.5 1.5-.673 1.5-1.5 1.5z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

1
ILSpy/Assets/Icons/Method.svg

@ -0,0 +1 @@ @@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-fg{fill:#F0EFF1;} .icon-vs-action-purple{fill:#652D90;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M15 3.349v8.403l-6.025 4.248h-.905l-7.07-4.418v-8.255l6.595-3.327h1.118l6.287 3.349z" id="outline"/><path class="icon-vs-fg" d="M12.715 4.398l-4.228 2.622-4.922-2.748 4.578-2.309 4.572 2.435zm-9.715.704l5 2.792v5.705l-5-3.125v-5.372zm6 8.434v-5.658l4-2.48v5.317l-4 2.821z" id="iconFg"/><path class="icon-vs-action-purple" d="M8.156.837l-6.156 3.105v7.085l6.517 4.073 5.483-3.867v-7.283l-5.844-3.113zm4.559 3.561l-4.228 2.622-4.922-2.748 4.578-2.309 4.572 2.435zm-9.715.704l5 2.792v5.705l-5-3.125v-5.372zm6 8.434v-5.658l4-2.48v5.317l-4 2.821z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 935 B

1
ILSpy/Assets/Icons/Namespace.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-bg{fill:#424242}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M9.26 11.984l.978-.021a.962.962 0 0 0 .09-.006c.011-.063.026-.179.026-.361V9.688c0-.679.185-1.257.53-1.707-.346-.452-.53-1.03-.53-1.705V4.35c0-.167-.021-.259-.034-.302L9.26 4.02V.973l1.011.011c2.167.024 3.409 1.156 3.409 3.105v1.962c0 .351.071.461.072.462l.936.06.053.927v1.936l-.936.061c-.076.016-.125.146-.125.424v2.017c0 .914-.332 3.043-3.408 3.078l-1.012.011v-3.043zm-3.521 3.032c-3.089-.035-3.422-2.164-3.422-3.078V9.921c0-.327-.066-.432-.067-.433l-.937-.06-.063-.929V6.563l.942-.06c.058 0 .125-.114.125-.452V4.09c0-1.949 1.248-3.081 3.422-3.105L6.75.973V4.02l-.975.023a.572.572 0 0 0-.093.01c.006.021-.019.115-.019.297v1.928c0 .675-.186 1.253-.534 1.705.348.45.534 1.028.534 1.707v1.907c0 .175.014.291.027.363.023.002 1.06.025 1.06.025v3.043l-1.011-.012z" id="outline"/><g id="iconBg"><path class="icon-vs-bg" d="M5.75 14.016c-1.623-.019-2.434-.711-2.434-2.078V9.921c0-.902-.355-1.376-1.066-1.422v-.998c.711-.045 1.066-.529 1.066-1.449V4.09c0-1.385.811-2.087 2.434-2.105v1.06c-.725.017-1.087.453-1.087 1.305v1.928c0 .92-.454 1.488-1.36 1.702V8c.907.201 1.36.763 1.36 1.688v1.907c0 .488.081.835.243 1.042.162.208.443.316.844.325v1.054zm7.99-5.517c-.706.045-1.06.52-1.06 1.422v2.017c0 1.367-.807 2.06-2.42 2.078v-1.053c.396-.009.678-.118.844-.328.167-.21.25-.556.25-1.039V9.688c0-.925.449-1.488 1.347-1.688v-.021c-.898-.214-1.347-.782-1.347-1.702V4.35c0-.852-.364-1.288-1.094-1.306v-1.06c1.613.018 2.42.72 2.42 2.105v1.962c0 .92.354 1.404 1.06 1.449v.999z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

1
ILSpy/Assets/Icons/Operator.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-fg{fill:#f0eff1}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 16H0V0h16v16z" id="outline"/><path class="icon-vs-action-blue" d="M1 1v14h14V1H1zm6 12H3v-1h4v1zm0-3H3V9h4v1zm0-5H5v2H4V5H2V4h2V2h1v2h2v1zm3.281 8H8.719l3-4h1.563l-3.001 4zM14 5H9V4h5v1z" id="iconBg"/><g id="iconFg"><path class="icon-vs-fg" d="M7 5H5v2H4V5H2V4h2V2h1v2h2v1zm7-1H9v1h5V4zM7 9H3v1h4V9zm0 3H3v1h4v-1zm3.281 1l3-4h-1.563l-3 4h1.563z"/></g></svg>

After

Width:  |  Height:  |  Size: 671 B

1
ILSpy/Assets/Icons/Property.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-bg{fill:#424242}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 5.5a5.5 5.5 0 0 1-5.5 5.5c-.275 0-.543-.027-.807-.066l-.079-.012a5.429 5.429 0 0 1-.81-.192l-4.537 4.537c-.472.473-1.1.733-1.767.733s-1.295-.26-1.768-.732a2.502 2.502 0 0 1 0-3.535l4.537-4.537a5.452 5.452 0 0 1-.191-.812c-.005-.025-.008-.051-.012-.077A5.503 5.503 0 0 1 5 5.5a5.5 5.5 0 1 1 11 0z" id="outline"/><path class="icon-vs-bg" d="M15 5.5a4.5 4.5 0 0 1-4.5 4.5c-.693 0-1.342-.17-1.929-.45l-5.01 5.01c-.293.294-.677.44-1.061.44s-.768-.146-1.061-.439a1.5 1.5 0 0 1 0-2.121l5.01-5.011A4.475 4.475 0 0 1 6 5.5 4.5 4.5 0 0 1 10.5 1c.693 0 1.342.17 1.929.45L9.636 4.243l2.121 2.121 2.793-2.793c.28.587.45 1.236.45 1.929z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 923 B

1
ILSpy/Assets/Icons/Struct.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 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M9 14V8H7v6H1V2h14v12H9z" id="outline"/><g id="iconBg"><path class="icon-vs-action-blue" d="M10 9h4v4h-4V9zm-8 4h4V9H2v4zM2 3v4h12V3H2z"/></g></svg>

After

Width:  |  Height:  |  Size: 433 B

1
ILSpy/ILSpy.csproj

@ -24,6 +24,7 @@ @@ -24,6 +24,7 @@
<PackageReference Include="Avalonia.Desktop" />
<PackageReference Include="Avalonia.Themes.Fluent" />
<PackageReference Include="Avalonia.Fonts.Inter" />
<PackageReference Include="Svg.Controls.Skia.Avalonia" />
<PackageReference Include="AvaloniaUI.DiagnosticsSupport">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>

48
ILSpy/Images.cs

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
// 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 Avalonia.Media;
using Avalonia.Svg.Skia;
namespace ILSpy.Images
{
public static class Images
{
static IImage Load(string name)
{
return new SvgImage {
Source = SvgSource.Load($"avares://ILSpy/Assets/Icons/{name}.svg", null)
};
}
public static readonly IImage Assembly = Load(nameof(Assembly));
public static readonly IImage AssemblyWarning = Load(nameof(AssemblyWarning));
public static readonly IImage Namespace = Load(nameof(Namespace));
public static readonly IImage Class = Load(nameof(Class));
public static readonly IImage Interface = Load(nameof(Interface));
public static readonly IImage Struct = Load(nameof(Struct));
public static readonly IImage Enum = Load(nameof(Enum));
public static readonly IImage Delegate = Load(nameof(Delegate));
public static readonly IImage Method = Load(nameof(Method));
public static readonly IImage Constructor = Load(nameof(Constructor));
public static readonly IImage Operator = Load(nameof(Operator));
public static readonly IImage Field = Load(nameof(Field));
public static readonly IImage Property = Load(nameof(Property));
public static readonly IImage Event = Load(nameof(Event));
}
}

39
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -17,16 +17,21 @@ @@ -17,16 +17,21 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.IO;
using System.Linq;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpyX;
using ILSpy.Images;
namespace ILSpy.TreeNodes
{
sealed class AssemblyTreeNode : ILSpyTreeNode
{
readonly LoadedAssembly assembly;
bool loadFailed;
string? loadError;
public LoadedAssembly LoadedAssembly => assembly;
@ -39,11 +44,41 @@ namespace ILSpy.TreeNodes @@ -39,11 +44,41 @@ namespace ILSpy.TreeNodes
public override object Text => assembly.ShortName;
public override object Icon => loadFailed ? Images.Images.AssemblyWarning : Images.Images.Assembly;
public override object? ToolTip => loadFailed ? loadError : assembly.FileName;
// When a load fails we still want the user to see the entry, but with the warning glyph
// and no expander chevron (LoadChildren produced nothing, ShowExpander becomes false).
public override bool ShowExpander => !loadFailed && base.ShowExpander;
protected override void LoadChildren()
{
var result = assembly.GetMetadataFileOrNullAsync().Result;
if (result is not MetadataFile module)
MetadataFile? module;
try
{
module = assembly.GetMetadataFileOrNullAsync().Result;
}
catch (Exception ex)
{
module = null;
loadError = $"Failed to load '{assembly.FileName}':\n{ex.GetBaseException().Message}";
}
if (module == null)
{
loadFailed = true;
if (loadError == null)
{
loadError = File.Exists(assembly.FileName)
? $"Failed to load '{assembly.FileName}'."
: $"File not found:\n{assembly.FileName}";
}
RaisePropertyChanged(nameof(Icon));
RaisePropertyChanged(nameof(ShowExpander));
RaisePropertyChanged(nameof(ToolTip));
return;
}
var metadata = module.Metadata;
var namespaces = metadata.TypeDefinitions

7
ILSpy/TreeNodes/MemberTreeNode.cs

@ -16,18 +16,23 @@ @@ -16,18 +16,23 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using Avalonia.Media;
namespace ILSpy.TreeNodes
{
sealed class MemberTreeNode : ILSpyTreeNode
{
readonly string name;
readonly IImage icon;
public MemberTreeNode(string name)
public MemberTreeNode(string name, IImage icon)
{
this.name = name;
this.icon = icon;
}
public override object Text => name;
public override object Icon => icon;
public override bool ShowExpander => false;
}
}

4
ILSpy/TreeNodes/NamespaceTreeNode.cs

@ -21,6 +21,8 @@ using System.Reflection.Metadata; @@ -21,6 +21,8 @@ using System.Reflection.Metadata;
using ICSharpCode.Decompiler.Metadata;
using ILSpy.Images;
namespace ILSpy.TreeNodes
{
sealed class NamespaceTreeNode : ILSpyTreeNode
@ -39,6 +41,8 @@ namespace ILSpy.TreeNodes @@ -39,6 +41,8 @@ namespace ILSpy.TreeNodes
public override object Text => name;
public override object Icon => Images.Images.Namespace;
protected override void LoadChildren()
{
var metadata = module.Metadata;

29
ILSpy/TreeNodes/TypeTreeNode.cs

@ -17,10 +17,13 @@ @@ -17,10 +17,13 @@
// DEALINGS IN THE SOFTWARE.
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler.Metadata;
using ILSpy.Images;
namespace ILSpy.TreeNodes
{
sealed class TypeTreeNode : ILSpyTreeNode
@ -45,6 +48,18 @@ namespace ILSpy.TreeNodes @@ -45,6 +48,18 @@ namespace ILSpy.TreeNodes
}
}
public override object Icon {
get {
var td = module.Metadata.GetTypeDefinition(handle);
var attrs = td.Attributes;
if ((attrs & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface)
return Images.Images.Interface;
// crude detection: real distinction needs base-type lookups (System.Enum, ValueType,
// MulticastDelegate). Refining will land with the broader tree-node port.
return Images.Images.Class;
}
}
protected override void LoadChildren()
{
var td = module.Metadata.GetTypeDefinition(handle);
@ -55,25 +70,31 @@ namespace ILSpy.TreeNodes @@ -55,25 +70,31 @@ namespace ILSpy.TreeNodes
foreach (var method in td.GetMethods())
{
var md = module.Metadata.GetMethodDefinition(method);
Children.Add(new MemberTreeNode(module.Metadata.GetString(md.Name)));
var name = module.Metadata.GetString(md.Name);
var icon = name == ".ctor" || name == ".cctor"
? Images.Images.Constructor
: ((md.Attributes & MethodAttributes.SpecialName) != 0 && name.StartsWith("op_"))
? Images.Images.Operator
: Images.Images.Method;
Children.Add(new MemberTreeNode(name, icon));
}
foreach (var field in td.GetFields())
{
var fd = module.Metadata.GetFieldDefinition(field);
Children.Add(new MemberTreeNode(module.Metadata.GetString(fd.Name)));
Children.Add(new MemberTreeNode(module.Metadata.GetString(fd.Name), Images.Images.Field));
}
foreach (var prop in td.GetProperties())
{
var pd = module.Metadata.GetPropertyDefinition(prop);
Children.Add(new MemberTreeNode(module.Metadata.GetString(pd.Name)));
Children.Add(new MemberTreeNode(module.Metadata.GetString(pd.Name), Images.Images.Property));
}
foreach (var evt in td.GetEvents())
{
var ed = module.Metadata.GetEventDefinition(evt);
Children.Add(new MemberTreeNode(module.Metadata.GetString(ed.Name)));
Children.Add(new MemberTreeNode(module.Metadata.GetString(ed.Name), Images.Images.Event));
}
}
}

Loading…
Cancel
Save