The headless UI tests synchronized with the application by pumping a fixed
number of frames (39 loops of RunJobs/Delay across 19 files) and by pressing
at a point computed once from a control's bounds. Both encode how fast the
machine that wrote the test was: on the loaded Windows Debug CI agent the
frame count comes up short and the point goes stale, which is the recurring
timeout in the tree context-menu tests and the reason each such failure was
repaired one test at a time.
Waiters.WaitForIdleAsync replaces the frame loops. It observes the actual
precondition - no dispatcher job queued at Background priority or above, no
assembly still loading in the background sweep, a frame rendered - and
requires it on two consecutive polls so a thread-pool continuation about to
post back is caught as well.
Window.ClickAsync replaces element-targeted MouseDown/MouseUp pairs. It
re-resolves the target on every poll and presses only once the window's hit
test at the click point answers with that target, reporting the point and
what was hit instead on timeout. That diagnostic exposed one vacuous test:
User_Click_On_Visible_Row_Does_Not_Recentre_Viewport clicked the centre of a
row wider than the tree viewport, which lies under the decompiler text view,
so its assertion held without the row ever being clicked. It now clamps the
point to the viewport like the other tree-row clicks.
Clicks at text positions and press-only gutter clicks stay raw; they do not
target an element.
Assisted-by: Claude:claude-fable-5:Claude Code
The WPF SharpTreeView bound ApplicationCommands.Delete at class level, so
Delete deleted the top-level selection of any tree whose nodes opt in via
CanDelete/Delete -- which is how a top-level analyzer entry was removed
from the Analyzer pane. The Avalonia tree never received that binding;
only the assembly list pane carried a hand-rolled Delete handler for
assemblies, so the analyzer pane lost the key entirely even though its
nodes still implement the deletion overrides.
Moving the gesture back into SharpTreeView restores it for every tree
and lets the pane-specific handler (with its own reselect logic) go.
Assisted-by: Claude:claude-fable-5:Claude Code
Avalonia 12 treats plain Enter/Space on a ListBoxItem as selection
input: the container marks the KeyDown handled before it bubbles, so
SharpTreeView.OnKeyDown never saw the keys and its activation handling
(navigate to the member from an analyzer row, toggle a checkable row)
was dead. Override ShouldTriggerSelection -- the extension point added
for this in Avalonia 12 -- to suppress the selection trigger exactly
for the case OnKeyDown activates instead: a single selected row that
is the row the key landed on. Multi-row selections keep the default
collapse-to-focused-row behaviour.
Assisted-by: Claude:claude-fable-5:Claude Code
The global:: prefixes existed because the test project's namespace
ICSharpCode.ILSpy.Tests used to shadow the app's old top-level ILSpy
namespace. With the UI code back under ICSharpCode.ILSpy there is
nothing left to shadow, so plain fully qualified names resolve fine.
Assisted-by: Claude:claude-fable-5:Claude Code
The Avalonia port had placed the UI app in an ILSpy.* namespace tree,
while the csproj RootNamespace and every prior release (through 10.1)
use ICSharpCode.ILSpy.*. Restoring the historical namespace reduces the
public API diff against release/10.1 for plugin authors and removes the
shadowing that forced global:: qualifiers in the test project. The
Images class and AccessOverlayIcon enum move back into the root
namespace (as in 10.1), since an ICSharpCode.ILSpy.Images namespace
would shadow the Images class for all code inside ICSharpCode.ILSpy.
Assisted-by: Claude:claude-fable-5:Claude Code
TreeKeyboardController (and its ITreeKeyboardTarget interface) drove keyboard gestures on the old ProDataGrid trees, including a reflection workaround for the grid's shift-range selection that only added and never shrank a range. Both trees are SharpTreeView (ListBox) now -- which handles those gestures natively in SharpTreeView.OnKeyDown and gets anchor extend/shrink for free -- so nothing constructs the controller. Remove it and the now-vestigial interface implementations on the two tree view-models.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Two implemented tree gestures had no coverage (flagged by a gesture audit): middle mouse button on a row opens it in a new document tab, and Ctrl+R analyzes the selected member into the analyzer pane.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Analyze dropped the entity's node into the tree collapsed, so the user had to expand it every time to see its analyzers (Used By, Uses, ...). Expand the freshly-added node on creation; re-analyzing an existing entity returns the same node without re-expanding it.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Replaces the ProDataGrid + HierarchicalModel bridge and the ~57 LOC HierarchicalNode selection-sync in AnalyzerTreeView with the new control: the view-model's Root binds straight to SharpTreeView.Root and selection mirrors at the SharpTreeNode level (no FindNode/unwrap). Double-tap navigation now flows through SharpTreeView.OnDoubleTapped (node.ActivateItem first, expand only if unhandled), so the analyzer entity rows still navigate. TextViewContext.TreeGrid is widened from DataGrid? to Control? so both the (not-yet-migrated) assembly DataGrid and the SharpTreeView satisfy it. All 150 analyzer tests pass.
Proves the TreeKeyboardController extraction: pressing Right expands a node in the analyzer tree the same way it does in the assembly tree, via the shared controller wired onto both.