using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; namespace Unity.VisualScripting { public abstract class Widget : IWidget where TCanvas : class, ICanvas where TItem : class, IGraphItem { #region Context Shortcuts protected IGraphContext context => LudiqGraphsEditorUtility.editedContext.value; protected GraphReference reference => context.reference; protected IGraph graph => canvas.graph; protected TCanvas canvas { get; } ICanvas IWidget.canvas => canvas; protected GraphSelection selection => canvas.selection; protected ICanvasWindow window => canvas.window; protected Vector2 mousePosition => canvas.mousePosition; #endregion protected Widget(TCanvas canvas, TItem item) { Ensure.That(nameof(canvas)).IsNotNull(canvas); Ensure.That(nameof(item)).IsNotNull(item); this.canvas = canvas; this.item = item; e = new EventWrapper(GetType()); // Micro optimization hasDescriptor = item.HasDescriptor(); if (hasDescriptor) { DescriptorProvider.instance.AddListener(item, CacheDescription); } } public bool disposed { get; private set; } public virtual void Dispose() { if (hasDescriptor) { DescriptorProvider.instance.RemoveListener(item, CacheDescription); } disposed = true; } protected EventWrapper e { get; } public override string ToString() { return GetType().Name + "\nCID: " + e.control; } public virtual IEnumerable subWidgets => Enumerable.Empty(); protected void SubWidgetsChanged() { canvas.Recollect(); } #region Model protected TItem item { get; } IGraphItem IWidget.item => item; protected readonly bool hasDescriptor; public Metadata metadata { get; private set; } private Metadata lastFetchedMetadata; private bool cachedItemOnce = false; public virtual Metadata FetchMetadata() { return null; } protected virtual void CacheItemFirstTime() { CacheDescription(); } public virtual void CacheItem() { if (!cachedItemOnce) { CacheItemFirstTime(); cachedItemOnce = true; } // Note: on UnitPortWidget, FetchMetadata depends on the description, so it's important to validate it first. if (hasDescriptor) { item.Descriptor().Validate(); } metadata = FetchMetadata(); if (metadata != lastFetchedMetadata) { lastFetchedMetadata = metadata; CacheMetadata(); } } protected virtual void CacheMetadata() { } protected virtual void CacheDescription() { } #endregion #region Lifecycle public void RegisterControl() { e.RegisterControl(FocusType.Passive); } public virtual void BeforeFrame() { if (BoltCore.Configuration.developerMode && BoltCore.Configuration.debug) { debug = $"CID: {e.control}"; } if (isVisible || !dimInitialized) { UpdateDim(); } } public virtual void HandleCapture() { e.HandleCapture(isMouseOver, false); } public virtual void HandleInput() { HandleContext(); } public virtual void HandleRelease() { e.HandleRelease(); } public virtual void Update() { } #endregion #region Positioning protected virtual bool snapToGrid => false; public bool isPositionValid { get; set; } public virtual IEnumerable positionDependers => Enumerable.Empty(); public virtual IEnumerable positionDependencies => Enumerable.Empty(); public abstract Rect position { get; set; } public abstract float zIndex { get; set; } public void Reposition() { isPositionValid = false; foreach (var positionDepender in positionDependers) { positionDepender.Reposition(); } } public virtual void CachePositionFirstPass() { } public virtual void CachePosition() { } public void BringToFront() { var frontmostWidget = canvas.widgets.OrderByDescending(widget => widget.zIndex).FirstOrDefault(); if (frontmostWidget != null) { zIndex = frontmostWidget.zIndex + 1; } } public void SendToBack() { var backmostWidget = canvas.widgets.OrderBy(widget => widget.zIndex).FirstOrDefault(); if (backmostWidget != null) { zIndex = backmostWidget.zIndex - 1; } } #endregion #region Viewing public virtual bool canClip => !e.controlsMouse; public virtual Rect clippingPosition => position; public virtual void OnViewportChange() { } public bool isVisible { get; set; } #endregion #region Hovering public virtual Rect hotArea => position; public bool isMouseThrough => hotArea.Contains(mousePosition); public bool isMouseOver => canvas.hoveredWidget == this; #endregion #region Context protected virtual void OnContext() { var contextOptions = this.contextOptions.ToArray(); canvas.delayCall += () => { LudiqGUI.Dropdown ( e.mousePosition, action => canvas.delayCall += (Action)action, contextOptions, null ); }; } protected void HandleContext() { if (e.IsContextClick) { OnContext(); e.Use(); } } protected virtual IEnumerable contextOptions { get { yield break; } } #endregion #region Drawing protected string debug { get; set; } public virtual bool foregroundRequiresInput => false; public virtual bool backgroundRequiresInput => false; public virtual bool overlayRequiresInput => false; public virtual void DrawForeground() { if (BoltCore.Configuration.developerMode && BoltCore.Configuration.debug && !string.IsNullOrEmpty(debug)) { var debugContent = new GUIContent(debug); var debugLabelPosition = new Rect ( position.x, position.yMax + 5, EditorStyles.whiteMiniLabel.CalcSize(debugContent).x, EditorStyles.whiteMiniLabel.CalcSize(debugContent).y ); GUI.Label(debugLabelPosition, debugContent, EditorStyles.whiteMiniLabel); } } public virtual void DrawBackground() { if (BoltCore.Configuration.developerMode && BoltCore.Configuration.debug) { EditorGUI.DrawRect(clippingPosition, new Color(0, 0, 0, 0.1f)); } } public virtual void DrawOverlay() { if (BoltCore.Configuration.developerMode && BoltCore.Configuration.debug) { if (e.controlsMouse && e.controlsKeyboard) { EditorGUI.DrawRect(position, Color.cyan.WithAlpha(0.25f)); } else if (e.controlsMouse) { EditorGUI.DrawRect(position, Color.green.WithAlpha(0.25f)); } else if (e.controlsKeyboard) { EditorGUI.DrawRect(position, Color.blue.WithAlpha(0.25f)); } else if (isMouseOver) { EditorGUI.DrawRect(position, Color.yellow.WithAlpha(0.25f)); } } } #endregion #region Dimming private float dimAlpha = 1; private float dimTarget = 1; private bool dimInitialized = false; private const float dimFadeDuration = 0.075f; protected virtual bool dim => false; protected void BeginDim() { LudiqGUI.color.BeginOverride(LudiqGUI.color.value.WithAlphaMultiplied(dimAlpha)); } protected void EndDim() { LudiqGUI.color.EndOverride(); } protected void UpdateDim() { dimTarget = dim ? GraphGUI.Styles.dimAlpha : 1; if (!dimInitialized) { dimAlpha = dimTarget; dimInitialized = true; } dimAlpha = Mathf.MoveTowards(dimAlpha, dimTarget, (1 / dimFadeDuration) * canvas.repaintDeltaTime); } #endregion } }