#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(); } }