// Copyright © Gamesmiths Guild. #if TOOLS using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using Gamesmiths.Forge.Godot.Resources.Statescript; using Godot; namespace Gamesmiths.Forge.Godot.Editor.Statescript; internal static class InlineConstantSummaryFormatter { private const string SummaryBadgeMetaKey = "forge_inline_summary_badge"; private const string SummaryBadgeInstanceIdMetaKey = "forge_inline_summary_badge_instance_id"; private const string SummaryBadgeResizeHookMetaKey = "forge_inline_summary_badge_resize_hook"; private const string SummaryBadgeKindMetaKey = "forge_inline_summary_badge_kind"; private const string SummaryBadgeTextMetaKey = "forge_inline_summary_badge_text"; private const string SummaryBadgeSelectedVariableMetaKey = "forge_inline_summary_badge_selected_variable"; private const string SummaryBadgeSelectedSharedVariableSetPathMetaKey = "forge_inline_summary_badge_selected_shared_set_path"; private const string SummaryBadgeSelectedSharedVariableMetaKey = "forge_inline_summary_badge_selected_shared_variable"; private const float MinimumBadgeWidth = 76f; private const float FoldableTitleChromeWidth = 30f; private const float FoldableTitleBadgeGap = 6f; private const float FoldableTitleRightPadding = 8f; private static readonly Color _numericIconColor = new(0x3dbcc9ff); private static readonly Color _numericBackgroundColor = new(0x3dbcc918); private static readonly Color _numericBorderColor = new(0x3dbcc9ff); private static readonly Color _vectorIconColor = new(0xd48a3aff); private static readonly Color _vectorBackgroundColor = new(0xd48a3a18); private static readonly Color _vectorBorderColor = new(0xd48a3aff); private static readonly Color _booleanIconColor = new(0xc2a24fff); private static readonly Color _booleanBackgroundColor = new(0xc2a24f18); private static readonly Color _booleanBorderColor = new(0xc2a24fff); private static readonly Color _resolverIconColor = new(0xc06bcfff); private static readonly Color _resolverBackgroundColor = new(0xc06bcf18); private static readonly Color _resolverBorderColor = new(0xc06bcfff); private static readonly Color _variableIconColor = new(0x5d7be0ff); private static readonly Color _variableBackgroundColor = new(0x5d7be018); private static readonly Color _variableBorderColor = new(0x5d7be0ff); private static readonly Color _sharedVariableIconColor = new(0x46a86fff); private static readonly Color _sharedVariableBackgroundColor = new(0x46a86f18); private static readonly Color _sharedVariableBorderColor = new(0x46a86fff); private static readonly Color _enumIconColor = new(0xc0c6d1ff); private static readonly Color _enumBackgroundColor = new(0xc0c6d118); private static readonly Color _enumBorderColor = new(0xc0c6d1ff); public static void ApplyFoldableTitle( string baseTitle, FoldableContainer foldable, NodeEditorProperty? editor) { EnsureResizeSyncHook(foldable); foldable.Title = baseTitle; SummaryBadgeData badgeData = GetBadgeData(foldable, editor); PanelContainer badge = GetOrCreateSummaryBadge(foldable); ConfigureSummaryBadge(badge, badgeData); SynchronizeSiblingBadgeWidths(foldable); } public static void ApplyFoldableTitle( string baseTitle, FoldableContainer foldable, string? summary, InlineSummaryBadgeKind badgeKind, bool isConstant = false, string? highlightedVariableName = null, string? highlightedSharedVariableSetPath = null, string? highlightedSharedVariableName = null) { EnsureResizeSyncHook(foldable); foldable.Title = baseTitle; SummaryBadgeData badgeData = foldable.Folded && !string.IsNullOrWhiteSpace(summary) ? CreateBadgeData( summary, badgeKind, isConstant, highlightedVariableName, highlightedSharedVariableSetPath, highlightedSharedVariableName) : SummaryBadgeData.Hidden; PanelContainer badge = GetOrCreateSummaryBadge(foldable); ConfigureSummaryBadge(badge, badgeData); SynchronizeSiblingBadgeWidths(foldable); } public static string GetFoldableTitle( string baseTitle, FoldableContainer foldable, NodeEditorProperty? editor) { if (!foldable.Folded || editor is null) { return baseTitle; } if (editor.TryGetInlineSummary(out string summary) && !string.IsNullOrWhiteSpace(summary)) { return $"{baseTitle} {summary}"; } return string.IsNullOrWhiteSpace(editor.DisplayName) ? baseTitle : $"{baseTitle} {editor.DisplayName}"; } public static string FormatVariant(Variant value, StatescriptVariableType valueType) { return valueType switch { StatescriptVariableType.Bool => value.AsBool() ? "True" : "False", StatescriptVariableType.Byte => value.AsInt32().ToString(CultureInfo.InvariantCulture), StatescriptVariableType.SByte => value.AsInt32().ToString(CultureInfo.InvariantCulture), StatescriptVariableType.Char => ((char)value.AsInt32()).ToString(), StatescriptVariableType.Decimal => value.AsDouble().ToString("G", CultureInfo.InvariantCulture), StatescriptVariableType.Double => value.AsDouble().ToString("G", CultureInfo.InvariantCulture), StatescriptVariableType.Float => value.AsSingle().ToString("G", CultureInfo.InvariantCulture), StatescriptVariableType.Int => value.AsInt32().ToString(CultureInfo.InvariantCulture), StatescriptVariableType.UInt => value.AsInt64().ToString(CultureInfo.InvariantCulture), StatescriptVariableType.Long => value.AsInt64().ToString(CultureInfo.InvariantCulture), StatescriptVariableType.ULong => value.AsInt64().ToString(CultureInfo.InvariantCulture), StatescriptVariableType.Short => value.AsInt32().ToString(CultureInfo.InvariantCulture), StatescriptVariableType.UShort => value.AsInt32().ToString(CultureInfo.InvariantCulture), StatescriptVariableType.Vector2 => FormatVector2(value.AsVector2()), StatescriptVariableType.Vector3 => FormatVector3(value.AsVector3()), StatescriptVariableType.Vector4 => FormatVector4(value.AsVector4()), StatescriptVariableType.Plane => FormatPlane(value.AsPlane()), StatescriptVariableType.Quaternion => FormatQuaternion(value.AsQuaternion()), _ => value.ToString(), }; } public static InlineSummaryBadgeKind GetBadgeKind(StatescriptVariableType valueType) { return valueType switch { StatescriptVariableType.Bool => InlineSummaryBadgeKind.Boolean, StatescriptVariableType.Vector2 => InlineSummaryBadgeKind.Vector, StatescriptVariableType.Vector3 => InlineSummaryBadgeKind.Vector, StatescriptVariableType.Vector4 => InlineSummaryBadgeKind.Vector, StatescriptVariableType.Plane => InlineSummaryBadgeKind.Vector, StatescriptVariableType.Quaternion => InlineSummaryBadgeKind.Vector, _ => InlineSummaryBadgeKind.Numeric, }; } internal static bool TryGetSummaryBadgeForHighlighting( FoldableContainer foldable, [NotNullWhen(true)] out PanelContainer? badge) { return TryGetSummaryBadge(foldable, out badge); } private static SummaryBadgeData GetBadgeData(FoldableContainer foldable, NodeEditorProperty? editor) { if (!foldable.Folded || editor is null) { return SummaryBadgeData.Hidden; } string? highlightedVariableName = null; string? highlightedSharedVariableSetPath = null; string? highlightedSharedVariableName = null; if (editor.TryGetHighlightedVariableName(out string propagatedVariableName) && !string.IsNullOrWhiteSpace(propagatedVariableName)) { highlightedVariableName = propagatedVariableName; } if (editor.TryGetHighlightedSharedVariable( out string propagatedSharedVariableSetPath, out string propagatedSharedVariableName) && !string.IsNullOrWhiteSpace(propagatedSharedVariableSetPath) && !string.IsNullOrWhiteSpace(propagatedSharedVariableName)) { highlightedSharedVariableSetPath = propagatedSharedVariableSetPath; highlightedSharedVariableName = propagatedSharedVariableName; } if (editor.TryGetInlineSummary(out string summary) && !string.IsNullOrWhiteSpace(summary)) { InlineSummaryBadgeKind badgeKind = editor.GetInlineSummaryBadgeKind(); return CreateBadgeData( summary, badgeKind, IsConstantBadgeKind(badgeKind), highlightedVariableName, highlightedSharedVariableSetPath, highlightedSharedVariableName); } return string.IsNullOrWhiteSpace(editor.DisplayName) ? SummaryBadgeData.Hidden : CreateBadgeData( editor.DisplayName, InlineSummaryBadgeKind.Resolver, false, highlightedVariableName, highlightedSharedVariableSetPath, highlightedSharedVariableName); } private static SummaryBadgeData CreateBadgeData( string text, InlineSummaryBadgeKind badgeKind, bool isConstant, string? highlightedVariableName = null, string? highlightedSharedVariableSetPath = null, string? highlightedSharedVariableName = null) { BadgeVisualStyle style = GetBadgeStyle(badgeKind); return new SummaryBadgeData( GetBadgeIcon(badgeKind, isConstant), text, highlightedVariableName ?? string.Empty, highlightedSharedVariableSetPath ?? string.Empty, highlightedSharedVariableName ?? string.Empty, badgeKind, isConstant, style.IconColor, style.BackgroundColor, style.BorderColor, true); } private static PanelContainer GetOrCreateSummaryBadge(FoldableContainer foldable) { if (TryGetSummaryBadge(foldable, out PanelContainer? existingBadge)) { return existingBadge; } var badge = new PanelContainer { Name = "InlineSummaryBadge", Visible = false, MouseFilter = Control.MouseFilterEnum.Ignore, SizeFlagsHorizontal = Control.SizeFlags.ShrinkCenter, }; var row = new HBoxContainer { Name = "Row", MouseFilter = Control.MouseFilterEnum.Ignore, SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, }; row.AddThemeConstantOverride("separation", 4); badge.AddChild(row); var iconLabel = new Label { Name = "Icon", MouseFilter = Control.MouseFilterEnum.Ignore, SizeFlagsHorizontal = Control.SizeFlags.ShrinkBegin, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Center, }; row.AddChild(iconLabel); var textLabel = new Label { Name = "Text", MouseFilter = Control.MouseFilterEnum.Ignore, SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center, TextOverrunBehavior = TextServer.OverrunBehavior.TrimEllipsis, }; row.AddChild(textLabel); foldable.AddTitleBarControl(badge); foldable.SetMeta(SummaryBadgeMetaKey, Variant.From(true)); foldable.SetMeta(SummaryBadgeInstanceIdMetaKey, Variant.From((long)badge.GetInstanceId())); return badge; } private static void EnsureResizeSyncHook(FoldableContainer foldable) { if (foldable.HasMeta(SummaryBadgeResizeHookMetaKey) && foldable.GetMeta(SummaryBadgeResizeHookMetaKey).AsBool()) { return; } foldable.Resized += () => SynchronizeSiblingBadgeWidths(foldable); foldable.SetMeta(SummaryBadgeResizeHookMetaKey, Variant.From(true)); } private static void ConfigureSummaryBadge(PanelContainer badge, SummaryBadgeData badgeData) { badge.Visible = badgeData.Visible; if (!badgeData.Visible) { badge.SetMeta(SummaryBadgeKindMetaKey, Variant.From((int)InlineSummaryBadgeKind.Resolver)); badge.SetMeta(SummaryBadgeTextMetaKey, Variant.From(string.Empty)); badge.CustomMinimumSize = Vector2.Zero; return; } badge.SetMeta(SummaryBadgeKindMetaKey, Variant.From((int)badgeData.BadgeKind)); badge.SetMeta(SummaryBadgeTextMetaKey, Variant.From(badgeData.Text)); badge.SetMeta( "forge_inline_summary_badge_highlight_variable", Variant.From(badgeData.HighlightVariableName)); badge.SetMeta( "forge_inline_summary_badge_highlight_shared_set_path", Variant.From(badgeData.HighlightSharedVariableSetPath)); badge.SetMeta( "forge_inline_summary_badge_highlight_shared_variable", Variant.From(badgeData.HighlightSharedVariableName)); StyleBoxFlat styleBox = new() { BgColor = badgeData.BackgroundColor, BorderColor = badgeData.BorderColor, CornerRadiusTopLeft = 8, CornerRadiusTopRight = 8, CornerRadiusBottomRight = 8, CornerRadiusBottomLeft = 8, BorderWidthLeft = 1, BorderWidthTop = 1, BorderWidthRight = 1, BorderWidthBottom = 1, ContentMarginLeft = 8, ContentMarginTop = 3, ContentMarginRight = 8, ContentMarginBottom = 3, }; badge.AddThemeStyleboxOverride("panel", styleBox); Label? iconLabel = badge.GetNodeOrNull