seperating out editor UI from editor receiving
This commit is contained in:
+204
-30
@@ -1849,6 +1849,19 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 762643242}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &816671309
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 141ce93d2d159c0448b5b8b33b1c0679, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
path: Assets/Resources
|
||||
--- !u!1 &869165413
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -2064,19 +2077,6 @@ Transform:
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 6
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &916416845
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 141ce93d2d159c0448b5b8b33b1c0679, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
path: Assets/Resources
|
||||
--- !u!1 &1031574851
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -2773,6 +2773,98 @@ Canvas:
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!1 &1377894061
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1377894063}
|
||||
- component: {fileID: 1377894062}
|
||||
m_Layer: 0
|
||||
m_Name: GameObject (2)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1377894062
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1377894061}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0bc895f6cb37b674995dc13b79783c55, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<Accounts>k__BackingField:
|
||||
rid: 6366722168162615296
|
||||
<Streams>k__BackingField:
|
||||
rid: 6366722168162615297
|
||||
<Branches>k__BackingField:
|
||||
rid: 6366722168162615298
|
||||
<Commits>k__BackingField:
|
||||
rid: 6366722168162615299
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 6366722168162615296
|
||||
type: {class: AccountSelection, ns: Speckle.ConnectorUnity, asm: Speckle.Connector}
|
||||
data:
|
||||
selectedIndex: 1
|
||||
- rid: 6366722168162615297
|
||||
type: {class: StreamSelection, ns: Speckle.ConnectorUnity, asm: Speckle.Connector}
|
||||
data:
|
||||
selectedIndex: 2
|
||||
<RequestLimit>k__BackingField: 50
|
||||
<AccountSelection>k__BackingField:
|
||||
rid: 6366722168162615296
|
||||
- rid: 6366722168162615298
|
||||
type: {class: BranchSelection, ns: Speckle.ConnectorUnity, asm: Speckle.Connector}
|
||||
data:
|
||||
selectedIndex: 5
|
||||
<BranchLimit>k__BackingField: 20
|
||||
<CommitLimit>k__BackingField: 15
|
||||
<StreamSelection>k__BackingField:
|
||||
rid: 6366722168162615297
|
||||
- rid: 6366722168162615299
|
||||
type: {class: CommitSelection, ns: Speckle.ConnectorUnity, asm: Speckle.Connector}
|
||||
data:
|
||||
selectedIndex: 1
|
||||
<BranchSelection>k__BackingField:
|
||||
rid: 6366722168162615298
|
||||
--- !u!4 &1377894063
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1377894061}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 10.1570635, y: -2.743827, z: 12.879177}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 11
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1390840085
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b3354e8208862c341940152f5340d41a, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &1397973070
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -2874,8 +2966,9 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
nativeCaches:
|
||||
- {fileID: 916416845}
|
||||
- {fileID: 1951948666}
|
||||
- {fileID: 1852085904}
|
||||
- {fileID: 816671309}
|
||||
- {fileID: 1390840085}
|
||||
--- !u!1 &1464556211
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3568,6 +3661,19 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1762991479}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1852085904
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 2a4a29c776298714c88f406ad39c6095, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
matchByName: 1
|
||||
--- !u!1 &1885647142
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -3872,18 +3978,6 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1903798475}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1951948666
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b3354e8208862c341940152f5340d41a, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &2014586909
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -4111,11 +4205,11 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
SelectedAccountIndex: 0
|
||||
SelectedStreamIndex: 8
|
||||
SelectedBranchIndex: 1
|
||||
SelectedStreamIndex: 0
|
||||
SelectedBranchIndex: 0
|
||||
SelectedCommitIndex: 0
|
||||
OldSelectedAccountIndex: 0
|
||||
OldSelectedStreamIndex: 8
|
||||
OldSelectedStreamIndex: 0
|
||||
--- !u!114 &2060322469
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -4429,3 +4523,83 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2074142232}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &7221284245862074005
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7221284245862074011}
|
||||
- component: {fileID: 7221284245862074008}
|
||||
m_Layer: 0
|
||||
m_Name: GameObject (1)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &7221284245862074008
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7221284245862074005}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0bc895f6cb37b674995dc13b79783c55, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<Accounts>k__BackingField:
|
||||
rid: 6366722168162615296
|
||||
<Streams>k__BackingField:
|
||||
rid: 6366722168162615297
|
||||
<Branches>k__BackingField:
|
||||
rid: 6366722168162615298
|
||||
<Commits>k__BackingField:
|
||||
rid: 6366722168162615299
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 6366722168162615296
|
||||
type: {class: AccountSelection, ns: Speckle.ConnectorUnity, asm: Speckle.Connector}
|
||||
data:
|
||||
selectedIndex: 1
|
||||
- rid: 6366722168162615297
|
||||
type: {class: StreamSelection, ns: Speckle.ConnectorUnity, asm: Speckle.Connector}
|
||||
data:
|
||||
selectedIndex: 2
|
||||
<RequestLimit>k__BackingField: 50
|
||||
<AccountSelection>k__BackingField:
|
||||
rid: 6366722168162615296
|
||||
- rid: 6366722168162615298
|
||||
type: {class: BranchSelection, ns: Speckle.ConnectorUnity, asm: Speckle.Connector}
|
||||
data:
|
||||
selectedIndex: 3
|
||||
<BranchLimit>k__BackingField: 20
|
||||
<CommitLimit>k__BackingField: 15
|
||||
<StreamSelection>k__BackingField:
|
||||
rid: 6366722168162615297
|
||||
- rid: 6366722168162615299
|
||||
type: {class: CommitSelection, ns: Speckle.ConnectorUnity, asm: Speckle.Connector}
|
||||
data:
|
||||
selectedIndex: 3
|
||||
<BranchSelection>k__BackingField:
|
||||
rid: 6366722168162615298
|
||||
--- !u!4 &7221284245862074011
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7221284245862074005}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 10.1570635, y: -2.743827, z: 12.879177}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 10
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.ConnectorUnity;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using Speckle.Core.Kits;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Transports;
|
||||
using UnityEditor.VersionControl;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Task = System.Threading.Tasks.Task;
|
||||
|
||||
|
||||
[ExecuteAlways]
|
||||
[AddComponentMenu("Speckle/Speckle Receiver")]
|
||||
[RequireComponent(typeof(RecursiveConverter))]
|
||||
public class SpeckleReceiver : MonoBehaviour, ISerializationCallbackReceiver
|
||||
{
|
||||
[field: SerializeReference]
|
||||
public AccountSelection Account { get; protected set; }
|
||||
|
||||
[field: SerializeReference]
|
||||
public StreamSelection Stream { get; protected set; }
|
||||
|
||||
[field: SerializeReference]
|
||||
public BranchSelection Branch { get; protected set; }
|
||||
|
||||
[field: SerializeReference]
|
||||
public CommitSelection Commit { get; protected set; }
|
||||
|
||||
public RecursiveConverter Converter { get; protected set; }
|
||||
|
||||
private CancellationTokenSource cancellationTokenSource;
|
||||
|
||||
public UnityEvent<ConcurrentDictionary<string, int>> OnProgressAction;
|
||||
public UnityEvent<string, Exception> OnErrorAction;
|
||||
public UnityEvent<int> OnTotalChildrenCountKnown;
|
||||
public UnityEvent<List<GameObject>> OnComplete;
|
||||
|
||||
#nullable enable
|
||||
public void Awake()
|
||||
{
|
||||
Initialise();
|
||||
Account!.RefreshOptions();
|
||||
Converter = GetComponent<RecursiveConverter>();
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
protected void Initialise()
|
||||
{
|
||||
Account ??= new AccountSelection();
|
||||
Stream ??= new StreamSelection(Account);
|
||||
Branch ??= new BranchSelection(Stream);
|
||||
Commit ??= new CommitSelection(Branch);
|
||||
Stream.Initialise();
|
||||
Branch.Initialise();
|
||||
Commit.Initialise();
|
||||
if(Account.Options is not {Length: > 0})
|
||||
Account.RefreshOptions();
|
||||
}
|
||||
|
||||
|
||||
public async void ReceiveAndConvert(CancellationToken token, Transform? parentDestination = null)
|
||||
{
|
||||
Account? account = Account.Selected;
|
||||
if (account == null) throw new SpeckleException("Cannot receive: Selected Account is null");
|
||||
Client client = Account.Client ?? new Client(account);
|
||||
Stream? stream = Stream.Selected;
|
||||
if (stream == null) throw new SpeckleException("Cannot receive: Selected Stream is null");
|
||||
Commit? commit = Commit.Selected;
|
||||
if (commit == null) throw new SpeckleException("Cannot receive: Selected Commit is null");
|
||||
|
||||
Base? commitObject = await ReceiveAsync(
|
||||
token: token,
|
||||
client: client,
|
||||
streamId: stream.id,
|
||||
objectId: commit.referencedObject,
|
||||
commitId: commit.id,
|
||||
onProgressAction: dict => OnProgressAction.Invoke(dict),
|
||||
onErrorAction: (m, e) => OnErrorAction.Invoke(m, e),
|
||||
onTotalChildrenCountKnown: c => OnTotalChildrenCountKnown.Invoke(c)
|
||||
);
|
||||
|
||||
Dispatcher.Instance().Enqueue(() =>
|
||||
{
|
||||
var converted = Converter.RecursivelyConvertToNative(commitObject, parentDestination);
|
||||
OnComplete.Invoke(converted);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static async Task<Base?> ReceiveAsync(CancellationToken token,
|
||||
Client client,
|
||||
string streamId,
|
||||
string objectId,
|
||||
string? commitId,
|
||||
Action<ConcurrentDictionary<string, int>>? onProgressAction = null,
|
||||
Action<string, Exception>? onErrorAction = null,
|
||||
Action<int>? onTotalChildrenCountKnown = null)
|
||||
{
|
||||
Base? ret = null;
|
||||
try
|
||||
{
|
||||
Analytics.TrackEvent(client.Account, Analytics.Events.Receive);
|
||||
|
||||
if (token.IsCancellationRequested) token.ThrowIfCancellationRequested();
|
||||
|
||||
var transport = new ServerTransport(client.Account, streamId);
|
||||
transport.CancellationToken = token;
|
||||
|
||||
ret = await Operations.Receive(
|
||||
objectId: objectId,
|
||||
cancellationToken: token,
|
||||
remoteTransport: transport,
|
||||
onProgressAction: onProgressAction,
|
||||
onErrorAction: onErrorAction,
|
||||
onTotalChildrenCountKnown: onTotalChildrenCountKnown,
|
||||
disposeTransports: true
|
||||
);
|
||||
|
||||
if (token.IsCancellationRequested) token.ThrowIfCancellationRequested();
|
||||
|
||||
try
|
||||
{
|
||||
await client.CommitReceived(token, new CommitReceivedInput
|
||||
{
|
||||
streamId = streamId,
|
||||
commitId = commitId,
|
||||
message = $"received commit from {Application.unityVersion}",
|
||||
sourceApplication = HostApplications.Unity.GetVersion(CoreUtils.GetHostAppVersion())
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Do nothing!
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
onErrorAction?.Invoke(e.Message, e);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
cancellationTokenSource.Cancel();
|
||||
Account?.Dispose();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
//pass
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
Initialise();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0bc895f6cb37b674995dc13b79783c55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,261 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class CommitSelection : OptionSelection<Commit>
|
||||
{
|
||||
[field: SerializeReference]
|
||||
public BranchSelection BranchSelection { get; private set; }
|
||||
|
||||
public CommitSelection(BranchSelection branchSelection)
|
||||
{
|
||||
BranchSelection = branchSelection;
|
||||
Initialise();
|
||||
|
||||
}
|
||||
|
||||
public void Initialise()
|
||||
{
|
||||
BranchSelection.OnSelectionChange = RefreshOptions;
|
||||
}
|
||||
|
||||
protected override string? KeyFunction(Commit? value) => value?.id;
|
||||
|
||||
public override void RefreshOptions()
|
||||
{
|
||||
Branch? branch = BranchSelection.Selected;
|
||||
if (branch == null) return;
|
||||
List<Commit> commits = branch.commits.items;
|
||||
GenerateOptions(commits, (_, i) => i == 0);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class BranchSelection : OptionSelection<Branch>
|
||||
{
|
||||
[field: SerializeField, Range(1,100)]
|
||||
public int BranchLimit { get; set; } = 30;
|
||||
[field: SerializeField, Range(1,100)]
|
||||
public int CommitLimit { get; set; } = 15;
|
||||
|
||||
[field: SerializeReference]
|
||||
public StreamSelection StreamSelection { get; private set; }
|
||||
|
||||
public BranchSelection(StreamSelection streamSelection)
|
||||
{
|
||||
StreamSelection = streamSelection;
|
||||
Initialise();
|
||||
}
|
||||
|
||||
public void Initialise()
|
||||
{
|
||||
StreamSelection.OnSelectionChange = RefreshOptions;
|
||||
}
|
||||
|
||||
protected override string? KeyFunction(Branch? value) => value?.name;
|
||||
|
||||
public override void RefreshOptions()
|
||||
{
|
||||
Stream? stream = StreamSelection.Selected;
|
||||
if (stream == null) return;
|
||||
List<Branch> branches = StreamSelection.Client!.StreamGetBranches(stream.id, BranchLimit, CommitLimit).GetAwaiter().GetResult();
|
||||
GenerateOptions(branches, (b, _) => b.name == "main");
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class StreamSelection : OptionSelection<Stream>
|
||||
{
|
||||
private const int DEFAULT_REQUEST_LIMIT = 50;
|
||||
[field: SerializeField, Range(1,100)]
|
||||
public int RequestLimit { get; set; } = DEFAULT_REQUEST_LIMIT;
|
||||
|
||||
[field: SerializeReference]
|
||||
public AccountSelection AccountSelection { get; private set; }
|
||||
|
||||
public StreamSelection(AccountSelection accountSelection)
|
||||
{
|
||||
AccountSelection = accountSelection;
|
||||
Initialise();
|
||||
}
|
||||
|
||||
public void Initialise()
|
||||
{
|
||||
AccountSelection.OnSelectionChange = RefreshOptions;
|
||||
}
|
||||
|
||||
internal Client? Client => AccountSelection.Client;
|
||||
|
||||
protected override string? KeyFunction(Stream? value) => value?.id;
|
||||
public override void RefreshOptions()
|
||||
{
|
||||
if (Client == null) return;
|
||||
List<Stream> streams = Client.StreamsGet(RequestLimit).GetAwaiter().GetResult();
|
||||
GenerateOptions(streams, (_, i) => i == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
public sealed class AccountSelection : OptionSelection<Account>, IDisposable
|
||||
{
|
||||
private Client? client;
|
||||
public Client? Client
|
||||
{
|
||||
get
|
||||
{
|
||||
Account? account = Selected;
|
||||
if (account == null) return client = null;
|
||||
if (client == null || !client.Account.Equals(account)) return client = new Client(Selected);
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
protected override string? KeyFunction(Account? value) => value?.id;
|
||||
|
||||
public override void RefreshOptions()
|
||||
{
|
||||
GenerateOptions(AccountManager.GetAccounts().ToArray(),
|
||||
isDefault: (a, _) => a.isDefault);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
client?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
public abstract class OptionSelection<TOption> where TOption : class
|
||||
{
|
||||
[SerializeField]
|
||||
private int selectedIndex = -1;
|
||||
|
||||
public int SelectedIndex
|
||||
{
|
||||
get => selectedIndex;
|
||||
set
|
||||
{
|
||||
selectedIndex = value;
|
||||
OnSelectionChange?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public TOption? Selected
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Options == null) return null;
|
||||
if (SelectedIndex < 0 || SelectedIndex >= Options.Length) return null;
|
||||
return Options[SelectedIndex];
|
||||
}
|
||||
}
|
||||
|
||||
public TOption[] Options { get; protected set; } = Array.Empty<TOption>();
|
||||
public Action? OnSelectionChange { get; set; }
|
||||
|
||||
[return: NotNullIfNotNull("value")]
|
||||
protected abstract string? KeyFunction(TOption? value);
|
||||
|
||||
public abstract void RefreshOptions();
|
||||
|
||||
protected void GenerateOptions(IList<TOption> source, Func<TOption, int, bool> isDefault)
|
||||
{
|
||||
List<TOption> optionsToAdd = new List<TOption>(source.Count);
|
||||
int defaultOption = -1;
|
||||
int index = 0;
|
||||
foreach (TOption? a in source)
|
||||
{
|
||||
if (a == null) continue;
|
||||
optionsToAdd.Add(a);
|
||||
if (isDefault(a, index)) defaultOption = index;
|
||||
index++;
|
||||
}
|
||||
|
||||
TOption? currentSelected = Selected;
|
||||
bool selectionOutOfRange = SelectedIndex < 0 || SelectedIndex >= optionsToAdd.Count;
|
||||
if (selectionOutOfRange
|
||||
|| (currentSelected != null
|
||||
&& KeyFunction(currentSelected) != KeyFunction(optionsToAdd[SelectedIndex])))
|
||||
{
|
||||
selectedIndex = defaultOption;
|
||||
}
|
||||
|
||||
Options = optionsToAdd.ToArray();
|
||||
Debug.Log($"{this.GetType()} updated");
|
||||
OnSelectionChange?.Invoke();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// [Serializable]
|
||||
// public abstract class OptionSelection<TOption> : ISerializationCallbackReceiver where TOption : class
|
||||
// {
|
||||
// [SerializeReference]
|
||||
// private int selectedIndex = -1;
|
||||
// [SerializeReference]
|
||||
// protected string? selectedId;
|
||||
// public TOption? Selected
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// if (selectedIndex == -1) return null;
|
||||
// if(Options.Any(a => a.Equals())
|
||||
// if (!Options.ContainsKey(selectedId)) return null;
|
||||
// return Options[selectedId];
|
||||
// }
|
||||
// set
|
||||
// {
|
||||
// selectedId = KeyFunction(value);
|
||||
// OnSelectionChange?.Invoke();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// //public IDictionary<string, TOption> Options { get; protected set; } = new Dictionary<string, TOption>();
|
||||
// public TOption[] Options { get; protected set; } = Array.Empty<TOption>();
|
||||
// internal Action? OnSelectionChange { get; set; }
|
||||
//
|
||||
// [return: NotNullIfNotNull("value")]
|
||||
// protected abstract string? KeyFunction(TOption? value);
|
||||
//
|
||||
// public abstract void RefreshOptions();
|
||||
//
|
||||
// protected void GenerateOptions(IEnumerable<TOption?> source, Func<TOption, int, bool> isDefault)
|
||||
// {
|
||||
// Options.Clear();
|
||||
// TOption? defaultOption = null;
|
||||
// int index = 0;
|
||||
// foreach (TOption? a in source)
|
||||
// {
|
||||
// if (a == null) continue;
|
||||
// Options.TryAdd(KeyFunction(a), a);
|
||||
// if (isDefault(a, index)) defaultOption = a;
|
||||
// index++;
|
||||
// }
|
||||
//
|
||||
// if (selectedId == null || !Options.ContainsKey(selectedId))
|
||||
// {
|
||||
// Selected = defaultOption;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public virtual void OnAfterDeserialize()
|
||||
// {
|
||||
// RefreshOptions();
|
||||
// }
|
||||
//
|
||||
// public virtual void OnBeforeSerialize() { /*pass*/ }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91a60a3a0e5293d499e746c08d071b28
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,263 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Credentials;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Experimental.GraphView;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
namespace Speckle.ConnectorUnity
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(AccountSelection))]
|
||||
public sealed class AccountSelectionDrawer : OptionSelectionDrawer<Account>
|
||||
{
|
||||
protected override bool DisplayRefresh => true;
|
||||
protected override string FormatOption(Account o) => $"{o.userInfo.email} | {o.serverInfo.name}";
|
||||
protected override int GUIDetailsPropertyCount => 4;
|
||||
protected override void OnGUIDetails(Rect position, SerializedProperty property, GUIContent label, Account selection)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
position.height = DetailsTextHeight;
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Id", selection.userInfo.id);
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Name", selection.userInfo.name);
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Server", selection.serverInfo.name);
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "URL", selection.serverInfo.url);
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(StreamSelection))]
|
||||
public sealed class StreamSelectionDrawer : OptionSelectionDrawer<Stream>
|
||||
{
|
||||
protected override bool DisplayRefresh => true;
|
||||
|
||||
protected override string FormatOption(Stream o) => $"{o.name}";
|
||||
protected override int GUIDetailsPropertyCount => 7;
|
||||
|
||||
protected override void OnGUIDetails(Rect position, SerializedProperty property, GUIContent label, Stream selection)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
position.height = DetailsTextHeight;
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Stream Id", selection.id);
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Description", selection.description);
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Is Public", selection.isPublic.ToString());
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Role", selection.role);
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Created At", selection.createdAt);
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Updated At", selection.updatedAt);
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
var nameField = EditorGUI.PropertyField(position, property.FindPropertyRelative($"<{nameof(StreamSelection.RequestLimit)}>k__BackingField"));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(BranchSelection))]
|
||||
public sealed class BranchSelectionDrawer : OptionSelectionDrawer<Branch>
|
||||
{
|
||||
protected override bool DisplayRefresh => true;
|
||||
|
||||
protected override string FormatOption(Branch o) => $"{o.name}";
|
||||
protected override int GUIDetailsPropertyCount => 1;
|
||||
|
||||
protected override void OnGUIDetails(Rect position, SerializedProperty property, GUIContent label, Branch selection)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
position.height = DetailsTextHeight;
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Description", selection.description);
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
}
|
||||
|
||||
[CustomPropertyDrawer(typeof(CommitSelection))]
|
||||
public sealed class CommitSelectionDrawer : OptionSelectionDrawer<Commit>
|
||||
{
|
||||
protected override string FormatOption(Commit o) => $"{o.message} - {o.id}";
|
||||
protected override int GUIDetailsPropertyCount => 5;
|
||||
|
||||
protected override void OnGUIDetails(Rect position, SerializedProperty property, GUIContent label, Commit selection)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
position.height = DetailsTextHeight;
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Commit Id", selection.id);
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Author Name", selection.authorName);
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Created At", selection.createdAt);
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Source Application", selection.sourceApplication);
|
||||
|
||||
position.y += DetailsTextHeight;
|
||||
EditorGUI.TextField(position, "Reference Object Id", selection.referencedObject);
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public abstract class OptionSelectionDrawer<TOption> : PropertyDrawer where TOption : class
|
||||
{
|
||||
private const float RefreshButtonWidthScale = 0.2f;
|
||||
private const float PrefixIndentation = 100f;
|
||||
protected readonly float DetailsTextHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
|
||||
private bool foldOutStatus = false;
|
||||
protected virtual bool DisplayRefresh => false;
|
||||
protected abstract string FormatOption(TOption o);
|
||||
protected abstract int GUIDetailsPropertyCount { get; }
|
||||
|
||||
private string[] GetFormattedOptions(TOption[] options)
|
||||
{
|
||||
int optionsCount = options.Length;
|
||||
string[] choices = new string[optionsCount];
|
||||
for (int i = 0; i < optionsCount; i++)
|
||||
{
|
||||
choices[i] = FormatOption(options[i]);
|
||||
}
|
||||
|
||||
return choices;
|
||||
}
|
||||
|
||||
protected abstract void OnGUIDetails(Rect position, SerializedProperty property, GUIContent label, TOption selection);
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
var t = (OptionSelection<TOption>)fieldInfo.GetValue(property.serializedObject.targetObject);
|
||||
|
||||
var selectionRect = position;//EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
|
||||
selectionRect.x += PrefixIndentation + 5;
|
||||
selectionRect.width -= PrefixIndentation + 5;
|
||||
|
||||
var popupSize = DisplayRefresh
|
||||
? new Rect(selectionRect.x, selectionRect.y, selectionRect.width * (1-RefreshButtonWidthScale), DetailsTextHeight)
|
||||
: selectionRect;
|
||||
//TODO: fancy popup
|
||||
|
||||
var selectedOption = t.Selected;
|
||||
string selectedChoice = selectedOption != null ? FormatOption(selectedOption) : "";
|
||||
|
||||
if (GUI.Button(popupSize, selectedChoice, EditorStyles.popup))
|
||||
{
|
||||
var windowPos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);
|
||||
var provider = ScriptableObject.CreateInstance<StringListSearchProvider>();
|
||||
provider.Title = typeof(TOption).Name;
|
||||
provider.listItems = GetFormattedOptions(t.Options);;
|
||||
provider.onSetIndexCallback = o => { t.SelectedIndex = o;};
|
||||
SearchWindow.Open(new SearchWindowContext(windowPos), provider);
|
||||
}
|
||||
|
||||
if (DisplayRefresh)
|
||||
{
|
||||
var buttonSize = new Rect(selectionRect.x + popupSize.width , selectionRect.y, selectionRect.width * RefreshButtonWidthScale, DetailsTextHeight);
|
||||
if (GUI.Button(buttonSize, "Refresh"))
|
||||
{
|
||||
t.RefreshOptions();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO drop down with details
|
||||
//EditorGUI.DropdownButton(position, "TEST", FocusType.Passive);
|
||||
|
||||
//position.y += DetailsTextHeight;
|
||||
{ // Details drop down
|
||||
int visiblePropCount = property.isExpanded ? GUIDetailsPropertyCount : 0;
|
||||
var detailsHeight = new Vector2(PrefixIndentation, DetailsTextHeight + visiblePropCount * DetailsTextHeight);
|
||||
var foldoutRect = new Rect(position.position, detailsHeight);
|
||||
property.isExpanded = EditorGUI.BeginFoldoutHeaderGroup(foldoutRect, property.isExpanded, label);
|
||||
if (property.isExpanded && selectedOption != null)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
OnGUIDetails(position, property, label, selectedOption);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
EditorGUI.EndFoldoutHeaderGroup();
|
||||
}
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
EditorUtility.SetDirty(property.serializedObject.targetObject);
|
||||
|
||||
}
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
var standardHeight = EditorGUIUtility.singleLineHeight;
|
||||
|
||||
if (!property.isExpanded) return standardHeight;
|
||||
|
||||
var detailsHeight = GUIDetailsPropertyCount * (standardHeight + EditorGUIUtility.standardVerticalSpacing);
|
||||
|
||||
return standardHeight + detailsHeight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#nullable disable
|
||||
|
||||
public sealed class StringListSearchProvider : ScriptableObject, ISearchWindowProvider
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string[] listItems;
|
||||
|
||||
public Action<int> onSetIndexCallback;
|
||||
|
||||
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
|
||||
{
|
||||
List<SearchTreeEntry> searchList = new(listItems.Length + 1) {new SearchTreeGroupEntry(new GUIContent(Title), 0)};
|
||||
|
||||
for(int i = 0; i < listItems.Length; i++)
|
||||
{
|
||||
SearchTreeEntry entry = new SearchTreeEntry(new GUIContent(listItems[i]))
|
||||
{
|
||||
level = 1,
|
||||
userData = i
|
||||
};
|
||||
searchList.Add(entry);
|
||||
}
|
||||
|
||||
return searchList;
|
||||
}
|
||||
|
||||
public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context)
|
||||
{
|
||||
onSetIndexCallback?.Invoke((int)SearchTreeEntry.userData);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc44e364c0d7b8a4c95f87d0210054be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user