Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature/XRI3] Augment SerializableDictionary to allow temporary duplicates in Editor #962

Open
wants to merge 1 commit into
base: feature/XRI3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,78 @@ public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IS

void ISerializationCallbackReceiver.OnBeforeSerialize()
{
#if !UNITY_EDITOR
entries.Clear();

foreach (KeyValuePair<TKey, TValue> pair in this)
{
entries.Add(new SerializableDictionaryEntry(pair.Key, pair.Value));
}
#else
// While in Editor, the serialized entries list is managed differently and is not necessarily a 1:1 representation of
// the dictionary. This allows for temporary duplicate keys, something the dictionary cannot do, while modifications
// are being made in the Inspector since the default behavior is to duplicate the last entry when adding a new one.

// Override the first entry that has a matching key from the dictionary, otherwise add to entries.
foreach (KeyValuePair<TKey, TValue> pair in this)
{
if (TryFindSerializableIndex(pair.Key, out int index))
{
entries[index] = new SerializableDictionaryEntry(pair.Key, pair.Value);
}
else
{
entries.Add(new SerializableDictionaryEntry(pair.Key, pair.Value));
}
}
#endif
}

void ISerializationCallbackReceiver.OnAfterDeserialize()
{
this.Clear();
base.Clear();

foreach (SerializableDictionaryEntry entry in entries)
{
this.Add(entry.Key, entry.Value);
base.TryAdd(entry.Key, entry.Value);
}
}

#if UNITY_EDITOR
public new void Clear()
{
entries.Clear();
base.Clear();
}

public new bool Remove(TKey key, out TValue value)
{
if (base.Remove(key, out value))
{
if (TryFindSerializableIndex(key, out int index))
{
entries.RemoveAt(index);
}

return true;
}

return false;
}

public new bool Remove(TKey key)
{
return Remove(key, out _);
}

private bool TryFindSerializableIndex(TKey key, out int index)
{
var keyComparer = EqualityComparer<TKey>.Default;

index = entries.FindIndex((entry) => keyComparer.Equals(entry.Key, key));
return index != -1;
}
#endif

[Serializable]
private struct SerializableDictionaryEntry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the BSD 3-Clause

using MixedReality.Toolkit.Editor;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

Expand All @@ -27,6 +28,17 @@ public override void OnInspectorGUI()
InteractionModeManager interactionModeManager = (InteractionModeManager)target;

// Raise lots of errors if the interaction mode manager is configured incorrectly
var duplicateInteractorGroupMappings = GetDuplicateInteractorGroupMappings();
if (duplicateInteractorGroupMappings.Count > 0)
{
var duplicatedNameString = interactionModeManager.CompileDuplicatedNames(duplicateInteractorGroupMappings);

InspectorUIUtility.DrawError($"Duplicate interactor group mapping keys detected in the interaction mode manager on {interactionModeManager.gameObject.name}. " +
$"Please check the following interactor group mappings: {duplicatedNameString}");

GUI.color = InspectorUIUtility.ErrorColor;
}

var duplicatedNames = interactionModeManager.GetDuplicateInteractionModes();
if (duplicatedNames.Count > 0)
{
Expand Down Expand Up @@ -58,5 +70,41 @@ public override void OnInspectorGUI()

serializedObject.ApplyModifiedProperties();
}

private HashSet<string> GetDuplicateInteractorGroupMappings()
{
HashSet<string> duplicatedNames = new HashSet<string>();

SerializedProperty interactorGroupMappings = serializedObject.FindProperty("interactorGroupMappings");
SerializedProperty entries = interactorGroupMappings?.FindPropertyRelative("entries");

if (entries != null && entries.arraySize > 0)
{
HashSet<int> seenInstanceIDs = new HashSet<int>();

for (int i = 0; i < entries.arraySize; ++i)
{
SerializedProperty entry = entries.GetArrayElementAtIndex(i);
SerializedProperty key = entry.FindPropertyRelative("key");

int instanceID = key != null && key.objectReferenceValue != null ?
key.objectReferenceValue.GetInstanceID() : 0;

if (seenInstanceIDs.Contains(instanceID))
{
string duplicateName = key != null && key.objectReferenceValue != null ?
key.objectReferenceValue.name : "None (Game Object)";

duplicatedNames.Add(duplicateName);
}
else
{
seenInstanceIDs.Add(instanceID);
}
}
}

return duplicatedNames;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static InteractionModeManager Instance
[Obsolete("This method is obsolete. Please use InitializeInteractorGroups instead.")]
public void InitializeControllers()
{
foreach (XRController xrController in FindObjectUtility.FindObjectsByType<XRController>())
foreach (XRBaseController xrController in FindObjectUtility.FindObjectsByType<XRBaseController>())
{
if (!interactorGroupMappings.ContainsKey(xrController.gameObject))
{
Expand Down