// Copyright © Gamesmiths Guild. #if TOOLS using System; using System.Collections.Generic; using Gamesmiths.Forge.Godot.Resources.Statescript; using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers; using Godot; namespace Gamesmiths.Forge.Godot.Editor.Statescript; /// /// Right-side panel for editing graph variables. Variables are created with a name and type via a creation dialog. /// Once created, only the initial value can be edited. To change name or type, delete and recreate the variable. /// [Tool] internal sealed partial class StatescriptVariablePanel : VBoxContainer, ISerializationListener { private const string ExpandedArraysMetaKey = "_expanded_arrays"; private const string HeaderRowNodeName = "HeaderRow"; private const string AddButtonNodeName = "AddButton"; private const string VariablesScrollNodeName = "VariablesScroll"; private const string VariableListNodeName = "VariableList"; private const string VariableNameButtonMetaKey = "_variable_name_button"; private static readonly Color _variableColor = new(0xe5c07bff); private static readonly Color _highlightColor = new(0x56b6c2ff); private readonly HashSet _expandedArrays = []; private StatescriptGraph? _graph; private VBoxContainer? _variableList; private Button? _addButton; private Window? _creationDialog; private LineEdit? _newNameEdit; private OptionButton? _newTypeDropdown; private CheckBox? _newArrayToggle; private Texture2D? _addIcon; private Texture2D? _removeIcon; private EditorUndoRedoManager? _undoRedo; private string? _selectedVariableName; /// /// Raised when any variable is added, removed, or its value changes. /// public event Action? VariablesChanged; /// /// Raised when an undo/redo action modifies the variable panel, so the dock can auto-expand it. /// public event Action? VariableUndoRedoPerformed; /// /// Raised when the user selects or deselects a variable for highlighting. /// public event Action? VariableHighlightChanged; public override void _Ready() { base._Ready(); _addIcon = EditorInterface.Singleton.GetEditorTheme().GetIcon("Add", "EditorIcons"); _removeIcon = EditorInterface.Singleton.GetEditorTheme().GetIcon("Remove", "EditorIcons"); SizeFlagsVertical = SizeFlags.ExpandFill; CustomMinimumSize = new Vector2(360, 0); var headerHBox = new HBoxContainer { Name = HeaderRowNodeName }; AddChild(headerHBox); var titleLabel = new Label { Text = "Graph Variables", SizeFlagsHorizontal = SizeFlags.ExpandFill, }; headerHBox.AddChild(titleLabel); _addButton = new Button { Name = AddButtonNodeName, Icon = _addIcon, Flat = true, TooltipText = "Add Variable", CustomMinimumSize = new Vector2(28, 28), }; _addButton.Pressed += OnAddPressed; headerHBox.AddChild(_addButton); var separator = new HSeparator(); AddChild(separator); var scrollContainer = new ScrollContainer { Name = VariablesScrollNodeName, SizeFlagsVertical = SizeFlags.ExpandFill, SizeFlagsHorizontal = SizeFlags.ExpandFill, }; AddChild(scrollContainer); _variableList = new VBoxContainer { Name = VariableListNodeName, SizeFlagsHorizontal = SizeFlags.ExpandFill, }; scrollContainer.AddChild(_variableList); } public override void _ExitTree() { base._ExitTree(); ReleaseUiState(); } public void OnBeforeSerialize() { ReleaseUiState(); } public void OnAfterDeserialize() { EnsureControlsCached(); if (_addButton is not null && IsInstanceValid(_addButton)) { _addButton.Pressed += OnAddPressed; } RebuildList(); } /// /// Sets the graph to display variables for. /// /// The graph resource, or null to clear. public void SetGraph(StatescriptGraph? graph) { _graph = graph; LoadExpandedArrayState(); RebuildList(); } public string? GetSelectedVariableName() { return _selectedVariableName; } public void RestoreSelectedVariable(string? variableName) { if (string.IsNullOrEmpty(variableName) || !HasVariableNamed(variableName)) { _selectedVariableName = null; } else { _selectedVariableName = variableName; } RefreshVariableSelectionVisuals(); VariableHighlightChanged?.Invoke(_selectedVariableName); } public void ClearSelectedVariable() { RestoreSelectedVariable(null); } /// /// Sets the used for undo/redo support. /// /// The undo/redo manager from the editor plugin. public void SetUndoRedo(EditorUndoRedoManager undoRedo) { _undoRedo = undoRedo; } /// /// Rebuilds the variable list UI from the current graph. /// public void RebuildList() { EnsureControlsCached(); if (_variableList is null) { return; } ClearVariableList(); if (_graph is null) { return; } for (int i = 0; i < _graph.Variables.Count; i++) { AddVariableRow(_graph.Variables[i], i); } } private static int FindTypeDropdownIndex(OptionButton dropdown, StatescriptVariableType variableType) { for (int i = 0; i < dropdown.ItemCount; i++) { if (dropdown.GetItemId(i) == (int)variableType) { return i; } } return 0; } private static void UpdateVariableNameButtonAppearance(Button button, bool isSelected) { Color buttonColor = isSelected ? _highlightColor : _variableColor; button.AddThemeColorOverride("font_color", buttonColor); button.AddThemeColorOverride("font_pressed_color", _highlightColor); button.AddThemeColorOverride("font_hover_color", buttonColor.Lightened(0.2f)); button.AddThemeColorOverride("font_hover_pressed_color", _highlightColor.Lightened(0.2f)); } private void EnsureControlsCached() { _addButton ??= GetNodeOrNull