using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; namespace Unity.VisualScripting { [Widget(typeof(FlowState))] public sealed class FlowStateWidget : NesterStateWidget, IDragAndDropHandler { public FlowStateWidget(StateCanvas canvas, FlowState state) : base(canvas, state) { state.nest.beforeGraphChange += BeforeGraphChange; state.nest.afterGraphChange += AfterGraphChange; if (state.nest.graph != null) { state.nest.graph.elements.CollectionChanged += CacheEventLinesOnUnityThread; } } public override void Dispose() { base.Dispose(); state.nest.beforeGraphChange -= BeforeGraphChange; state.nest.afterGraphChange -= AfterGraphChange; } private void BeforeGraphChange() { if (state.nest.graph != null) { state.nest.graph.elements.CollectionChanged -= CacheEventLinesOnUnityThread; } } private void AfterGraphChange() { CacheEventLinesOnUnityThread(); if (state.nest.graph != null) { state.nest.graph.elements.CollectionChanged += CacheEventLinesOnUnityThread; } } #region Model private List eventLines { get; } = new List(); private void CacheEventLinesOnUnityThread() { UnityAPI.Async(CacheEventLines); } private void CacheEventLines() { eventLines.Clear(); if (state.nest.graph != null) { eventLines.AddRange(state.nest.graph.units .OfType() .Select(e => e.GetType()) .Distinct() .Select(eventType => new EventLine(eventType)) .OrderBy(eventLine => eventLine.content.text)); } Reposition(); } protected override void CacheItemFirstTime() { base.CacheItemFirstTime(); CacheEventLines(); } #endregion #region Positioning public Dictionary eventLinesPositions { get; } = new Dictionary(); public override void CachePosition() { base.CachePosition(); eventLinesPositions.Clear(); var y = contentInnerPosition.y; foreach (var eventLine in eventLines) { var eventLinePosition = new Rect ( contentInnerPosition.x, y, contentInnerPosition.width, eventLine.GetHeight(contentInnerPosition.width) ); eventLinesPositions.Add(eventLine, eventLinePosition); y += eventLinePosition.height; } } protected override float GetContentHeight(float width) { var eventLinesHeight = 0f; foreach (var eventLine in eventLines) { eventLinesHeight += eventLine.GetHeight(width); } return eventLinesHeight; } #endregion #region Drawing protected override bool showContent => eventLines.Count > 0; protected override void DrawContent() { foreach (var eventLine in eventLines) { eventLine.Draw(eventLinesPositions[eventLine]); } } #endregion #region Drag & Drop public DragAndDropVisualMode dragAndDropVisualMode => DragAndDropVisualMode.Generic; public bool AcceptsDragAndDrop() { return DragAndDropUtility.Is(); } public void PerformDragAndDrop() { UndoUtility.RecordEditedObject("Drag & Drop Macro"); state.nest.source = GraphSource.Macro; state.nest.macro = DragAndDropUtility.Get(); state.nest.embed = null; GUI.changed = true; } public void UpdateDragAndDrop() { } public void DrawDragAndDropPreview() { GraphGUI.DrawDragAndDropPreviewLabel(new Vector2(edgePosition.x, outerPosition.yMax), "Replace with: " + DragAndDropUtility.Get().name, typeof(ScriptGraphAsset).Icon()); } public void ExitDragAndDrop() { } #endregion public new static class Styles { static Styles() { eventLine = new GUIStyle(EditorStyles.label); eventLine.wordWrap = true; eventLine.imagePosition = ImagePosition.TextOnly; // The icon is drawn manually eventLine.padding = new RectOffset(0, 0, 3, 3); } public static readonly GUIStyle eventLine; public static readonly float spaceAroundLineIcon = 5; } public class EventLine { public EventLine(Type eventType) { content = new GUIContent(BoltFlowNameUtility.UnitTitle(eventType, false, true), eventType.Icon()?[IconSize.Small]); } public GUIContent content { get; } public float GetHeight(float width) { var labelWidth = width - Styles.spaceAroundLineIcon - IconSize.Small - Styles.spaceAroundLineIcon; return Styles.eventLine.CalcHeight(content, labelWidth); } public void Draw(Rect position) { var iconPosition = new Rect ( position.x + Styles.spaceAroundLineIcon, position.y + Styles.eventLine.padding.top - 1, IconSize.Small, IconSize.Small ); var labelPosition = new Rect ( iconPosition.xMax + Styles.spaceAroundLineIcon, position.y, position.width - Styles.spaceAroundLineIcon - iconPosition.width - Styles.spaceAroundLineIcon, position.height ); if (content.image != null) { GUI.DrawTexture(iconPosition, content.image); } GUI.Label(labelPosition, content, Styles.eventLine); } } } }