initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3dfa3c8fac2134fdda65f882f5a1a014
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,13 @@
using UnityEditor;
namespace Unity.VisualScripting
{
public class EditorPreferencesProvider : Editor
{
[SettingsProvider]
public static SettingsProvider CreateEditorPreferencesProvider()
{
return new EditorPreferencesProviderView();
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ba9c71f100044b3a9dca49c8f9e489cb
timeCreated: 1625864018

View file

@ -0,0 +1,46 @@
using UnityEngine;
using UnityEditor;
namespace Unity.VisualScripting
{
internal class EditorPreferencesProviderView : SettingsProvider
{
private const string Path = "Preferences/Visual Scripting";
private const string Title = "Visual Scripting";
private const string ID = "Bolt";
private readonly GUIStyle marginStyle = new GUIStyle() { margin = new RectOffset(10, 10, 10, 10) };
public EditorPreferencesProviderView() : base(Path, SettingsScope.User)
{
label = Title;
}
private void EnsureConfig()
{
if (BoltCore.instance == null || BoltCore.Configuration == null)
{
PluginContainer.Initialize();
}
}
public override void OnGUI(string searchContext)
{
EnsureConfig();
GUILayout.BeginVertical(marginStyle);
// happens when opening unity with the settings window already opened. there's a delay until the singleton is assigned
if (BoltCore.instance == null)
{
EditorGUILayout.HelpBox("Loading Configuration...", MessageType.Info);
return;
}
var instance = (BoltProduct)ProductContainer.GetProduct(ID);
instance.configurationPanel.PreferenceItem();
GUILayout.EndVertical();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b7453dd8d3980405794f631eea9f420e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a06b4d04018343b99f255da7f18559d1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,77 @@
using UnityEngine;
using UnityEditor;
namespace Unity.VisualScripting
{
public class AssemblyOptionsSettings
{
private const string CompleteLabel = "Regenerate Nodes";
private readonly PluginConfigurationItemMetadata _assemblyOptionsMetadata;
private bool _showAssembly = false;
private const string TitleAssembly = "Node Library";
private const string DescriptionAssembly = "Choose the assemblies in which you want to look for nodes.\n"
+ "By default, all project and Unity assemblies are included.\n"
+ "Unless you use a third-party plugin distributed as a DLL, you shouldn't need to change this.";
public AssemblyOptionsSettings(BoltCoreConfiguration coreConfig)
{
_assemblyOptionsMetadata = coreConfig.GetMetadata(nameof(coreConfig.assemblyOptions));
}
private static class Styles
{
public static readonly GUIStyle background;
public static readonly GUIStyle defaultsButton;
public const float OptionsWidth = 250;
static Styles()
{
background = new GUIStyle(LudiqStyles.windowBackground);
background.padding = new RectOffset(20, 20, 20, 20);
defaultsButton = new GUIStyle("Button");
defaultsButton.padding = new RectOffset(10, 10, 4, 4);
}
}
public void OnGUI()
{
_showAssembly = EditorGUILayout.Foldout(_showAssembly, new GUIContent(TitleAssembly, DescriptionAssembly));
if (_showAssembly)
{
GUILayout.BeginVertical(Styles.background, GUILayout.ExpandHeight(true));
float height = LudiqGUI.GetInspectorHeight(null, _assemblyOptionsMetadata, Styles.OptionsWidth, GUIContent.none);
EditorGUI.BeginChangeCheck();
var position = GUILayoutUtility.GetRect(Styles.OptionsWidth, height);
LudiqGUI.Inspector(_assemblyOptionsMetadata, position, GUIContent.none);
if (EditorGUI.EndChangeCheck())
{
_assemblyOptionsMetadata.Save();
Codebase.UpdateSettings();
}
if (GUILayout.Button("Reset to Defaults", Styles.defaultsButton))
{
_assemblyOptionsMetadata.Reset(true);
_assemblyOptionsMetadata.Save();
}
LudiqGUI.EndVertical();
}
if (GUILayout.Button(CompleteLabel, Styles.defaultsButton))
{
UnitBase.Rebuild();
EditorUtility.DisplayDialog("Visual Script", "Regenerate Nodes completed", "OK");
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7cb1615bd41a3442a8aabbcb872046a3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,46 @@
using System.Diagnostics;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
public class BackupSettings
{
private const string Title = "Backup Graphs";
private const string ButtonBackupLabel = "Create Backup";
private const string ButtonRestoreLabel = "Restore Backup";
public void OnGUI()
{
GUILayout.Space(5f);
GUILayout.Label(Title, EditorStyles.boldLabel);
GUILayout.Space(5f);
if (GUILayout.Button(ButtonBackupLabel, Styles.defaultsButton))
{
VSBackupUtility.Backup();
EditorUtility.DisplayDialog("Backup", "Backup completed successfully.", "OK");
}
if (GUILayout.Button(ButtonRestoreLabel, Styles.defaultsButton))
{
PathUtility.CreateDirectoryIfNeeded(Paths.backups);
Process.Start(Paths.backups);
}
}
private static class Styles
{
static Styles()
{
defaultsButton = new GUIStyle("Button");
defaultsButton.padding = new RectOffset(10, 10, 4, 4);
}
public static readonly GUIStyle defaultsButton;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50455e66302b715488a9218e30b0ab24
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,45 @@
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
public class CustomPropertyProviderSettings
{
private const string Title = "Custom Inspector Properties";
private const string ButtonLabel = "Generate";
public void OnGUI()
{
GUILayout.Space(5f);
GUILayout.Label(Title, EditorStyles.boldLabel);
GUILayout.Space(5f);
string label = "Inspectors in Visual Scripting plugins can handle many custom types besides Unity primites and objects. ";
label += "However, to be compatible with your custom editor drawers, some additional property provider scripts must be generated. ";
GUILayout.BeginHorizontal(EditorStyles.helpBox);
GUILayout.Label(EditorGUIUtility.IconContent("console.infoicon"), GUILayout.ExpandWidth(true));
GUILayout.Box(label, EditorStyles.wordWrappedLabel);
GUILayout.EndHorizontal();
if (GUILayout.Button(ButtonLabel, Styles.defaultsButton))
{
SerializedPropertyProviderProvider.instance.GenerateProviderScripts();
EditorUtility.DisplayDialog("Custom Inspector Generation", "Custom inspector generation has completed successfully.", "OK");
}
}
private static class Styles
{
static Styles()
{
defaultsButton = new GUIStyle("Button");
defaultsButton.padding = new RectOffset(10, 10, 4, 4);
}
public static readonly GUIStyle defaultsButton;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7da9d8250917648728b856bfab5f1320
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,36 @@
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
public class ScriptReferenceResolverSettings
{
private const string Title = "Script Reference Resolver";
private const string ButtonLabel = "Fix Missing Scripts";
public void OnGUI()
{
GUILayout.Space(5f);
GUILayout.Label(Title, EditorStyles.boldLabel);
GUILayout.Space(5f);
if (GUILayout.Button(ButtonLabel, Styles.defaultsButton))
{
ScriptReferenceResolver.Run(ScriptReferenceResolver.Mode.Dialog);
}
}
private static class Styles
{
static Styles()
{
defaultsButton = new GUIStyle("Button");
defaultsButton.padding = new RectOffset(10, 10, 4, 4);
}
public static readonly GUIStyle defaultsButton;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a8c0a3364f2ef4e42820eb87b5bdab69
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,69 @@
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
public class TypeOptionsSettings
{
private readonly PluginConfigurationItemMetadata _typeOptionsMetadata;
private bool _showTypeOption = false;
private const string TitleTypeOption = "Type Options";
private const string DescriptionTypeOption = "Choose the types you want to use for variables and nodes.\n"
+ "MonoBehaviour types are always included.";
private static class Styles
{
public static readonly GUIStyle background;
public static readonly GUIStyle defaultsButton;
public const float OptionsWidth = 250;
static Styles()
{
background = new GUIStyle(LudiqStyles.windowBackground);
background.padding = new RectOffset(20, 20, 20, 20);
defaultsButton = new GUIStyle("Button");
defaultsButton.padding = new RectOffset(10, 10, 4, 4);
}
}
public TypeOptionsSettings(BoltCoreConfiguration coreConfig)
{
_typeOptionsMetadata = coreConfig.GetMetadata(nameof(coreConfig.typeOptions));
}
public void OnGUI()
{
_showTypeOption = EditorGUILayout.Foldout(_showTypeOption, new GUIContent(TitleTypeOption, DescriptionTypeOption));
if (_showTypeOption)
{
GUILayout.BeginVertical(Styles.background, GUILayout.ExpandHeight(true));
float height =
LudiqGUI.GetInspectorHeight(null, _typeOptionsMetadata, Styles.OptionsWidth, GUIContent.none);
EditorGUI.BeginChangeCheck();
var position = GUILayoutUtility.GetRect(Styles.OptionsWidth, height);
LudiqGUI.Inspector(_typeOptionsMetadata, position, GUIContent.none);
if (EditorGUI.EndChangeCheck())
{
_typeOptionsMetadata.Save();
Codebase.UpdateSettings();
}
if (GUILayout.Button("Reset to Defaults", Styles.defaultsButton))
{
_typeOptionsMetadata.Reset(true);
_typeOptionsMetadata.Save();
}
LudiqGUI.EndVertical();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f061d8e7ad514436d82f47b34306a22c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,13 @@
using UnityEditor;
namespace Unity.VisualScripting
{
public class ProjectSettingsProvider : Editor
{
[SettingsProvider]
public static SettingsProvider CreateProjectSettingProvider()
{
return new ProjectSettingsProviderView();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d6a0cc840e6084f62a3e3538f14bf3ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,119 @@
using UnityEngine;
using UnityEditor;
namespace Unity.VisualScripting
{
internal class ProjectSettingsProviderView : SettingsProvider
{
private const string Path = "Project/Visual Scripting";
private const string Title = "Visual Scripting";
private const string TitleGroup = "Generate Nodes";
private readonly GUIStyle marginStyle = new GUIStyle() { margin = new RectOffset(10, 10, 10, 10) };
private AssemblyOptionsSettings _assemblyOptionsSettings;
private TypeOptionsSettings _typeOptionsSettings;
private CustomPropertyProviderSettings _customPropertyProviderSettings;
private BackupSettings _backupSettings;
private ScriptReferenceResolverSettings _scriptReferenceResolverSettings;
private BoltCoreConfiguration _vsCoreConfig = null;
public ProjectSettingsProviderView() : base(Path, SettingsScope.Project)
{
label = Title;
EditorTypeUtility.Initialize();
}
private void CreateOptionsIfNeeded()
{
_assemblyOptionsSettings ??= new AssemblyOptionsSettings(_vsCoreConfig);
_typeOptionsSettings ??= new TypeOptionsSettings(_vsCoreConfig);
_customPropertyProviderSettings ??= new CustomPropertyProviderSettings();
_backupSettings ??= new BackupSettings();
_scriptReferenceResolverSettings ??= new ScriptReferenceResolverSettings();
}
private void EnsureConfig()
{
if (_vsCoreConfig != null)
return;
if (BoltCore.instance == null || BoltCore.Configuration == null)
{
UnityAPI.Initialize();
PluginContainer.Initialize();
}
_vsCoreConfig = BoltCore.Configuration;
}
public override void OnGUI(string searchContext)
{
GUILayout.BeginVertical(marginStyle);
if (VSUsageUtility.isVisualScriptingUsed)
{
EnsureConfig();
GUILayout.Space(5f);
GUILayout.Label(TitleGroup, EditorStyles.boldLabel);
GUILayout.Space(10f);
// happens when opening unity with the settings window already opened. there's a delay until the singleton is assigned
if (_vsCoreConfig == null)
{
EditorGUILayout.HelpBox("Loading Configuration...", MessageType.Info);
return;
}
CreateOptionsIfNeeded();
_typeOptionsSettings.OnGUI();
GUILayout.Space(10f);
_assemblyOptionsSettings.OnGUI();
GUILayout.Space(10f);
_customPropertyProviderSettings.OnGUI();
GUILayout.Space(10f);
_backupSettings.OnGUI();
GUILayout.Space(10f);
_scriptReferenceResolverSettings.OnGUI();
}
else
{
GUILayout.Space(5f);
GUILayout.BeginHorizontal(EditorStyles.label);
if (GUILayout.Button("Initialize Visual Scripting", Styles.defaultsButton))
{
VSUsageUtility.isVisualScriptingUsed = true;
}
GUILayout.Space(5f);
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
}
private static class Styles
{
static Styles()
{
defaultsButton = new GUIStyle("Button");
defaultsButton.padding = new RectOffset(10, 10, 4, 4);
}
public static readonly GUIStyle defaultsButton;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4eea00f49056f4ee98c82b6923b44600
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,21 @@
{
"name": "Unity.VisualScripting.SettingsProvider.Editor",
"references": [
"Unity.VisualScripting.Core",
"Unity.VisualScripting.Core.Editor",
"Unity.VisualScripting.Flow",
"Unity.VisualScripting.Flow.Editor",
"Unity.VisualScripting.State"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2aa54ddaa48744e1caddee916e39805e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7abb56c9dd89a544a9995a060ab55368
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a0a037cc0a236493996d666b0ab8690c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,27 @@
namespace Unity.VisualScripting
{
public abstract class Analyser<TTarget, TAnalysis> : Assigner<TTarget, TAnalysis>, IAnalyser
where TAnalysis : class, IAnalysis, new()
{
protected Analyser(GraphReference reference, TTarget target) : base(target, new TAnalysis())
{
Ensure.That(nameof(reference)).IsNotNull(reference);
this.reference = reference;
// HACK: It makes more sense to think of analysis as reference-bound,
// however in practice they are context-bound and therefore it is safe
// (and more importantly faster) to cache the context once for recursive
// analyses.
this.context = reference.Context();
}
public TAnalysis analysis => assignee;
IAnalysis IAnalyser.analysis => analysis;
protected IGraphContext context { get; }
public GraphReference reference { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7cf3624e65e804bfb9dd35ee9884f519
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,15 @@
using System;
namespace Unity.VisualScripting
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class AnalyserAttribute : Attribute, IDecoratorAttribute
{
public AnalyserAttribute(Type type)
{
this.type = type;
}
public Type type { get; private set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b7663ba3a43454ba48fd2c7c17a0ae75
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,89 @@
using System;
namespace Unity.VisualScripting
{
public sealed class AnalyserProvider : SingleDecoratorProvider<object, IAnalyser, AnalyserAttribute>
{
protected override bool cache => true;
public GraphReference reference { get; }
public AnalyserProvider(GraphReference reference)
{
this.reference = reference;
}
protected override IAnalyser CreateDecorator(Type decoratorType, object decorated)
{
return (IAnalyser)decoratorType.Instantiate(true, reference, decorated);
}
public override bool IsValid(object analyzed)
{
return !analyzed.IsUnityNull();
}
public void Analyze(object analyzed)
{
GetDecorator(analyzed).isDirty = true;
}
public void AnalyzeAll()
{
foreach (var analyser in decorators.Values)
{
analyser.isDirty = true;
}
}
}
public static class XAnalyserProvider
{
// Analysis are conceptually reference-bound, but practically context-bound,
// so it's faster to avoid the reference-to-context lookup if we can avoid it.
public static IAnalyser Analyser(this object target, IGraphContext context)
{
return context.analyserProvider.GetDecorator(target);
}
public static TAnalyser Analyser<TAnalyser>(this object target, IGraphContext context) where TAnalyser : IAnalyser
{
return context.analyserProvider.GetDecorator<TAnalyser>(target);
}
public static IAnalysis Analysis(this object target, IGraphContext context)
{
var analyser = target.Analyser(context);
analyser.Validate();
return analyser.analysis;
}
public static TAnalysis Analysis<TAnalysis>(this object target, IGraphContext context) where TAnalysis : IAnalysis
{
return (TAnalysis)target.Analysis(context);
}
// Shortcuts, but the above are faster because Context doesn't have to be looked up
public static IAnalyser Analyser(this object target, GraphReference reference)
{
return target.Analyser(reference.Context());
}
public static TAnalyser Analyser<TAnalyser>(this object target, GraphReference reference) where TAnalyser : IAnalyser
{
return target.Analyser<TAnalyser>(reference.Context());
}
public static IAnalysis Analysis(this object target, GraphReference reference)
{
return target.Analysis(reference.Context());
}
public static TAnalysis Analysis<TAnalysis>(this object target, GraphReference reference) where TAnalysis : IAnalysis
{
return target.Analysis<TAnalysis>(reference.Context());
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c1743061b8e147ff9c808d137b1014e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,6 @@
namespace Unity.VisualScripting
{
public abstract class Analysis : IAnalysis
{
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f01a1ac261c724daca990596c32f7f10
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Unity.VisualScripting
{
public class GraphElementAnalysis : IGraphElementAnalysis
{
public List<Warning> warnings { get; set; } = new List<Warning>();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ac0dd11da2f64fc5b55b1b8a1d36b55
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
namespace Unity.VisualScripting
{
public interface IAnalyser
{
IAnalysis analysis { get; }
bool isDirty { get; set; }
void Validate();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b1eb0e9411d1e4ed880da3147ac22dc8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,6 @@
namespace Unity.VisualScripting
{
public interface IAnalysis
{
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4fb36d0252722495cbe1fddf45ba9185
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Unity.VisualScripting
{
public interface IGraphElementAnalysis : IAnalysis
{
List<Warning> warnings { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bf7f2015398494d498495147f2028b49
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fae0344df02e74e0988c577297cc556d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,55 @@
using UnityEditor;
namespace Unity.VisualScripting
{
public sealed class UsageAnalytics
{
const int k_MaxEventsPerHour = 1000;
const int k_MaxNumberOfElements = 1000;
const string k_VendorKey = "unity.bolt";
const string k_EventName = "BoltUsage";
static bool isRegistered = false;
public static void CollectAndSend()
{
if (!EditorAnalytics.enabled)
return;
if (!RegisterEvent())
return;
var data = CollectData();
EditorAnalytics.SendEventWithLimit(k_EventName, data);
}
private static bool RegisterEvent()
{
if (!isRegistered)
{
var result = EditorAnalytics.RegisterEventWithLimit(k_EventName, k_MaxEventsPerHour, k_MaxNumberOfElements, k_VendorKey);
if (result == UnityEngine.Analytics.AnalyticsResult.Ok)
{
isRegistered = true;
}
}
return isRegistered;
}
private static UsageAnalyticsData CollectData()
{
var data = new UsageAnalyticsData
{
productVersion = BoltProduct.instance.version.ToString(),
};
return data;
}
private struct UsageAnalyticsData
{
public string productVersion;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 59718df20b933421a9c465fcbeff7836
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,26 @@
using System;
using System.Text.RegularExpressions;
namespace Unity.VisualScripting.Analytics
{
internal static class AnalyticsUtilities
{
internal static string AnonymizeException(Exception e)
{
const string pathSectionOutsidePackage = "in (?<P>.*)Packages\\\\com.unity.visualscripting";
// Placing a '^' character to distinguish these lines from file paths outside our package
const string pathSectionOutsidePackageReplacement = "in ^Packages\\com.unity.visualscripting";
var anonymizedString = Regex.Replace(e.ToString(), pathSectionOutsidePackage, pathSectionOutsidePackageReplacement);
// Detecting any callstack line that doesn't match our previously anonymized package paths
const string filePathsOutsidePackage = ". at.*in (?<P>[^^]*:[0-9]*)";
const string filePathsOutsidePackageReplacement = " at <method> in <file>";
anonymizedString = Regex.Replace(anonymizedString, filePathsOutsidePackage,
filePathsOutsidePackageReplacement);
return anonymizedString;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7d2bbdd7f3404e04941d986015e27acb
timeCreated: 1621016306

View file

@ -0,0 +1,161 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting.Analytics
{
internal static class HotkeyUsageAnalytics
{
private const int MaxEventsPerHour = 120;
private const int MaxNumberOfElements = 1000;
private const string VendorKey = "unity.visualscripting";
private const string EventName = "VScriptHotkeyUsage";
private static bool _isRegistered = false;
private const int HotkeyUseLimitBeforeSend = 10000;
private static bool _interruptEventsRegistered = false;
private static bool _onControlSchemeEventRegistered = false;
private static Data _collectedData = null;
// Limiting to stop spam from some continuous events like pan or zoom
private const int HotkeyFrameLimit = 2;
private static Dictionary<Hotkey, FrameLimiterUtility> _frameLimiters;
internal static void HotkeyUsed(Hotkey key)
{
EnsureCollectedDataInitialized();
EnsureInterruptEventsRegistered();
EnsureOnControlSchemeChangedRegistered();
_collectedData.canvasControlSchemeName = Enum.GetName(typeof(CanvasControlScheme), BoltCore.Configuration.controlScheme);
if (!_frameLimiters.ContainsKey(key))
_frameLimiters.Add(key, new FrameLimiterUtility(HotkeyFrameLimit));
if (!_frameLimiters[key].IsWithinFPSLimit()) return;
var enumStringVal = Enum.GetName(typeof(Hotkey), key);
if (enumStringVal == null)
return;
if (!_collectedData.HotkeyUsageCountDict.ContainsKey(enumStringVal))
_collectedData.HotkeyUsageCountDict.Add(enumStringVal, 0);
_collectedData.HotkeyUsageCountDict[enumStringVal]++;
if (_collectedData.HotkeyUsageCountDict[enumStringVal] >= HotkeyUseLimitBeforeSend)
Send();
}
private static void Send()
{
if (!EditorAnalytics.enabled)
return;
if (!RegisterEvent())
return;
// Because dicts aren't serializable, convert it to a list right before we send
foreach (var count in _collectedData.HotkeyUsageCountDict)
{
var c = new HotkeyStringUsageCount();
c.hotkey = count.Key;
c.count = count.Value;
_collectedData.hotkeyUsageCount.Add(c);
}
EditorAnalytics.SendEventWithLimit(EventName, _collectedData);
ResetCollectedData();
}
private static bool RegisterEvent()
{
if (!_isRegistered)
{
var result = EditorAnalytics.RegisterEventWithLimit(EventName, MaxEventsPerHour, MaxNumberOfElements, VendorKey);
if (result == UnityEngine.Analytics.AnalyticsResult.Ok)
{
_isRegistered = true;
}
}
return _isRegistered;
}
private static void EnsureInterruptEventsRegistered()
{
if (_interruptEventsRegistered) return;
EditorApplication.quitting += Send;
AssemblyReloadEvents.beforeAssemblyReload += Send;
_interruptEventsRegistered = true;
}
private static void EnsureOnControlSchemeChangedRegistered()
{
if (_onControlSchemeEventRegistered) return;
BoltCore.Configuration.ControlSchemeChanged += Send;
_onControlSchemeEventRegistered = true;
}
private static void EnsureCollectedDataInitialized()
{
if (_collectedData != null) return;
ResetCollectedData();
_frameLimiters = new Dictionary<Hotkey, FrameLimiterUtility>();
}
private static void ResetCollectedData()
{
var controlScheme = Enum.GetName(typeof(CanvasControlScheme), BoltCore.Configuration.controlScheme);
_collectedData = new Data()
{
canvasControlSchemeName = controlScheme,
hotkeyUsageCount = new List<HotkeyStringUsageCount>(),
HotkeyUsageCountDict = new Dictionary<string, int>(),
};
}
[Serializable]
private class Data
{
// Note: using strings instead of enums to make it resilient to enum order changing
[SerializeField]
internal string canvasControlSchemeName;
[SerializeField]
internal List<HotkeyStringUsageCount> hotkeyUsageCount;
// Not actually sent to analytics because analytics uses UnitySerializer under the hood
// which cannot serialize dictionaries
internal Dictionary<string, int> HotkeyUsageCountDict;
}
[Serializable]
private struct HotkeyStringUsageCount
{
[SerializeField] internal string hotkey;
[SerializeField] internal int count;
}
internal enum Hotkey
{
Zoom,
Scroll,
PanMmb,
PanAltLmb,
Home,
RmbRemoveConnections,
Cut,
Copy,
Paste,
Duplicate,
Delete,
SelectAll,
Tab,
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5e84744b83a8434ab42fa513f9bf00ad
timeCreated: 1617297094

View file

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting.Analytics
{
internal static class MigrationAnalytics
{
private const int MaxEventsPerHour = 120;
private const int MaxNumberOfElements = 1000;
private const string VendorKey = "unity.visualscripting";
private const string EventName = "VScriptMigration";
private static bool _isRegistered = false;
internal static void Send(Data data)
{
if (!EditorAnalytics.enabled)
return;
if (!RegisterEvent())
return;
EditorAnalytics.SendEventWithLimit(EventName, data);
}
private static bool RegisterEvent()
{
if (!_isRegistered)
{
var result = EditorAnalytics.RegisterEventWithLimit(EventName, MaxEventsPerHour, MaxNumberOfElements, VendorKey);
if (result == UnityEngine.Analytics.AnalyticsResult.Ok)
{
_isRegistered = true;
}
}
return _isRegistered;
}
[Serializable]
internal class Data
{
[SerializeField]
internal MigrationStepAnalyticsData total;
[SerializeField]
internal List<MigrationStepAnalyticsData> steps;
}
[Serializable]
internal class MigrationStepAnalyticsData
{
[SerializeField]
internal string pluginId;
[SerializeField]
internal string from;
[SerializeField]
internal string to;
[SerializeField]
internal bool success;
[SerializeField]
internal string exception;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 624ee9ba5d484bf4c9e9e910d66273e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting.Analytics
{
internal static class NodeUsageAnalytics
{
private const int MaxEventsPerHour = 120;
private const int MaxNumberOfElements = 1000;
private const string VendorKey = "unity.visualscripting";
private const string EventName = "VScriptNodeUsage";
private static bool _isRegistered = false;
private const int NodeUseLimitBeforeSend = 100;
private static bool _interruptEventsRegistered = false;
private static Data _collectedData = null;
private static readonly HashSet<string> AllowedNamespaces = new HashSet<string>()
{
"System",
"Mono",
"Unity", // Includes UnityEngine, UnityEngineInternal, UnityEditor, UnityEditorInternal
};
internal static void NodeAdded(AnalyticsIdentifier aid)
{
EnsureCollectedDataInitialized();
EnsureInterruptEventsRegistered();
var node = GetNodeStringFromAnalyticsIdentifier(aid);
_collectedData.nodeUsageCount.Add(new NodeCount() { node = node, count = 1 });
if (_collectedData.nodeUsageCount.Count >= NodeUseLimitBeforeSend)
Send();
}
internal static void NodeRemoved(AnalyticsIdentifier aid)
{
EnsureCollectedDataInitialized();
EnsureInterruptEventsRegistered();
var node = GetNodeStringFromAnalyticsIdentifier(aid);
_collectedData.nodeUsageCount.Add(new NodeCount() { node = node, count = -1 });
if (_collectedData.nodeUsageCount.Count >= NodeUseLimitBeforeSend)
Send();
}
private static string GetNodeStringFromAnalyticsIdentifier(AnalyticsIdentifier aid)
{
foreach (var allowedNamespace in AllowedNamespaces)
{
if (aid.Namespace.StartsWith(allowedNamespace))
return aid.Identifier;
}
return aid.Hashcode.ToString();
}
private static void Send()
{
if (!EditorAnalytics.enabled)
return;
if (!RegisterEvent())
return;
EditorAnalytics.SendEventWithLimit(EventName, _collectedData);
ResetCollectedData();
}
private static bool RegisterEvent()
{
if (!_isRegistered)
{
var result = EditorAnalytics.RegisterEventWithLimit(EventName, MaxEventsPerHour, MaxNumberOfElements, VendorKey);
if (result == UnityEngine.Analytics.AnalyticsResult.Ok)
{
_isRegistered = true;
}
}
return _isRegistered;
}
private static void EnsureInterruptEventsRegistered()
{
if (_interruptEventsRegistered) return;
EditorApplication.quitting += Send;
AssemblyReloadEvents.beforeAssemblyReload += Send;
_interruptEventsRegistered = true;
}
private static void EnsureCollectedDataInitialized()
{
if (_collectedData != null) return;
ResetCollectedData();
}
private static void ResetCollectedData()
{
_collectedData = new Data()
{
nodeUsageCount = new List<NodeCount>(),
};
}
[Serializable]
private class Data
{
[SerializeField]
internal List<NodeCount> nodeUsageCount;
}
[Serializable]
private struct NodeCount
{
[SerializeField]
internal string node;
[SerializeField]
internal int count;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9ef9ab17827342b781ff73737f13ddb7
timeCreated: 1617896748

View file

@ -0,0 +1,51 @@
using System;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting.Analytics
{
internal static class OnPreprocessBuildAnalytics
{
private const int MaxEventsPerHour = 120;
private const int MaxNumberOfElements = 1000;
private const string VendorKey = "unity.visualscripting";
private const string EventName = "VScriptOnPreprocessBuild";
private static bool _isRegistered = false;
internal static void Send(Data data)
{
if (!EditorAnalytics.enabled)
return;
if (!RegisterEvent())
return;
EditorAnalytics.SendEventWithLimit(EventName, data);
}
private static bool RegisterEvent()
{
if (!_isRegistered)
{
var result = EditorAnalytics.RegisterEventWithLimit(EventName, MaxEventsPerHour, MaxNumberOfElements, VendorKey);
if (result == UnityEngine.Analytics.AnalyticsResult.Ok)
{
_isRegistered = true;
}
}
return _isRegistered;
}
[Serializable]
internal struct Data
{
[SerializeField]
internal string guid;
[SerializeField]
internal BuildTarget buildTarget;
[SerializeField]
internal BuildTargetGroup buildTargetGroup;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b1903aa39b6b4857be4ef4e143617965
timeCreated: 1616172851

View file

@ -0,0 +1,23 @@
using UnityEditor;
using UnityEditor.Build.Reporting;
namespace Unity.VisualScripting.Analytics
{
internal class OnPreprocessBuildAnalyticsEventHandler : UnityEditor.Build.IPreprocessBuildWithReport
{
public int callbackOrder => 0;
public void OnPreprocessBuild(BuildReport report)
{
if (!EditorAnalytics.enabled || !VSUsageUtility.isVisualScriptingUsed)
return;
OnPreprocessBuildAnalytics.Send(new OnPreprocessBuildAnalytics.Data()
{
guid = report.summary.guid.ToString(),
buildTarget = report.summary.platform,
buildTargetGroup = report.summary.platformGroup
});
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 53a1e1bb3f40450abf503305ff74ce82
timeCreated: 1616694961

View file

@ -0,0 +1,18 @@
#if VISUAL_SCRIPT_INTERNAL
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class AssetBundleCreator : MonoBehaviour
{
//TODO: Create CI to automatically create and update the assetbundle
[MenuItem("Tools/Visual Scripting/Internal/Build Asset Bundle")]
private static void BuildAssetBundle()
{
BuildPipeline.BuildAssetBundles("Assets/", BuildAssetBundleOptions.None,
BuildTarget.StandaloneOSX);
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5f1b15ef6f7374405b6abe5fae72c2e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 662cc5e740e0841e1bb91853ab803f33
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Unity.VisualScripting
{
public abstract class Assigner<TTarget, TAssignee> : IAssigner
where TAssignee : class
{
protected Assigner(TTarget target, TAssignee assignee)
{
Ensure.That(nameof(target)).IsNotNull(target);
Ensure.That(nameof(assignee)).IsNotNull(assignee);
this.target = target;
this.assignee = assignee;
var assignerType = GetType();
if (!_assignments.ContainsKey(assignerType))
{
_assignments.Add(assignerType, Assignment.Fetch(assignerType, typeof(TAssignee)).ToArray());
_transientAssignments.Add(assignerType, _assignments[assignerType].Where(a => !a.cache).ToArray());
}
}
public TTarget target { get; }
public TAssignee assignee { get; }
public bool isDirty { get; set; } = true;
public void Validate()
{
if (isDirty)
{
AssignAll();
}
else
{
AssignTransient();
}
}
protected void AssignAll()
{
isDirty = false;
foreach (var assignment in assignments)
{
assignment.Run(this, assignee);
}
}
protected void AssignTransient()
{
foreach (var assignment in transientAssignments)
{
assignment.Run(this, assignee);
}
}
public virtual void ValueChanged() { }
public Assignment[] assignments => _assignments[GetType()];
public Assignment[] transientAssignments => _transientAssignments[GetType()];
private static readonly Dictionary<Type, Assignment[]> _assignments = new Dictionary<Type, Assignment[]>();
private static readonly Dictionary<Type, Assignment[]> _transientAssignments = new Dictionary<Type, Assignment[]>();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e82979a3184424cf390bc7483f03771c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class Assignment
{
public Assignment(Member assigner, Type assigneeType)
{
Ensure.That(nameof(assigneeType)).IsNotNull(assigneeType);
this.assigner = assigner;
var assignsAttribute = assigner.info.GetAttribute<AssignsAttribute>();
assignee = new Member(assigneeType, assignsAttribute?.memberName ?? assigner.name.FirstCharacterToLower());
requiresAPI = assigner.info.HasAttribute<RequiresUnityAPIAttribute>();
cache = assignsAttribute?.cache ?? true;
assigner.Prewarm();
assignee.Prewarm();
}
public Member assigner { get; }
public Member assignee { get; }
public bool requiresAPI { get; }
public bool cache { get; }
public void Run(object assigner, object assignee)
{
if (requiresAPI)
{
UnityAPI.Async(() => _Run(assigner, assignee));
}
else
{
_Run(assigner, assignee);
}
}
private void _Run(object assigner, object assignee)
{
var oldValue = this.assignee.Get(assignee);
var newValue = ConversionUtility.Convert(this.assigner.Invoke(assigner), this.assignee.type);
this.assignee.Set(assignee, newValue);
if (!Equals(oldValue, newValue))
{
if (assigner is IAssigner _assigner)
{
_assigner.ValueChanged();
}
}
}
public static IEnumerable<Assignment> Fetch(Type descriptorType, Type descriptionType)
{
var bindingFlags = BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic;
return descriptorType.GetMethods(bindingFlags)
.Where(m => m.HasAttribute<AssignsAttribute>())
.Select(m => new Assignment(m.ToManipulator(descriptorType), descriptionType));
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 505852ed4622a47088bd0f2f07041314
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,19 @@
using System;
namespace Unity.VisualScripting
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class AssignsAttribute : Attribute
{
public AssignsAttribute() { }
public AssignsAttribute(string memberName)
{
this.memberName = memberName;
}
public string memberName { get; }
public bool cache { get; set; } = true;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d195fcaa7976240d09b19fccb42c2068
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,7 @@
namespace Unity.VisualScripting
{
public interface IAssigner
{
void ValueChanged();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c1fabba6ac68944be828b41a0b09b932
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,51 @@
using UnityEditor;
using UnityEngine;
using UnityEvent = UnityEngine.Event;
namespace Unity.VisualScripting
{
public static class BoltGUI
{
private static UnityEvent e => UnityEvent.current;
public static float GetVariableFieldHeight(GUIContent label, string value, ActionDirection direction)
{
return EditorGUIUtility.singleLineHeight;
}
public static string VariableField(Rect position, GUIContent label, string value, ActionDirection direction)
{
position = EditorGUI.PrefixLabel(position, label);
var style = direction != ActionDirection.Any ? BoltStyles.variableFieldWithDirectionIndicator : BoltStyles.variableFieldWithoutDirectionIndicator;
value = EditorGUI.TextField(position, GUIContent.none, value, style);
var iconPosition = new Rect
(
position.x + 3,
position.y + 2,
12,
12
);
GUI.DrawTexture(iconPosition, BoltCore.Icons.variable?[IconSize.Small]);
if (direction != ActionDirection.Any)
{
var arrowPosition = new Rect
(
iconPosition.xMax + (direction == ActionDirection.Get ? 12 : -2),
position.y + 2,
12 * (direction == ActionDirection.Get ? -1 : 1),
12
);
if (e.type == EventType.Repaint)
{
BoltStyles.variableFieldDirectionIndicator.Draw(arrowPosition, false, false, false, false);
}
}
return value;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c9b7e151c1f64d48b39eca8d419c2b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
namespace Unity.VisualScripting
{
[Product(ID)]
public sealed class BoltProduct : Product
{
public BoltProduct() { }
public override void Initialize()
{
base.Initialize();
logo = BoltCore.Resources.LoadTexture("LogoBolt.png", CreateTextureOptions.Scalable)?.Single();
}
public override string configurationPanelLabel => "Bolt";
public override string name => "Bolt";
public override string description => "";
public override string authorLabel => "Designed & Developed by ";
public override string author => "";
public override string copyrightHolder => "Unity";
public override SemanticVersion version => PackageVersionUtility.version;
public override string publisherUrl => "";
public override string websiteUrl => "";
public override string supportUrl => "";
public override string manualUrl => "https://docs.unity3d.com/Packages/com.unity.bolt@latest";
public override string assetStoreUrl => "http://u3d.as/1Md2";
public const string ID = "Bolt";
#if VISUAL_SCRIPT_INTERNAL
public const int ToolsMenuPriority = -990000;
public const int DeveloperToolsMenuPriority = ToolsMenuPriority + 1000;
#endif
public static BoltProduct instance => (BoltProduct)ProductContainer.GetProduct(ID);
private static bool PrepareForRelease()
{
if (!EditorUtility.DisplayDialog("Delete Generated Files", "This action will delete all generated files, including those containing user data.\n\nAre you sure you want to continue?", "Confirm", "Cancel"))
{
return false;
}
PluginConfiguration.DeleteAllProjectSettings();
foreach (var plugin in PluginContainer.plugins)
{
PathUtility.DeleteDirectoryIfExists(plugin.paths.persistentGenerated);
PathUtility.DeleteDirectoryIfExists(plugin.paths.transientGenerated);
}
return true;
}
#if VISUAL_SCRIPT_INTERNAL
[MenuItem("Tools/Bolt/Internal/Export Release Asset Package...", priority = DeveloperToolsMenuPriority + 102)]
#endif
private static void ExportReleasePackage()
{
if (!PrepareForRelease())
{
return;
}
var exportPath = EditorUtility.SaveFilePanel("Export Release Package",
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
"Bolt_" + instance.version.ToString().Replace(".", "_").Replace(" ", "_"),
"unitypackage");
if (exportPath == null)
{
return;
}
var packageDirectory = Path.GetDirectoryName(exportPath);
var paths = new List<string>()
{
PathUtility.GetPackageRootPath()
};
AssetDatabase.ExportPackage(paths.ToArray(), exportPath, ExportPackageOptions.Recurse);
if (EditorUtility.DisplayDialog("Export Release Package", "Release package export complete.\nOpen containing folder?", "Open Folder", "Close"))
{
Process.Start(packageDirectory);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d800ad61ae46243a0bc7d8d834b1bf3f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,24 @@
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
public static class BoltStyles
{
static BoltStyles()
{
// Variables
variableFieldDirectionIndicator = "AC LeftArrow";
variableFieldWithoutDirectionIndicator = new GUIStyle(EditorStyles.textField);
variableFieldWithoutDirectionIndicator.padding.left = IconSize.Small;
variableFieldWithDirectionIndicator = new GUIStyle(variableFieldWithoutDirectionIndicator);
variableFieldWithDirectionIndicator.padding.left = 24;
}
// Variables
public static readonly GUIStyle variableFieldDirectionIndicator;
public static readonly GUIStyle variableFieldWithDirectionIndicator;
public static readonly GUIStyle variableFieldWithoutDirectionIndicator;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2a3cd7080fc4042768e4e591975cfbc5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 31d3bc055420f4ee59008eb8996d7df2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,35 @@
namespace Unity.VisualScripting
{
public enum AlignOperation
{
/// <summary>
/// Align the left edges of the selected elements.
/// </summary>
AlignLeftEdges,
/// <summary>
/// Align the horizontal centers of the selected elements.
/// </summary>
AlignCenters,
/// <summary>
/// Align the right edges of the selected elements.
/// </summary>
AlignRightEdges,
/// <summary>
/// Align the top edges of the selected elements.
/// </summary>
AlignTopEdges,
/// <summary>
/// Align the vertical middles of the selected elements.
/// </summary>
AlignMiddles,
/// <summary>
/// Align the bottom edges of the selected elements.
/// </summary>
AlignBottomEdges
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 57d833ea26c9149528d23f0ec150b1d2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,15 @@
using System;
namespace Unity.VisualScripting
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public sealed class CanvasAttribute : Attribute, IDecoratorAttribute
{
public CanvasAttribute(Type type)
{
this.type = type;
}
public Type type { get; private set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d719ba635ebc49c5a2132cc199b06d3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,10 @@
namespace Unity.VisualScripting
{
public enum CanvasControlScheme
{
[RenamedFrom("Unity")]
Default,
[RenamedFrom("Unreal")]
Alternate
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c47a48caf94f0400ebbbbbe83ae13c45
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,52 @@
namespace Unity.VisualScripting
{
public class CanvasProvider : SingleDecoratorProvider<IGraph, ICanvas, CanvasAttribute>
{
static CanvasProvider()
{
instance = new CanvasProvider();
}
public static CanvasProvider instance { get; }
protected override bool cache => true;
public override bool IsValid(IGraph graph)
{
return true;
}
}
public static class XCanvasProvider
{
public static ICanvas Canvas(this IGraph graph)
{
return CanvasProvider.instance.GetDecorator(graph);
}
public static TCanvas Canvas<TCanvas>(this IGraph graph) where TCanvas : ICanvas
{
return CanvasProvider.instance.GetDecorator<TCanvas>(graph);
}
public static IWidget Widget(this ICanvas context, IGraphItem item)
{
return context.widgetProvider.GetDecorator(item);
}
public static TWidget Widget<TWidget>(this ICanvas context, IGraphItem item) where TWidget : IWidget
{
return context.widgetProvider.GetDecorator<TWidget>(item);
}
public static IGraphElementWidget Widget(this ICanvas context, IGraphElement element)
{
return (IGraphElementWidget)context.widgetProvider.GetDecorator(element);
}
public static TWidget Widget<TWidget>(this ICanvas context, IGraphElement element) where TWidget : IGraphElementWidget
{
return context.widgetProvider.GetDecorator<TWidget>(element);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b32061678e736470f9787cdd51b40816
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,53 @@
namespace Unity.VisualScripting
{
public enum DistributeOperation
{
/// <summary>
/// Distribute the selected elements so that the left edges
/// are at equal distance of one another.
/// </summary>
DistributeLeftEdges,
/// <summary>
/// Distribute the selected elements so that the horizontal centers
/// are at equal distance of one another.
/// </summary>
DistributeCenters,
/// <summary>
/// Distribute the selected elements so that the right edges
/// are at equal distance of one another.
/// </summary>
DistributeRightEdges,
/// <summary>
/// Distribute the selected elements so that the horizontal gaps
/// are all of equal size.
/// </summary>
DistributeHorizontalGaps,
/// <summary>
/// Distribute the selected elements so that the top edges
/// are at equal distance of one another.
/// </summary>
DistributeTopEdges,
/// <summary>
/// Distribute the selected elements so that the vertical middles
/// are at equal distance of one another.
/// </summary>
DistributeMiddles,
/// <summary>
/// Distribute the selected elements so that the bottom edges
/// are at equal distance of one another.
/// </summary>
DistributeBottomEdges,
/// <summary>
/// Distribute the selected elements so that the vertical gaps
/// are all of equal size.
/// </summary>
DistributeVerticalGaps
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 734135440ea3f4d6297720ac1b3be02b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Unity.VisualScripting
{
public interface ICanvas : IDisposable, IDragAndDropHandler
{
void Cache();
#region Model
ICanvasWindow window { get; set; }
IGraph graph { get; }
WidgetProvider widgetProvider { get; }
GraphSelection selection { get; }
#endregion
#region Widgets
IEnumerable<IWidget> widgets { get; }
void Recollect();
void CacheWidgetCollections();
#endregion
#region Hovering
IWidget hoveredWidget { get; }
#endregion
#region Deleting
void DeleteSelection();
#endregion
#region Clipboard
void ShrinkCopyGroup(HashSet<IGraphElement> copyGroup);
#endregion
#region Lifecycle
void RegisterControls();
void Open();
void Close();
void Update();
void BeforeFrame();
void OnGUI();
#endregion
#region Viewing
float zoom { get; }
Vector2 pan { get; }
void UpdateViewport();
Rect viewport { get; set; }
Vector2 mousePosition { get; }
bool isMouseOver { get; }
bool isMouseOverBackground { get; }
void ViewElements(IEnumerable<IGraphElement> elements);
bool IsVisible(IWidget widget);
#endregion
#region Positioning
void CacheWidgetPositions();
#endregion
#region Lassoing
bool isLassoing { get; }
Rect lassoArea { get; }
#endregion
#region Selecting
bool isSelecting { get; }
Rect selectionArea { get; }
#endregion
#region Grouping
bool isGrouping { get; }
Rect groupArea { get; }
#endregion
#region Dragging
bool isDragging { get; }
void BeginDrag(EventWrapper e);
void Drag(EventWrapper e);
void EndDrag(EventWrapper e);
#endregion
#region Layout
void Align(AlignOperation operation);
void Distribute(DistributeOperation operation);
#endregion
#region Timing
float frameDeltaTime { get; }
float eventDeltaTime { get; }
float repaintDeltaTime { get; }
#endregion
#region Window
void OnToolbarGUI();
event Action delayCall;
Queue<Action> delayedCalls { get; }
#endregion
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: efc35be9d817e474abdbae4ad8bcce69
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Unity.VisualScripting
{
public interface IGraphContextExtension : IDragAndDropHandler
{
IEnumerable<GraphContextMenuItem> contextMenuItems { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3fc8c30ce61444945ab84043028247dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1483dfd529fe4aeeaba721add9fceb5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,134 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Unity.VisualScripting
{
/// <summary>
/// A list of widgets that can be safely iterated over even if the collection changes during iteration.
/// </summary>
public class WidgetList<TWidget> : Collection<TWidget>, IEnumerable<TWidget> where TWidget : class, IWidget
{
private uint version;
private readonly ICanvas canvas;
public WidgetList(ICanvas canvas)
{
Ensure.That(nameof(canvas)).IsNotNull(canvas);
this.canvas = canvas;
}
protected override void InsertItem(int index, TWidget item)
{
base.InsertItem(index, item);
version++;
}
protected override void RemoveItem(int index)
{
base.RemoveItem(index);
version++;
}
protected override void ClearItems()
{
base.ClearItems();
version++;
}
public new Enumerator GetEnumerator()
{
return new Enumerator(this);
}
IEnumerator<TWidget> IEnumerable<TWidget>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public struct Enumerator : IEnumerator<TWidget>
{
private readonly WidgetList<TWidget> list;
private int index;
private TWidget current;
private bool invalid;
private readonly uint version;
private IGraph graph;
public Enumerator(WidgetList<TWidget> list) : this()
{
this.list = list;
version = list.version;
// Micro optim
graph = list.canvas.graph;
}
public void Dispose() { }
public bool MoveNext()
{
// Stop if the list changed
if (version != list.version)
{
current = null;
invalid = true;
return false;
}
// Micro optim
var count = list.Count;
// Find the next element that is within the graph
while (index < count)
{
current = list[index];
index++;
if (current.item.graph == graph)
{
return true;
}
}
// Expleted the list
current = null;
invalid = true;
return false;
}
public TWidget Current => current;
object IEnumerator.Current
{
get
{
if (invalid)
{
throw new InvalidOperationException();
}
return Current;
}
}
void IEnumerator.Reset()
{
throw new InvalidOperationException();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f799a0c75a66440bd87f44b34a197d9c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d95a4548a35ec4b78ad08d0210b776ec
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,321 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Unity.VisualScripting
{
[InitializeAfterPlugins]
public static class GraphClipboard
{
static GraphClipboard()
{
singleClipboard = new Clipboard();
groupClipboard = new Clipboard();
GraphWindow.activeContextChanged += OnContextChange;
}
private static Event e => Event.current;
private static void OnContextChange(IGraphContext context)
{
GraphClipboard.context = context;
}
private static IGraphContext context;
#region Context Shortcuts
private static GraphReference reference => context.reference;
private static IGraph graph => context.graph;
private static ICanvas canvas => context.canvas;
private static GraphSelection selection => context.selection;
#endregion
public static Clipboard singleClipboard { get; }
public static Clipboard groupClipboard { get; }
public static bool canCopySelection => selection.Count > 0;
public static bool canPaste
{
get
{
if (selection.Count == 1 && CanPasteInside(selection.Single()))
{
return true;
}
else
{
return canPasteOutside;
}
}
}
public static bool canPasteOutside => groupClipboard.containsData && GetPasteGroup().Any();
public static bool canDuplicateSelection => GetCopyGroup(selection).Count > 0;
private static HashSet<IGraphElement> GetCopyGroup(IEnumerable<IGraphElement> elements)
{
var copyGroup = new HashSet<IGraphElement>();
foreach (var element in elements)
{
copyGroup.Add(element);
canvas.Widget(element).ExpandCopyGroup(copyGroup);
}
canvas.ShrinkCopyGroup(copyGroup);
return copyGroup;
}
private static List<IGraphElement> GetPasteGroup()
{
return groupClipboard.Paste<HashSet<IGraphElement>>()
.Where(e => graph.elements.Includes(e.GetType()))
.OrderBy(e => e.dependencyOrder)
.ToList();
}
public static void CopyElement(IGraphElement element)
{
Ensure.That(nameof(element)).IsNotNull(element);
singleClipboard.Copy(element);
groupClipboard.Copy(GetCopyGroup(element.Yield()));
}
public static void CopySelection()
{
if (!canCopySelection)
{
throw new InvalidOperationException();
}
if (selection.Count == 1)
{
CopyElement(selection.Single());
}
else
{
singleClipboard.Clear();
groupClipboard.Copy(GetCopyGroup(selection));
}
e?.TryUse();
}
public static void Paste(Vector2? position = null)
{
if (!canPaste)
{
throw new InvalidOperationException();
}
if (selection.Count == 1 && CanPasteInside(selection.Single()))
{
PasteInside(selection.Single());
}
else
{
PasteOutside(true, position);
}
}
public static bool CanPasteInside(IGraphElement element)
{
Ensure.That(nameof(element)).IsNotNull(element);
// TODO: A solid PasteInside implementation would work like ReplaceUnit:
// Implement an IPreservable interface, preserve, remove, recreate, apply.
// This would make entirely sure that all OnAdd/OnRemove handlers get called,
// and wouldn't require any per-element implementation. Plus, it would
// allow pasting across element types while preserving connections/transitions!
return false;
}
public static void PasteInside(IGraphElement element)
{
Ensure.That(nameof(element)).IsNotNull(element);
if (!CanPasteInside(element))
{
throw new InvalidOperationException();
}
UndoUtility.RecordEditedObject("Paste Graph Element");
throw new NotImplementedException();
//GUI.changed = true;
//e?.TryUse();
}
public static void PasteOutside(bool reposition, Vector2? position = null)
{
if (!canPasteOutside)
{
throw new InvalidOperationException();
}
UndoUtility.RecordEditedObject("Paste Graph Elements");
var pastedElements = GetPasteGroup();
// Assign new GUIDs
foreach (var pastedElement in pastedElements)
{
pastedElement.guid = Guid.NewGuid();
}
// Add elements to graph and selection
selection.Clear();
foreach (var pastedElement in pastedElements)
{
if (!pastedElement.HandleDependencies())
{
continue;
}
graph.elements.Add(pastedElement);
selection.Add(pastedElement);
}
canvas.Cache();
foreach (var pastedElement in pastedElements)
{
var pastedWidget = canvas.Widget(pastedElement);
pastedWidget.BringToFront();
}
var pastedWidgets = pastedElements.Select(e => canvas.Widget(e)).ToList();
// Recenter elements in graph view
if (reposition)
{
var area = GraphGUI.CalculateArea(pastedWidgets.Where(widget => widget.canDrag));
Vector2 delta;
if (position.HasValue)
{
delta = position.Value - area.position;
}
else
{
delta = graph.pan - area.center;
}
foreach (var pastedWidget in pastedWidgets)
{
if (pastedWidget.canDrag)
{
pastedWidget.position = new Rect(pastedWidget.position.position + delta, pastedWidget.position.size).PixelPerfect();
pastedWidget.Reposition();
pastedWidget.CachePositionFirstPass();
pastedWidget.CachePosition();
}
}
}
// Space out overlapping elements
foreach (var pastedWidget in pastedWidgets)
{
if (pastedWidget.canDrag)
{
var distanciation = 20;
var timeout = 100;
var timeoutIndex = 0;
while (GraphGUI.PositionOverlaps(canvas, pastedWidget, 5))
{
// Space the widget out
pastedWidget.position = new Rect(pastedWidget.position.position + new Vector2(distanciation, distanciation), pastedWidget.position.size).PixelPerfect();
// Calculate the resulting position immediately
pastedWidget.CachePositionFirstPass();
pastedWidget.CachePosition();
// Mark it as invalid still so dependencies like ports will be recached
pastedWidget.Reposition();
// Failsafe to keep the editor from freezing
if (++timeoutIndex > timeout)
{
Debug.LogWarning($"Failed to space out pasted element: {pastedWidget.element}");
break;
}
}
}
}
canvas.Cache();
GUI.changed = true;
e?.TryUse();
}
public static void CutSelection()
{
UndoUtility.RecordEditedObject("Cut Graph Element Selection");
CopySelection();
canvas.DeleteSelection();
}
public static void DuplicateSelection()
{
object singleClipboardRestore = null;
object groupClipboardRestore = null;
if (singleClipboard.containsData)
{
singleClipboardRestore = singleClipboard.Paste();
}
if (groupClipboard.containsData)
{
groupClipboardRestore = groupClipboard.Paste();
}
UndoUtility.RecordEditedObject("Duplicate Graph Element Selection");
CopySelection();
PasteOutside(false);
if (singleClipboardRestore != null)
{
singleClipboard.Copy(singleClipboardRestore);
}
else
{
singleClipboard.Clear();
}
if (groupClipboardRestore != null)
{
groupClipboard.Copy(groupClipboardRestore);
}
else
{
groupClipboard.Clear();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c89d16b46444140d3bbbc42881903a35
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,114 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using UnityEditor;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
public abstract class GraphContext<TGraph, TCanvas> : IGraphContext
where TGraph : class, IGraph
where TCanvas : class, ICanvas
{
protected GraphContext(GraphReference reference)
{
Ensure.That(nameof(reference)).IsNotNull(reference);
Ensure.That(nameof(reference.graph)).IsOfType<TGraph>(reference.graph);
this.reference = reference;
analyserProvider = new AnalyserProvider(reference);
graphMetadata = Metadata.Root().StaticObject(reference.graph);
extensions = this.Extensions().ToList().AsReadOnly();
sidebarPanels = SidebarPanels().ToList().AsReadOnly();
}
public GraphReference reference { get; }
public AnalyserProvider analyserProvider { get; }
public TCanvas canvas => (TCanvas)graph.Canvas();
ICanvas IGraphContext.canvas => canvas;
public GraphSelection selection => canvas.selection;
public TGraph graph => (TGraph)reference.graph;
IGraph IGraphContext.graph => graph;
public Metadata graphMetadata { get; }
public Metadata selectionMetadata => selection.Count == 1 ? ElementMetadata(selection.Single()) : null;
public Metadata ElementMetadata(IGraphElement element)
{
// Static object is faster than indexer
// return graphMetadata[nameof(IGraph.elements)].Indexer(element.guid).Cast(element.GetType());
return graphMetadata[nameof(IGraph.elements)].StaticObject(element);
}
public ReadOnlyCollection<IGraphContextExtension> extensions { get; }
IEnumerable<IGraphContextExtension> IGraphContext.extensions => extensions;
public virtual string windowTitle => "Graph";
protected virtual IEnumerable<ISidebarPanelContent> SidebarPanels() => Enumerable.Empty<ISidebarPanelContent>();
public ReadOnlyCollection<ISidebarPanelContent> sidebarPanels { get; }
IEnumerable<ISidebarPanelContent> IGraphContext.sidebarPanels => sidebarPanels;
public bool isPrefabInstance => reference.serializedObject?.IsConnectedPrefabInstance() ?? false;
#region Lifecycle
public virtual void Dispose()
{
// Manually free the providers so that the
// disposable decorators free their event handlers
analyserProvider?.FreeAll();
}
public void BeginEdit(bool disablePrefabInstance = true)
{
LudiqEditorUtility.editedObject.BeginOverride(reference.serializedObject);
LudiqGraphsEditorUtility.editedContext.BeginOverride(this);
EditorGUI.BeginDisabledGroup(disablePrefabInstance && isPrefabInstance);
EditorGUI.BeginChangeCheck();
}
public void EndEdit()
{
if (EditorGUI.EndChangeCheck())
{
DescribeAndAnalyze();
}
EditorGUI.EndDisabledGroup();
LudiqGraphsEditorUtility.editedContext.EndOverride();
LudiqEditorUtility.editedObject.EndOverride();
}
public void DescribeAndAnalyze()
{
foreach (var element in graph.elements)
{
if (element.HasDescriptor())
{
element.Describe();
}
}
graph.Describe();
analyserProvider.AnalyzeAll();
reference.parent.Describe();
}
#endregion
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ca981340ac4c94929b5263c2e31a0d6c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,15 @@
using System;
namespace Unity.VisualScripting
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public sealed class GraphContextAttribute : Attribute, IDecoratorAttribute
{
public GraphContextAttribute(Type type)
{
this.type = type;
}
public Type type { get; private set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 159639efa590a4849a09879979cbba8b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
public abstract class GraphContextExtension<TGraphContext> : IGraphContextExtension
where TGraphContext : IGraphContext
{
protected GraphContextExtension(TGraphContext context)
{
this.context = context;
}
public TGraphContext context { get; }
#region Context Shortcuts
protected GraphReference reference => context.reference;
protected IGraph graph => context.graph;
protected ICanvas canvas => context.canvas;
protected GraphSelection selection => context.selection;
#endregion
public virtual IEnumerable<GraphContextMenuItem> contextMenuItems => Enumerable.Empty<GraphContextMenuItem>();
public virtual DragAndDropVisualMode dragAndDropVisualMode => DragAndDropVisualMode.Generic;
public virtual bool AcceptsDragAndDrop() => false;
public virtual void PerformDragAndDrop() { }
public virtual void UpdateDragAndDrop() { }
public virtual void DrawDragAndDropPreview() { }
public virtual void ExitDragAndDrop() { }
protected static Event e => Event.current;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 075261282385d497b8b191d4e752d49b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show more