2023-03-28 13:24:16 -04:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Linq ;
using System.Threading ;
using UnityEngine ;
using UnityObject = UnityEngine . Object ;
namespace Unity.VisualScripting
{
public class UnitOptionTree : ExtensibleFuzzyOptionTree
{
#region Initialization
public UnitOptionTree ( GUIContent label ) : base ( label )
{
favorites = new Favorites ( this ) ;
showBackgroundWorkerProgress = true ;
}
public override IFuzzyOption Option ( object item )
{
if ( item is Namespace @namespace )
{
return new NamespaceOption ( @namespace , true ) ;
}
if ( item is Type type )
{
return new TypeOption ( type , true ) ;
}
return base . Option ( item ) ;
}
public override void Prewarm ( )
{
filter = filter ? ? UnitOptionFilter . Any ;
try
{
options = new HashSet < IUnitOption > ( UnitBase . Subset ( filter , reference ) ) ;
}
catch ( Exception ex )
{
Debug . LogError ( $"Failed to fetch node options for fuzzy finder (error log below).\nTry rebuilding the node options from '{UnitOptionUtility.GenerateUnitDatabasePath}'.\n\n{ex}" ) ;
options = new HashSet < IUnitOption > ( ) ;
}
typesWithMembers = new HashSet < Type > ( ) ;
foreach ( var option in options )
{
if ( option is IMemberUnitOption memberUnitOption & & memberUnitOption . targetType ! = null )
{
typesWithMembers . Add ( memberUnitOption . targetType ) ;
}
}
}
private HashSet < IUnitOption > options ;
private HashSet < Type > typesWithMembers ;
#endregion
#region Configuration
public UnitOptionFilter filter { get ; set ; }
public GraphReference reference { get ; set ; }
public bool includeNone { get ; set ; }
public bool surfaceCommonTypeLiterals { get ; set ; }
public object [ ] rootOverride { get ; set ; }
public FlowGraph graph = > reference . graph as FlowGraph ;
public GameObject self = > reference . self ;
public ActionDirection direction { get ; set ; } = ActionDirection . Any ;
#endregion
#region Hierarchy
private readonly FuzzyGroup enumsGroup = new FuzzyGroup ( "(Enums)" , typeof ( Enum ) . Icon ( ) ) ;
private readonly FuzzyGroup selfGroup = new FuzzyGroup ( "This" , typeof ( GameObject ) . Icon ( ) ) ;
private IEnumerable < UnitCategory > SpecialCategories ( )
{
yield return new UnitCategory ( "Codebase" ) ;
yield return new UnitCategory ( "Events" ) ;
yield return new UnitCategory ( "Variables" ) ;
yield return new UnitCategory ( "Math" ) ;
yield return new UnitCategory ( "Nesting" ) ;
yield return new UnitCategory ( "Graphs" ) ;
}
public override IEnumerable < object > Root ( )
{
if ( rootOverride ! = null & & rootOverride . Length > 0 )
{
foreach ( var item in rootOverride )
{
yield return item ;
}
yield break ;
}
if ( filter . CompatibleOutputType ! = null )
{
var outputType = filter . CompatibleOutputType ;
var outputTypeLiteral = options . FirstOrDefault ( option = > option is LiteralOption literalOption & & literalOption . literalType = = outputType ) ;
if ( outputTypeLiteral ! = null )
{
yield return outputTypeLiteral ;
}
HashSet < Type > noSurfaceConstructors = new HashSet < Type > ( )
{
typeof ( string ) ,
typeof ( object )
} ;
if ( ! noSurfaceConstructors . Contains ( outputType ) )
{
var outputTypeConstructors = options . Where ( option = > option is InvokeMemberOption invokeMemberOption & &
invokeMemberOption . targetType = = outputType & &
invokeMemberOption . unit . member . isConstructor ) ;
foreach ( var outputTypeConstructor in outputTypeConstructors )
{
yield return outputTypeConstructor ;
}
}
if ( outputType = = typeof ( bool ) )
{
foreach ( var logicOperation in CategoryChildren ( new UnitCategory ( "Logic" ) ) )
{
yield return logicOperation ;
}
}
if ( outputType . IsNumeric ( ) )
{
foreach ( var mathOperation in CategoryChildren ( new UnitCategory ( "Math/Scalar" ) ) )
{
yield return mathOperation ;
}
}
if ( outputType = = typeof ( Vector2 ) )
{
foreach ( var mathOperation in CategoryChildren ( new UnitCategory ( "Math/Vector 2" ) ) )
{
yield return mathOperation ;
}
}
if ( outputType = = typeof ( Vector3 ) )
{
foreach ( var mathOperation in CategoryChildren ( new UnitCategory ( "Math/Vector 3" ) ) )
{
yield return mathOperation ;
}
}
if ( outputType = = typeof ( Vector4 ) )
{
foreach ( var mathOperation in CategoryChildren ( new UnitCategory ( "Math/Vector 4" ) ) )
{
yield return mathOperation ;
}
}
}
if ( surfaceCommonTypeLiterals )
{
foreach ( var commonType in EditorTypeUtility . commonTypes )
{
if ( commonType = = filter . CompatibleOutputType )
{
continue ;
}
var commonTypeLiteral = options . FirstOrDefault ( option = > option is LiteralOption literalOption & & literalOption . literalType = = commonType ) ;
if ( commonTypeLiteral ! = null )
{
yield return commonTypeLiteral ;
}
}
}
if ( filter . CompatibleInputType ! = null )
{
var inputType = filter . CompatibleInputType ;
if ( ! inputType . IsPrimitive & & inputType ! = typeof ( object ) )
{
yield return inputType ;
}
if ( inputType = = typeof ( bool ) )
{
yield return options . Single ( o = > o . UnitIs < If > ( ) ) ;
yield return options . Single ( o = > o . UnitIs < SelectUnit > ( ) ) ;
}
if ( inputType = = typeof ( bool ) | | inputType . IsNumeric ( ) )
{
foreach ( var logicOperation in CategoryChildren ( new UnitCategory ( "Logic" ) ) )
{
yield return logicOperation ;
}
}
if ( inputType . IsNumeric ( ) )
{
foreach ( var mathOperation in CategoryChildren ( new UnitCategory ( "Math/Scalar" ) ) )
{
yield return mathOperation ;
}
}
if ( inputType = = typeof ( Vector2 ) )
{
foreach ( var mathOperation in CategoryChildren ( new UnitCategory ( "Math/Vector 2" ) ) )
{
yield return mathOperation ;
}
}
if ( inputType = = typeof ( Vector3 ) )
{
foreach ( var mathOperation in CategoryChildren ( new UnitCategory ( "Math/Vector 3" ) ) )
{
yield return mathOperation ;
}
}
if ( inputType = = typeof ( Vector4 ) )
{
foreach ( var mathOperation in CategoryChildren ( new UnitCategory ( "Math/Vector 4" ) ) )
{
yield return mathOperation ;
}
}
if ( typeof ( IEnumerable ) . IsAssignableFrom ( inputType ) & & ( inputType ! = typeof ( string ) & & inputType ! = typeof ( Transform ) ) )
{
foreach ( var mathOperation in CategoryChildren ( new UnitCategory ( "Collections" ) , false ) )
{
yield return mathOperation ;
}
}
if ( typeof ( IList ) . IsAssignableFrom ( inputType ) )
{
foreach ( var listOperation in CategoryChildren ( new UnitCategory ( "Collections/Lists" ) ) )
{
yield return listOperation ;
}
}
if ( typeof ( IDictionary ) . IsAssignableFrom ( inputType ) )
{
foreach ( var dictionaryOperation in CategoryChildren ( new UnitCategory ( "Collections/Dictionaries" ) ) )
{
yield return dictionaryOperation ;
}
}
}
2023-05-07 18:43:11 -04:00
if ( filter . NoConnection )
{
yield return new StickyNoteOption ( ) ;
}
2023-03-28 13:24:16 -04:00
if ( UnityAPI . Await
(
( ) = >
{
if ( self ! = null )
{
selfGroup . label = self . name ;
selfGroup . icon = self . Icon ( ) ;
return true ;
}
return false ;
}
)
)
{
yield return selfGroup ;
}
foreach ( var category in options . Select ( option = > option . category ? . root )
. NotNull ( )
. Concat ( SpecialCategories ( ) )
. Distinct ( )
. OrderBy ( c = > c . name ) )
{
yield return category ;
}
foreach ( var extensionRootItem in base . Root ( ) )
{
yield return extensionRootItem ;
}
if ( filter . Self )
{
var self = options . FirstOrDefault ( option = > option . UnitIs < This > ( ) ) ;
if ( self ! = null )
{
yield return self ;
}
}
foreach ( var unit in CategoryChildren ( null ) )
{
yield return unit ;
}
if ( includeNone )
{
yield return null ;
}
}
public override IEnumerable < object > Children ( object parent )
{
if ( parent is Namespace @namespace )
{
return NamespaceChildren ( @namespace ) ;
}
else if ( parent is Type type )
{
return TypeChildren ( type ) ;
}
else if ( parent = = enumsGroup )
{
return EnumsChildren ( ) ;
}
else if ( parent = = selfGroup )
{
return SelfChildren ( ) ;
}
else if ( parent is UnitCategory unitCategory )
{
return CategoryChildren ( unitCategory ) ;
}
else if ( parent is VariableKind variableKind )
{
return VariableKindChildren ( variableKind ) ;
}
else
{
return base . Children ( parent ) ;
}
}
private IEnumerable < object > SelfChildren ( )
{
yield return typeof ( GameObject ) ;
// Self components can be null if no script is assigned to them
// https://support.ludiq.io/forums/5-bolt/topics/817-/
foreach ( var selfComponentType in UnityAPI . Await ( ( ) = > self . GetComponents < Component > ( ) . NotUnityNull ( ) . Select ( c = > c . GetType ( ) ) ) )
{
yield return selfComponentType ;
}
}
private IEnumerable < object > CodebaseChildren ( )
{
foreach ( var rootNamespace in typesWithMembers . Where ( t = > ! t . IsEnum )
. Select ( t = > t . Namespace ( ) . Root )
. OrderBy ( ns = > ns . DisplayName ( false ) )
. Distinct ( ) )
{
yield return rootNamespace ;
}
if ( filter . Literals & & options . Any ( option = > option is LiteralOption literalOption & & literalOption . literalType . IsEnum ) )
{
yield return enumsGroup ;
}
}
private IEnumerable < object > MathChildren ( )
{
foreach ( var mathMember in GetMembers ( typeof ( Mathf ) ) . Where ( option = > ! ( ( MemberUnit ) option . unit ) . member . requiresTarget ) )
{
yield return mathMember ;
}
}
private IEnumerable < object > TimeChildren ( )
{
foreach ( var timeMember in GetMembers ( typeof ( Time ) ) . Where ( option = > ! ( ( MemberUnit ) option . unit ) . member . requiresTarget ) )
{
yield return timeMember ;
}
}
private IEnumerable < object > NestingChildren ( )
{
foreach ( var nester in options . Where ( option = > option . UnitIs < IGraphNesterElement > ( ) & & ( ( IGraphNesterElement ) option . unit ) . nest . macro = = null )
. OrderBy ( option = > option . label ) )
{
yield return nester ;
}
}
private IEnumerable < object > MacroChildren ( )
{
foreach ( var macroNester in options . Where ( option = > option . UnitIs < IGraphNesterElement > ( ) & & ( ( IGraphNesterElement ) option . unit ) . nest . macro ! = null )
. OrderBy ( option = > option . label ) )
{
yield return macroNester ;
}
}
private IEnumerable < object > VariablesChildren ( )
{
yield return VariableKind . Flow ;
yield return VariableKind . Graph ;
yield return VariableKind . Object ;
yield return VariableKind . Scene ;
yield return VariableKind . Application ;
yield return VariableKind . Saved ;
}
private IEnumerable < object > VariableKindChildren ( VariableKind kind )
{
foreach ( var variable in options . OfType < IUnifiedVariableUnitOption > ( )
. Where ( option = > option . kind = = kind )
. OrderBy ( option = > option . name ) )
{
yield return variable ;
}
}
private IEnumerable < object > NamespaceChildren ( Namespace @namespace )
{
foreach ( var childNamespace in GetChildrenNamespaces ( @namespace ) )
{
yield return childNamespace ;
}
foreach ( var type in GetNamespaceTypes ( @namespace ) )
{
yield return type ;
}
}
private IEnumerable < Namespace > GetChildrenNamespaces ( Namespace @namespace )
{
if ( ! @namespace . IsGlobal )
{
foreach ( var childNamespace in typesWithMembers . Where ( t = > ! t . IsEnum )
. SelectMany ( t = > t . Namespace ( ) . AndAncestors ( ) )
. Distinct ( )
. Where ( ns = > ns . Parent = = @namespace )
. OrderBy ( ns = > ns . DisplayName ( false ) ) )
{
yield return childNamespace ;
}
}
}
private IEnumerable < Type > GetNamespaceTypes ( Namespace @namespace )
{
foreach ( var type in typesWithMembers . Where ( t = > t . Namespace ( ) = = @namespace & & ! t . IsEnum )
. OrderBy ( t = > t . DisplayName ( ) ) )
{
yield return type ;
}
}
private IEnumerable < object > TypeChildren ( Type type )
{
foreach ( var literal in options . Where ( option = > option is LiteralOption literalOption & & literalOption . literalType = = type ) )
{
yield return literal ;
}
foreach ( var expose in options . Where ( option = > option is ExposeOption exposeOption & & exposeOption . exposedType = = type ) )
{
yield return expose ;
}
if ( type . IsStruct ( ) )
{
foreach ( var createStruct in options . Where ( option = > option is CreateStructOption createStructOption & & createStructOption . structType = = type ) )
{
yield return createStruct ;
}
}
foreach ( var member in GetMembers ( type ) )
{
yield return member ;
}
}
private IEnumerable < IUnitOption > GetMembers ( Type type )
{
foreach ( var member in options . Where ( option = > option is IMemberUnitOption memberUnitOption & & memberUnitOption . targetType = = type & & option . unit . canDefine )
. OrderBy ( option = > BoltCore . Configuration . groupInheritedMembers & & ( ( MemberUnit ) option . unit ) . member . isPseudoInherited )
. ThenBy ( option = > option . order )
. ThenBy ( option = > option . label ) )
{
yield return member ;
}
}
private IEnumerable < object > EnumsChildren ( )
{
foreach ( var literal in options . Where ( option = > option is LiteralOption literalOption & & literalOption . literalType . IsEnum )
. OrderBy ( option = > option . label ) )
{
yield return literal ;
}
}
private IEnumerable < object > CategoryChildren ( UnitCategory category , bool subCategories = true )
{
if ( category ! = null & & subCategories )
{
foreach ( var subCategory in options . SelectMany ( option = > option . category = = null ? Enumerable . Empty < UnitCategory > ( ) : option . category . AndAncestors ( ) )
. Distinct ( )
. Where ( c = > c . parent = = category )
. OrderBy ( c = > c . name ) )
{
yield return subCategory ;
}
}
foreach ( var unit in options . Where ( option = > option . category = = category )
. Where ( option = > ! option . unitType . HasAttribute < SpecialUnitAttribute > ( ) )
. OrderBy ( option = > option . order )
. ThenBy ( option = > option . label ) )
{
yield return unit ;
}
if ( category ! = null )
{
if ( category . root . name = = "Events" )
{
foreach ( var eventChild in EventsChildren ( category ) )
{
yield return eventChild ;
}
}
else if ( category . fullName = = "Codebase" )
{
foreach ( var codebaseChild in CodebaseChildren ( ) )
{
yield return codebaseChild ;
}
}
else if ( category . fullName = = "Variables" )
{
foreach ( var variableChild in VariablesChildren ( ) )
{
yield return variableChild ;
}
}
else if ( category . fullName = = "Math" )
{
foreach ( var mathChild in MathChildren ( ) )
{
yield return mathChild ;
}
}
else if ( category . fullName = = "Time" )
{
foreach ( var timeChild in TimeChildren ( ) )
{
yield return timeChild ;
}
}
else if ( category . fullName = = "Nesting" )
{
foreach ( var nestingChild in NestingChildren ( ) )
{
yield return nestingChild ;
}
}
else if ( category . fullName = = "Graphs" )
{
foreach ( var macroChild in MacroChildren ( ) )
{
yield return macroChild ;
}
}
}
}
private IEnumerable < object > EventsChildren ( UnitCategory category )
{
foreach ( var unit in options . Where ( option = > option . UnitIs < IEventUnit > ( ) & & option . category = = category )
. OrderBy ( option = > option . order )
. ThenBy ( option = > option . label ) )
{
yield return unit ;
}
}
#endregion
#region Search
public override bool searchable { get ; } = true ;
public override IEnumerable < ISearchResult > SearchResults ( string query , CancellationToken cancellation )
{
foreach ( var typeResult in typesWithMembers . Cancellable ( cancellation ) . OrderableSearchFilter ( query , t = > t . DisplayName ( ) ) )
{
yield return typeResult ;
}
foreach ( var optionResult in options . Cancellable ( cancellation )
. OrderableSearchFilter ( query , o = > o . haystack , o = > o . formerHaystack )
. WithoutInheritedDuplicates ( r = > r . result , cancellation ) )
{
yield return optionResult ;
}
}
public override string SearchResultLabel ( object item , string query )
{
if ( item is Type type )
{
return TypeOption . SearchResultLabel ( type , query ) ;
}
else if ( item is IUnitOption unitOption )
{
return unitOption . SearchResultLabel ( query ) ;
}
else
{
return base . SearchResultLabel ( item , query ) ;
}
}
#endregion
#region Favorites
public override ICollection < object > favorites { get ; }
public override bool CanFavorite ( object item )
{
return ( item as IUnitOption ) ? . favoritable ? ? false ;
}
public override string FavoritesLabel ( object item )
{
return SearchResultLabel ( item , null ) ;
}
public override void OnFavoritesChange ( )
{
BoltFlow . Configuration . Save ( ) ;
}
private class Favorites : ICollection < object >
{
public Favorites ( UnitOptionTree tree )
{
this . tree = tree ;
}
private UnitOptionTree tree { get ; }
private IEnumerable < IUnitOption > options = > tree . options . Where ( option = > BoltFlow . Configuration . favoriteUnitOptions . Contains ( option . favoriteKey ) ) ;
public bool IsReadOnly = > false ;
public int Count = > BoltFlow . Configuration . favoriteUnitOptions . Count ;
public IEnumerator < object > GetEnumerator ( )
{
foreach ( var option in options )
{
yield return option ;
}
}
IEnumerator IEnumerable . GetEnumerator ( )
{
return GetEnumerator ( ) ;
}
public bool Contains ( object item )
{
var option = ( IUnitOption ) item ;
return BoltFlow . Configuration . favoriteUnitOptions . Contains ( option . favoriteKey ) ;
}
public void Add ( object item )
{
var option = ( IUnitOption ) item ;
BoltFlow . Configuration . favoriteUnitOptions . Add ( option . favoriteKey ) ;
}
public bool Remove ( object item )
{
var option = ( IUnitOption ) item ;
return BoltFlow . Configuration . favoriteUnitOptions . Remove ( option . favoriteKey ) ;
}
public void Clear ( )
{
BoltFlow . Configuration . favoriteUnitOptions . Clear ( ) ;
}
public void CopyTo ( object [ ] array , int arrayIndex )
{
if ( array = = null )
{
throw new ArgumentNullException ( nameof ( array ) ) ;
}
if ( arrayIndex < 0 )
{
throw new ArgumentOutOfRangeException ( nameof ( arrayIndex ) ) ;
}
if ( array . Length - arrayIndex < Count )
{
throw new ArgumentException ( ) ;
}
var i = 0 ;
foreach ( var item in this )
{
array [ i + arrayIndex ] = item ;
i + + ;
}
}
}
#endregion
}
}