rokojori_action_library/Runtime/UI/Nodes/UIRegion.cs

760 lines
18 KiB
C#

using Godot;
using System.Collections.Generic;
using System.Linq;
namespace Rokojori
{
[Tool]
[GlobalClass,Icon("res://addons/rokojori_action_library/Icons/UIRegion.svg")]
public partial class UIRegion : Control, UIStylePropertyContainerNode, UIHolderControl
{
[Export]
public bool activateDebugging = false;
public bool HasDebugFlag()
{
return activateDebugging;
}
[Export]
public string currentSelectors = "";
[ExportGroup("Actions")]
[Export]
public Action onVisible;
[Export]
public Action onInvisible;
[Export]
public Action onVisibilityChanged;
[ExportGroup( "Interactivity" )]
[Export]
public UICursor hoverCursor;
public UICursor GetHoverCursor( UIStylePropertyContainer container )
{
return hoverCursor;
}
[Export]
public Action onUIConfirm;
public enum ConfirmClick
{
Ignore,
MouseDown,
MouseRelease,
Focused_MouseDown,
Focused_MouseRelease
}
[Export]
public ConfirmClick confirmClickMode = ConfirmClick.MouseDown;
[Export]
public Action onFocusEntered;
[Export]
public Action onFocusExited;
[Export]
public Control[] setActiveWhenFocused = [];
[Export]
public Control focusControl;
[Export]
public bool handleMouseEvents = false;
[Export]
public Action onLeftClick;
[Export]
public Action onMiddleClick;
[Export]
public Action onRightClick;
public override void _GuiInput( InputEvent inputEvent )
{
var ui = GetUI();
// this.LogInfo( "InputEvent", inputEvent, ui.settings.uiConfirm.IsDown( inputEvent ) );
if ( ui.settings.uiConfirm.IsDown( inputEvent ) )
{
onUIConfirm?.Trigger();
// GD.Print("ui_accept pressed while focused");
// AcceptEvent(); // Optional: stop propagation
}
if ( inputEvent is InputEventMouseButton lmb )
{
if ( lmb.IsPressed() )
{
AddUISelectorFlag( UISelectorFlag.Dragging );
if ( focusControl != null )
{
focusControl.GrabFocus();
}
}
if ( lmb.IsReleased() )
{
RemoveUISelectorFlag( UISelectorFlag.Dragging );
}
if ( lmb.Pressed && lmb.ButtonIndex == MouseButton.Left )
{
if ( ConfirmClick.MouseDown == confirmClickMode || ConfirmClick.Focused_MouseDown == confirmClickMode )
{
if ( HasFocus() || ConfirmClick.MouseDown == confirmClickMode )
{
onUIConfirm?.Trigger();
}
}
}
else if (
lmb.IsReleased() && lmb.ButtonIndex == MouseButton.Left &&
_selectorFlags != null && _selectorFlags.Contains( UISelectorFlag.Hover )
)
{
if ( ConfirmClick.MouseRelease == confirmClickMode || ConfirmClick.Focused_MouseRelease == confirmClickMode )
{
if ( HasFocus() || ConfirmClick.MouseRelease == confirmClickMode )
{
onUIConfirm?.Trigger();
}
}
}
}
if ( ! handleMouseEvents )
{
return;
}
if ( inputEvent is InputEventMouseButton mb )
{
if ( mb.Pressed )
{
this.LogInfo( "Clicked" );
if ( mb.ButtonIndex == MouseButton.Left )
{
Action.Trigger( onLeftClick );
}
if ( mb.ButtonIndex == MouseButton.Middle )
{
Action.Trigger( onMiddleClick );
}
if ( mb.ButtonIndex == MouseButton.Right )
{
Action.Trigger( onRightClick );
}
}
}
}
[ExportGroup( "Functions" )]
[Export]
public UI.FocusOrderType focusOrderType = UI.FocusOrderType.Vertical_List;
[ExportToolButton( "Set Focus Connections" )]
public Callable setFocusConnectionButton => Callable.From(
( ) =>
{
UI.SetChildFocusOrder( this, focusOrderType, null, FocusModeEnum.Click );
}
);
#if TOOLS
[ExportGroup("Editor SceneSetup")]
[Export]
public UISettings uiSettings;
[Export]
public bool updateInEditor = false;
[Export]
public float fontZoom = 1f;
[Export]
public bool reassignUI;
[ExportGroup("Editor SceneSetup/Read Only")]
[Export]
public float computedFontSize = 0f;
#endif
[ExportCategory( "UIStyle" )]
[ExportGroup( "Size & Margins" )]
[Export]
public UINumber width;
[Export]
public UINumber height;
[Export]
public UINumber margin;
[Export]
public UINumber marginLeft;
[Export]
public UINumber marginTop;
[Export]
public UINumber marginRight;
[Export]
public UINumber marginBottom;
[ExportGroup( "Position" )]
[Export]
public UIPosition position;
[Export]
public UILineWrap lineWrap;
[Export]
public UINumber left;
[Export]
public UINumber top;
[Export]
public UINumber right;
[Export]
public UINumber bottom;
[ExportGroup( "Child Layout" )]
[Export]
public UILayout layout;
[Export]
public UINumber horizontalAlignment;
[Export]
public UINumber verticalAlignment;
[Export]
public UINumber verticalPlacement;
[Export]
public UINumber elementSpacing;
[Export]
public UINumber lineSpacing;
[ExportGroup( "Font" )]
[Export]
public Font font;
[Export]
public UINumber fontSize;
[Export]
public UIColor fontColor;
[Export]
public UINumber outlineSize;
[Export]
public UIColor outlineColor;
[Export]
public UINumber shadowSize;
[Export]
public UIColor shadowColor;
[Export]
public UINumber shadowOffsetX;
[Export]
public UINumber shadowOffsetY;
[ExportGroup( "Modulation" )]
[Export]
public UIColor modulationColor;
[Export]
public UIColor selfModulationColor;
[ExportGroup("Transitions")]
[Export]
public TransitionSettingsAll transitionSettings;
public TransitionSettingsAll GetTransitionSettingsAll()
{
return transitionSettings;
}
[Export]
public UINumberTransition[] numberTransitions = new UINumberTransition[ 0 ];
public UINumberTransition[] GetNumberTransitions()
{
return numberTransitions;
}
public List<ActiveStyleTransition<UINumber,UIStyleNumberPropertyAndName>> activeNumberTransitions = new List<ActiveStyleTransition<UINumber,UIStyleNumberPropertyAndName>>();
public List<ActiveStyleTransition<UINumber,UIStyleNumberPropertyAndName>> GetActiveUINumberTransitions()
{
return activeNumberTransitions;
}
[Export]
public UIColorTransition[] colorTransitions = new UIColorTransition[ 0 ];
public UIColorTransition[] GetColorTransitions()
{
return colorTransitions;
}
public List<ActiveStyleTransition<UIColor,UIStyleColorPropertyAndName>> activeColorTransitions = new List<ActiveStyleTransition<UIColor,UIStyleColorPropertyAndName>>();
public List<ActiveStyleTransition<UIColor,UIStyleColorPropertyAndName>> GetActiveUIColorTransitions()
{
return activeColorTransitions;
}
[ExportGroup( "Sound" )]
[Export]
public UISoundData onFocusSound;
[Export]
public UISoundData onBlurSound;
public UISoundData GetUISoundProperty( UISoundProperty property )
{
if ( UISoundProperty.FocusEntered == property )
{
return onFocusSound;
}
else if ( UISoundProperty.FocusExited == property )
{
return onFocusSound;
}
return null;
}
public void SetUISoundProperty( UISoundProperty property, UISoundData stream )
{
if ( UISoundProperty.FocusEntered == property )
{
onFocusSound = stream;
}
else if ( UISoundProperty.FocusExited == property )
{
onFocusSound = stream;
}
}
[ExportGroup( "" )]
[Export]
public UIStyle parentStyle;
public UIStyle GetUIStyleParent()
{
return parentStyle;
}
public void SetUIStyleParent( UIStyle uiStyle )
{
parentStyle = uiStyle;
}
public UIPosition GetUIPosition()
{
return position;
}
public UILineWrap GetUILineWrap()
{
return lineWrap;
}
public UILayout GetUILayout()
{
return layout;
}
public ShaderUIColor[] GetShaderUIColors()
{
return null;
}
public ShaderUINumber[] GetShaderUINumbers()
{
return null;
}
public Font GetFont()
{
return font;
}
public Vector2 GetUISize()
{
return GetSize();
}
public UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property, string shaderPropertyName, UIStylePropertyContainer source )
{
switch ( property )
{
case UIStyleNumberProperty.Left: return left;
case UIStyleNumberProperty.Right: return right;
case UIStyleNumberProperty.Top: return top;
case UIStyleNumberProperty.Bottom: return bottom;
case UIStyleNumberProperty.Width: return width;
case UIStyleNumberProperty.Height: return height;
case UIStyleNumberProperty.HorizontalAlignment: return horizontalAlignment;
case UIStyleNumberProperty.VerticalAlignment: return verticalAlignment;
case UIStyleNumberProperty.VerticalPlacement: return verticalPlacement;
case UIStyleNumberProperty.ElementSpacing: return elementSpacing;
case UIStyleNumberProperty.LineSpacing: return lineSpacing;
case UIStyleNumberProperty.Margin: return margin;
case UIStyleNumberProperty.MarginLeft: return marginLeft;
case UIStyleNumberProperty.MarginRight: return marginRight;
case UIStyleNumberProperty.MarginTop: return marginTop;
case UIStyleNumberProperty.MarginBottom: return marginBottom;
case UIStyleNumberProperty.FontSize: return fontSize;
case UIStyleNumberProperty.FontOutlineSize: return outlineSize;
case UIStyleNumberProperty.FontShadowSize: return shadowSize;
case UIStyleNumberProperty.FontShadowOffsetX: return shadowOffsetX;
case UIStyleNumberProperty.FontShadowOffsetY: return shadowOffsetY;
}
return null;
}
public void SetUIStyleNumberProperty( UIStyleNumberProperty property, UINumber number )
{
switch ( property )
{
case UIStyleNumberProperty.Left: { left = number; } break;
case UIStyleNumberProperty.Right: { right = number; } break;
case UIStyleNumberProperty.Top: { top = number; } break;
case UIStyleNumberProperty.Bottom: { bottom = number; } break;
case UIStyleNumberProperty.HorizontalAlignment: { horizontalAlignment = number; } break;
case UIStyleNumberProperty.VerticalAlignment: { verticalAlignment = number; } break;
case UIStyleNumberProperty.VerticalPlacement: { verticalPlacement = number; } break;
case UIStyleNumberProperty.ElementSpacing: { elementSpacing = number; } break;
case UIStyleNumberProperty.LineSpacing: { lineSpacing = number; } break;
case UIStyleNumberProperty.Width: { width = number; } break;
case UIStyleNumberProperty.Height: { height = number; } break;
case UIStyleNumberProperty.Margin: { margin = number; } break;
case UIStyleNumberProperty.MarginLeft: { marginLeft = number; } break;
case UIStyleNumberProperty.MarginRight: { marginRight = number; } break;
case UIStyleNumberProperty.MarginTop: { marginTop = number; } break;
case UIStyleNumberProperty.MarginBottom: { marginBottom = number; } break;
case UIStyleNumberProperty.FontSize: { fontSize = number; } break;
case UIStyleNumberProperty.FontOutlineSize: { outlineSize = number; } break;
case UIStyleNumberProperty.FontShadowSize: { shadowSize = number; } break;
case UIStyleNumberProperty.FontShadowOffsetX: { shadowOffsetX = number; } break;
case UIStyleNumberProperty.FontShadowOffsetY: { shadowOffsetY = number; } break;
}
this.SetLayoutDirtyFlag();
}
bool _isAnimated = false;
int _isLayoutDirty = 3;
public void ResetDirtyFlags()
{
_isAnimated = false;
_isLayoutDirty = Mathf.Max( 0, _isLayoutDirty - 1 );
}
public void SetAnimatedFlag()
{
_isAnimated = true;
this.SetDirty();
}
public void SetLayoutDirtyFlag()
{
_isLayoutDirty = 3;
this.SetDirty();
}
public bool IsDirty()
{
return _isAnimated || _isLayoutDirty != 0 || this.HasActiveTransitions();
}
public UIColor GetUIStyleColorProperty( UIStyleColorProperty property, string shaderPropertyName, UIStylePropertyContainer source )
{
switch ( property )
{
case UIStyleColorProperty.FontColor: return fontColor;
case UIStyleColorProperty.FontOutlineColor: return outlineColor;
case UIStyleColorProperty.FontShadowColor: return shadowColor;
case UIStyleColorProperty.ModulationColor: return modulationColor;
case UIStyleColorProperty.SelfModulationColor: return selfModulationColor;
}
return null;
}
string hoverID = IDGenerator.GenerateID();
UICursor appliedCursor = null;
public override void _Ready()
{
VisibilityChanged += ()=>
{
var visible = IsVisibleInTree();
if ( visible )
{
onVisible?.Trigger();
}
else
{
onInvisible?.Trigger();
}
onVisibilityChanged?.Trigger();
};
FocusEntered += ()=>
{
AddUISelectorFlag( UISelectorFlag.Focus );
var sound = UIStyle.GetUISound( this, UISoundProperty.FocusEntered );
if ( sound != null )
{
GetUI().PlaySound( sound );
}
onFocusEntered?.Trigger();
};
FocusExited += ()=>
{
RemoveUISelectorFlag( UISelectorFlag.Focus );
var sound = UIStyle.GetUISound( this, UISoundProperty.FocusExited );
if ( sound != null )
{
GetUI().PlaySound( sound );
}
onFocusExited?.Trigger();
};
MouseEntered += ()=>
{
AddUISelectorFlag( UISelectorFlag.Hover, hoverID );
this.SetDirty();
var currentHoverCursor = UIStyle.ResolveHoverCursor( this );
if ( currentHoverCursor != null )
{
currentHoverCursor.ApplyCursor( this );
appliedCursor = currentHoverCursor;
this.LogInfo( "Set Cursor on Hover:", appliedCursor );
}
};
MouseExited += ()=>
{
RemoveUISelectorFlag( UISelectorFlag.Hover, hoverID );
this.SetDirty();
if ( appliedCursor != null )
{
this.LogInfo( "Reset Cursor on Exit");
appliedCursor.ClearCursor( this );
}
};
}
int _uiAncestorDepth;
public void ComputeUIAncestorDepth()
{
SetUIAncestorDepth( NodesWalker.Get().GetAncestorDistance( this, GetUI( false ) ) );
}
public void SetUIAncestorDepth( int depth )
{
_uiAncestorDepth = depth;
}
public int GetUIAncestorDepth()
{
return _uiAncestorDepth;
}
public override void _EnterTree()
{
ComputeUIAncestorDepth();
}
public override void _ExitTree()
{
ui = null;
_uiAncestorDepth = -1;
}
[Export]
public UISelectorFlag[] additionalSelectorFlags = [];
MapList<UISelectorFlag,string> _selectorFlagReferenceCounter = new MapList<UISelectorFlag, string>();
public void AddUISelectorFlag( UISelectorFlag flag, string reference = "" )
{
SetSelectorFlagReference( flag, reference, true );
}
public void RemoveUISelectorFlag( UISelectorFlag flag, string reference = "" )
{
SetSelectorFlagReference( flag, reference, false );
}
protected override void Dispose( bool disposing )
{
_selectorFlagReferenceCounter.Clear();
}
void SetSelectorFlagReference( UISelectorFlag flag, string reference, bool enable )
{
if ( enable )
{
_selectorFlagReferenceCounter.AddIfNotPresent( flag, reference );
}
else
{
_selectorFlagReferenceCounter.Remove( flag, reference );
}
var numFlagsBefore = _selectorFlags.Count;
_selectorFlags = _selectorFlagReferenceCounter.Keys.ToList();
_selectorFlags.AddRange( additionalSelectorFlags );
var changed = numFlagsBefore != _selectorFlags.Count;
UISelector.UpdateParentUISelectorFlags( this );
// this.LogInfo(
// "flag:", flag,
// "reference:", reference,
// "enable:", enable,
// "changed:", changed,
// "numFlagsBefore:", numFlagsBefore,
// "_selectorFlags:", _selectorFlags.Map( sf => sf.ResourcePath + " " + sf.ResourceName ).Join( ", " )
// );
if ( changed )
{
currentSelectors = _selectorFlags.Map( sf => RegexUtility.TrimToLastPathFragment( sf.ResourcePath ) ).Join( ", " );
this.SetDirty();
}
}
List<UISelectorFlag> _selectorFlags = [];
List<UISelectorFlag> _parentSelectorFlags = [];
public List<UISelectorFlag> GetUISelectorFlags()
{
return _selectorFlags;
}
public List<UISelectorFlag> GetParentUISelectorFlags()
{
return _parentSelectorFlags;
}
public virtual void Layout()
{
var layout = UIStyle.Layout( this );
switch ( layout )
{
case UILayout.___:
case UILayout.Flow_Left_Top:
{
UIFlowLayout.Apply( this );
}
break;
}
}
public Vector2 contentSize = Vector2.Zero;
public Vector2 contentOffset = Vector2.Zero;
UI ui;
public void SetUI( UI ui, bool computeDepth = true )
{
this.ui = ui;
if ( computeDepth )
{
ComputeUIAncestorDepth();
}
}
public UI GetUI( bool computeDepth = true )
{
if ( this.ui != null )
{
return this.ui;
}
var ui = this.FindParentThatIs<UI>();
if ( ui == null )
{
_uiAncestorDepth =-1;
this.LogInfo( "No UI in parents >", ui );
return null;
}
if ( computeDepth )
{
ComputeUIAncestorDepth();
}
return ui;
}
}
}