Skip to content

Commit 8d21d03

Browse files
Use the bare SliceFactory stem for the Avalonia field-control factory
Reviewer chose the bare legacy stem over the qualified RegionSliceFactory, making the FwAvalonia.Region type a deliberate cross-namespace twin of DetailControls.SliceFactory; no CS0104 alias was needed (no site references the bare stem while importing DetailControls), and the engine-isolation audit's source denylist drops SliceFactory since FwAvalonia.Region now owns the twin while the assembly-level guard still enforces isolation; behavior-preserving. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent ac50ecc commit 8d21d03

4 files changed

Lines changed: 26 additions & 22 deletions

File tree

Src/Common/FwAvalonia/FwAvaloniaTests/EngineIsolationAuditTests.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,17 @@ public class EngineIsolationAuditTests
3434
// Identifiers from the forbidden-symbol list that
3535
// production source must not name (native Views render/editor pipeline, legacy DataTree/Slice
3636
// editor surface, native render engines, browser/PDF engines).
37+
// "SliceFactory" is deliberately absent: FwAvalonia.Region owns its own SliceFactory (a
38+
// cross-namespace twin of the legacy DetailControls.SliceFactory), so the bare stem is now a
39+
// legitimate first-party code symbol. Isolation from the native twin is still enforced by
40+
// ProductionAssembly_ReferencesNoNativeRenderLegacyOrDomainAssemblies (DetailControls assembly).
3741
private static readonly string[] ForbiddenSourceSymbols =
3842
{
3943
"IVwRootBox", "IVwEnv", "IVwGraphics", "VwRootBox", "ManagedVwWindow",
4044
"IRenderEngine", "IRenderEngineFactory", "GraphiteEngineClass", "UniscribeEngineClass",
4145
"FwGrEngine", "GraphiteSegment", "RootSiteControl",
4246
"GeckoWebBrowser", "XWebBrowser", "GeckofxHtmlToPdf", "FieldWorksPdfMaker",
43-
"DataTree", "Slice", "SliceFactory", "XmlView", "BrowseViewer"
47+
"DataTree", "Slice", "XmlView", "BrowseViewer"
4448
};
4549

4650
[Test]
@@ -79,7 +83,7 @@ public void ProductionSource_NamesNoForbiddenNativeRenderSymbols()
7983
foreach (var path in sources)
8084
{
8185
// Comments and string literals are stripped before matching. The DataTree/Slice/
82-
// SliceFactory/BrowseViewer parity symbols are legitimately named throughout this
86+
// BrowseViewer parity symbols are legitimately named throughout this
8387
// codebase's XML-doc comments and diagnostic message text to document exactly which
8488
// legacy behavior a migrated code path mirrors; that is
8589
// documentation, not a dependency. What this audit must catch is the symbol being

Src/Common/FwAvalonia/FwAvaloniaTests/RegionSliceFactoryTests.cs renamed to Src/Common/FwAvalonia/FwAvaloniaTests/SliceFactoryTests.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ namespace FwAvaloniaTests
1515
/// The shared <see cref="RegionFieldKind"/>→control dispatch both the detail-pane region view
1616
/// and the browse in-cell editor route through. These pin that one switch produces the right control
1717
/// per surviving kind (Text / Chooser / ReferenceVector / Literal / Custom / Unsupported), and that
18-
/// the all-nullable <see cref="RegionSliceFactoryContext"/> serves both surfaces — the browse cell
18+
/// the all-nullable <see cref="SliceFactoryContext"/> serves both surfaces — the browse cell
1919
/// passes null menu/link callbacks and suppresses the WS-abbreviation gutter while the detail pane
2020
/// passes the full set — without either surface hand-rolling its own dispatch.
2121
/// </summary>
2222
[TestFixture]
23-
public class RegionSliceFactoryTests
23+
public class SliceFactoryTests
2424
{
2525
private static RegionField Field(RegionFieldKind kind, string selectedOption = null,
2626
System.Func<Control> controlFactory = null)
@@ -33,22 +33,22 @@ private static RegionField Field(RegionFieldKind kind, string selectedOption = n
3333

3434
[AvaloniaTest]
3535
public void TextKind_BuildsMultiWsTextField()
36-
=> Assert.That(RegionSliceFactory.Build(Field(RegionFieldKind.Text), "Auto.Id", null),
36+
=> Assert.That(SliceFactory.Build(Field(RegionFieldKind.Text), "Auto.Id", null),
3737
Is.InstanceOf<FwMultiWsTextField>());
3838

3939
[AvaloniaTest]
4040
public void ChooserKind_BuildsChooserField()
41-
=> Assert.That(RegionSliceFactory.Build(Field(RegionFieldKind.Chooser), "Auto.Id", null),
41+
=> Assert.That(SliceFactory.Build(Field(RegionFieldKind.Chooser), "Auto.Id", null),
4242
Is.InstanceOf<FwChooserField>());
4343

4444
[AvaloniaTest]
4545
public void ReferenceVectorKind_BuildsReferenceVectorField()
46-
=> Assert.That(RegionSliceFactory.Build(Field(RegionFieldKind.ReferenceVector), "Auto.Id", null),
46+
=> Assert.That(SliceFactory.Build(Field(RegionFieldKind.ReferenceVector), "Auto.Id", null),
4747
Is.InstanceOf<FwReferenceVectorField>());
4848

4949
[AvaloniaTest]
5050
public void UnsupportedKind_BuildsUnsupportedTextBlock()
51-
=> Assert.That(RegionSliceFactory.Build(Field(RegionFieldKind.Unsupported), "Auto.Id", null),
51+
=> Assert.That(SliceFactory.Build(Field(RegionFieldKind.Unsupported), "Auto.Id", null),
5252
Is.InstanceOf<TextBlock>());
5353

5454
// Literal: a static text renderer (legacy MessageSlice) — the label/message text is the
@@ -62,21 +62,21 @@ public void LiteralKind_BuildsStaticTextBlock_ShowingTheLabel()
6262
automationId: "Auto.Lit", localizationKey: null, routing: SurfaceRouting.Product,
6363
values: new List<RegionWsValue> { new RegionWsValue("", "Read this carefully:") },
6464
options: null, selectedOptionKey: null, isEditable: false);
65-
var control = RegionSliceFactory.Build(field, "Auto.Lit", null);
65+
var control = SliceFactory.Build(field, "Auto.Lit", null);
6666
Assert.That(control, Is.InstanceOf<TextBlock>());
6767
Assert.That(((TextBlock)control).Text, Is.EqualTo("Read this carefully:"));
6868
}
6969

7070
[AvaloniaTest]
7171
public void CustomKind_NullFactory_DegradesToUnsupportedRow()
72-
=> Assert.That(RegionSliceFactory.Build(Field(RegionFieldKind.Custom, controlFactory: null),
72+
=> Assert.That(SliceFactory.Build(Field(RegionFieldKind.Custom, controlFactory: null),
7373
"Auto.Id", null), Is.InstanceOf<TextBlock>());
7474

7575
[AvaloniaTest]
7676
public void CustomKind_FactoryControl_IsReturned()
7777
{
7878
var marker = new Border();
79-
var control = RegionSliceFactory.Build(
79+
var control = SliceFactory.Build(
8080
Field(RegionFieldKind.Custom, controlFactory: () => marker), "Auto.Id", null);
8181
Assert.That(control, Is.SameAs(marker));
8282
}
@@ -86,9 +86,9 @@ public void BrowseStyleContext_TextField_SuppressesWritingSystemAbbreviation()
8686
{
8787
// The dense browse cell context (null callbacks, no abbreviation gutter) must still build a
8888
// usable text field — the same control the detail pane gets, just configured for the cell.
89-
var browseContext = new RegionSliceFactoryContext(
89+
var browseContext = new SliceFactoryContext(
9090
editContext: null, writingSystemFocused: _ => { }, showWritingSystemAbbreviation: false);
91-
Assert.That(RegionSliceFactory.Build(Field(RegionFieldKind.Text), "Auto.Id", browseContext),
91+
Assert.That(SliceFactory.Build(Field(RegionFieldKind.Text), "Auto.Id", browseContext),
9292
Is.InstanceOf<FwMultiWsTextField>());
9393
}
9494
}

Src/Common/FwAvalonia/Region/RegionDataTree.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,11 +576,11 @@ private sealed class CollapsibleHeader
576576
}
577577

578578
// The field→control dispatch is shared with the browse in-cell editor through
579-
// RegionSliceFactory. The detail pane passes its full callback set (per-WS keyboard, slice
579+
// SliceFactory. The detail pane passes its full callback set (per-WS keyboard, slice
580580
// menu, link, clipboard) and routes reference-vector gesture completion to its validation-gated
581581
// OnSave (the autosave). New RegionFieldKinds are added once, in the factory.
582582
private Control BuildEditor(RegionField field, string automationId)
583-
=> RegionSliceFactory.Build(field, automationId, new RegionSliceFactoryContext(
583+
=> SliceFactory.Build(field, automationId, new SliceFactoryContext(
584584
editContext: _editContext,
585585
writingSystemFocused: _writingSystemFocused,
586586
menuRequested: _menuRequested,

Src/Common/FwAvalonia/Region/RegionSliceFactory.cs renamed to Src/Common/FwAvalonia/Region/SliceFactory.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ namespace SIL.FieldWorks.Common.FwAvalonia.Region
1515
{
1616
/// <summary>
1717
/// The small bundle of (all-nullable) collaborators a <see cref="RegionFieldKind"/> editor needs,
18-
/// passed to <see cref="RegionSliceFactory.Build"/> so the SAME field→control dispatch serves
18+
/// passed to <see cref="SliceFactory.Build"/> so the SAME field→control dispatch serves
1919
/// every hosting surface (today the detail-pane region view, <c>RegionDataTree.BuildEditor</c>;
2020
/// any future in-cell editor passes only the collaborators it has). Every member is optional: a null
2121
/// edit context yields read-only display; a null callback simply disables that affordance.
2222
/// One switch, so new kinds live in one place.
2323
/// </summary>
24-
public sealed class RegionSliceFactoryContext
24+
public sealed class SliceFactoryContext
2525
{
26-
public RegionSliceFactoryContext(
26+
public SliceFactoryContext(
2727
IRegionEditContext editContext = null,
2828
Action<string> writingSystemFocused = null,
2929
Action<RegionMenuRequest> menuRequested = null,
@@ -76,15 +76,15 @@ public RegionSliceFactoryContext(
7676
/// (<c>EditableCellHost.Activate</c>, a 2-kind Chooser/Text subset) both route here rather than
7777
/// hand-rolling their own dispatch, so adding a kind (or changing how a kind is built) happens once.
7878
/// The factory is pure (static) — all per-surface variation arrives through the
79-
/// <see cref="RegionSliceFactoryContext"/>.
79+
/// <see cref="SliceFactoryContext"/>.
8080
/// </summary>
81-
public static class RegionSliceFactory
81+
public static class SliceFactory
8282
{
8383
public static Control Build(RegionField field, string automationId,
84-
RegionSliceFactoryContext context)
84+
SliceFactoryContext context)
8585
{
8686
if (field == null) throw new ArgumentNullException(nameof(field));
87-
context = context ?? new RegionSliceFactoryContext();
87+
context = context ?? new SliceFactoryContext();
8888

8989
switch (field.Kind)
9090
{

0 commit comments

Comments
 (0)