UnitOptionTree.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading;
  6. using UnityEngine;
  7. using UnityObject = UnityEngine.Object;
  8. namespace Unity.VisualScripting
  9. {
  10. public class UnitOptionTree : ExtensibleFuzzyOptionTree
  11. {
  12. #region Initialization
  13. public UnitOptionTree(GUIContent label) : base(label)
  14. {
  15. favorites = new Favorites(this);
  16. showBackgroundWorkerProgress = true;
  17. }
  18. public override IFuzzyOption Option(object item)
  19. {
  20. if (item is Namespace @namespace)
  21. {
  22. return new NamespaceOption(@namespace, true);
  23. }
  24. if (item is Type type)
  25. {
  26. return new TypeOption(type, true);
  27. }
  28. return base.Option(item);
  29. }
  30. public override void Prewarm()
  31. {
  32. filter = filter ?? UnitOptionFilter.Any;
  33. try
  34. {
  35. options = new HashSet<IUnitOption>(UnitBase.Subset(filter, reference));
  36. }
  37. catch (Exception ex)
  38. {
  39. Debug.LogError($"Failed to fetch node options for fuzzy finder (error log below).\nTry rebuilding the node options from '{UnitOptionUtility.GenerateUnitDatabasePath}'.\n\n{ex}");
  40. options = new HashSet<IUnitOption>();
  41. }
  42. typesWithMembers = new HashSet<Type>();
  43. foreach (var option in options)
  44. {
  45. if (option is IMemberUnitOption memberUnitOption && memberUnitOption.targetType != null)
  46. {
  47. typesWithMembers.Add(memberUnitOption.targetType);
  48. }
  49. }
  50. }
  51. private HashSet<IUnitOption> options;
  52. private HashSet<Type> typesWithMembers;
  53. #endregion
  54. #region Configuration
  55. public UnitOptionFilter filter { get; set; }
  56. public GraphReference reference { get; set; }
  57. public bool includeNone { get; set; }
  58. public bool surfaceCommonTypeLiterals { get; set; }
  59. public object[] rootOverride { get; set; }
  60. public FlowGraph graph => reference.graph as FlowGraph;
  61. public GameObject self => reference.self;
  62. public ActionDirection direction { get; set; } = ActionDirection.Any;
  63. #endregion
  64. #region Hierarchy
  65. private readonly FuzzyGroup enumsGroup = new FuzzyGroup("(Enums)", typeof(Enum).Icon());
  66. private readonly FuzzyGroup selfGroup = new FuzzyGroup("This", typeof(GameObject).Icon());
  67. private IEnumerable<UnitCategory> SpecialCategories()
  68. {
  69. yield return new UnitCategory("Codebase");
  70. yield return new UnitCategory("Events");
  71. yield return new UnitCategory("Variables");
  72. yield return new UnitCategory("Math");
  73. yield return new UnitCategory("Nesting");
  74. yield return new UnitCategory("Graphs");
  75. }
  76. public override IEnumerable<object> Root()
  77. {
  78. if (rootOverride != null && rootOverride.Length > 0)
  79. {
  80. foreach (var item in rootOverride)
  81. {
  82. yield return item;
  83. }
  84. yield break;
  85. }
  86. if (filter.CompatibleOutputType != null)
  87. {
  88. var outputType = filter.CompatibleOutputType;
  89. var outputTypeLiteral = options.FirstOrDefault(option => option is LiteralOption literalOption && literalOption.literalType == outputType);
  90. if (outputTypeLiteral != null)
  91. {
  92. yield return outputTypeLiteral;
  93. }
  94. HashSet<Type> noSurfaceConstructors = new HashSet<Type>()
  95. {
  96. typeof(string),
  97. typeof(object)
  98. };
  99. if (!noSurfaceConstructors.Contains(outputType))
  100. {
  101. var outputTypeConstructors = options.Where(option => option is InvokeMemberOption invokeMemberOption &&
  102. invokeMemberOption.targetType == outputType &&
  103. invokeMemberOption.unit.member.isConstructor);
  104. foreach (var outputTypeConstructor in outputTypeConstructors)
  105. {
  106. yield return outputTypeConstructor;
  107. }
  108. }
  109. if (outputType == typeof(bool))
  110. {
  111. foreach (var logicOperation in CategoryChildren(new UnitCategory("Logic")))
  112. {
  113. yield return logicOperation;
  114. }
  115. }
  116. if (outputType.IsNumeric())
  117. {
  118. foreach (var mathOperation in CategoryChildren(new UnitCategory("Math/Scalar")))
  119. {
  120. yield return mathOperation;
  121. }
  122. }
  123. if (outputType == typeof(Vector2))
  124. {
  125. foreach (var mathOperation in CategoryChildren(new UnitCategory("Math/Vector 2")))
  126. {
  127. yield return mathOperation;
  128. }
  129. }
  130. if (outputType == typeof(Vector3))
  131. {
  132. foreach (var mathOperation in CategoryChildren(new UnitCategory("Math/Vector 3")))
  133. {
  134. yield return mathOperation;
  135. }
  136. }
  137. if (outputType == typeof(Vector4))
  138. {
  139. foreach (var mathOperation in CategoryChildren(new UnitCategory("Math/Vector 4")))
  140. {
  141. yield return mathOperation;
  142. }
  143. }
  144. }
  145. if (surfaceCommonTypeLiterals)
  146. {
  147. foreach (var commonType in EditorTypeUtility.commonTypes)
  148. {
  149. if (commonType == filter.CompatibleOutputType)
  150. {
  151. continue;
  152. }
  153. var commonTypeLiteral = options.FirstOrDefault(option => option is LiteralOption literalOption && literalOption.literalType == commonType);
  154. if (commonTypeLiteral != null)
  155. {
  156. yield return commonTypeLiteral;
  157. }
  158. }
  159. }
  160. if (filter.CompatibleInputType != null)
  161. {
  162. var inputType = filter.CompatibleInputType;
  163. if (!inputType.IsPrimitive && inputType != typeof(object))
  164. {
  165. yield return inputType;
  166. }
  167. if (inputType == typeof(bool))
  168. {
  169. yield return options.Single(o => o.UnitIs<If>());
  170. yield return options.Single(o => o.UnitIs<SelectUnit>());
  171. }
  172. if (inputType == typeof(bool) || inputType.IsNumeric())
  173. {
  174. foreach (var logicOperation in CategoryChildren(new UnitCategory("Logic")))
  175. {
  176. yield return logicOperation;
  177. }
  178. }
  179. if (inputType.IsNumeric())
  180. {
  181. foreach (var mathOperation in CategoryChildren(new UnitCategory("Math/Scalar")))
  182. {
  183. yield return mathOperation;
  184. }
  185. }
  186. if (inputType == typeof(Vector2))
  187. {
  188. foreach (var mathOperation in CategoryChildren(new UnitCategory("Math/Vector 2")))
  189. {
  190. yield return mathOperation;
  191. }
  192. }
  193. if (inputType == typeof(Vector3))
  194. {
  195. foreach (var mathOperation in CategoryChildren(new UnitCategory("Math/Vector 3")))
  196. {
  197. yield return mathOperation;
  198. }
  199. }
  200. if (inputType == typeof(Vector4))
  201. {
  202. foreach (var mathOperation in CategoryChildren(new UnitCategory("Math/Vector 4")))
  203. {
  204. yield return mathOperation;
  205. }
  206. }
  207. if (typeof(IEnumerable).IsAssignableFrom(inputType) && (inputType != typeof(string) && inputType != typeof(Transform)))
  208. {
  209. foreach (var mathOperation in CategoryChildren(new UnitCategory("Collections"), false))
  210. {
  211. yield return mathOperation;
  212. }
  213. }
  214. if (typeof(IList).IsAssignableFrom(inputType))
  215. {
  216. foreach (var listOperation in CategoryChildren(new UnitCategory("Collections/Lists")))
  217. {
  218. yield return listOperation;
  219. }
  220. }
  221. if (typeof(IDictionary).IsAssignableFrom(inputType))
  222. {
  223. foreach (var dictionaryOperation in CategoryChildren(new UnitCategory("Collections/Dictionaries")))
  224. {
  225. yield return dictionaryOperation;
  226. }
  227. }
  228. }
  229. if (UnityAPI.Await
  230. (
  231. () =>
  232. {
  233. if (self != null)
  234. {
  235. selfGroup.label = self.name;
  236. selfGroup.icon = self.Icon();
  237. return true;
  238. }
  239. return false;
  240. }
  241. )
  242. )
  243. {
  244. yield return selfGroup;
  245. }
  246. foreach (var category in options.Select(option => option.category?.root)
  247. .NotNull()
  248. .Concat(SpecialCategories())
  249. .Distinct()
  250. .OrderBy(c => c.name))
  251. {
  252. yield return category;
  253. }
  254. foreach (var extensionRootItem in base.Root())
  255. {
  256. yield return extensionRootItem;
  257. }
  258. if (filter.Self)
  259. {
  260. var self = options.FirstOrDefault(option => option.UnitIs<This>());
  261. if (self != null)
  262. {
  263. yield return self;
  264. }
  265. }
  266. foreach (var unit in CategoryChildren(null))
  267. {
  268. yield return unit;
  269. }
  270. if (includeNone)
  271. {
  272. yield return null;
  273. }
  274. }
  275. public override IEnumerable<object> Children(object parent)
  276. {
  277. if (parent is Namespace @namespace)
  278. {
  279. return NamespaceChildren(@namespace);
  280. }
  281. else if (parent is Type type)
  282. {
  283. return TypeChildren(type);
  284. }
  285. else if (parent == enumsGroup)
  286. {
  287. return EnumsChildren();
  288. }
  289. else if (parent == selfGroup)
  290. {
  291. return SelfChildren();
  292. }
  293. else if (parent is UnitCategory unitCategory)
  294. {
  295. return CategoryChildren(unitCategory);
  296. }
  297. else if (parent is VariableKind variableKind)
  298. {
  299. return VariableKindChildren(variableKind);
  300. }
  301. else
  302. {
  303. return base.Children(parent);
  304. }
  305. }
  306. private IEnumerable<object> SelfChildren()
  307. {
  308. yield return typeof(GameObject);
  309. // Self components can be null if no script is assigned to them
  310. // https://support.ludiq.io/forums/5-bolt/topics/817-/
  311. foreach (var selfComponentType in UnityAPI.Await(() => self.GetComponents<Component>().NotUnityNull().Select(c => c.GetType())))
  312. {
  313. yield return selfComponentType;
  314. }
  315. }
  316. private IEnumerable<object> CodebaseChildren()
  317. {
  318. foreach (var rootNamespace in typesWithMembers.Where(t => !t.IsEnum)
  319. .Select(t => t.Namespace().Root)
  320. .OrderBy(ns => ns.DisplayName(false))
  321. .Distinct())
  322. {
  323. yield return rootNamespace;
  324. }
  325. if (filter.Literals && options.Any(option => option is LiteralOption literalOption && literalOption.literalType.IsEnum))
  326. {
  327. yield return enumsGroup;
  328. }
  329. }
  330. private IEnumerable<object> MathChildren()
  331. {
  332. foreach (var mathMember in GetMembers(typeof(Mathf)).Where(option => !((MemberUnit)option.unit).member.requiresTarget))
  333. {
  334. yield return mathMember;
  335. }
  336. }
  337. private IEnumerable<object> TimeChildren()
  338. {
  339. foreach (var timeMember in GetMembers(typeof(Time)).Where(option => !((MemberUnit)option.unit).member.requiresTarget))
  340. {
  341. yield return timeMember;
  342. }
  343. }
  344. private IEnumerable<object> NestingChildren()
  345. {
  346. foreach (var nester in options.Where(option => option.UnitIs<IGraphNesterElement>() && ((IGraphNesterElement)option.unit).nest.macro == null)
  347. .OrderBy(option => option.label))
  348. {
  349. yield return nester;
  350. }
  351. }
  352. private IEnumerable<object> MacroChildren()
  353. {
  354. foreach (var macroNester in options.Where(option => option.UnitIs<IGraphNesterElement>() && ((IGraphNesterElement)option.unit).nest.macro != null)
  355. .OrderBy(option => option.label))
  356. {
  357. yield return macroNester;
  358. }
  359. }
  360. private IEnumerable<object> VariablesChildren()
  361. {
  362. yield return VariableKind.Flow;
  363. yield return VariableKind.Graph;
  364. yield return VariableKind.Object;
  365. yield return VariableKind.Scene;
  366. yield return VariableKind.Application;
  367. yield return VariableKind.Saved;
  368. }
  369. private IEnumerable<object> VariableKindChildren(VariableKind kind)
  370. {
  371. foreach (var variable in options.OfType<IUnifiedVariableUnitOption>()
  372. .Where(option => option.kind == kind)
  373. .OrderBy(option => option.name))
  374. {
  375. yield return variable;
  376. }
  377. }
  378. private IEnumerable<object> NamespaceChildren(Namespace @namespace)
  379. {
  380. foreach (var childNamespace in GetChildrenNamespaces(@namespace))
  381. {
  382. yield return childNamespace;
  383. }
  384. foreach (var type in GetNamespaceTypes(@namespace))
  385. {
  386. yield return type;
  387. }
  388. }
  389. private IEnumerable<Namespace> GetChildrenNamespaces(Namespace @namespace)
  390. {
  391. if (!@namespace.IsGlobal)
  392. {
  393. foreach (var childNamespace in typesWithMembers.Where(t => !t.IsEnum)
  394. .SelectMany(t => t.Namespace().AndAncestors())
  395. .Distinct()
  396. .Where(ns => ns.Parent == @namespace)
  397. .OrderBy(ns => ns.DisplayName(false)))
  398. {
  399. yield return childNamespace;
  400. }
  401. }
  402. }
  403. private IEnumerable<Type> GetNamespaceTypes(Namespace @namespace)
  404. {
  405. foreach (var type in typesWithMembers.Where(t => t.Namespace() == @namespace && !t.IsEnum)
  406. .OrderBy(t => t.DisplayName()))
  407. {
  408. yield return type;
  409. }
  410. }
  411. private IEnumerable<object> TypeChildren(Type type)
  412. {
  413. foreach (var literal in options.Where(option => option is LiteralOption literalOption && literalOption.literalType == type))
  414. {
  415. yield return literal;
  416. }
  417. foreach (var expose in options.Where(option => option is ExposeOption exposeOption && exposeOption.exposedType == type))
  418. {
  419. yield return expose;
  420. }
  421. if (type.IsStruct())
  422. {
  423. foreach (var createStruct in options.Where(option => option is CreateStructOption createStructOption && createStructOption.structType == type))
  424. {
  425. yield return createStruct;
  426. }
  427. }
  428. foreach (var member in GetMembers(type))
  429. {
  430. yield return member;
  431. }
  432. }
  433. private IEnumerable<IUnitOption> GetMembers(Type type)
  434. {
  435. foreach (var member in options.Where(option => option is IMemberUnitOption memberUnitOption && memberUnitOption.targetType == type && option.unit.canDefine)
  436. .OrderBy(option => BoltCore.Configuration.groupInheritedMembers && ((MemberUnit)option.unit).member.isPseudoInherited)
  437. .ThenBy(option => option.order)
  438. .ThenBy(option => option.label))
  439. {
  440. yield return member;
  441. }
  442. }
  443. private IEnumerable<object> EnumsChildren()
  444. {
  445. foreach (var literal in options.Where(option => option is LiteralOption literalOption && literalOption.literalType.IsEnum)
  446. .OrderBy(option => option.label))
  447. {
  448. yield return literal;
  449. }
  450. }
  451. private IEnumerable<object> CategoryChildren(UnitCategory category, bool subCategories = true)
  452. {
  453. if (category != null && subCategories)
  454. {
  455. foreach (var subCategory in options.SelectMany(option => option.category == null ? Enumerable.Empty<UnitCategory>() : option.category.AndAncestors())
  456. .Distinct()
  457. .Where(c => c.parent == category)
  458. .OrderBy(c => c.name))
  459. {
  460. yield return subCategory;
  461. }
  462. }
  463. foreach (var unit in options.Where(option => option.category == category)
  464. .Where(option => !option.unitType.HasAttribute<SpecialUnitAttribute>())
  465. .OrderBy(option => option.order)
  466. .ThenBy(option => option.label))
  467. {
  468. yield return unit;
  469. }
  470. if (category != null)
  471. {
  472. if (category.root.name == "Events")
  473. {
  474. foreach (var eventChild in EventsChildren(category))
  475. {
  476. yield return eventChild;
  477. }
  478. }
  479. else if (category.fullName == "Codebase")
  480. {
  481. foreach (var codebaseChild in CodebaseChildren())
  482. {
  483. yield return codebaseChild;
  484. }
  485. }
  486. else if (category.fullName == "Variables")
  487. {
  488. foreach (var variableChild in VariablesChildren())
  489. {
  490. yield return variableChild;
  491. }
  492. }
  493. else if (category.fullName == "Math")
  494. {
  495. foreach (var mathChild in MathChildren())
  496. {
  497. yield return mathChild;
  498. }
  499. }
  500. else if (category.fullName == "Time")
  501. {
  502. foreach (var timeChild in TimeChildren())
  503. {
  504. yield return timeChild;
  505. }
  506. }
  507. else if (category.fullName == "Nesting")
  508. {
  509. foreach (var nestingChild in NestingChildren())
  510. {
  511. yield return nestingChild;
  512. }
  513. }
  514. else if (category.fullName == "Graphs")
  515. {
  516. foreach (var macroChild in MacroChildren())
  517. {
  518. yield return macroChild;
  519. }
  520. }
  521. }
  522. }
  523. private IEnumerable<object> EventsChildren(UnitCategory category)
  524. {
  525. foreach (var unit in options.Where(option => option.UnitIs<IEventUnit>() && option.category == category)
  526. .OrderBy(option => option.order)
  527. .ThenBy(option => option.label))
  528. {
  529. yield return unit;
  530. }
  531. }
  532. #endregion
  533. #region Search
  534. public override bool searchable { get; } = true;
  535. public override IEnumerable<ISearchResult> SearchResults(string query, CancellationToken cancellation)
  536. {
  537. foreach (var typeResult in typesWithMembers.Cancellable(cancellation).OrderableSearchFilter(query, t => t.DisplayName()))
  538. {
  539. yield return typeResult;
  540. }
  541. foreach (var optionResult in options.Cancellable(cancellation)
  542. .OrderableSearchFilter(query, o => o.haystack, o => o.formerHaystack)
  543. .WithoutInheritedDuplicates(r => r.result, cancellation))
  544. {
  545. yield return optionResult;
  546. }
  547. }
  548. public override string SearchResultLabel(object item, string query)
  549. {
  550. if (item is Type type)
  551. {
  552. return TypeOption.SearchResultLabel(type, query);
  553. }
  554. else if (item is IUnitOption unitOption)
  555. {
  556. return unitOption.SearchResultLabel(query);
  557. }
  558. else
  559. {
  560. return base.SearchResultLabel(item, query);
  561. }
  562. }
  563. #endregion
  564. #region Favorites
  565. public override ICollection<object> favorites { get; }
  566. public override bool CanFavorite(object item)
  567. {
  568. return (item as IUnitOption)?.favoritable ?? false;
  569. }
  570. public override string FavoritesLabel(object item)
  571. {
  572. return SearchResultLabel(item, null);
  573. }
  574. public override void OnFavoritesChange()
  575. {
  576. BoltFlow.Configuration.Save();
  577. }
  578. private class Favorites : ICollection<object>
  579. {
  580. public Favorites(UnitOptionTree tree)
  581. {
  582. this.tree = tree;
  583. }
  584. private UnitOptionTree tree { get; }
  585. private IEnumerable<IUnitOption> options => tree.options.Where(option => BoltFlow.Configuration.favoriteUnitOptions.Contains(option.favoriteKey));
  586. public bool IsReadOnly => false;
  587. public int Count => BoltFlow.Configuration.favoriteUnitOptions.Count;
  588. public IEnumerator<object> GetEnumerator()
  589. {
  590. foreach (var option in options)
  591. {
  592. yield return option;
  593. }
  594. }
  595. IEnumerator IEnumerable.GetEnumerator()
  596. {
  597. return GetEnumerator();
  598. }
  599. public bool Contains(object item)
  600. {
  601. var option = (IUnitOption)item;
  602. return BoltFlow.Configuration.favoriteUnitOptions.Contains(option.favoriteKey);
  603. }
  604. public void Add(object item)
  605. {
  606. var option = (IUnitOption)item;
  607. BoltFlow.Configuration.favoriteUnitOptions.Add(option.favoriteKey);
  608. }
  609. public bool Remove(object item)
  610. {
  611. var option = (IUnitOption)item;
  612. return BoltFlow.Configuration.favoriteUnitOptions.Remove(option.favoriteKey);
  613. }
  614. public void Clear()
  615. {
  616. BoltFlow.Configuration.favoriteUnitOptions.Clear();
  617. }
  618. public void CopyTo(object[] array, int arrayIndex)
  619. {
  620. if (array == null)
  621. {
  622. throw new ArgumentNullException(nameof(array));
  623. }
  624. if (arrayIndex < 0)
  625. {
  626. throw new ArgumentOutOfRangeException(nameof(arrayIndex));
  627. }
  628. if (array.Length - arrayIndex < Count)
  629. {
  630. throw new ArgumentException();
  631. }
  632. var i = 0;
  633. foreach (var item in this)
  634. {
  635. array[i + arrayIndex] = item;
  636. i++;
  637. }
  638. }
  639. }
  640. #endregion
  641. }
  642. }