initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3dfa3c8fac2134fdda65f882f5a1a014
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
using UnityEditor;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public class EditorPreferencesProvider : Editor
|
||||
{
|
||||
[SettingsProvider]
|
||||
public static SettingsProvider CreateEditorPreferencesProvider()
|
||||
{
|
||||
return new EditorPreferencesProviderView();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ba9c71f100044b3a9dca49c8f9e489cb
|
||||
timeCreated: 1625864018
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b7453dd8d3980405794f631eea9f420e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a06b4d04018343b99f255da7f18559d1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7cb1615bd41a3442a8aabbcb872046a3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 50455e66302b715488a9218e30b0ab24
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7da9d8250917648728b856bfab5f1320
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a8c0a3364f2ef4e42820eb87b5bdab69
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f061d8e7ad514436d82f47b34306a22c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
using UnityEditor;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public class ProjectSettingsProvider : Editor
|
||||
{
|
||||
[SettingsProvider]
|
||||
public static SettingsProvider CreateProjectSettingProvider()
|
||||
{
|
||||
return new ProjectSettingsProviderView();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d6a0cc840e6084f62a3e3538f14bf3ce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4eea00f49056f4ee98c82b6923b44600
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2aa54ddaa48744e1caddee916e39805e
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7abb56c9dd89a544a9995a060ab55368
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a0a037cc0a236493996d666b0ab8690c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7cf3624e65e804bfb9dd35ee9884f519
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b7663ba3a43454ba48fd2c7c17a0ae75
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8c1743061b8e147ff9c808d137b1014e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,6 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class Analysis : IAnalysis
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f01a1ac261c724daca990596c32f7f10
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public class GraphElementAnalysis : IGraphElementAnalysis
|
||||
{
|
||||
public List<Warning> warnings { get; set; } = new List<Warning>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1ac0dd11da2f64fc5b55b1b8a1d36b55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IAnalyser
|
||||
{
|
||||
IAnalysis analysis { get; }
|
||||
|
||||
bool isDirty { get; set; }
|
||||
|
||||
void Validate();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b1eb0e9411d1e4ed880da3147ac22dc8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,6 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IAnalysis
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4fb36d0252722495cbe1fddf45ba9185
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphElementAnalysis : IAnalysis
|
||||
{
|
||||
List<Warning> warnings { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bf7f2015398494d498495147f2028b49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fae0344df02e74e0988c577297cc556d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 59718df20b933421a9c465fcbeff7836
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7d2bbdd7f3404e04941d986015e27acb
|
||||
timeCreated: 1621016306
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5e84744b83a8434ab42fa513f9bf00ad
|
||||
timeCreated: 1617297094
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 624ee9ba5d484bf4c9e9e910d66273e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9ef9ab17827342b781ff73737f13ddb7
|
||||
timeCreated: 1617896748
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b1903aa39b6b4857be4ef4e143617965
|
||||
timeCreated: 1616172851
|
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 53a1e1bb3f40450abf503305ff74ce82
|
||||
timeCreated: 1616694961
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5f1b15ef6f7374405b6abe5fae72c2e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 662cc5e740e0841e1bb91853ab803f33
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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[]>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e82979a3184424cf390bc7483f03771c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 505852ed4622a47088bd0f2f07041314
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d195fcaa7976240d09b19fccb42c2068
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IAssigner
|
||||
{
|
||||
void ValueChanged();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c1fabba6ac68944be828b41a0b09b932
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0c9b7e151c1f64d48b39eca8d419c2b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d800ad61ae46243a0bc7d8d834b1bf3f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2a3cd7080fc4042768e4e591975cfbc5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 31d3bc055420f4ee59008eb8996d7df2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 57d833ea26c9149528d23f0ec150b1d2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4d719ba635ebc49c5a2132cc199b06d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public enum CanvasControlScheme
|
||||
{
|
||||
[RenamedFrom("Unity")]
|
||||
Default,
|
||||
[RenamedFrom("Unreal")]
|
||||
Alternate
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c47a48caf94f0400ebbbbbe83ae13c45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b32061678e736470f9787cdd51b40816
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 734135440ea3f4d6297720ac1b3be02b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: efc35be9d817e474abdbae4ad8bcce69
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphContextExtension : IDragAndDropHandler
|
||||
{
|
||||
IEnumerable<GraphContextMenuItem> contextMenuItems { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3fc8c30ce61444945ab84043028247dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d1483dfd529fe4aeeaba721add9fceb5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f799a0c75a66440bd87f44b34a197d9c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d95a4548a35ec4b78ad08d0210b776ec
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c89d16b46444140d3bbbc42881903a35
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ca981340ac4c94929b5263c2e31a0d6c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 159639efa590a4849a09879979cbba8b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue