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> activeNumberTransitions = new List>(); public List> GetActiveUINumberTransitions() { return activeNumberTransitions; } [Export] public UIColorTransition[] colorTransitions = new UIColorTransition[ 0 ]; public UIColorTransition[] GetColorTransitions() { return colorTransitions; } public List> activeColorTransitions = new List>(); public List> 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 _selectorFlagReferenceCounter = new MapList(); 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 _selectorFlags = []; List _parentSelectorFlags = []; public List GetUISelectorFlags() { return _selectorFlags; } public List 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(); if ( ui == null ) { _uiAncestorDepth =-1; this.LogInfo( "No UI in parents >", ui ); return null; } if ( computeDepth ) { ComputeUIAncestorDepth(); } return ui; } } }