Repository navigation
LT-22689: Give detail section headers a +/- box and lasting state - #1184
Merged
Merged
Conversation
Collapsible section headers in the Avalonia detail view show a +/- box, drawn from new tokens, in place of a text triangle. Header text and the "Field Options" link take the slice label size, and header text takes a colour token of its own instead of the button theme's link blue. A toggle inserts or removes only the rows under the header. The header itself stays, so it keeps its focus, its place in the view, and the second click of a double-click. On a 311-row test view a toggle went from about 2.7 s to under 0.1 s. Expansion state is keyed per section by the object's GUID, not its per-session HVO, so it survives restarts and sense reorders. The WinForms DataTree keys by object alone, which links an entry's sections together on return; that is not copied. Inserting into a collapsed section expands it, and headers expose the UI Automation ExpandCollapse pattern. The LT-22688 tab order is unchanged: Tab still skips headers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1184 +/- ##
==========================================
+ Coverage 39.49% 39.52% +0.03%
==========================================
Files 1537 1538 +1
Lines 354392 354569 +177
Branches 41058 41090 +32
==========================================
+ Hits 139974 140154 +180
+ Misses 185044 185038 -6
- Partials 29374 29377 +3
🚀 New features to boost your workflow:
|
Drop the reasons for not using other colours, styles or keys from the token and header comments, and fix two test comments that still described a toggle as rebuilding every row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The insert auto-expand skipped every re-show that followed the view's own edit. A re-show can carry an insert from another window, or from a menu command whose refresh joined the edit's re-show, and those inserts stayed hidden in their collapsed sections. The view now records the sections the user collapsed after its own edit completed, and the reveal leaves out only those. Typing into a section and then collapsing it still leaves it collapsed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The insert auto-expand judged each collapsed header on its own. When a parent the user collapsed after an edit stayed closed, a collapsed child under it was still expanded and saved, so reopening the parent later also reopened the child. The reveal now decides per new row: a row that a must-stay-collapsed header hides contributes none of its headers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
thejambi
marked this pull request as ready for review
October 13, 2026 22:18
mark-sil
approved these changes
Oct 14, 2026
mark-sil
left a comment
Contributor
There was a problem hiding this comment.
@mark-sil reviewed 15 files and all commit messages.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on thejambi).
This was referenced Oct 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Start here:
Src/Common/FwAvalonia/Detail/DataTree.cs,ApplyVisibility()andToggleSection(). Nearly everything else hangs off one change: a toggle now touches only the rows under the header.Draft. The LT-22689 acceptance test passes in manual testing; CI pending.
Collapsible section headers in the New UI detail view now behave like the legacy DataTree's:
Headless control-level captures (Avalonia Skia), not FLEx screenshots. Look at the box beside each header.
What breaks when a toggle stops rebuilding everything? The full rebuild used to reset each row's editor, reference-vector selection and label width on every toggle. One
ShownRow[]record per field now holds those. The existing Tab-order, focus-memory, disposal and editing tests pass unchanged. On a 311-row headless view a toggle dropped from about 2.7 s to about 94 ms.Where to look
ApplyVisibility: item positions across mixed insert/remove passes. Pinned byNestedCollapse_SurvivesParentCollapseAndReExpand_LikeLegacyandToggling_KeepsTheHeaderWhereItWasInTheViewport.{layout node}@{object GUID}; they used the per-session HVO. Pinned byDetailExpansionComposeTests.RevealObjectsAddedSince_LeavesASectionCollapsedAfterTheOwnEditCollapsedandRevealObjectsAddedSince_ExpandsASectionCollapsedBeforeTheOwnEdit.Deliberately not here (listed on LT-22689 for Jason)
collapsedLayout, the alternate summary legacy shows for a collapsed sense, is a proposed follow-up.Verification:
build.ps1with comment and token hygiene: clean.doNotExpandin legacy.Next: review
ApplyVisibility; I'll mark this ready once CI is green.Reading this a year from now -- start here
The analysis and working notes behind this change never lived in the repository, so this description is the record. The sections below hold:
Decisions, and why
{layout node StableId}@{guid:N}. Item headers use{node}/item@{item guid}.DetailField.StableIdis unchanged. Automation ids, focus memory and tests depend on its format, so the key is a separateExpansionKeythat falls back toStableId.Slice.InsertObject.Buttonrenders asAccessText, which the detail view'sOfType<TextBlock>()style doesn't match. So header size depended on which style happened to reach it: 14px from the button theme, 11px from the view's text style.DetailSectionHeader.CreateLabel, at 13px (legacy's 10pt) inFwSectionHeaderBrush(legacy's ControlDarkDark).DetailField.ExpansionKeyhas a public setter, likeHelpTopicIdand the other properties the composer sets after construction. That avoids a 29th constructor parameter.Paths not taken
Reversals inside this commit
These are not visible in
git log, because the branch is one commit.Focus and scroll anchoring were built, then removed. After each full rebuild, the clicked header's viewport position was captured and restored, and focus was moved to its replacement once attached (
FocusOnceAttached,ScrollToViewportTop). Once toggles became incremental, the header is never replaced, so both were deleted. The tests they introduced,Toggling_KeepsTheHeaderWhereItWasInTheViewportandToggling_KeepsFocusOnTheHeader, stayed and now pin the incremental behaviour.DetailSectionHeader.IsExpandedwent from settable to read-only and back. It was read-only while every toggle rebuilt the header, and became settable again when the same header started flipping its box in place.The insert auto-expand's own-edit rule was narrowed after PR feedback (a follow-up commit). It first skipped every re-show that followed the view's own edit; that also hid inserts from another window, or from a menu command whose refresh merged into the edit's re-show. It now leaves out only sections collapsed after the edit.
Surprising findings about legacy
SliceTreeNodedraws it only whenShowPlusMinusis true, and nothing sets that. The visible control isSummarySlice.ExpandCollapseButton, a WinFormsButtondrawing the TreeView glyph. Even so, legacy Tab skips it (checked live).expansionfrom the<slice>element, not the layout<part>. So all 85expansion="expanded"attributes in the shipped layouts are ignored, and the default is expanded anyway. The singledoNotExpand(Notebook subrecords) is on a<part>too, so by the code it has no effect. That hasn't been confirmed live."expand" + Base64(Object.Guid), with nothing identifying the section.HeavySummaryheader collapses. The Avalonia composer adds the banner.Deferred, and what would unblock it
collapsedLayout. Legacy swaps a collapsed sense, Etymology, ExtendedNote or subrecord header to aSummaryCollapsedsummary line. Needs a second summary composition per header; proposed as a follow-up ticket.DetailHostControl's same-record check has no test. That WinForms host has no test harness; the logic underneath it has three tests.LexEditExpansion:...@{hvo}settings are orphaned. Harmless for a preview UI, and legacy doesn't prune its keys either.Evidence
CollapsibleHeader_DrawsAPlusMinusBox_ThatFlipsWithTheToggle,HeaderWithNothingUnderIt_DrawsNoBox_AndIgnoresClicksToggling_KeepsFocusOnTheHeader,Toggling_KeepsTheHeaderWhereItWasInTheViewportDoubleClickingAHeader_TogglesItTwice. Headless also passed before the fix, because there the rebuild finished before the second click; confirmed manually in FLEx.AutomationPeer_ReportsAndDrivesTheExpandCollapseStateSectionHeaderAndFieldOptionsText_TakeTheirOwnSizeAndColourDetailExpansionComposeTests,ExpansionState_IsRecordedUnderTheExpansionKey,ComputeVisibility_ReadsStateByExpansionKey_NotStableIdExpansionState_PersistsToLocalSetting_AndIsReadBackInANewSessionImport_SectionExpansion_ComesFromThePartReference(4 cases),Compose_ShippedLayout_StartsEverySectionExpandedRevealObjectsAddedSince_*(3),GetHeadersHidingNewObjects_*(3)CollapsingTheSectionOfTheCurrentVectorItem_LeavesNoCurrentItemHeadless control-level capture. The collapsible "Allomorphs" and the plain "Grammatical Info." header share size and colour; "Field options" is regular weight.
Preflight review details
Code Review Summary
Branch: LT-22689-section-expansion
Base: origin/main (a90754c)
Date: 2026-10-13
Review model: Claude Opus 5.5 (Claude Code), with an independent read-only reviewer subagent
Files changed: 15 (one commit, 1a0e95a, in-review fixes folded in)
Overview
LT-22689 asks for section collapse/expand in the Avalonia detail view to match the
WinForms DataTree:
The branch adds:
DetailSectionHeadercontrol with a token-styled +/- box;header, its focus, its scroll position and a double-click's second click;
The analysis found no critical issues. One important issue was found and fixed during
review: the auto-expand could undo a collapse made in the same gesture as a commit. The
acceptance test passes in manual testing by the author.
Jira: LT-22689 (user-visible; a ticket exists).
Contract/API Changes
New public members in FwAvalonia (internal consumers only, same build):
DetailSectionHeaderandDetailExpanderBox.DetailField.ExpansionKey(settable; falls back toStableId)DetailVisibility.GetHeadersHidingNewObjectsDataTree.RevealObjectsAddedSince(DataTree)FwAvaloniaDensity:SectionHeaderFontSize,HotlinkFontSize,SectionHeaderBrush,and the
TreeBox*membersDetailHostControl.ShowDetailgains an optionalrootHvoparameter. Itsonly caller is
RecordEditView.DataTree.TreeBoxSize,TreeBoxGlyphLength,TreeBoxGlyphThickness,TreeBoxGapFwTreeBoxBorderBrush,FwTreeBoxGlyphBrush,FwSectionHeaderBrushPersisted settings: the
LexEditExpansion:local-setting keys now end in@{guid:N}instead of@{hvo}. Old keys are orphaned and harmless; there is nomigration, which is acceptable for a preview UI.
Findings
Critical - Must address before merge
None.
Important - Should address before merge
commit. Typing in a ghost row, then clicking the section's +/- box, committed on the
press, collapsed on the release, and the commit's re-show then auto-expanded and saved
"expanded". (fixed during review:
RevealObjectsAddedSincetakes the outgoing view and leaves out the sectionsthe user collapsed after that view's own edit. A first version skipped every re-show after an
own edit, which also hid inserts from other windows or a merged menu-command refresh; PR
feedback narrowed it. Tests
RevealObjectsAddedSince_LeavesASectionCollapsedAfterTheOwnEditCollapsedand
RevealObjectsAddedSince_ExpandsASectionCollapsedBeforeTheOwnEdit; each confirmed by mutation.)Minor - Consider
Auto-expand also fires on same-root re-shows that are not inserts (compositionrecovery, Show Hidden Fields, a refresh from another window), and saves the expansion.
(author: accept; rare, at worst reopens a collapsed section once, no data loss; note
in PR)
DetailComposeritem-header comment did not say whichkey is item-based, and the
DetailVisibilityclass summary compared itself to howDataTreeused to work. (fixed during review: both reworded; the rewritten summaryalso drops the word "legacy")
DetailField.ExpansionKeyhas a public setter on an otherwise read-only model.(author: dismiss;
DetailFieldalready has several properties the composer sets afterconstruction, and a 29th constructor parameter isn't worth the churn)
CollapsingTheSectionOfTheCurrentVectorItem_LeavesNoCurrentItem, confirmed bymutation. Already covered: removed editors disposed. Accepted gaps: the unsaved-text
check ignoring removed rows, and the
DetailHostControlsame-root gate, a WinForms hostwith no test harness; the logic beneath it has three tests.)
A collapse through the automation pattern (no focus move) can leave an edit(author:inside the section pending until the next focus change, Enter or Escape.
accept and note in PR as a pre-existing difference in how the Avalonia edit session
saves; not a parity match. The WinForms DataTree writes as you type; the old full
rebuild had the same gap; no data is lost)
Required Validation / Evidence
Run on the rebased branch with all in-review fixes staged:
./build.ps1 -BuildTests -SkipNative -CommentHygiene -TokenHygiene: succeeded; bothhygiene checks clean. Native was skipped because no native files changed on the branch
or upstream since the last full native build.
./test.ps1 -TestProject Src/Common/FwAvalonia/FwAvaloniaTests: 903 passed, 1 skipped../test.ps1 -TestProject Src/xWorks/xWorksTests: 1781 passed, 2 skipped.gitlinton the branch commit: clean.Output/ManualEvidence/LT-22689/Detail-SectionExpander-0{1,2,3}(control-level headless, not FLEx screenshots).
Manual validation:
the other entry sections share one saved state on return; no "Senses" banner, only
per-sense headers.
collapsed (author confirmed).
doNotExpandin the WinForms DataTree (Q6), not yetchecked.
Positive Observations
ApplyVisibilityreplaces four parallel lists with oneShownRow[], so a row's partscan't get out of step. Toggle cost went from about 2.7 s to about 94 ms on a 311-row
headless view.
through a reorder.
explicit, so it no longer depends on which style reaches it.
double-click, font size), and the review fixes were mutation-checked.
Interview Notes
LT-22689.
can expand and then collapse now that the view only rebuilds the related fields." This
is correct in substance. Clarified in discussion: the header itself is never replaced,
so the second click lands on the same control. The author asked for a chunk-by-chunk
explanation of the branch first, and it was given.
per-object state is judged a bug and is not copied.
the ticket comment.
is unchanged.
collapsedLayoutis proposed as a follow-up ticket.In-Review Quality Check
In-review changes, folded into the branch commit:
DataTree.cs,DetailHostControl.cs,DetailVisibility.cs,DetailComposer.cs,DetailViewingParityTests.cs. The build withboth hygiene checks and both full test suites pass with them in place (figures above).
Suggested Review Focus
DataTree.ApplyVisibility: the position bookkeeping for_form.Itemsacross mixedinsert/remove passes, and removing a row before disposing its editor.
RevealObjectsAddedSinceand_collapsedAfterOwnEdit: when an insert reveals a sectionand when it must not.
ExpansionKeyformat and the per-section divergence from the WinForms DataTree'skey.
DetailSectionHeaderautomation peer: ExpandCollapse state and its changenotification.
This change is