Files
MovementTests/addons/forge/editor/statescript/StatescriptGraphNode.Highlighting.cs
Minimata e09714cf83
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 27s
Create tag and build when new code gets to main / Export (push) Successful in 7m25s
update forge
2026-05-17 00:06:44 +02:00

392 lines
12 KiB
C#

// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
using Godot;
namespace Gamesmiths.Forge.Godot.Editor.Statescript;
public partial class StatescriptGraphNode
{
private readonly Dictionary<OptionButton, StyleBoxFlat> _baseDropdownStyles = [];
private StyleBoxFlat? _basePanelStyle;
private StyleBoxFlat? _baseSelectedPanelStyle;
private static bool ResolverReferencesVariable(StatescriptResolverResource? resolver, string variableName)
{
if (resolver is null || string.IsNullOrEmpty(variableName))
{
return false;
}
var visited = new HashSet<nint>();
return ResolverReferencesVariableRecursive(resolver, variableName, visited);
}
private static bool ResolverReferencesVariableRecursive(
StatescriptResolverResource resolver,
string variableName,
HashSet<nint> visited)
{
nint instanceId = (nint)resolver.GetInstanceId();
if (!visited.Add(instanceId))
{
return false;
}
if (resolver is VariableResolverResource variableResolver
&& string.Equals(variableResolver.VariableName, variableName, StringComparison.Ordinal))
{
return true;
}
foreach (PropertyInfo property in resolver.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (!property.CanRead || !typeof(StatescriptResolverResource).IsAssignableFrom(property.PropertyType))
{
continue;
}
if (property.GetValue(resolver) is StatescriptResolverResource nestedResolver
&& ResolverReferencesVariableRecursive(nestedResolver, variableName, visited))
{
return true;
}
}
return false;
}
private static bool ResolverReferencesSharedVariable(
StatescriptResolverResource? resolver,
string sharedVariableSetPath,
string variableName)
{
if (resolver is null
|| string.IsNullOrEmpty(sharedVariableSetPath)
|| string.IsNullOrEmpty(variableName))
{
return false;
}
var visited = new HashSet<nint>();
return ResolverReferencesSharedVariableRecursive(resolver, sharedVariableSetPath, variableName, visited);
}
private static bool ResolverReferencesSharedVariableRecursive(
StatescriptResolverResource resolver,
string sharedVariableSetPath,
string variableName,
HashSet<nint> visited)
{
nint instanceId = (nint)resolver.GetInstanceId();
if (!visited.Add(instanceId))
{
return false;
}
if (resolver is SharedVariableResolverResource sharedVariableResolver
&& string.Equals(sharedVariableResolver.SharedVariableSetPath, sharedVariableSetPath, StringComparison.Ordinal)
&& string.Equals(sharedVariableResolver.VariableName, variableName, StringComparison.Ordinal))
{
return true;
}
foreach (PropertyInfo property in resolver.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (!property.CanRead || !typeof(StatescriptResolverResource).IsAssignableFrom(property.PropertyType))
{
continue;
}
if (property.GetValue(resolver) is StatescriptResolverResource nestedResolver
&& ResolverReferencesSharedVariableRecursive(
nestedResolver,
sharedVariableSetPath,
variableName,
visited))
{
return true;
}
}
return false;
}
private bool ReferencesVariable(string variableName)
{
if (NodeResource is null)
{
return false;
}
return NodeResource.PropertyBindings.Any(binding => ResolverReferencesVariable(binding.Resolver, variableName));
}
private bool ReferencesSharedVariable(string? sharedVariableSetPath, string? variableName)
{
if (NodeResource is null
|| string.IsNullOrEmpty(sharedVariableSetPath)
|| string.IsNullOrEmpty(variableName))
{
return false;
}
return NodeResource.PropertyBindings.Any(binding =>
ResolverReferencesSharedVariable(binding.Resolver, sharedVariableSetPath, variableName));
}
private void ApplyHighlightBorder()
{
EnsureBasePanelStylesStored();
if (_basePanelStyle is null || _baseSelectedPanelStyle is null)
{
return;
}
if (_isHighlighted)
{
StyleBoxFlat baseStyle = _basePanelStyle;
var highlightStyle = (StyleBoxFlat)baseStyle.Duplicate();
highlightStyle.BorderColor = _highlightColor;
highlightStyle.BorderWidthTop = 2;
highlightStyle.BorderWidthBottom = 2;
highlightStyle.BorderWidthLeft = 2;
highlightStyle.BorderWidthRight = 2;
highlightStyle.BgColor = baseStyle.BgColor.Lerp(_highlightColor, 0.15f);
AddThemeStyleboxOverride("panel", highlightStyle);
AddThemeStyleboxOverride("panel_selected", highlightStyle);
}
else
{
AddThemeStyleboxOverride("panel", (StyleBoxFlat)_basePanelStyle.Duplicate());
AddThemeStyleboxOverride("panel_selected", (StyleBoxFlat)_baseSelectedPanelStyle.Duplicate());
}
}
private void UpdateChildHighlights()
{
UpdateHighlightsRecursive(this);
}
private void UpdateHighlightsRecursive(Node parent)
{
foreach (Node child in parent.GetChildren())
{
if (child is OptionButton optionButton)
{
HighlightOptionButtonIfMatches(optionButton);
}
else if (child is Label label)
{
HighlightLabelIfMatches(label);
}
else if (child is FoldableContainer foldable)
{
HighlightFoldableSummaryBadgeIfPresent(foldable);
}
else if (child is PanelContainer badge && badge.HasMeta("forge_inline_summary_badge_kind"))
{
HighlightSummaryBadgeIfMatches(badge);
}
UpdateHighlightsRecursive(child);
}
}
private void HighlightFoldableSummaryBadgeIfPresent(FoldableContainer foldable)
{
if (!InlineConstantSummaryFormatter.TryGetSummaryBadgeForHighlighting(foldable, out PanelContainer? badge))
{
return;
}
HighlightSummaryBadgeIfMatches(badge);
}
private void HighlightOptionButtonIfMatches(OptionButton dropdown)
{
EnsureBaseDropdownStyleStored(dropdown);
if (!dropdown.HasMeta("is_variable_dropdown") && !dropdown.HasMeta("is_shared_variable_dropdown"))
{
return;
}
if (!_baseDropdownStyles.TryGetValue(dropdown, out StyleBoxFlat? baseStyle))
{
return;
}
if (string.IsNullOrEmpty(_highlightedVariableName)
&& (string.IsNullOrEmpty(_highlightedSharedVariableSetPath)
|| string.IsNullOrEmpty(_highlightedSharedVariableName)))
{
dropdown.AddThemeStyleboxOverride("normal", (StyleBoxFlat)baseStyle.Duplicate());
return;
}
int selectedIdx = dropdown.Selected;
if (selectedIdx < 0)
{
dropdown.AddThemeStyleboxOverride("normal", (StyleBoxFlat)baseStyle.Duplicate());
return;
}
string selectedText = dropdown.GetItemText(selectedIdx);
bool isSharedVariableDropdown = dropdown.HasMeta("is_shared_variable_dropdown");
string dropdownSetPath = dropdown.HasMeta("shared_variable_set_path")
? dropdown.GetMeta("shared_variable_set_path").AsString()
: string.Empty;
bool isMatch = isSharedVariableDropdown
? !string.IsNullOrEmpty(_highlightedSharedVariableSetPath)
&& !string.IsNullOrEmpty(_highlightedSharedVariableName)
&& selectedText == _highlightedSharedVariableName
&& dropdownSetPath == _highlightedSharedVariableSetPath
: selectedText == _highlightedVariableName;
if (isMatch)
{
var highlightStyle = (StyleBoxFlat)baseStyle.Duplicate();
highlightStyle.BgColor = baseStyle.BgColor.Lerp(_highlightColor, 0.25f);
dropdown.AddThemeStyleboxOverride("normal", highlightStyle);
}
else
{
dropdown.AddThemeStyleboxOverride("normal", (StyleBoxFlat)baseStyle.Duplicate());
}
}
private void EnsureBasePanelStylesStored()
{
if (_basePanelStyle is null && GetThemeStylebox("panel") is StyleBoxFlat panelStyle)
{
_basePanelStyle = (StyleBoxFlat)panelStyle.Duplicate();
}
if (_baseSelectedPanelStyle is null
&& GetThemeStylebox("panel_selected") is StyleBoxFlat selectedPanelStyle)
{
_baseSelectedPanelStyle = (StyleBoxFlat)selectedPanelStyle.Duplicate();
}
}
private void EnsureBaseDropdownStyleStored(OptionButton dropdown)
{
if (_baseDropdownStyles.ContainsKey(dropdown))
{
return;
}
if (dropdown.GetThemeStylebox("normal") is StyleBoxFlat baseStyle)
{
_baseDropdownStyles[dropdown] = (StyleBoxFlat)baseStyle.Duplicate();
}
}
private void HighlightLabelIfMatches(Label label)
{
if (string.IsNullOrEmpty(_highlightedVariableName))
{
if (label.HasMeta("is_highlight_colored"))
{
label.RemoveThemeColorOverride("font_color");
label.RemoveMeta("is_highlight_colored");
}
return;
}
if (label.Text == _highlightedVariableName)
{
label.AddThemeColorOverride("font_color", _highlightColor);
label.SetMeta("is_highlight_colored", true);
}
else if (label.HasMeta("is_highlight_colored"))
{
label.RemoveThemeColorOverride("font_color");
label.RemoveMeta("is_highlight_colored");
}
}
private void HighlightSummaryBadgeIfMatches(PanelContainer badge)
{
if (badge.GetNodeOrNull<Label>("Row/Text") is not Label textLabel
|| badge.GetNodeOrNull<Label>("Row/Icon") is not Label iconLabel)
{
return;
}
if (!badge.HasMeta("forge_inline_summary_badge_base_stylebox")
&& badge.GetThemeStylebox("panel") is StyleBoxFlat baseStyle)
{
badge.SetMeta("forge_inline_summary_badge_base_stylebox", Variant.From(baseStyle.Duplicate()));
}
if (!badge.HasMeta("forge_inline_summary_badge_base_stylebox")
|| badge.GetMeta("forge_inline_summary_badge_base_stylebox").Obj is not StyleBoxFlat storedBase)
{
return;
}
string propagatedVariableName = badge.HasMeta("forge_inline_summary_badge_highlight_variable")
? badge.GetMeta("forge_inline_summary_badge_highlight_variable").AsString()
: string.Empty;
string propagatedSharedVariableName = badge.HasMeta("forge_inline_summary_badge_highlight_shared_variable")
? badge.GetMeta("forge_inline_summary_badge_highlight_shared_variable").AsString()
: string.Empty;
string propagatedSharedVariableSetPath = badge.HasMeta("forge_inline_summary_badge_highlight_shared_set_path")
? badge.GetMeta("forge_inline_summary_badge_highlight_shared_set_path").AsString()
: string.Empty;
bool isMatch = !string.IsNullOrEmpty(_highlightedVariableName)
&& (textLabel.Text == _highlightedVariableName
|| propagatedVariableName == _highlightedVariableName);
isMatch = isMatch
|| (!string.IsNullOrEmpty(_highlightedSharedVariableSetPath)
&& !string.IsNullOrEmpty(_highlightedSharedVariableName)
&& (textLabel.Text == _highlightedSharedVariableName
|| propagatedSharedVariableName == _highlightedSharedVariableName)
&& propagatedSharedVariableSetPath == _highlightedSharedVariableSetPath);
badge.SetMeta("forge_inline_summary_badge_selected_variable", Variant.From(_highlightedVariableName ?? string.Empty));
badge.SetMeta(
"forge_inline_summary_badge_selected_shared_set_path",
Variant.From(_highlightedSharedVariableSetPath ?? string.Empty));
badge.SetMeta(
"forge_inline_summary_badge_selected_shared_variable",
Variant.From(_highlightedSharedVariableName ?? string.Empty));
var style = (StyleBoxFlat)storedBase.Duplicate();
if (isMatch)
{
style.BorderWidthLeft = Math.Max(style.BorderWidthLeft, 2);
style.BorderWidthTop = Math.Max(style.BorderWidthTop, 2);
style.BorderWidthRight = Math.Max(style.BorderWidthRight, 2);
style.BorderWidthBottom = Math.Max(style.BorderWidthBottom, 2);
style.BorderColor = _highlightColor;
style.BgColor = _highlightColor;
iconLabel.AddThemeColorOverride("font_color", Colors.Black);
textLabel.AddThemeColorOverride("font_color", Colors.Black);
}
else
{
iconLabel.RemoveThemeColorOverride("font_color");
textLabel.RemoveThemeColorOverride("font_color");
}
badge.AddThemeStyleboxOverride("panel", style);
}
}
#endif