LT-22685: Add sense pictures to the legacy Lexicon Edit pane (split from #964) - #1012
LT-22685: Add sense pictures to the legacy Lexicon Edit pane (split from #964)#1012johnml1135 wants to merge 1 commit into
Conversation
This comment has been minimized.
This comment has been minimized.
NUnit Tests 1 files ±0 1 suites ±0 10m 54s ⏱️ + 3m 35s For more details on these failures, see this check. Results for commit bde4d5d. ± Comparison against base commit db459c6. ♻️ This comment has been updated with latest results. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1012 +/- ##
=======================================
Coverage 38.43% 38.43%
=======================================
Files 1507 1507
Lines 350657 350657
Branches 40307 40307
=======================================
+ Hits 134773 134789 +16
+ Misses 186651 186638 -13
+ Partials 29233 29230 -3 🚀 New features to boost your workflow:
|
jasonleenaylor
left a comment
There was a problem hiding this comment.
The diagnosis is right and I verified it: LexSense.fwlayout:46 really does carry
<part ref="Pictures" param="Normal"/>, git log -S "LexSense-Detail-Pictures" returns
nothing, and CmdInsertPicture is live on mnuDataTree-Sense
(DataTreeInclude.xml:448) — so users can create sense pictures today and watch them
vanish. Rejecting editor="picture" was also correct: SliceFactory.cs:210 does a hard
(ICmPicture)obj cast and would receive the owning LexSense.
Two things you got right that are worth recording, because the next reader will
otherwise re-litigate them:
- No
ghostis the correct call.DataTree.MakeGhostSlice(DataTree.cs:2800)
always constructs aGhostStringSlice, whose contract is strict:ghostWsis
mandatory (GhostStringSlice.cs:116) andMakeRealObjectthrows "ghost property must
store strings!" unless the target field is String/MultiString/MultiUnicode
(:305-318). A ghost onPictureswould therefore have to create aCmPicturefrom
typed text — yielding a picture record with a caption and noPictureFileRA. Pictures
needPicturePropertiesDialogto choose a file first (DTMenuHandler.cs:224-236),
which a ghost cannot express. Worth one clause in the comment so nobody "fixes" this
later. - Senses with no pictures stay clean.
DataTree.cs:2912only adds a slice when
cobj > 0or aghostis present, so there is no empty-row clutter.
What needs to change before this merges.
1. A DataTree test is a condition of this PR, not a follow-up.
This is the part I do not want to trade away. ref="Pictures" has exactly one referent
and no part has ever resolved it, so this three-line change is the switch that first
energises five CmPicture-Detail-* parts, PictureSlice, CmFile-Detail-FileName, and
a defaultVectorReference on PublishIn — roughly 35 lines of configuration that have
never executed in production, for every user with sense pictures, ungated. CI reporting
4302 tests identical to base confirms the suite is blind to it, and "verified only for
XML well-formedness" is not evidence for a change of that reach. You already crashed the
legacy DataTree in this exact area, which is the argument for the test rather than
against it.
A test asserting the five slices materialise for a sense with one picture is the
minimum. This is reachable through the DataTree slice-creation path without
constructing a Form, so the no-WinForms-dialog-tests rule is not in the way.
2. Drop layout="Normal".
DataTree.cs:2918 reads
layoutOverride = XmlUtils.GetOptionalAttributeValue(node, "layout", layoutName), where
layoutName is the caller's param. The layout node already says param="Normal", so
the attribute is a no-op today and a silent override tomorrow — any future
<part ref="Pictures" param="Something"/> would be ignored. The live exemplar,
LexSense-Detail-Examples (LexSenseParts.xml:620-623), omits it and lets param flow
through.
3. Fix or drop menu="mnuDataTree-Picture".
Two problems. It is the wrong menu: DataTreeInclude.xml:403-410 shows
mnuDataTree-Picture holds Properties / MoveUp / MoveDown / Delete and no insert
command, so the body's "insert/delete menu" is inaccurate. The established idiom for a
seq node is the plural sequence-level menu — mnuDataTree-Examples
(DataTreeInclude.xml:468) and mnuDataTree-ExtendedNotes (:500) both hold the insert
command — and no mnuDataTree-Pictures exists.
It also probably never fires: DTMenuHandler.MakeSliceContextMenu
(DTMenuHandler.cs:1708-1716) reads the menu id off slice.CallerNode /
slice.ConfigurationNode, and with no ghost this seq node becomes neither. Each
CmPicture-Detail-* slice already carries its own menu="mnuDataTree-Picture"
(LexSenseParts.xml:887, 903, 906, 909). Either add a mnuDataTree-Pictures containing
CmdInsertPicture, or drop the attribute — but do not leave a dead attribute that
implies an insert affordance that is not there.
4. Rewrite the comment.
Six lines and roughly 570 characters, against a target of 3-4 sentences for a doc
comment and 200 characters for an implementation comment. Every line exceeds the
98-column limit from .editorconfig (102, 105, 107, 105, 97, 107), and line 3 contains a
literal em dash where the standard is ASCII. .xml sits outside
comment-hygiene.ps1's globs so nothing caught it mechanically, but the convention still
applies.
Content problems, in order of consequence:
- It cites a dead exemplar.
LexSense-Detail-ExampleList(:60-62) exists but nothing
in the repo referencesref="ExampleList". The part that actually resolves is
LexSense-Detail-Examples(:620), and it uses a different form. - It misquotes the layout node it explains: the node is
<part ref="Pictures" param="Normal"/>, not<part ref="Pictures"/>. The dropped
attribute is exactly the one that makes item 2 above a finding. The same misquote is in
the PR body and the commit message. - "the malformed form this replaces" describes a version that never existed in the
repository, and "silently omitted there for years because no LexSense-Detail-Pictures
part existed" narrates what was missing rather than what the part does. - Two of the four sentences narrate the cast mechanics of the rejected alternative. That
warning is genuinely valuable — keep it as one sentence ("Do not use
editor="picture"here; the slice factory casts the owning object toICmPicture.")
rather than a paragraph.
Roughly: what the part renders, the editor="picture" trap, and the ghost note from
above.
5. The body understates the blast radius, and the Avalonia gap needs a Jira record.
The body calls this "an ungated, legacy-visible behavior change." It is not legacy-only.
DetailComposer.cs:950-954 routes DetailEditorCategory.Picture to WalkUnsupported,
so the moment this part resolves, every sense with a picture grows a labeled
"Unsupported" row in the Avalonia UI. Legacy users get an image; new-UI users get a
worklist row they did not have before.
Fixing DetailComposer is out of scope here — that is the migration work this split was
designed to escape. But please correct the body to say both modes, and record the picture
gap as a known divergence in the Avalonia conversion's Jira issue so it is tracked before
it ships rather than discovered by a user.
6. Add LabelAbbreviations entries for the newly-visible labels.
strings-en.xml group LabelAbbreviations (line 367) carries an abbreviation for every
routinely-visible sense field ("Scientific Name" txt="sc", line 427). "Picture",
"Caption", "File", and "Publish Picture In" appear nowhere in strings-en.xml, so in
abbreviated-label mode these rows fall back to raw English. No hardcoded strings were
added by this PR, so nothing is violated — the catalog is just incomplete for the surface
this exposes.
7. Commit title is 79 characters against gitlint's 72.
Already flagged by the Actions bot, still unfixed.
A question, not a change request: why is the thumbnail the part that is turned off?
The CmPicture Normal layout enables CmPicture-Detail-Picture (full size) and sets
CmPicture-Detail-Thumbnail to visibility="never" (LexSense.fwlayout:82-88).
PictureSlice.InstallPicture (PictureSlice.cs:59-73) does File.ReadAllBytes into a
MemoryStream into Image.FromStream with no size cap, per picture, per sense render on
the UI thread. Nobody has measured that on a picture-heavy lexicon because this code has
never run in this pane. Given a Thumbnail part was deliberately provided, flipping which
one is visible may be the cheaper default — but I would rather hear your reasoning than
assume mine.
|
Before I answer the seven points: I wrote the What the test shows I built the test against the production Parts/Layouts (
Why
<part id="LexSense-Detail-Pictures" type="Detail"><seq field="Pictures" /></part>which is functionally what this PR hand-writes. It is produced at build time by Why we both missed it
What this does to your seven points You were right on all seven as statements about the diff -- I checked each against the Point 1 is the one that matters: you made the test a condition of merge and the test is What is actually left One real behaviour change, which is your closing question. You asked why the thumbnail is So I would rather not land this as written. Before I restructure it, how do you want to |
The legacy LexSense layout has carried <part ref="Pictures"/> for years, but no LexSense-Detail-Pictures part existed to resolve it, so DataTree silently omitted the slice and sense pictures never appeared in the Lexicon Edit pane. This adds the missing part (the standard owned-sequence idiom, like LexSense-Detail-ExampleList), making the Picture/Caption/File/Publish-In slices and the mnuDataTree-Picture menu appear for any sense with pictures. Split out of the Phase-1 Avalonia spine (#964) for independent assessment: it is a plausible long-standing legacy bug-fix, but the parts inventory is shared across UIMode, so it is an ungated, legacy-visible behavior change and the team should decide on it separately. Untested here beyond XML well-formedness; a legacy DataTree test + a screenshot are the natural follow-up if accepted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4531bec to
bde4d5d
Compare
|
Closing: verified live in the running app that sense pictures already render in the legacy Lexicon Edit pane on The premise this PR was opened on was false — The real defect this investigation turned up is unrelated to sense pictures: the Avalonia |
Sense pictures in the Lexicon Edit detail pane
Jira: LT-22685 · split out of the Phase-1 Avalonia spine (#964)
One
<part>element — three lines of XML plus a six-line comment, in oneconfiguration file. No C#, no tests, no build changes.
LexSense.fwlayout:46hascarried
<part ref="Pictures" param="Normal"/>for years; the detail pane isdata-driven, so naming a
LexSense-Detail-Picturespart is the entire switch.Every piece behind it —
PictureSlice, the fiveCmPicture-Detail-*parts,CmFile-Detail-FileName,mnuDataTree-Picture— already ships.The premise this PR was opened on is wrong, and that changes what it is. The
review asked for a
DataTreetest as a merge condition. That test was written,and it reports the legacy pane already renders Picture / Caption / File /
Publish Picture In on pristine
main.DistFiles/Parts/GeneratedParts.xml:1494has carried an equivalent part all along, emitted at build time and gitignored
(
.gitignore:131) — which is whygit log -S "LexSense-Detail-Pictures"foundnothing and read as proof of absence.
So this is not a legacy fix. It is an Avalonia-visible change, because the two
UIs read different part inventories.
Where to look:
the hand-written one merely overrides the generated default with two attributes.
DetailComposer.cs:3226loads onlyConfiguration\Parts— never the generated inventory — so this is the firstLexSense-Detail-Picturesit can see, andDetailComposer.cs:958routesPicturetoWalkUnsupported. Senses with pictures gain an "Unsupported" row.ShippedLayouts_UnresolvedParts_AreExactlyTheLegacyUnresolvableSetfails:
LexSense-Picturesleaves the baseline (63 → 62). That test's own commentencodes the same false premise.
menuandlayoutattributes are both inert. Review points 2 and 3.Not here: no
DataTreetest committed, noLabelAbbreviationsentries, noDetailComposerpicture support, no Jira record of the Avalonia gap.Status: draft, CHANGES_REQUESTED, CI red. The open question is not "is the
diff right" but "does this PR still have a purpose" — see the last accordion.
Reading this a year from now — start here
This PR was split out of #964 on the belief that sense pictures had never
appeared in the Lexicon Edit pane. That belief was false, and the review's own
merge condition — write the
DataTreetest — is what disproved it. The bodyabove is the corrected account; the accordions hold the mechanism, the evidence,
and the review history that is otherwise only recoverable by reading a long
thread.
The Aug 27 comment on this PR is the original write-up of the falsification. Its
substance is folded in here so the description is the single record.
Why three lines of XML is the whole change
The FLEx detail pane is configuration, not code. A
.fwlayoutnames fields aspart references;
DataTreeresolves<part ref="X"/>against the partsinventory by composing
{ClassName}-Detail-{X}, and silently omits the slicewhen no such part id exists (
DataTree.cs:2445, "Just omit the missingpart"). There is no error, no log line, no test failure — a missing part is
indistinguishable from a field nobody configured.
So making a field appear is not a code change. It is one part id. The rendering
machinery is generic and already present:
LexSense.fwlayout:46PictureSlice.csLexSenseParts.xml:895-920(CmPicture-Detail-*)CmFile-Detail-FileName,LexSenseParts.xml:887mnuDataTree-Picture,Lexicon/DataTreeInclude.xml:403CmdInsertPictureonmnuDataTree-Sense,Lexicon/DataTreeInclude.xml:448Nine lines added: three of XML, six of comment.
git diff --stat main...HEADis one file,
+9 -0.The surprising finding: a gitignored half of the inventory
Build/SetupInclude.targets(GeneratePartsAndLayoutFiles) runsPartGenerate.xsltoverMasterLCModel.xmlat build time and emits a defaultDetail part for every field in the model. The output lands in
DistFiles/Parts/GeneratedParts.xml, which.gitignore:131excludes.Line 1494 of that file:
The
Inventoryconstructor unconditionally prependsFwDirectoryFinder.GetCodeSubDirectory("Parts")to its search paths(
Inventory.cs:174), and the parts inventory's file pattern is*Parts.xml(
LayoutCache.cs:128-132) — whichGeneratedParts.xmlmatches.LoadElementswalks the paths in order and
InsertNodeInDocreplaces on a key collision(
Inventory.cs:1400-1425), so a hand-written part inConfiguration/Partsoverrides the generated default rather than colliding with it.
The lesson worth keeping:
git log -Scannot see the generated half of theparts inventory. For any future configuration-resolution question, check the
built
DistFiles/Parts/output, not just the tracked XML. The absence of historywas evidence of nothing.
Why legacy and Avalonia disagree — the two inventories
GeneratedParts.xml?DataTreeDistFiles/Parts(viaInventory.cs:174) plus…/Configuration/Parts(LayoutCache.cs:103)DetailComposer…\Language Explorer\Configuration\Partsonly (DetailComposer.cs:3226)LayoutImportCoveragetestsLayoutImportCoverageTests.cs:516)That asymmetry is the whole story of this PR. Because the Avalonia composer never
loads the generated defaults, it has been resolving a strictly smaller inventory
than production legacy — and its unresolved-part baseline has been measuring that
smaller set while describing it as "what legacy also omits".
Adding the part to
Configuration/Partsis therefore the first timeLexSense-Detail-Picturesbecomes visible to Avalonia.DetailComposer.cs:958sends
DetailEditorCategory.PicturetoWalkUnsupported, so the user-visibleeffect lands entirely on the new UI: legacy users see no change, new-UI users
gain a labeled "Unsupported" worklist row on every sense with a picture.
Whether the Avalonia composer should load the generated inventory is a larger
question than this branch, and is the thing most worth deciding before it merges.
Review points, and where each one stands
From the
CHANGES_REQUESTEDreview of 2026-08-24. Each was checked against thecurrent tree; the line numbers in the review were accurate.
DataTreetest is a merge conditionlayout="Normal"(a no-op today, a silent override tomorrow,DataTree.cs:2918)menu="mnuDataTree-Picture"(wrong menu —Lexicon/DataTreeInclude.xml:403holds Properties/MoveUp/MoveDown/Delete and no insert command; and aseqnode is neitherCallerNodenorConfigurationNode, so it likely never fires)ref="ExampleList"exemplar, and a misquote of the layout node)LabelAbbreviationsfor Picture / Caption / File / Publish Picture Inmainbug, not a bug of this PR: those rows already renderCheck commit messagespassesPictureSlice.InstallPicturedoes an uncappedFile.ReadAllBytes→Image.FromStreamper picture on the UI thread (PictureSlice.cs:59-73)mainquestion, since that code already runsTwo things the review credited that still hold, recorded so they are not
re-litigated: rejecting
editor="picture"was right (SliceFactory.cs:210hard-caststo
ICmPictureand would receive the owningLexSense), and omittingghostwasright (
MakeGhostSlicealways builds aGhostStringSlice, which cannot express aCmPicturethat needsPicturePropertiesDialogto pick a file first).Evidence
The legacy no-op. A
DataTreetest built against the productionFwDirectoryFinder.FlexFolder+Configuration/Parts(not theDetailControlsTestsfixtures, since what is under test is whether the shippedXML resolves) creates a
LexSensewith oneCmPictureand callsShowObject(sense, "Normal", …). On pristineorigin/main, with none of thisbranch applied:
Applying this branch leaves that list unchanged.
The Avalonia effect.
FwAvaloniaTests.LayoutImportCoverageTests.ShippedLayouts_UnresolvedParts_AreExactlyTheLegacyUnresolvableSetfails on this branch (
LayoutImportCoverageTests.cs:678):The baseline holds
LexSense-Picturesas unresolvable; this part resolves it.Merging as-is requires deleting that entry, changing the occurrence ceiling from
63 to 62, and rewriting the comment above it, which currently reads "LexSense
Pictureshas no LexSense-Detail-Pictures part, so legacy DataTree (and thisimporter) omit sense pictures" — false for legacy on
main.CI state:
Build Debug and run testsandNUnit Testsfail on that one test.Everything else passes: commit messages, comment hygiene, whitespace, lychee,
stray-docs, codecov.
What is left to decide
Given that legacy already works, three routes:
here and in LT-22685.
DetailComposerload thegenerated parts, which is the actual defect this investigation found. That is
larger than this branch and belongs with the migration work.
Configuration/Partsis more legible than a gitignored generated default, andthat
menu=/layout=can be corrected per review points 2 and 3. This stillrequires the coverage-test baseline update and accepts the "Unsupported" row.
Route 1 or 2 seems right; route 3 needs a reason to prefer explicitness that
outweighs handing new-UI users a worklist row. Reviewer's call.
Two findings deserve their own tickets regardless of what happens to this branch,
because both are true on
maintoday: the missingLabelAbbreviationsentriesfor four already-visible sense-picture labels, and the uncapped full-size image
decode on the UI thread in
PictureSlice.InstallPicture.This change is