update forge
This commit is contained in:
@@ -16,7 +16,15 @@ namespace Gamesmiths.Forge.Godot.Editor.Statescript;
|
||||
[Tool]
|
||||
internal sealed partial class SharedVariableSetEditorProperty : EditorProperty, ISerializationListener
|
||||
{
|
||||
private const string BackgroundPanelNodeName = "BackgroundPanel";
|
||||
private const string RootNodeName = "Root";
|
||||
private const string HeaderRowNodeName = "HeaderRow";
|
||||
private const string AddButtonNodeName = "AddButton";
|
||||
private const string VariableListNodeName = "VariableList";
|
||||
private const string VariableNameButtonMetaKey = "_shared_variable_name_button";
|
||||
|
||||
private static readonly Color _variableColor = new(0xe5c07bff);
|
||||
private static readonly Color _highlightColor = new(0x56b6c2ff);
|
||||
|
||||
private readonly HashSet<string> _expandedArrays = [];
|
||||
|
||||
@@ -33,6 +41,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
private Texture2D? _addIcon;
|
||||
private Texture2D? _removeIcon;
|
||||
private string? _selectedVariableName;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <see cref="EditorUndoRedoManager"/> used for undo/redo support.
|
||||
@@ -45,11 +54,21 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
SharedVariableHighlightState.Changed += OnSharedVariableHighlightChanged;
|
||||
|
||||
_addIcon = EditorInterface.Singleton.GetEditorTheme().GetIcon("Add", "EditorIcons");
|
||||
_removeIcon = EditorInterface.Singleton.GetEditorTheme().GetIcon("Remove", "EditorIcons");
|
||||
|
||||
if (GetEditedObject() is ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
SharedVariableHighlightState.SetInspectorContext(sharedVariableSet.ResourcePath);
|
||||
SharedVariableHighlightState.SetSelection(sharedVariableSet.ResourcePath, _selectedVariableName);
|
||||
}
|
||||
|
||||
var backgroundPanel = new PanelContainer
|
||||
{
|
||||
Name = BackgroundPanelNodeName,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
|
||||
@@ -70,14 +89,19 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
AddChild(backgroundPanel);
|
||||
SetBottomEditor(backgroundPanel);
|
||||
|
||||
_root = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
_root = new VBoxContainer
|
||||
{
|
||||
Name = RootNodeName,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
backgroundPanel.AddChild(_root);
|
||||
|
||||
var headerHBox = new HBoxContainer();
|
||||
var headerHBox = new HBoxContainer { Name = HeaderRowNodeName };
|
||||
_root.AddChild(headerHBox);
|
||||
|
||||
_addButton = new Button
|
||||
{
|
||||
Name = AddButtonNodeName,
|
||||
Text = "Add Variable",
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
@@ -87,34 +111,55 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
_root.AddChild(new HSeparator());
|
||||
|
||||
_variableList = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
_variableList = new VBoxContainer
|
||||
{
|
||||
Name = VariableListNodeName,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
};
|
||||
_root.AddChild(_variableList);
|
||||
}
|
||||
|
||||
public override void _UpdateProperty()
|
||||
{
|
||||
EnsureControlsCached();
|
||||
|
||||
if (GetEditedObject() is ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
SharedVariableHighlightState.SetInspectorContext(sharedVariableSet.ResourcePath);
|
||||
}
|
||||
|
||||
SyncSelectedVariableFromHighlightState();
|
||||
RebuildList();
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
SharedVariableHighlightState.Changed -= OnSharedVariableHighlightChanged;
|
||||
|
||||
if (GetEditedObject() is ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
SharedVariableHighlightState.ClearInspectorContext(sharedVariableSet.ResourcePath);
|
||||
}
|
||||
|
||||
ReleaseUiState();
|
||||
FreeAllChildren();
|
||||
|
||||
base._ExitTree();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
if (_addButton is not null)
|
||||
{
|
||||
_addButton.Pressed -= OnAddPressed;
|
||||
}
|
||||
|
||||
ClearVariableList();
|
||||
|
||||
_creationDialog?.Free();
|
||||
_creationDialog = null;
|
||||
_newNameEdit = null;
|
||||
_newTypeDropdown = null;
|
||||
_newArrayToggle = null;
|
||||
SharedVariableHighlightState.Changed -= OnSharedVariableHighlightChanged;
|
||||
ReleaseUiState();
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
if (_addButton is not null)
|
||||
EnsureControlsCached();
|
||||
SharedVariableHighlightState.Changed += OnSharedVariableHighlightChanged;
|
||||
SyncSelectedVariableFromHighlightState();
|
||||
|
||||
if (_addButton is not null && IsInstanceValid(_addButton))
|
||||
{
|
||||
_addButton.Pressed += OnAddPressed;
|
||||
}
|
||||
@@ -122,6 +167,28 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
RebuildList();
|
||||
}
|
||||
|
||||
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", buttonColor);
|
||||
button.AddThemeColorOverride("font_hover_color", buttonColor.Lightened(0.2f));
|
||||
button.AddThemeColorOverride("font_hover_pressed_color", buttonColor.Lightened(0.2f));
|
||||
}
|
||||
|
||||
private Array<ForgeSharedVariableDefinition> GetDefinitions()
|
||||
{
|
||||
GodotObject obj = GetEditedObject();
|
||||
@@ -133,7 +200,18 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
private void NotifyChanged()
|
||||
{
|
||||
if (GetEditedObject() is Resource resource)
|
||||
GodotObject obj = GetEditedObject();
|
||||
string propertyName = GetEditedProperty();
|
||||
|
||||
if (obj is not ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
obj.Set(propertyName, sharedVariableSet.Variables);
|
||||
EmitChanged(propertyName, sharedVariableSet.Variables);
|
||||
|
||||
if (obj is Resource resource)
|
||||
{
|
||||
resource.EmitChanged();
|
||||
}
|
||||
@@ -141,35 +219,31 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
private void RebuildList()
|
||||
{
|
||||
EnsureControlsCached();
|
||||
|
||||
if (_variableList is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Defer the actual rebuild so that any in-progress signal emission (e.g. a button Pressed handler that
|
||||
// triggered an add/remove) finishes before we free the emitting nodes.
|
||||
CallDeferred(MethodName.RebuildListDeferred);
|
||||
}
|
||||
|
||||
private void RebuildListDeferred()
|
||||
{
|
||||
if (_variableList is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SyncSelectedVariableFromHighlightState();
|
||||
|
||||
ClearVariableList();
|
||||
|
||||
Array<ForgeSharedVariableDefinition> definitions = GetDefinitions();
|
||||
|
||||
for (var i = 0; i < definitions.Count; i++)
|
||||
for (int i = 0; i < definitions.Count; i++)
|
||||
{
|
||||
AddVariableRow(definitions, i);
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisuals();
|
||||
}
|
||||
|
||||
private void ClearVariableList()
|
||||
{
|
||||
EnsureControlsCached();
|
||||
|
||||
if (_variableList is null)
|
||||
{
|
||||
return;
|
||||
@@ -182,6 +256,75 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureControlsCached()
|
||||
{
|
||||
_root ??= GetNodeOrNull<VBoxContainer>(
|
||||
$"{BackgroundPanelNodeName}/{RootNodeName}");
|
||||
_addButton ??= GetNodeOrNull<Button>(
|
||||
$"{BackgroundPanelNodeName}/{RootNodeName}/{HeaderRowNodeName}/{AddButtonNodeName}");
|
||||
_variableList ??= GetNodeOrNull<VBoxContainer>(
|
||||
$"{BackgroundPanelNodeName}/{RootNodeName}/{VariableListNodeName}");
|
||||
}
|
||||
|
||||
private void ReleaseUiState()
|
||||
{
|
||||
if (_addButton is not null && IsInstanceValid(_addButton))
|
||||
{
|
||||
_addButton.Pressed -= OnAddPressed;
|
||||
}
|
||||
|
||||
ClearVariableList();
|
||||
|
||||
if (_creationDialog is not null && IsInstanceValid(_creationDialog))
|
||||
{
|
||||
_creationDialog.Confirmed -= OnCreationConfirmed;
|
||||
_creationDialog.Canceled -= OnCreationCanceled;
|
||||
_creationDialog.Free();
|
||||
}
|
||||
|
||||
_creationDialog = null;
|
||||
_newNameEdit = null;
|
||||
_newTypeDropdown = null;
|
||||
_newArrayToggle = null;
|
||||
_root = null;
|
||||
_variableList = null;
|
||||
_addButton = null;
|
||||
}
|
||||
|
||||
private void FreeAllChildren()
|
||||
{
|
||||
for (int i = GetChildCount() - 1; i >= 0; i--)
|
||||
{
|
||||
Node child = GetChild(i);
|
||||
RemoveChild(child);
|
||||
child.Free();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSharedVariableHighlightChanged()
|
||||
{
|
||||
SyncSelectedVariableFromHighlightState();
|
||||
RefreshVariableSelectionVisuals();
|
||||
}
|
||||
|
||||
private void SyncSelectedVariableFromHighlightState()
|
||||
{
|
||||
if (GetEditedObject() is not ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
_selectedVariableName = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (SharedVariableHighlightState.TryGetActiveSelection(out string selectedSetPath, out string variableName)
|
||||
&& string.Equals(selectedSetPath, sharedVariableSet.ResourcePath, System.StringComparison.Ordinal))
|
||||
{
|
||||
_selectedVariableName = variableName;
|
||||
return;
|
||||
}
|
||||
|
||||
_selectedVariableName = null;
|
||||
}
|
||||
|
||||
private void AddVariableRow(Array<ForgeSharedVariableDefinition> definitions, int index)
|
||||
{
|
||||
if (_variableList is null || index < 0 || index >= definitions.Count)
|
||||
@@ -197,17 +340,25 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
var headerRow = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
|
||||
rowContainer.AddChild(headerRow);
|
||||
|
||||
var nameLabel = new Label
|
||||
bool isSelected = _selectedVariableName == def.VariableName;
|
||||
|
||||
var nameButton = new Button
|
||||
{
|
||||
Text = def.VariableName,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill,
|
||||
Flat = true,
|
||||
ToggleMode = true,
|
||||
ButtonPressed = isSelected,
|
||||
Alignment = HorizontalAlignment.Left,
|
||||
};
|
||||
|
||||
nameLabel.AddThemeColorOverride("font_color", _variableColor);
|
||||
nameLabel.AddThemeFontOverride(
|
||||
nameButton.SetMeta(VariableNameButtonMetaKey, def.VariableName);
|
||||
UpdateVariableNameButtonAppearance(nameButton, isSelected);
|
||||
nameButton.AddThemeFontOverride(
|
||||
"font",
|
||||
EditorInterface.Singleton.GetEditorTheme().GetFont("bold", "EditorFonts"));
|
||||
headerRow.AddChild(nameLabel);
|
||||
nameButton.Toggled += pressed => SetSelectedVariable(def.VariableName, pressed);
|
||||
headerRow.AddChild(nameButton);
|
||||
|
||||
var typeLabel = new Label
|
||||
{
|
||||
@@ -218,7 +369,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
typeLabel.AddThemeColorOverride("font_color", new Color(0.6f, 0.6f, 0.6f));
|
||||
headerRow.AddChild(typeLabel);
|
||||
|
||||
var capturedIndex = index;
|
||||
int capturedIndex = index;
|
||||
|
||||
var deleteButton = new Button
|
||||
{
|
||||
@@ -245,6 +396,56 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
rowContainer.AddChild(new HSeparator());
|
||||
}
|
||||
|
||||
private void SetSelectedVariable(string variableName, bool selected)
|
||||
{
|
||||
if (selected)
|
||||
{
|
||||
_selectedVariableName = variableName;
|
||||
}
|
||||
else if (_selectedVariableName == variableName)
|
||||
{
|
||||
_selectedVariableName = null;
|
||||
}
|
||||
|
||||
if (GetEditedObject() is ForgeSharedVariableSet sharedVariableSet)
|
||||
{
|
||||
SharedVariableHighlightState.SetInspectorContext(sharedVariableSet.ResourcePath);
|
||||
SharedVariableHighlightState.SetSelection(sharedVariableSet.ResourcePath, _selectedVariableName);
|
||||
}
|
||||
else
|
||||
{
|
||||
SharedVariableHighlightState.SetSelection(null, null);
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisuals();
|
||||
}
|
||||
|
||||
private void RefreshVariableSelectionVisuals()
|
||||
{
|
||||
if (_variableList is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisualsRecursive(_variableList);
|
||||
}
|
||||
|
||||
private void RefreshVariableSelectionVisualsRecursive(Node parent)
|
||||
{
|
||||
foreach (Node child in parent.GetChildren())
|
||||
{
|
||||
if (child is Button button && button.HasMeta(VariableNameButtonMetaKey))
|
||||
{
|
||||
string variableName = button.GetMeta(VariableNameButtonMetaKey).AsString();
|
||||
bool isSelected = _selectedVariableName == variableName;
|
||||
button.SetPressedNoSignal(isSelected);
|
||||
UpdateVariableNameButtonAppearance(button, isSelected);
|
||||
}
|
||||
|
||||
RefreshVariableSelectionVisualsRecursive(child);
|
||||
}
|
||||
}
|
||||
|
||||
private Control CreateValueEditor(ForgeSharedVariableDefinition def)
|
||||
{
|
||||
if (def.VariableType == StatescriptVariableType.Bool)
|
||||
@@ -310,7 +511,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
var headerRow = new HBoxContainer();
|
||||
vBox.AddChild(headerRow);
|
||||
|
||||
var isExpanded = _expandedArrays.Contains(def.VariableName);
|
||||
bool isExpanded = _expandedArrays.Contains(def.VariableName);
|
||||
|
||||
var elementsContainer = new VBoxContainer
|
||||
{
|
||||
@@ -330,7 +531,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
{
|
||||
elementsContainer.Visible = x;
|
||||
|
||||
var wasExpanded = !x;
|
||||
bool wasExpanded = !x;
|
||||
|
||||
if (x)
|
||||
{
|
||||
@@ -379,9 +580,9 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
vBox.AddChild(elementsContainer);
|
||||
|
||||
for (var i = 0; i < def.InitialArrayValues.Count; i++)
|
||||
for (int i = 0; i < def.InitialArrayValues.Count; i++)
|
||||
{
|
||||
var capturedIndex = i;
|
||||
int capturedIndex = i;
|
||||
|
||||
if (def.VariableType == StatescriptVariableType.Bool)
|
||||
{
|
||||
@@ -502,7 +703,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
|
||||
private void AddArrayElement(ForgeSharedVariableDefinition def, Variant value)
|
||||
{
|
||||
var wasExpanded = _expandedArrays.Contains(def.VariableName);
|
||||
bool wasExpanded = _expandedArrays.Contains(def.VariableName);
|
||||
|
||||
if (_undoRedo is not null)
|
||||
{
|
||||
@@ -583,6 +784,8 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
(int)variableType);
|
||||
}
|
||||
|
||||
_newTypeDropdown.Selected = FindTypeDropdownIndex(_newTypeDropdown, StatescriptVariableType.Int);
|
||||
|
||||
typeRow.AddChild(_newTypeDropdown);
|
||||
|
||||
var arrayRow = new HBoxContainer();
|
||||
@@ -605,7 +808,7 @@ internal sealed partial class SharedVariableSetEditorProperty : EditorProperty,
|
||||
return;
|
||||
}
|
||||
|
||||
var name = _newNameEdit.Text.Trim();
|
||||
string name = _newNameEdit.Text.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user