diff --git a/src/AcDream.App/UI/MarkupDocument.cs b/src/AcDream.App/UI/MarkupDocument.cs
index 3be8a555..5c7baaa3 100644
--- a/src/AcDream.App/UI/MarkupDocument.cs
+++ b/src/AcDream.App/UI/MarkupDocument.cs
@@ -58,14 +58,17 @@ public static class MarkupDocument
var max = BindUint((string?)el.Attribute("max"), binding);
panel.AddChild(new UiMeter
{
- Left = F(el, "x"),
- Top = F(el, "y"),
- Width = F(el, "w"),
- Height = F(el, "h"),
- BarColor = Color((string?)el.Attribute("color")),
- Fill = BindFloat((string?)el.Attribute("fill"), binding),
- Label = () => (cur(), max()) is (uint c, uint m) ? $"{c}/{m}" : null,
- Anchors = Anchor((string?)el.Attribute("anchor")),
+ Left = F(el, "x"),
+ Top = F(el, "y"),
+ Width = F(el, "w"),
+ Height = F(el, "h"),
+ BarColor = Color((string?)el.Attribute("color")),
+ Fill = BindFloat((string?)el.Attribute("fill"), binding),
+ Label = () => (cur(), max()) is (uint c, uint m) ? $"{c}/{m}" : null,
+ Anchors = Anchor((string?)el.Attribute("anchor")),
+ SpriteResolve = resolve,
+ BackSpriteId = Hex((string?)el.Attribute("back")),
+ FrontSpriteId = Hex((string?)el.Attribute("front")),
});
break;
// future element kinds (label, button, image) added here
@@ -125,6 +128,15 @@ public static class MarkupDocument
return binding.GetType().GetProperty(expr[1..^1]);
}
+ private static uint Hex(string? s)
+ {
+ if (string.IsNullOrWhiteSpace(s)) return 0;
+ var t = s.Trim();
+ if (t.StartsWith("0x", System.StringComparison.OrdinalIgnoreCase)) t = t[2..];
+ return uint.TryParse(t, System.Globalization.NumberStyles.HexNumber,
+ System.Globalization.CultureInfo.InvariantCulture, out var v) ? v : 0u;
+ }
+
private static AnchorEdges Anchor(string? csv)
{
if (string.IsNullOrWhiteSpace(csv)) return AnchorEdges.Left | AnchorEdges.Top;
diff --git a/src/AcDream.App/UI/UiMeter.cs b/src/AcDream.App/UI/UiMeter.cs
index ef2883c2..48911c14 100644
--- a/src/AcDream.App/UI/UiMeter.cs
+++ b/src/AcDream.App/UI/UiMeter.cs
@@ -24,6 +24,14 @@ public sealed class UiMeter : UiElement
public Vector4 BgColor { get; set; } = new(0f, 0f, 0f, 0.5f);
public Vector4 LabelColor { get; set; } = new(1f, 1f, 1f, 1f);
+ /// Resolver from a RenderSurface DataId to (GL handle, w, h). When set
+ /// with Back/Front sprite ids, the bar draws the retail sprites instead of solid color.
+ public Func? SpriteResolve { get; set; }
+ /// Empty-track sprite (drawn full width). 0 = none.
+ public uint BackSpriteId { get; set; }
+ /// Colored-fill sprite (drawn cropped to the fill fraction). 0 = none.
+ public uint FrontSpriteId { get; set; }
+
public UiMeter() { ClickThrough = true; }
/// Clamp to [0,1] and return the fill rect
@@ -38,13 +46,32 @@ public sealed class UiMeter : UiElement
protected override void OnDraw(UiRenderContext ctx)
{
- ctx.DrawRect(0, 0, Width, Height, BgColor);
-
float? pct = Fill();
- if (pct is float p)
+ float p = pct is float pf ? (pf < 0f ? 0f : pf > 1f ? 1f : pf) : 0f;
+
+ if (SpriteResolve is { } resolve && (BackSpriteId != 0 || FrontSpriteId != 0))
{
- var (fx, fy, fw, fh) = ComputeFillRect(p, Width, Height);
- if (fw > 0f) ctx.DrawRect(fx, fy, fw, fh, BarColor);
+ // Retail bar: empty track full width, colored fill cropped to p (left→right).
+ if (BackSpriteId != 0)
+ {
+ var (bt, _, _) = resolve(BackSpriteId);
+ if (bt != 0) ctx.DrawSprite(bt, 0, 0, Width, Height, 0, 0, 1, 1, Vector4.One);
+ }
+ if (FrontSpriteId != 0 && pct is not null && p > 0f)
+ {
+ var (ft, _, _) = resolve(FrontSpriteId);
+ if (ft != 0) ctx.DrawSprite(ft, 0, 0, Width * p, Height, 0, 0, p, 1, Vector4.One);
+ }
+ }
+ else
+ {
+ // Placeholder solid-color fallback.
+ ctx.DrawRect(0, 0, Width, Height, BgColor);
+ if (pct is not null && p > 0f)
+ {
+ var (fx, fy, fw, fh) = ComputeFillRect(p, Width, Height);
+ if (fw > 0f) ctx.DrawRect(fx, fy, fw, fh, BarColor);
+ }
}
string? label = Label();
diff --git a/src/AcDream.App/UI/assets/vitals.xml b/src/AcDream.App/UI/assets/vitals.xml
index 08e065d6..2f7292e5 100644
--- a/src/AcDream.App/UI/assets/vitals.xml
+++ b/src/AcDream.App/UI/assets/vitals.xml
@@ -1,5 +1,5 @@
-
-
-
+
+
+
diff --git a/tests/AcDream.App.Tests/UI/MarkupDocumentTests.cs b/tests/AcDream.App.Tests/UI/MarkupDocumentTests.cs
index 5e76ab95..ed717bbd 100644
--- a/tests/AcDream.App.Tests/UI/MarkupDocumentTests.cs
+++ b/tests/AcDream.App.Tests/UI/MarkupDocumentTests.cs
@@ -56,4 +56,17 @@ public class MarkupDocumentTests
Assert.True(panel.ResizeX);
Assert.False(panel.ResizeY);
}
+
+ [Fact]
+ public void Build_ParsesBackFrontSpriteIds()
+ {
+ const string xml = "" +
+ "" +
+ "";
+ var panel = MarkupDocument.Build(xml, new FakeBinding(), _ => ((uint)7, 32, 32));
+ var meter = Assert.IsType(panel.Children[1]);
+ Assert.Equal(0x06005F3Cu, meter.BackSpriteId);
+ Assert.Equal(0x06005F3Du, meter.FrontSpriteId);
+ Assert.NotNull(meter.SpriteResolve);
+ }
}