feat(ui): D.2b-B — sub-window mount (inheritor attaches base subtree)
LayoutImporter.Resolve now captures the resolved base element's children and, for a pure-container leaf (no own children + no own media) inheriting from a base WITH content, attaches that subtree. This pulls the nested gmInventoryUI panels' content (paperdoll/backpack/3D-items) in through the existing BaseElement+BaseLayoutId path. ShouldMountBaseChildren is a pure, unit-tested predicate; it's inert for media-bearing inheritors and childless style prototypes, so vitals/chat/toolbar are unaffected (existing importer tests still green). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4fd4b09f3f
commit
85098f535d
2 changed files with 42 additions and 2 deletions
|
|
@ -191,6 +191,14 @@ public static class LayoutImporter
|
||||||
|
|
||||||
// ── Inheritance resolution ────────────────────────────────────────────────
|
// ── Inheritance resolution ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>True when a pure-container leaf should inherit its base's subtree (the
|
||||||
|
/// gmInventoryUI sub-window mount): the derived element has no own children, no own
|
||||||
|
/// state media, and the base resolved to ≥1 child. Inert for media-bearing inheritors
|
||||||
|
/// (close button / title), elements with their own children, and childless style
|
||||||
|
/// prototypes (vitals/chat/toolbar text — the base prototype has no children).</summary>
|
||||||
|
internal static bool ShouldMountBaseChildren(int derivedChildCount, int derivedMediaCount, int baseChildCount)
|
||||||
|
=> derivedChildCount == 0 && derivedMediaCount == 0 && baseChildCount > 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts an <see cref="ElementDesc"/> to a resolved <see cref="ElementInfo"/>:
|
/// Converts an <see cref="ElementDesc"/> to a resolved <see cref="ElementInfo"/>:
|
||||||
/// reads own fields + media, applies the BaseElement / BaseLayoutId chain
|
/// reads own fields + media, applies the BaseElement / BaseLayoutId chain
|
||||||
|
|
@ -204,6 +212,7 @@ public static class LayoutImporter
|
||||||
// Read this element's own fields + media (no inheritance, no children yet).
|
// Read this element's own fields + media (no inheritance, no children yet).
|
||||||
var self = ToInfo(d);
|
var self = ToInfo(d);
|
||||||
var result = self;
|
var result = self;
|
||||||
|
List<ElementInfo>? baseChildren = null;
|
||||||
|
|
||||||
// Apply BaseElement / BaseLayoutId inheritance if present.
|
// Apply BaseElement / BaseLayoutId inheritance if present.
|
||||||
if (d.BaseElement != 0 && d.BaseLayoutId != 0
|
if (d.BaseElement != 0 && d.BaseLayoutId != 0
|
||||||
|
|
@ -215,9 +224,9 @@ public static class LayoutImporter
|
||||||
{
|
{
|
||||||
// Recurse the base chain (already guarded by the HashSet add above).
|
// Recurse the base chain (already guarded by the HashSet add above).
|
||||||
var baseInfo = Resolve(dats, baseDesc, baseChain);
|
var baseInfo = Resolve(dats, baseDesc, baseChain);
|
||||||
// Derived fields override the base; result.Children is still empty here
|
// Derived fields override the base; children are attached below.
|
||||||
// — children are attached below from the DERIVED element's own tree.
|
|
||||||
result = ElementReader.Merge(baseInfo, self);
|
result = ElementReader.Merge(baseInfo, self);
|
||||||
|
baseChildren = baseInfo.Children; // capture for the sub-window mount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -226,6 +235,15 @@ public static class LayoutImporter
|
||||||
foreach (var kv in d.Children)
|
foreach (var kv in d.Children)
|
||||||
result.Children.Add(Resolve(dats, kv.Value, new HashSet<(uint, uint)>()));
|
result.Children.Add(Resolve(dats, kv.Value, new HashSet<(uint, uint)>()));
|
||||||
|
|
||||||
|
// Sub-window mount: a pure-container leaf (no own children, no own media) that inherits
|
||||||
|
// from a base WITH content attaches the base's subtree. Targets the gmInventoryUI panels
|
||||||
|
// (0x100001CD/CE/CF — Type-0, media-less, childless, BaseLayoutId → a gm*UI window);
|
||||||
|
// inert for media-bearing inheritors (close button/title) and childless style prototypes
|
||||||
|
// (vitals/chat/toolbar). See ShouldMountBaseChildren + the B-Grid spec.
|
||||||
|
if (baseChildren is not null
|
||||||
|
&& ShouldMountBaseChildren(d.Children.Count, self.StateMedia.Count, baseChildren.Count))
|
||||||
|
result.Children.AddRange(baseChildren);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
22
tests/AcDream.App.Tests/UI/LayoutImporterMountTests.cs
Normal file
22
tests/AcDream.App.Tests/UI/LayoutImporterMountTests.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
using AcDream.App.UI.Layout;
|
||||||
|
|
||||||
|
namespace AcDream.App.Tests.UI;
|
||||||
|
|
||||||
|
public class LayoutImporterMountTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Mounts_ChildlessMediaLessInheritor_WithContentfulBase()
|
||||||
|
=> Assert.True(LayoutImporter.ShouldMountBaseChildren(derivedChildCount: 0, derivedMediaCount: 0, baseChildCount: 5));
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DoesNotMount_WhenDerivedHasOwnMedia() // close button / title
|
||||||
|
=> Assert.False(LayoutImporter.ShouldMountBaseChildren(0, 1, 5));
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DoesNotMount_WhenDerivedHasOwnChildren()
|
||||||
|
=> Assert.False(LayoutImporter.ShouldMountBaseChildren(2, 0, 5));
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DoesNotMount_WhenBaseIsChildless() // vitals/chat/toolbar style prototypes
|
||||||
|
=> Assert.False(LayoutImporter.ShouldMountBaseChildren(0, 0, 0));
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue