A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using UnityEditor;
5using UnityEngine;
6
7namespace Unity.VisualScripting
8{
9 [Widget(typeof(FlowState))]
10 public sealed class FlowStateWidget : NesterStateWidget<FlowState>, IDragAndDropHandler
11 {
12 public FlowStateWidget(StateCanvas canvas, FlowState state) : base(canvas, state)
13 {
14 state.nest.beforeGraphChange += BeforeGraphChange;
15 state.nest.afterGraphChange += AfterGraphChange;
16
17 if (state.nest.graph != null)
18 {
19 state.nest.graph.elements.CollectionChanged += CacheEventLinesOnUnityThread;
20 }
21 }
22
23 public override void Dispose()
24 {
25 base.Dispose();
26
27 state.nest.beforeGraphChange -= BeforeGraphChange;
28 state.nest.afterGraphChange -= AfterGraphChange;
29 }
30
31 private void BeforeGraphChange()
32 {
33 if (state.nest.graph != null)
34 {
35 state.nest.graph.elements.CollectionChanged -= CacheEventLinesOnUnityThread;
36 }
37 }
38
39 private void AfterGraphChange()
40 {
41 CacheEventLinesOnUnityThread();
42
43 if (state.nest.graph != null)
44 {
45 state.nest.graph.elements.CollectionChanged += CacheEventLinesOnUnityThread;
46 }
47 }
48
49 #region Model
50
51 private List<EventLine> eventLines { get; } = new List<EventLine>();
52
53 private void CacheEventLinesOnUnityThread()
54 {
55 UnityAPI.Async(CacheEventLines);
56 }
57
58 private void CacheEventLines()
59 {
60 eventLines.Clear();
61
62 if (state.nest.graph != null)
63 {
64 eventLines.AddRange(state.nest.graph.units
65 .OfType<IEventUnit>()
66 .Select(e => e.GetType())
67 .Distinct()
68 .Select(eventType => new EventLine(eventType))
69 .OrderBy(eventLine => eventLine.content.text));
70 }
71
72 Reposition();
73 }
74
75 protected override void CacheItemFirstTime()
76 {
77 base.CacheItemFirstTime();
78
79 CacheEventLines();
80 }
81
82 #endregion
83
84
85 #region Positioning
86
87 public Dictionary<EventLine, Rect> eventLinesPositions { get; } = new Dictionary<EventLine, Rect>();
88
89 public override void CachePosition()
90 {
91 base.CachePosition();
92
93 eventLinesPositions.Clear();
94
95 var y = contentInnerPosition.y;
96
97 foreach (var eventLine in eventLines)
98 {
99 var eventLinePosition = new Rect
100 (
101 contentInnerPosition.x,
102 y,
103 contentInnerPosition.width,
104 eventLine.GetHeight(contentInnerPosition.width)
105 );
106
107 eventLinesPositions.Add(eventLine, eventLinePosition);
108
109 y += eventLinePosition.height;
110 }
111 }
112
113 protected override float GetContentHeight(float width)
114 {
115 var eventLinesHeight = 0f;
116
117 foreach (var eventLine in eventLines)
118 {
119 eventLinesHeight += eventLine.GetHeight(width);
120 }
121
122 return eventLinesHeight;
123 }
124
125 #endregion
126
127
128 #region Drawing
129
130 protected override bool showContent => eventLines.Count > 0;
131
132 protected override void DrawContent()
133 {
134 foreach (var eventLine in eventLines)
135 {
136 eventLine.Draw(eventLinesPositions[eventLine]);
137 }
138 }
139
140 #endregion
141
142
143 #region Drag & Drop
144
145 public DragAndDropVisualMode dragAndDropVisualMode => DragAndDropVisualMode.Generic;
146
147 public bool AcceptsDragAndDrop()
148 {
149 return DragAndDropUtility.Is<ScriptGraphAsset>();
150 }
151
152 public void PerformDragAndDrop()
153 {
154 UndoUtility.RecordEditedObject("Drag & Drop Macro");
155 state.nest.source = GraphSource.Macro;
156 state.nest.macro = DragAndDropUtility.Get<ScriptGraphAsset>();
157 state.nest.embed = null;
158 GUI.changed = true;
159 }
160
161 public void UpdateDragAndDrop() { }
162
163 public void DrawDragAndDropPreview()
164 {
165 GraphGUI.DrawDragAndDropPreviewLabel(new Vector2(edgePosition.x, outerPosition.yMax), "Replace with: " + DragAndDropUtility.Get<ScriptGraphAsset>().name, typeof(ScriptGraphAsset).Icon());
166 }
167
168 public void ExitDragAndDrop() { }
169
170 #endregion
171
172
173 public new static class Styles
174 {
175 static Styles()
176 {
177 eventLine = new GUIStyle(EditorStyles.label);
178 eventLine.wordWrap = true;
179 eventLine.imagePosition = ImagePosition.TextOnly; // The icon is drawn manually
180 eventLine.padding = new RectOffset(0, 0, 3, 3);
181 }
182
183 public static readonly GUIStyle eventLine;
184
185 public static readonly float spaceAroundLineIcon = 5;
186 }
187
188 public class EventLine
189 {
190 public EventLine(Type eventType)
191 {
192 content = new GUIContent(BoltFlowNameUtility.UnitTitle(eventType, false, true), eventType.Icon()?[IconSize.Small]);
193 }
194
195 public GUIContent content { get; }
196
197 public float GetHeight(float width)
198 {
199 var labelWidth = width - Styles.spaceAroundLineIcon - IconSize.Small - Styles.spaceAroundLineIcon;
200
201 return Styles.eventLine.CalcHeight(content, labelWidth);
202 }
203
204 public void Draw(Rect position)
205 {
206 var iconPosition = new Rect
207 (
208 position.x + Styles.spaceAroundLineIcon,
209 position.y + Styles.eventLine.padding.top - 1,
210 IconSize.Small,
211 IconSize.Small
212 );
213
214 var labelPosition = new Rect
215 (
216 iconPosition.xMax + Styles.spaceAroundLineIcon,
217 position.y,
218 position.width - Styles.spaceAroundLineIcon - iconPosition.width - Styles.spaceAroundLineIcon,
219 position.height
220 );
221
222 if (content.image != null)
223 {
224 GUI.DrawTexture(iconPosition, content.image);
225 }
226
227 GUI.Label(labelPosition, content, Styles.eventLine);
228 }
229 }
230 }
231}