seperating out editor UI from editor receiving

This commit is contained in:
JR-Morgan
2022-11-04 02:50:37 +00:00
parent 8d2fc0072d
commit 9f2744df2c
7 changed files with 929 additions and 30 deletions
+204 -30
View File
@@ -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}
+168
View File
@@ -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();
}
}
+11
View File
@@ -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: