using Godot; using System; using System.Collections.Generic; namespace Rokojori { [Tool] [GlobalClass,Icon("res://addons/rokojori_action_library/Icons/UI.svg")] public partial class UI : Control, IDisposable { [Export] public UISettings settings; [Export] public float fontZoom = 1; public enum UpdateMode { Always, Optimized, Only_Manual_Updates } [Export] public UpdateMode updateMode = UpdateMode.Always; [Export] public bool useParentSize = false; [ExportGroup("Functions")] [ExportToolButton( "Mouse:Stop => Pass")] public Callable StopToPassButton => Callable.From( ()=>{ MakeStopToPass(); } ); [Export] public Node[] stopToPassRoots = []; [ExportGroup("Read Only")] [Export] public float X_computedFontSizePixels = 1; List _customDisposers = []; public void AddForDisposing( ICustomDisposer customDisposer ) { _customDisposers.Add( customDisposer ); } protected override void Dispose( bool disposing ) { _customDisposers.ForEach( cd => cd.Dispose() ); _customDisposers = []; } [Export] public string[] customDisposerIDs = []; public void MakeStopToPass() { var roots = stopToPassRoots; if ( roots == null || roots.Length == 0 ) { roots = [ this ]; } foreach ( var n in roots ) { if ( n is Control c ) { if ( c.MouseFilter == MouseFilterEnum.Stop ) { c.MouseFilter = MouseFilterEnum.Pass; } } n.ForEach( ( c )=> { if ( c.MouseFilter == MouseFilterEnum.Stop ) { c.MouseFilter = MouseFilterEnum.Pass; } } ); } } Vector2 cachedSize; public static UI Get( Control c ) { return UIHolder.GetUI( c ); } public static UIStylePropertyContainer GetStylePropertyContainerParent( Control c ) { var it = c as Node; var walker = NodesWalker.Get(); it = walker.Parent( it ); if ( it is UI ) { return null; } while ( it != null ) { if ( it is UIStylePropertyContainer ) { return it as UIStylePropertyContainer; } it = walker.Parent( it ); if ( it is UI ) { it = null; } } return null; } public static TimeLine GetTimeLine( Control c, TimeLine timeLine ) { if ( timeLine != null ) { return timeLine; } var ui = Get( c ); if ( ui == null || ui.settings == null ) { return null; } return ui.settings.defaultTimeline; } Vector2 windowSize = new Vector2(); bool _resizeFlag = false; bool _fontSizeFlag = false; public bool sizeChanged => _resizeFlag; public bool fontSizeChanged => _fontSizeFlag; public override void _Process( double delta ) { if ( settings == null ) { return; } _resizeFlag = false; _fontSizeFlag = false; var newWindowSize = GetWindowSize(); if ( windowSize != newWindowSize ) { _resizeFlag = true; } if ( useParentSize ) { var parentSize = GetParent().Size; if ( parentSize != cachedSize ) { Size = parentSize; cachedSize = parentSize; _resizeFlag = true; } } onProcess.DispatchEvent( (float)delta ); UpdateFontSize(); UpdateUIElements(); // customDisposerIDs = _customDisposers.Map( c => c.GetUID() + ":" + c.GetInfo() ).ToArray(); } public void UpdateExternal( float delta) { onProcess.DispatchEvent( delta ); UpdateFontSize(); } public readonly EventSlot onInputEvent = new EventSlot(); public readonly EventSlot onProcess = new EventSlot(); public override void _Input( InputEvent ie ) { onInputEvent.DispatchEvent( ie ); } public void BindOwnChildren() { BindChildrenOf( this ); } public void BindChildrenOf( Node node ) { node.ForEach( img => img.SetUI( this ) ); node.ForEach( info => info.SetUI( this ) ); node.ForEach( text => text.SetUI( this ) ); node.ForEach( region => region.SetUI( this ) ); } public override void _Ready() { var sm = Unique.Get(); if ( sm != null ) { sm._onActiveDeviceChange.AddAction( a => UpdateUIInputs() ); } UpdateUIInputs(); } void UpdateUIInputs() { Nodes.ForEach( this, uii => uii.updateInfo = true ); } void UpdateFontSize() { if ( settings == null ) { return; } X_computedFontSizePixels = UINumber.Compute( this, settings.fontSize ) * fontZoom; if ( Theme != null ) { Theme.DefaultFontSize = Mathf.RoundToInt( X_computedFontSizePixels ); } } HashSet _dirty = new HashSet(); HashSet _updated = new HashSet(); public void SetDirty( UIStylePropertyContainerNode control ) { _dirty.Add( control ); } public void SetUpdated( UIStylePropertyContainerNode control ) { _updated.Add( control ); } void UpdateUIElements() { if ( UpdateMode.Always == updateMode ) { UpdateAllUIRegions(); } else if ( UpdateMode.Optimized == updateMode ) { if ( _fontSizeFlag || _resizeFlag ) { _dirty.Clear(); UpdateAllUIRegions(); } else { UpdateUIElementsOptimized(); } } } float time = 0; void UpdateUIElementsOptimized() { var timeNow = TimeLine.osTime; var elapsed = timeNow - time; time = timeNow; // this.LogInfo( Mathf.RoundToInt( elapsed * 1000f ) ); var parent = GetParent(); var sorted = new List( [ .. _dirty ]); if ( sorted.Count > 0 ) { // this.LogInfo( "Updating:", sorted.Count ); } _updated.Clear(); sorted.Sort( ( a, b ) => { return Mathf.RoundToInt( Mathf.Sign( a.GetUIAncestorDepth() - b.GetUIAncestorDepth() ) ); } ); sorted.ForEach( ( s )=> { if ( _updated.Contains( s ) ) { return; } // ( s as Control).LogInfo( "Updating:", s ); UpdateElement( s ); } ); _updated.Clear(); _dirty.RemoveWhere( d => ! d.IsDirty() ); foreach ( var d in _dirty ) { d.ResetDirtyFlags(); } } void UpdateElement( UIStylePropertyContainerNode control ) { if ( control is UIRegion region ) { region.Layout(); } else { UILayouting.UpdateChild( control as Control ); if ( UIStyle.Position( control ) == UIPosition.From_Layout ) { UILayouting.SetPositionInParentAnchor( control ); } } } public void UpdateAllUIRegions() { Nodes.ForEachDirectChild( this, r => r.Layout() ); } public static float GetWindowWidth( Control control ) { if ( Engine.IsEditorHint() ) { return ProjectSettings.GetSetting( "display/window/size/viewport_width" ).AsInt32(); } else { return control.GetWindow().Size.X; } } public static float GetWindowHeight( Control control ) { if ( Engine.IsEditorHint() ) { return ProjectSettings.GetSetting( "display/window/size/viewport_height" ).AsInt32(); } else { return control.GetWindow().Size.Y; } } public Vector2 GetWindowSize() { if ( Engine.IsEditorHint() ) { var width = ProjectSettings.GetSetting( "display/window/size/viewport_width" ).AsInt32(); var height = ProjectSettings.GetSetting( "display/window/size/viewport_height" ).AsInt32(); return new Vector2( width, height ); } else { return GetWindow().Size; } } } }