No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UnitOptionTree.cs 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  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 (filter.NoConnection)
  230. {
  231. yield return new StickyNoteOption();
  232. }
  233. if (UnityAPI.Await
  234. (
  235. () =>
  236. {
  237. if (self != null)
  238. {
  239. selfGroup.label = self.name;
  240. selfGroup.icon = self.Icon();
  241. return true;
  242. }
  243. return false;
  244. }
  245. )
  246. )
  247. {
  248. yield return selfGroup;
  249. }
  250. foreach (var category in options.Select(option => option.category?.root)
  251. .NotNull()
  252. .Concat(SpecialCategories())
  253. .Distinct()
  254. .OrderBy(c => c.name))
  255. {
  256. yield return category;
  257. }
  258. foreach (var extensionRootItem in base.Root())
  259. {
  260. yield return extensionRootItem;
  261. }
  262. if (filter.Self)
  263. {
  264. var self = options.FirstOrDefault(option => option.UnitIs<This>());
  265. if (self != null)
  266. {
  267. yield return self;
  268. }
  269. }
  270. foreach (var unit in CategoryChildren(null))
  271. {
  272. yield return unit;
  273. }
  274. if (includeNone)
  275. {
  276. yield return null;
  277. }
  278. }
  279. public override IEnumerable<object> Children(object parent)
  280. {
  281. if (parent is Namespace @namespace)
  282. {
  283. return NamespaceChildren(@namespace);
  284. }
  285. else if (parent is Type type)
  286. {
  287. return TypeChildren(type);
  288. }
  289. else if (parent == enumsGroup)
  290. {
  291. return EnumsChildren();
  292. }
  293. else if (parent == selfGroup)
  294. {
  295. return SelfChildren();
  296. }
  297. else if (parent is UnitCategory unitCategory)
  298. {
  299. return CategoryChildren(unitCategory);
  300. }
  301. else if (parent is VariableKind variableKind)
  302. {
  303. return VariableKindChildren(variableKind);
  304. }
  305. else
  306. {
  307. return base.Children(parent);
  308. }
  309. }
  310. private IEnumerable<object> SelfChildren()
  311. {
  312. yield return typeof(GameObject);
  313. // Self components can be null if no script is assigned to them
  314. // https://support.ludiq.io/forums/5-bolt/topics/817-/
  315. foreach (var selfComponentType in UnityAPI.Await(() => self.GetComponents<Component>().NotUnityNull().Select(c => c.GetType())))
  316. {
  317. yield return selfComponentType;
  318. }
  319. }
  320. private IEnumerable<object> CodebaseChildren()
  321. {
  322. foreach (var rootNamespace in typesWithMembers.Where(t => !t.IsEnum)
  323. .Select(t => t.Namespace().Root)
  324. .OrderBy(ns => ns.DisplayName(false))
  325. .Distinct())
  326. {
  327. yield return rootNamespace;
  328. }
  329. if (filter.Literals && options.Any(option => option is LiteralOption literalOption && literalOption.literalType.IsEnum))
  330. {
  331. yield return enumsGroup;
  332. }
  333. }
  334. private IEnumerable<object> MathChildren()
  335. {
  336. foreach (var mathMember in GetMembers(typeof(Mathf)).Where(option => !((MemberUnit)option.unit).member.requiresTarget))
  337. {
  338. yield return mathMember;
  339. }
  340. }
  341. private IEnumerable<object> TimeChildren()
  342. {
  343. foreach (var timeMember in GetMembers(typeof(Time)).Where(option => !((MemberUnit)option.unit).member.requiresTarget))
  344. {
  345. yield return timeMember;
  346. }
  347. }
  348. private IEnumerable<object> NestingChildren()
  349. {
  350. foreach (var nester in options.Where(option => option.UnitIs<IGraphNesterElement>() && ((IGraphNesterElement)option.unit).nest.macro == null)
  351. .OrderBy(option => option.label))
  352. {
  353. yield return nester;
  354. }
  355. }
  356. private IEnumerable<object> MacroChildren()
  357. {
  358. foreach (var macroNester in options.Where(option => option.UnitIs<IGraphNesterElement>() && ((IGraphNesterElement)option.unit).nest.macro != null)
  359. .OrderBy(option => option.label))
  360. {
  361. yield return macroNester;
  362. }
  363. }
  364. private IEnumerable<object> VariablesChildren()
  365. {
  366. yield return VariableKind.Flow;
  367. yield return VariableKind.Graph;
  368. yield return VariableKind.Object;
  369. yield return VariableKind.Scene;
  370. yield return VariableKind.Application;
  371. yield return VariableKind.Saved;
  372. }
  373. private IEnumerable<object> VariableKindChildren(VariableKind kind)
  374. {
  375. foreach (var variable in options.OfType<IUnifiedVariableUnitOption>()
  376. .Where(option => option.kind == kind)
  377. .OrderBy(option => option.name))
  378. {
  379. yield return variable;
  380. }
  381. }
  382. private IEnumerable<object> NamespaceChildren(Namespace @namespace)
  383. {
  384. foreach (var childNamespace in GetChildrenNamespaces(@namespace))
  385. {
  386. yield return childNamespace;
  387. }
  388. foreach (var type in GetNamespaceTypes(@namespace))
  389. {
  390. yield return type;
  391. }
  392. }
  393. private IEnumerable<Namespace> GetChildrenNamespaces(Namespace @namespace)
  394. {
  395. if (!@namespace.IsGlobal)
  396. {
  397. foreach (var childNamespace in typesWithMembers.Where(t => !t.IsEnum)
  398. .SelectMany(t => t.Namespace().AndAncestors())
  399. .Distinct()
  400. .Where(ns => ns.Parent == @namespace)
  401. .OrderBy(ns => ns.DisplayName(false)))
  402. {
  403. yield return childNamespace;
  404. }
  405. }
  406. }
  407. private IEnumerable<Type> GetNamespaceTypes(Namespace @namespace)
  408. {
  409. foreach (var type in typesWithMembers.Where(t => t.Namespace() == @namespace && !t.IsEnum)
  410. .OrderBy(t => t.DisplayName()))
  411. {
  412. yield return type;
  413. }
  414. }
  415. private IEnumerable<object> TypeChildren(Type type)
  416. {
  417. foreach (var literal in options.Where(option => option is LiteralOption literalOption && literalOption.literalType == type))
  418. {
  419. yield return literal;
  420. }
  421. foreach (var expose in options.Where(option => option is ExposeOption exposeOption && exposeOption.exposedType == type))
  422. {
  423. yield return expose;
  424. }
  425. if (type.IsStruct())
  426. {
  427. foreach (var createStruct in options.Where(option => option is CreateStructOption createStructOption && createStructOption.structType == type))
  428. {
  429. yield return createStruct;
  430. }
  431. }
  432. foreach (var member in GetMembers(type))
  433. {
  434. yield return member;
  435. }
  436. }
  437. private IEnumerable<IUnitOption> GetMembers(Type type)
  438. {
  439. foreach (var member in options.Where(option => option is IMemberUnitOption memberUnitOption && memberUnitOption.targetType == type && option.unit.canDefine)
  440. .OrderBy(option => BoltCore.Configuration.groupInheritedMembers && ((MemberUnit)option.unit).member.isPseudoInherited)
  441. .ThenBy(option => option.order)
  442. .ThenBy(option => option.label))
  443. {
  444. yield return member;
  445. }
  446. }
  447. private IEnumerable<object> EnumsChildren()
  448. {
  449. foreach (var literal in options.Where(option => option is LiteralOption literalOption && literalOption.literalType.IsEnum)
  450. .OrderBy(option => option.label))
  451. {
  452. yield return literal;
  453. }
  454. }
  455. private IEnumerable<object> CategoryChildren(UnitCategory category, bool subCategories = true)
  456. {
  457. if (category != null && subCategories)
  458. {
  459. foreach (var subCategory in options.SelectMany(option => option.category == null ? Enumerable.Empty<UnitCategory>() : option.category.AndAncestors())
  460. .Distinct()
  461. .Where(c => c.parent == category)
  462. .OrderBy(c => c.name))
  463. {
  464. yield return subCategory;
  465. }
  466. }
  467. foreach (var unit in options.Where(option => option.category == category)
  468. .Where(option => !option.unitType.HasAttribute<SpecialUnitAttribute>())
  469. .OrderBy(option => option.order)
  470. .ThenBy(option => option.label))
  471. {
  472. yield return unit;
  473. }
  474. if (category != null)
  475. {
  476. if (category.root.name == "Events")
  477. {
  478. foreach (var eventChild in EventsChildren(category))
  479. {
  480. yield return eventChild;
  481. }
  482. }
  483. else if (category.fullName == "Codebase")
  484. {
  485. foreach (var codebaseChild in CodebaseChildren())
  486. {
  487. yield return codebaseChild;
  488. }
  489. }
  490. else if (category.fullName == "Variables")
  491. {
  492. foreach (var variableChild in VariablesChildren())
  493. {
  494. yield return variableChild;
  495. }
  496. }
  497. else if (category.fullName == "Math")
  498. {
  499. foreach (var mathChild in MathChildren())
  500. {
  501. yield return mathChild;
  502. }
  503. }
  504. else if (category.fullName == "Time")
  505. {
  506. foreach (var timeChild in TimeChildren())
  507. {
  508. yield return timeChild;
  509. }
  510. }
  511. else if (category.fullName == "Nesting")
  512. {
  513. foreach (var nestingChild in NestingChildren())
  514. {
  515. yield return nestingChild;
  516. }
  517. }
  518. else if (category.fullName == "Graphs")
  519. {
  520. foreach (var macroChild in MacroChildren())
  521. {
  522. yield return macroChild;
  523. }
  524. }
  525. }
  526. }
  527. private IEnumerable<object> EventsChildren(UnitCategory category)
  528. {
  529. foreach (var unit in options.Where(option => option.UnitIs<IEventUnit>() && option.category == category)
  530. .OrderBy(option => option.order)
  531. .ThenBy(option => option.label))
  532. {
  533. yield return unit;
  534. }
  535. }
  536. #endregion
  537. #region Search
  538. public override bool searchable { get; } = true;
  539. public override IEnumerable<ISearchResult> SearchResults(string query, CancellationToken cancellation)
  540. {
  541. foreach (var typeResult in typesWithMembers.Cancellable(cancellation).OrderableSearchFilter(query, t => t.DisplayName()))
  542. {
  543. yield return typeResult;
  544. }
  545. foreach (var optionResult in options.Cancellable(cancellation)
  546. .OrderableSearchFilter(query, o => o.haystack, o => o.formerHaystack)
  547. .WithoutInheritedDuplicates(r => r.result, cancellation))
  548. {
  549. yield return optionResult;
  550. }
  551. }
  552. public override string SearchResultLabel(object item, string query)
  553. {
  554. if (item is Type type)
  555. {
  556. return TypeOption.SearchResultLabel(type, query);
  557. }
  558. else if (item is IUnitOption unitOption)
  559. {
  560. return unitOption.SearchResultLabel(query);
  561. }
  562. else
  563. {
  564. return base.SearchResultLabel(item, query);
  565. }
  566. }
  567. #endregion
  568. #region Favorites
  569. public override ICollection<object> favorites { get; }
  570. public override bool CanFavorite(object item)
  571. {
  572. return (item as IUnitOption)?.favoritable ?? false;
  573. }
  574. public override string FavoritesLabel(object item)
  575. {
  576. return SearchResultLabel(item, null);
  577. }
  578. public override void OnFavoritesChange()
  579. {
  580. BoltFlow.Configuration.Save();
  581. }
  582. private class Favorites : ICollection<object>
  583. {
  584. public Favorites(UnitOptionTree tree)
  585. {
  586. this.tree = tree;
  587. }
  588. private UnitOptionTree tree { get; }
  589. private IEnumerable<IUnitOption> options => tree.options.Where(option => BoltFlow.Configuration.favoriteUnitOptions.Contains(option.favoriteKey));
  590. public bool IsReadOnly => false;
  591. public int Count => BoltFlow.Configuration.favoriteUnitOptions.Count;
  592. public IEnumerator<object> GetEnumerator()
  593. {
  594. foreach (var option in options)
  595. {
  596. yield return option;
  597. }
  598. }
  599. IEnumerator IEnumerable.GetEnumerator()
  600. {
  601. return GetEnumerator();
  602. }
  603. public bool Contains(object item)
  604. {
  605. var option = (IUnitOption)item;
  606. return BoltFlow.Configuration.favoriteUnitOptions.Contains(option.favoriteKey);
  607. }
  608. public void Add(object item)
  609. {
  610. var option = (IUnitOption)item;
  611. BoltFlow.Configuration.favoriteUnitOptions.Add(option.favoriteKey);
  612. }
  613. public bool Remove(object item)
  614. {
  615. var option = (IUnitOption)item;
  616. return BoltFlow.Configuration.favoriteUnitOptions.Remove(option.favoriteKey);
  617. }
  618. public void Clear()
  619. {
  620. BoltFlow.Configuration.favoriteUnitOptions.Clear();
  621. }
  622. public void CopyTo(object[] array, int arrayIndex)
  623. {
  624. if (array == null)
  625. {
  626. throw new ArgumentNullException(nameof(array));
  627. }
  628. if (arrayIndex < 0)
  629. {
  630. throw new ArgumentOutOfRangeException(nameof(arrayIndex));
  631. }
  632. if (array.Length - arrayIndex < Count)
  633. {
  634. throw new ArgumentException();
  635. }
  636. var i = 0;
  637. foreach (var item in this)
  638. {
  639. array[i + arrayIndex] = item;
  640. i++;
  641. }
  642. }
  643. }
  644. #endregion
  645. }
  646. }