#nullable enable
using ImGuiNET;
using T3.Core.DataTypes.Vector;
using T3.Editor.Gui.Styling;
namespace T3.Editor.Gui.Windows.TestRunner;
///
/// Shared renderer for a guided-test-set row, used by the runner's pick list and the welcome window.
/// Draws the background, completion checkmark, title, scope, step count, warning icon, tag pills, and
/// the per-step status bar (with a per-tick hover tooltip). Selection and any extra tooltip (e.g. the
/// runner's intro markdown) are left to the caller, via the returned .
///
internal static class TestSetRow
{
/// The row was clicked this frame.
/// The row is hovered.
/// A status tick is hovered (its tooltip is already shown — suppress others).
internal readonly record struct Result(bool Clicked, bool Hovered, bool TickHovered);
internal static Result Draw(TestSet set, bool selected, float height)
{
var scale = T3Ui.UiScaleFactor;
var dl = ImGui.GetWindowDrawList();
ImGui.PushID(set.Id);
var rowSize = new Vector2(ImGui.GetContentRegionAvail().X, height * scale);
var clicked = ImGui.InvisibleButton("##row", rowSize);
var hovered = ImGui.IsItemHovered();
var min = ImGui.GetItemRectMin();
var max = ImGui.GetItemRectMax();
var bg = selected
? Color.Mix(UiColors.StatusActivated, UiColors.BackgroundFull, 0.2f).Fade(hovered ? 1f : 0.9f)
: hovered
? UiColors.ForegroundFull.Fade(0.1f)
: UiColors.ForegroundFull.Fade(0.04f);
dl.AddRectFilled(min, max, bg, 4 * scale);
var textColor = selected ? UiColors.ForegroundFull : UiColors.Text;
var muted = selected ? UiColors.ForegroundFull.Fade(0.75f) : UiColors.TextMuted;
var padX = 12 * scale;
var padY = 6 * scale;
// Last-run result drives the left-gutter checkmark and the per-step status bar.
TestRunResults.SetResult? runResult = null;
if (TestRunResults.TryGet(set.Id, out var found))
runResult = found;
var statusGutter = 18 * scale; // always reserved so titles align whether or not a checkmark is shown
if (runResult != null)
{
var checkColor = runResult.HadIssues ? UiColors.StatusAttention : UiColors.StatusOkay;
Icons.DrawIconAtScreenPosition(Icon.Checkmark, new Vector2(min.X + padX, min.Y + padY), dl, checkColor);
}
var leftX = min.X + padX + statusGutter;
// Title (top-left).
dl.AddText(Fonts.FontBold, Fonts.FontBold.FontSize, new Vector2(leftX, min.Y + padY), textColor, set.Title);
// Scope (bottom-left).
if (!string.IsNullOrEmpty(set.Scope))
dl.AddText(Fonts.FontSmall, Fonts.FontSmall.FontSize,
new Vector2(leftX, min.Y + padY + Fonts.FontBold.FontSize + 2 * scale), muted, set.Scope);
// Step count (top-right).
var stepCount = $"{set.Steps.Count} step{(set.Steps.Count == 1 ? "" : "s")}";
ImGui.PushFont(Fonts.FontSmall);
var stepsWidth = ImGui.CalcTextSize(stepCount).X;
ImGui.PopFont();
dl.AddText(Fonts.FontSmall, Fonts.FontSmall.FontSize, new Vector2(max.X - padX - stepsWidth, min.Y + padY), muted, stepCount);
// Warning icon (parse warnings) next to the step count.
if (set.ParseWarnings.Count > 0)
{
var warnX = max.X - padX - stepsWidth - 18 * scale;
Icons.DrawIconAtScreenPosition(Icon.Warning, new Vector2(warnX, min.Y + padY - 1 * scale), dl, UiColors.StatusWarning);
}
// Tag pills (bottom-right).
var rightCursor = max.X - padX;
DrawTagPills(dl, set.Tags, ref rightCursor, max.Y - padY - Fonts.FontSmall.FontSize, muted, scale);
// Per-step status bar (bottom-left, 3px from the row bottom) + per-tick hover tooltip.
var tickHovered = false;
if (runResult != null)
{
var hoveredTick = TestStatusBar.Draw(dl, leftX, max.Y - 3 * scale, runResult.Steps, scale);
if (hovered && hoveredTick >= 0)
{
tickHovered = true;
var stepTitle = hoveredTick < set.Steps.Count ? set.Steps[hoveredTick].Title : $"Step {hoveredTick + 1}";
DrawStepTooltip(stepTitle, runResult.Steps[hoveredTick], scale);
}
}
ImGui.PopID();
return new Result(clicked, hovered, tickHovered);
}
private static void DrawStepTooltip(string stepTitle, TestRunResults.StepInfo info, float scale)
{
ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(10, 8) * scale);
ImGui.BeginTooltip();
ImGui.PushFont(Fonts.FontBold);
ImGui.TextUnformatted(stepTitle);
ImGui.PopFont();
ImGui.TextColored(TestStatusBar.OutcomeColor(info.Outcome), info.Outcome.ToString());
if (!string.IsNullOrWhiteSpace(info.Comment))
{
ImGui.PushTextWrapPos(320 * scale);
ImGui.TextWrapped(info.Comment);
ImGui.PopTextWrapPos();
}
ImGui.EndTooltip();
ImGui.PopStyleVar();
}
private static void DrawTagPills(ImDrawListPtr dl, IReadOnlyList tags, ref float rightX, float y, Color textColor, float scale)
{
if (tags.Count == 0)
return;
ImGui.PushFont(Fonts.FontSmall);
var pillPadX = 6 * scale;
var pillPadY = 1 * scale;
var fontSize = Fonts.FontSmall.FontSize;
var bgColor = ImGui.GetColorU32(UiColors.ForegroundFull.Fade(0.12f).Rgba);
var fgColor = ImGui.GetColorU32(textColor.Rgba);
for (var i = tags.Count - 1; i >= 0; i--)
{
var label = tags[i].ToUpperInvariant();
var w = ImGui.CalcTextSize(label).X + pillPadX * 2;
rightX -= w;
dl.AddRectFilled(new Vector2(rightX, y - pillPadY), new Vector2(rightX + w, y + fontSize + pillPadY), bgColor, 6 * scale);
dl.AddText(Fonts.FontSmall, fontSize, new Vector2(rightX + pillPadX, y), fgColor, label);
rightX -= 4 * scale;
}
ImGui.PopFont();
}
}