diff --git a/src/Clay/Clay.cs b/src/Clay/Clay.cs
index 2144136..4805f85 100644
--- a/src/Clay/Clay.cs
+++ b/src/Clay/Clay.cs
@@ -225,6 +225,22 @@ public static void SetDebugModeEnabled(bool enabled)
_context.DebugModeEnabled = enabled;
}
+ ///
+ /// Enables or disables the pointer-highlight debug overlay: the topmost
+ /// element under the pointer is outlined lime, occluded elements beneath
+ /// it at the same point are outlined red.
+ ///
+ public static void SetPointerHighlightEnabled(bool enabled)
+ {
+ if (_context != null)
+ _context.PointerHighlightEnabled = enabled;
+ }
+
+ ///
+ /// Returns true if the pointer-highlight debug overlay is enabled.
+ ///
+ public static bool IsPointerHighlightEnabled() => _context?.PointerHighlightEnabled ?? false;
+
///
/// Enables or disables culling.
///
diff --git a/src/Clay/Core/ClayContext.cs b/src/Clay/Core/ClayContext.cs
index 1b45bb6..0ff88af 100644
--- a/src/Clay/Core/ClayContext.cs
+++ b/src/Clay/Core/ClayContext.cs
@@ -67,6 +67,9 @@ public class ClayContext : IDisposable
public uint Generation;
public float DeltaTime;
public bool DebugModeEnabled;
+ // Debug overlay: outline the elements under the pointer (topmost lime,
+ // occluded-beneath red). Emitted by GeneratePointerHighlightCommands.
+ public bool PointerHighlightEnabled;
public bool CullingDisabled;
// Text measurement
@@ -236,9 +239,69 @@ public ReadOnlySpan EndLayout()
CloseElement();
ComputeLayout();
GenerateRenderCommands();
+ GeneratePointerHighlightCommands();
return RenderCommands.AsReadOnlySpan();
}
+ // Debug overlay: frame the elements under the pointer. The topmost hit
+ // (PointerOverIds[0]) is outlined lime; every element occluded beneath it
+ // at the same point is outlined red. Appended after the normal commands so
+ // the outlines paint on top. Pure bbox/z hit — the host's per-pixel
+ // CustomHitTest is NOT applied here, so this shows Clay's own stacking pick.
+ private void GeneratePointerHighlightCommands()
+ {
+ if (!PointerHighlightEnabled)
+ return;
+
+ // Gray frame around every element laid out this frame (Generation + 1
+ // is this frame's stamp; the hash map keeps recycled entries from prior
+ // frames). Emitted first so the lime/red pointer outlines paint over it.
+ var gray = new Color(150, 150, 150, 110);
+ for (int i = 0; i < LayoutElementsHashMapInternal.Length; i++)
+ {
+ ref var item = ref LayoutElementsHashMapInternal[i];
+ if (item.Generation != Generation + 1)
+ continue;
+ RenderCommands.Add(new RenderCommand
+ {
+ BoundingBox = item.BoundingBox,
+ CommandType = RenderCommandType.Border,
+ Id = item.ElementId.Id,
+ ZIndex = short.MaxValue,
+ Border = new BorderRenderData
+ {
+ Color = gray,
+ Width = BorderWidth.All(1),
+ CornerRadius = default,
+ }
+ });
+ }
+
+ // Rebuild against the freshly computed layout (SetPointerState earlier
+ // in the frame hit-tested the previous frame's tree).
+ RebuildPointerOverIds(PointerInfo.Position);
+
+ var lime = new Color(0, 255, 0, 255);
+ var red = new Color(255, 0, 0, 255);
+
+ for (int i = 0; i < PointerOverIds.Length; i++)
+ {
+ RenderCommands.Add(new RenderCommand
+ {
+ BoundingBox = _pointerOverBoxes[i],
+ CommandType = RenderCommandType.Border,
+ Id = PointerOverIds[i].Id,
+ ZIndex = short.MaxValue,
+ Border = new BorderRenderData
+ {
+ Color = i == 0 ? lime : red,
+ Width = BorderWidth.All(2),
+ CornerRadius = default,
+ }
+ });
+ }
+ }
+
///
/// Opens a new element.
///