// 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