|
@@ -15,50 +15,75 @@ namespace BansheeEditor
|
|
|
|
|
|
|
|
protected Dictionary<int, GUIDictionaryFieldRow> rows = new Dictionary<int, GUIDictionaryFieldRow>();
|
|
protected Dictionary<int, GUIDictionaryFieldRow> rows = new Dictionary<int, GUIDictionaryFieldRow>();
|
|
|
protected GUIDictionaryFieldRow editRow;
|
|
protected GUIDictionaryFieldRow editRow;
|
|
|
|
|
+ protected GUILayoutY guiLayout;
|
|
|
protected GUILayoutX guiChildLayout;
|
|
protected GUILayoutX guiChildLayout;
|
|
|
protected GUILayoutX guiTitleLayout;
|
|
protected GUILayoutX guiTitleLayout;
|
|
|
protected GUILayoutY guiContentLayout;
|
|
protected GUILayoutY guiContentLayout;
|
|
|
protected bool isExpanded;
|
|
protected bool isExpanded;
|
|
|
protected int depth;
|
|
protected int depth;
|
|
|
|
|
+ protected LocString title;
|
|
|
|
|
|
|
|
private int editRowIdx = -1;
|
|
private int editRowIdx = -1;
|
|
|
private object editKey;
|
|
private object editKey;
|
|
|
private object editValue;
|
|
private object editValue;
|
|
|
private object editOriginalKey;
|
|
private object editOriginalKey;
|
|
|
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Constructs a new GUI dictionary.
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- protected GUIDictionaryFieldBase()
|
|
|
|
|
- { }
|
|
|
|
|
|
|
+ private State state;
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
- /// Builds the dictionary GUI elements. Must be called at least once in order for the contents to be populated.
|
|
|
|
|
|
|
+ /// Constructs a new GUI dictionary.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- /// <typeparam name="T">Type of rows that are used to handle GUI for individual dictionary elements.</typeparam>
|
|
|
|
|
- /// <param name="title">Label to display on the dictionary GUI title.</param>
|
|
|
|
|
- /// <param name="empty">Should the created field represent a null object.</param>
|
|
|
|
|
- /// <param name="numRows">Number of rows to create GUI for. Only matters for a non-null dictionary.</param>
|
|
|
|
|
|
|
+ /// <param name="title">Label to display on the dictionary GUI title.</param>
|
|
|
/// <param name="layout">Layout to which to append the list GUI elements to.</param>
|
|
/// <param name="layout">Layout to which to append the list GUI elements to.</param>
|
|
|
/// <param name="depth">Determines at which depth to render the background. Useful when you have multiple
|
|
/// <param name="depth">Determines at which depth to render the background. Useful when you have multiple
|
|
|
/// nested containers whose backgrounds are overlaping. Also determines background style,
|
|
/// nested containers whose backgrounds are overlaping. Also determines background style,
|
|
|
/// depths divisible by two will use an alternate style.</param>
|
|
/// depths divisible by two will use an alternate style.</param>
|
|
|
- protected void BuildGUI<T>(LocString title, bool empty, int numRows, GUILayout layout,
|
|
|
|
|
- int depth = 0) where T : GUIDictionaryFieldRow, new()
|
|
|
|
|
|
|
+ protected GUIDictionaryFieldBase(LocString title, GUILayout layout, int depth = 0)
|
|
|
{
|
|
{
|
|
|
- Destroy();
|
|
|
|
|
-
|
|
|
|
|
|
|
+ this.title = title;
|
|
|
|
|
+ this.guiLayout = layout.AddLayoutY();
|
|
|
this.depth = depth;
|
|
this.depth = depth;
|
|
|
- this.editKey = CreateKey();
|
|
|
|
|
- this.editValue = CreateValue();
|
|
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (empty)
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Completely rebuilds the dictionary GUI elements.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ protected void BuildGUI()
|
|
|
|
|
+ {
|
|
|
|
|
+ editKey = CreateKey();
|
|
|
|
|
+ editValue = CreateValue();
|
|
|
|
|
+
|
|
|
|
|
+ UpdateHeaderGUI(true);
|
|
|
|
|
+
|
|
|
|
|
+ if (!IsNull())
|
|
|
{
|
|
{
|
|
|
- rows.Clear();
|
|
|
|
|
|
|
+ // Hidden dependency: BuildGUI must be called after all elements are
|
|
|
|
|
+ // in the dictionary so we do it in two steps
|
|
|
|
|
+ int numRows = GetNumRows();
|
|
|
|
|
+ for (int i = 0; i < numRows; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ GUIDictionaryFieldRow newRow = CreateRow();
|
|
|
|
|
+ rows.Add(i, newRow);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ editRow = CreateRow();
|
|
|
|
|
+ editRow.BuildGUI(this, guiContentLayout, numRows, depth + 1);
|
|
|
|
|
+ editRow.Enabled = false;
|
|
|
|
|
|
|
|
- guiChildLayout = null;
|
|
|
|
|
- guiContentLayout = null;
|
|
|
|
|
- guiTitleLayout = layout.AddLayoutX();
|
|
|
|
|
|
|
+ for (int i = 0; i < numRows; i++)
|
|
|
|
|
+ rows[i].BuildGUI(this, guiContentLayout, i, depth + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Rebuilds the GUI dictionary header if needed.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="forceRebuild">Forces the header to be rebuilt.</param>
|
|
|
|
|
+ protected void UpdateHeaderGUI(bool forceRebuild)
|
|
|
|
|
+ {
|
|
|
|
|
+ Action BuildEmptyGUI = () =>
|
|
|
|
|
+ {
|
|
|
|
|
+ guiTitleLayout = guiLayout.AddLayoutX();
|
|
|
|
|
|
|
|
guiTitleLayout.AddElement(new GUILabel(title));
|
|
guiTitleLayout.AddElement(new GUILabel(title));
|
|
|
guiTitleLayout.AddElement(new GUILabel("Empty", GUIOption.FixedWidth(100)));
|
|
guiTitleLayout.AddElement(new GUILabel("Empty", GUIOption.FixedWidth(100)));
|
|
@@ -67,8 +92,9 @@ namespace BansheeEditor
|
|
|
GUIButton createBtn = new GUIButton(createIcon, GUIOption.FixedWidth(30));
|
|
GUIButton createBtn = new GUIButton(createIcon, GUIOption.FixedWidth(30));
|
|
|
createBtn.OnClick += OnCreateButtonClicked;
|
|
createBtn.OnClick += OnCreateButtonClicked;
|
|
|
guiTitleLayout.AddElement(createBtn);
|
|
guiTitleLayout.AddElement(createBtn);
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ Action BuildFilledGUI = () =>
|
|
|
{
|
|
{
|
|
|
GUIToggle guiFoldout = new GUIToggle(title, EditorStyles.Foldout);
|
|
GUIToggle guiFoldout = new GUIToggle(title, EditorStyles.Foldout);
|
|
|
guiFoldout.Value = isExpanded;
|
|
guiFoldout.Value = isExpanded;
|
|
@@ -82,12 +108,12 @@ namespace BansheeEditor
|
|
|
GUIButton guiAddBtn = new GUIButton(addIcon, GUIOption.FixedWidth(30));
|
|
GUIButton guiAddBtn = new GUIButton(addIcon, GUIOption.FixedWidth(30));
|
|
|
guiAddBtn.OnClick += OnAddButtonClicked;
|
|
guiAddBtn.OnClick += OnAddButtonClicked;
|
|
|
|
|
|
|
|
- guiTitleLayout = layout.AddLayoutX();
|
|
|
|
|
|
|
+ guiTitleLayout = guiLayout.AddLayoutX();
|
|
|
guiTitleLayout.AddElement(guiFoldout);
|
|
guiTitleLayout.AddElement(guiFoldout);
|
|
|
guiTitleLayout.AddElement(guiAddBtn);
|
|
guiTitleLayout.AddElement(guiAddBtn);
|
|
|
guiTitleLayout.AddElement(guiClearBtn);
|
|
guiTitleLayout.AddElement(guiClearBtn);
|
|
|
|
|
|
|
|
- guiChildLayout = layout.AddLayoutX();
|
|
|
|
|
|
|
+ guiChildLayout = guiLayout.AddLayoutX();
|
|
|
guiChildLayout.AddSpace(IndentAmount);
|
|
guiChildLayout.AddSpace(IndentAmount);
|
|
|
|
|
|
|
|
GUIPanel guiContentPanel = guiChildLayout.AddPanel();
|
|
GUIPanel guiContentPanel = guiChildLayout.AddPanel();
|
|
@@ -109,27 +135,77 @@ namespace BansheeEditor
|
|
|
GUITexture inspectorContentBg = new GUITexture(null, bgPanelStyle);
|
|
GUITexture inspectorContentBg = new GUITexture(null, bgPanelStyle);
|
|
|
backgroundPanel.AddElement(inspectorContentBg);
|
|
backgroundPanel.AddElement(inspectorContentBg);
|
|
|
|
|
|
|
|
- // Hidden dependency: BuildGUI must be called after all elements are
|
|
|
|
|
- // in the dictionary so we do it in two steps
|
|
|
|
|
- for (int i = rows.Count; i < numRows; i++)
|
|
|
|
|
|
|
+ ToggleFoldout(isExpanded);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (forceRebuild)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (state != State.None)
|
|
|
|
|
+ guiTitleLayout.Destroy();
|
|
|
|
|
+
|
|
|
|
|
+ if (state == State.Filled)
|
|
|
|
|
+ guiChildLayout.Destroy();
|
|
|
|
|
+
|
|
|
|
|
+ state = State.None;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (state == State.None)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!IsNull())
|
|
|
{
|
|
{
|
|
|
- GUIDictionaryFieldRow newRow = new T();
|
|
|
|
|
- rows.Add(i, newRow);
|
|
|
|
|
|
|
+ BuildFilledGUI();
|
|
|
|
|
+ state = State.Filled;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ BuildEmptyGUI();
|
|
|
|
|
+
|
|
|
|
|
+ state = State.Empty;
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (state == State.Empty)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!IsNull())
|
|
|
|
|
+ {
|
|
|
|
|
+ guiTitleLayout.Destroy();
|
|
|
|
|
+ BuildFilledGUI();
|
|
|
|
|
+ state = State.Filled;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (state == State.Filled)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (IsNull())
|
|
|
|
|
+ {
|
|
|
|
|
+ guiTitleLayout.Destroy();
|
|
|
|
|
+ guiChildLayout.Destroy();
|
|
|
|
|
+ BuildEmptyGUI();
|
|
|
|
|
|
|
|
- for (int i = numRows; i < rows.Count; i++)
|
|
|
|
|
- rows.Remove(i);
|
|
|
|
|
|
|
+ state = State.Empty;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if(editRow == null)
|
|
|
|
|
- editRow = new T();
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Rebuilds GUI for all existing dictionary rows.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void BuildRows()
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (var KVP in rows)
|
|
|
|
|
+ KVP.Value.Destroy();
|
|
|
|
|
|
|
|
- editRow.BuildGUI(this, guiContentLayout, numRows, depth + 1);
|
|
|
|
|
- editRow.Enabled = false;
|
|
|
|
|
|
|
+ editRow.Destroy();
|
|
|
|
|
|
|
|
- for (int i = 0; i < numRows; i++)
|
|
|
|
|
- rows[i].BuildGUI(this, guiContentLayout, i, depth + 1);
|
|
|
|
|
|
|
+ if (!IsNull())
|
|
|
|
|
+ {
|
|
|
|
|
+ editRow.BuildGUI(this, guiContentLayout, rows.Count, depth + 1);
|
|
|
|
|
+ editRow.Enabled = false;
|
|
|
|
|
|
|
|
- ToggleFoldout(isExpanded);
|
|
|
|
|
|
|
+ foreach (var KVP in rows)
|
|
|
|
|
+ KVP.Value.BuildGUI(this, guiContentLayout, KVP.Key, depth + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ rows.Clear();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -145,11 +221,8 @@ namespace BansheeEditor
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Refreshes contents of all dictionary rows and checks if anything was modified.
|
|
/// Refreshes contents of all dictionary rows and checks if anything was modified.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- /// <returns>True if any entry in the list was modified, false otherwise.</returns>
|
|
|
|
|
- public bool Refresh()
|
|
|
|
|
|
|
+ public void Refresh()
|
|
|
{
|
|
{
|
|
|
- bool anythingModified = false;
|
|
|
|
|
-
|
|
|
|
|
for (int i = 0; i < rows.Count; i++)
|
|
for (int i = 0; i < rows.Count; i++)
|
|
|
{
|
|
{
|
|
|
if (rows[i].Refresh())
|
|
if (rows[i].Refresh())
|
|
@@ -162,7 +235,6 @@ namespace BansheeEditor
|
|
|
editRow.BuildGUI(this, guiContentLayout, rows.Count, depth + 1);
|
|
editRow.BuildGUI(this, guiContentLayout, rows.Count, depth + 1);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return anythingModified;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -170,23 +242,25 @@ namespace BansheeEditor
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
public void Destroy()
|
|
public void Destroy()
|
|
|
{
|
|
{
|
|
|
- if (guiTitleLayout != null)
|
|
|
|
|
|
|
+ if (guiLayout != null)
|
|
|
{
|
|
{
|
|
|
- guiTitleLayout.Destroy();
|
|
|
|
|
- guiTitleLayout = null;
|
|
|
|
|
|
|
+ guiLayout.Destroy();
|
|
|
|
|
+ guiLayout = null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (guiChildLayout != null)
|
|
|
|
|
- {
|
|
|
|
|
- guiChildLayout.Destroy();
|
|
|
|
|
- guiChildLayout = null;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ guiLayout = null;
|
|
|
|
|
+ guiTitleLayout = null;
|
|
|
|
|
+ guiChildLayout = null;
|
|
|
|
|
|
|
|
for (int i = 0; i < rows.Count; i++)
|
|
for (int i = 0; i < rows.Count; i++)
|
|
|
rows[i].Destroy();
|
|
rows[i].Destroy();
|
|
|
|
|
|
|
|
|
|
+ rows.Clear();
|
|
|
|
|
+
|
|
|
if (editRow != null)
|
|
if (editRow != null)
|
|
|
editRow.Destroy();
|
|
editRow.Destroy();
|
|
|
|
|
+
|
|
|
|
|
+ editRow = null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -208,6 +282,18 @@ namespace BansheeEditor
|
|
|
return editRowIdx != -1;
|
|
return editRowIdx != -1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Returns the number of rows in the dictionary.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <returns>Number of rows in the dictionary.</returns>
|
|
|
|
|
+ protected abstract int GetNumRows();
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Checks is the dictionary instance not assigned.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <returns>True if there is not dictionary instance.</returns>
|
|
|
|
|
+ protected abstract bool IsNull();
|
|
|
|
|
+
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Gets a value of an element at the specified index in the list. Also handles temporary edit fields.
|
|
/// Gets a value of an element at the specified index in the list. Also handles temporary edit fields.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -263,6 +349,12 @@ namespace BansheeEditor
|
|
|
return GetKey(rowIdx);
|
|
return GetKey(rowIdx);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Creates a new dictionary row GUI.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <returns>Object containing a dictionary row GUI.</returns>
|
|
|
|
|
+ protected abstract GUIDictionaryFieldRow CreateRow();
|
|
|
|
|
+
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Gets a key for a row at the specified index.
|
|
/// Gets a key for a row at the specified index.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -324,6 +416,16 @@ namespace BansheeEditor
|
|
|
/// <returns>True if the key exists in the dictionary, false otherwise.</returns>
|
|
/// <returns>True if the key exists in the dictionary, false otherwise.</returns>
|
|
|
protected internal abstract bool Contains(object key);
|
|
protected internal abstract bool Contains(object key);
|
|
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Creates a brand new dictionary with zero elements in the place of the current dictionary.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ protected abstract void CreateDictionary();
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Deletes the current dictionary object.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ protected abstract void DeleteDictionary();
|
|
|
|
|
+
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Hides or shows the dictionary rows.
|
|
/// Hides or shows the dictionary rows.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -340,7 +442,12 @@ namespace BansheeEditor
|
|
|
/// Triggered when the user clicks on the create button on the title bar. Creates a brand new dictionary with zero
|
|
/// Triggered when the user clicks on the create button on the title bar. Creates a brand new dictionary with zero
|
|
|
/// elements in the place of the current dictionary.
|
|
/// elements in the place of the current dictionary.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- protected abstract void OnCreateButtonClicked();
|
|
|
|
|
|
|
+ protected void OnCreateButtonClicked()
|
|
|
|
|
+ {
|
|
|
|
|
+ CreateDictionary();
|
|
|
|
|
+ UpdateHeaderGUI(false);
|
|
|
|
|
+ BuildRows();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Triggered when the user clicks on the add button on the title bar. Adds a new empty element to the dictionary.
|
|
/// Triggered when the user clicks on the add button on the title bar. Adds a new empty element to the dictionary.
|
|
@@ -381,7 +488,12 @@ namespace BansheeEditor
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Triggered when the user clicks on the clear button on the title bar. Deletes the current dictionary object.
|
|
/// Triggered when the user clicks on the clear button on the title bar. Deletes the current dictionary object.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- protected abstract void OnClearButtonClicked();
|
|
|
|
|
|
|
+ protected void OnClearButtonClicked()
|
|
|
|
|
+ {
|
|
|
|
|
+ DeleteDictionary();
|
|
|
|
|
+ UpdateHeaderGUI(false);
|
|
|
|
|
+ BuildRows();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Triggered when the user clicks on the delete button next to a dictionary entry. Deletes an element in the
|
|
/// Triggered when the user clicks on the delete button next to a dictionary entry. Deletes an element in the
|
|
@@ -393,7 +505,43 @@ namespace BansheeEditor
|
|
|
if (IsEditInProgress())
|
|
if (IsEditInProgress())
|
|
|
DiscardChanges();
|
|
DiscardChanges();
|
|
|
else
|
|
else
|
|
|
|
|
+ {
|
|
|
|
|
+ // Remove the entry, but ensure the rows keep referencing the original keys (dictionaries have undefined
|
|
|
|
|
+ // order so we need to compare old vs. new elements to determine if any changed).
|
|
|
|
|
+ int oldNumRows = GetNumRows();
|
|
|
|
|
+ Dictionary<object, int> oldKeys = new Dictionary<object, int>();
|
|
|
|
|
+ for (int i = 0; i < oldNumRows; i++)
|
|
|
|
|
+ oldKeys.Add(GetKey(i), i);
|
|
|
|
|
+
|
|
|
RemoveEntry(GetKey(rowIdx));
|
|
RemoveEntry(GetKey(rowIdx));
|
|
|
|
|
+
|
|
|
|
|
+ int newNumRows = GetNumRows();
|
|
|
|
|
+ Dictionary<object, int> newKeys = new Dictionary<object, int>();
|
|
|
|
|
+ for (int i = 0; i < newNumRows; i++)
|
|
|
|
|
+ newKeys.Add(GetKey(i), i);
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var KVP in oldKeys)
|
|
|
|
|
+ {
|
|
|
|
|
+ int newRowIdx;
|
|
|
|
|
+ if (newKeys.TryGetValue(KVP.Key, out newRowIdx))
|
|
|
|
|
+ {
|
|
|
|
|
+ if (KVP.Value != newRowIdx)
|
|
|
|
|
+ {
|
|
|
|
|
+ GUIDictionaryFieldRow temp = rows[KVP.Value];
|
|
|
|
|
+ rows[KVP.Value] = rows[newRowIdx];
|
|
|
|
|
+ rows[newRowIdx] = temp;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = newNumRows; i < oldNumRows; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ rows[i].Destroy();
|
|
|
|
|
+ rows.Remove(i);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ BuildRows();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -554,11 +702,50 @@ namespace BansheeEditor
|
|
|
rows[editRowIdx].EditMode = false;
|
|
rows[editRowIdx].EditMode = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Add/remove the entry, but ensure the rows keep referencing the original keys (dictionaries have undefined
|
|
|
|
|
+ // order so we need to compare old vs. new elements to determine if any changed).
|
|
|
|
|
+ int oldNumRows = GetNumRows();
|
|
|
|
|
+ Dictionary<object, int> oldKeys = new Dictionary<object, int>();
|
|
|
|
|
+ for (int i = 0; i < oldNumRows; i++)
|
|
|
|
|
+ oldKeys.Add(GetKey(i), i);
|
|
|
|
|
+
|
|
|
if (editOriginalKey != null) // Editing
|
|
if (editOriginalKey != null) // Editing
|
|
|
EditEntry(editOriginalKey, editKey, editValue);
|
|
EditEntry(editOriginalKey, editKey, editValue);
|
|
|
else // Adding/Cloning
|
|
else // Adding/Cloning
|
|
|
AddEntry(editKey, editValue);
|
|
AddEntry(editKey, editValue);
|
|
|
|
|
|
|
|
|
|
+ int newNumRows = GetNumRows();
|
|
|
|
|
+ Dictionary<object, int> newKeys = new Dictionary<object, int>();
|
|
|
|
|
+ for (int i = 0; i < newNumRows; i++)
|
|
|
|
|
+ newKeys.Add(GetKey(i), i);
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var KVP in oldKeys)
|
|
|
|
|
+ {
|
|
|
|
|
+ int newRowIdx;
|
|
|
|
|
+ if (newKeys.TryGetValue(KVP.Key, out newRowIdx))
|
|
|
|
|
+ {
|
|
|
|
|
+ if (KVP.Value != newRowIdx)
|
|
|
|
|
+ {
|
|
|
|
|
+ GUIDictionaryFieldRow temp = rows[KVP.Value];
|
|
|
|
|
+ rows[KVP.Value] = rows[newRowIdx];
|
|
|
|
|
+ rows[newRowIdx] = temp;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (int i = newNumRows; i < oldNumRows; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ rows[i].Destroy();
|
|
|
|
|
+ rows.Remove(i);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Hidden dependency: BuildGUI must be called after all elements are
|
|
|
|
|
+ // in the dictionary so we do it in two steps
|
|
|
|
|
+ for (int i = oldNumRows; i < newNumRows; i++)
|
|
|
|
|
+ rows[i] = CreateRow();
|
|
|
|
|
+
|
|
|
|
|
+ BuildRows();
|
|
|
|
|
+
|
|
|
editKey = CreateKey();
|
|
editKey = CreateKey();
|
|
|
editValue = CreateValue();
|
|
editValue = CreateValue();
|
|
|
editOriginalKey = null;
|
|
editOriginalKey = null;
|
|
@@ -576,6 +763,16 @@ namespace BansheeEditor
|
|
|
{
|
|
{
|
|
|
if (IsEditInProgress())
|
|
if (IsEditInProgress())
|
|
|
{
|
|
{
|
|
|
|
|
+ if (IsTemporaryRow(editRowIdx))
|
|
|
|
|
+ {
|
|
|
|
|
+ editRow.EditMode = false;
|
|
|
|
|
+ editRow.Enabled = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ rows[editRowIdx].EditMode = false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
editKey = CreateKey();
|
|
editKey = CreateKey();
|
|
|
editValue = CreateValue();
|
|
editValue = CreateValue();
|
|
|
editOriginalKey = null;
|
|
editOriginalKey = null;
|
|
@@ -585,6 +782,16 @@ namespace BansheeEditor
|
|
|
ToggleFoldout(isExpanded);
|
|
ToggleFoldout(isExpanded);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Possible states dictionary GUI can be in.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private enum State
|
|
|
|
|
+ {
|
|
|
|
|
+ None,
|
|
|
|
|
+ Empty,
|
|
|
|
|
+ Filled
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -593,7 +800,8 @@ namespace BansheeEditor
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
/// <typeparam name="Key">Type of key used by the dictionary.</typeparam>
|
|
/// <typeparam name="Key">Type of key used by the dictionary.</typeparam>
|
|
|
/// <typeparam name="Value">Type of value stored in the dictionary.</typeparam>
|
|
/// <typeparam name="Value">Type of value stored in the dictionary.</typeparam>
|
|
|
- public class GUIDictionaryField<Key, Value> : GUIDictionaryFieldBase
|
|
|
|
|
|
|
+ /// <typeparam name="RowType">Type of rows that are used to handle GUI for individual dictionary elements.</typeparam>
|
|
|
|
|
+ public class GUIDictionaryField<Key, Value, RowType> : GUIDictionaryFieldBase where RowType : GUIDictionaryFieldRow, new()
|
|
|
{
|
|
{
|
|
|
public delegate int SortDictionaryDelegate(Key a, Key b);
|
|
public delegate int SortDictionaryDelegate(Key a, Key b);
|
|
|
|
|
|
|
@@ -604,10 +812,15 @@ namespace BansheeEditor
|
|
|
public Action<Dictionary<Key, Value>> OnChanged;
|
|
public Action<Dictionary<Key, Value>> OnChanged;
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
- /// Triggered when an element in the list has been changed.
|
|
|
|
|
|
|
+ /// Triggered when an element in the list has been added or changed.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
public Action<Key> OnValueChanged;
|
|
public Action<Key> OnValueChanged;
|
|
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Triggered when an element in the dictionary has been removed.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public Action<Key> OnValueRemoved;
|
|
|
|
|
+
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Optional method that will be used for sorting the elements in the dictionary.
|
|
/// Optional method that will be used for sorting the elements in the dictionary.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -622,10 +835,20 @@ namespace BansheeEditor
|
|
|
private List<Key> orderedKeys = new List<Key>();
|
|
private List<Key> orderedKeys = new List<Key>();
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
- /// Constructs a new empty dictionary GUI.
|
|
|
|
|
|
|
+ /// Constructs a new GUI dictionary.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- public GUIDictionaryField()
|
|
|
|
|
- { }
|
|
|
|
|
|
|
+ /// <param name="title">Label to display on the dictionary GUI title.</param>
|
|
|
|
|
+ /// <param name="dictionary">Object containing the data. Can be null.</param>
|
|
|
|
|
+ /// <param name="layout">Layout to which to append the list GUI elements to.</param>
|
|
|
|
|
+ /// <param name="depth">Determines at which depth to render the background. Useful when you have multiple
|
|
|
|
|
+ /// nested containers whose backgrounds are overlaping. Also determines background style,
|
|
|
|
|
+ /// depths divisible by two will use an alternate style.</param>
|
|
|
|
|
+ protected GUIDictionaryField(LocString title, Dictionary<Key, Value> dictionary, GUILayout layout, int depth = 0)
|
|
|
|
|
+ : base(title, layout, depth)
|
|
|
|
|
+ {
|
|
|
|
|
+ this.dictionary = dictionary;
|
|
|
|
|
+ UpdateKeys();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Builds the dictionary GUI elements. Must be called at least once in order for the contents to be populated.
|
|
/// Builds the dictionary GUI elements. Must be called at least once in order for the contents to be populated.
|
|
@@ -637,17 +860,15 @@ namespace BansheeEditor
|
|
|
/// <param name="depth">Determines at which depth to render the background. Useful when you have multiple
|
|
/// <param name="depth">Determines at which depth to render the background. Useful when you have multiple
|
|
|
/// nested containers whose backgrounds are overlaping. Also determines background style,
|
|
/// nested containers whose backgrounds are overlaping. Also determines background style,
|
|
|
/// depths divisible by two will use an alternate style.</param>
|
|
/// depths divisible by two will use an alternate style.</param>
|
|
|
- public void BuildGUI<RowType>(LocString title, Dictionary<Key, Value> dictionary,
|
|
|
|
|
|
|
+ public static GUIDictionaryField<Key, Value, RowType> Create(LocString title, Dictionary<Key, Value> dictionary,
|
|
|
GUILayout layout, int depth = 0)
|
|
GUILayout layout, int depth = 0)
|
|
|
- where RowType : GUIDictionaryFieldRow, new()
|
|
|
|
|
|
|
+
|
|
|
{
|
|
{
|
|
|
- this.dictionary = dictionary;
|
|
|
|
|
- UpdateKeys();
|
|
|
|
|
|
|
+ GUIDictionaryField<Key, Value, RowType> guiDictionary = new GUIDictionaryField<Key, Value, RowType>(
|
|
|
|
|
+ title, dictionary, layout, depth);
|
|
|
|
|
|
|
|
- if (dictionary != null)
|
|
|
|
|
- base.BuildGUI<RowType>(title, false, dictionary.Count, layout, depth);
|
|
|
|
|
- else
|
|
|
|
|
- base.BuildGUI<RowType>(title, true, 0, layout, depth);
|
|
|
|
|
|
|
+ guiDictionary.BuildGUI();
|
|
|
|
|
+ return guiDictionary;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -670,6 +891,27 @@ namespace BansheeEditor
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
|
|
+ protected override GUIDictionaryFieldRow CreateRow()
|
|
|
|
|
+ {
|
|
|
|
|
+ return new RowType();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
|
|
+ protected override int GetNumRows()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (dictionary != null)
|
|
|
|
|
+ return dictionary.Count;
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <inheritdoc/>
|
|
|
|
|
+ protected override bool IsNull()
|
|
|
|
|
+ {
|
|
|
|
|
+ return dictionary == null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/// <inheritdoc/>
|
|
/// <inheritdoc/>
|
|
|
protected internal override object GetKey(int rowIdx)
|
|
protected internal override object GetKey(int rowIdx)
|
|
|
{
|
|
{
|
|
@@ -703,8 +945,11 @@ namespace BansheeEditor
|
|
|
dictionary.Remove((Key)oldKey);
|
|
dictionary.Remove((Key)oldKey);
|
|
|
dictionary[(Key)newKey] = (Value)value;
|
|
dictionary[(Key)newKey] = (Value)value;
|
|
|
|
|
|
|
|
- if (OnChanged != null)
|
|
|
|
|
- OnChanged(dictionary);
|
|
|
|
|
|
|
+ if (OnValueRemoved != null)
|
|
|
|
|
+ OnValueRemoved((Key)oldKey);
|
|
|
|
|
+
|
|
|
|
|
+ if (OnValueChanged != null)
|
|
|
|
|
+ OnValueChanged((Key)newKey);
|
|
|
|
|
|
|
|
UpdateKeys();
|
|
UpdateKeys();
|
|
|
}
|
|
}
|
|
@@ -714,8 +959,8 @@ namespace BansheeEditor
|
|
|
{
|
|
{
|
|
|
dictionary[(Key)key] = (Value)value;
|
|
dictionary[(Key)key] = (Value)value;
|
|
|
|
|
|
|
|
- if (OnChanged != null)
|
|
|
|
|
- OnChanged(dictionary);
|
|
|
|
|
|
|
+ if (OnValueChanged != null)
|
|
|
|
|
+ OnValueChanged((Key)key);
|
|
|
|
|
|
|
|
UpdateKeys();
|
|
UpdateKeys();
|
|
|
}
|
|
}
|
|
@@ -725,8 +970,8 @@ namespace BansheeEditor
|
|
|
{
|
|
{
|
|
|
dictionary.Remove((Key) key);
|
|
dictionary.Remove((Key) key);
|
|
|
|
|
|
|
|
- if (OnChanged != null)
|
|
|
|
|
- OnChanged(dictionary);
|
|
|
|
|
|
|
+ if (OnValueRemoved != null)
|
|
|
|
|
+ OnValueRemoved((Key)key);
|
|
|
|
|
|
|
|
UpdateKeys();
|
|
UpdateKeys();
|
|
|
}
|
|
}
|
|
@@ -744,7 +989,7 @@ namespace BansheeEditor
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
/// <inheritdoc/>
|
|
|
- protected override void OnCreateButtonClicked()
|
|
|
|
|
|
|
+ protected override void CreateDictionary()
|
|
|
{
|
|
{
|
|
|
dictionary = new Dictionary<Key, Value>();
|
|
dictionary = new Dictionary<Key, Value>();
|
|
|
|
|
|
|
@@ -755,7 +1000,7 @@ namespace BansheeEditor
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
/// <inheritdoc/>
|
|
|
- protected override void OnClearButtonClicked()
|
|
|
|
|
|
|
+ protected override void DeleteDictionary()
|
|
|
{
|
|
{
|
|
|
dictionary = null;
|
|
dictionary = null;
|
|
|
|
|
|
|
@@ -780,6 +1025,7 @@ namespace BansheeEditor
|
|
|
private GUIButton editBtn;
|
|
private GUIButton editBtn;
|
|
|
private bool localTitleLayout;
|
|
private bool localTitleLayout;
|
|
|
private bool enabled = true;
|
|
private bool enabled = true;
|
|
|
|
|
+ private bool editMode = false;
|
|
|
private GUIDictionaryFieldBase parent;
|
|
private GUIDictionaryFieldBase parent;
|
|
|
|
|
|
|
|
protected int rowIdx;
|
|
protected int rowIdx;
|
|
@@ -817,6 +1063,8 @@ namespace BansheeEditor
|
|
|
deleteBtn.SetContent(deleteIcon);
|
|
deleteBtn.SetContent(deleteIcon);
|
|
|
editBtn.SetContent(editIcon);
|
|
editBtn.SetContent(editIcon);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ editMode = value;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -837,21 +1085,17 @@ namespace BansheeEditor
|
|
|
/// <param name="depth">Determines the depth at which the element is rendered.</param>
|
|
/// <param name="depth">Determines the depth at which the element is rendered.</param>
|
|
|
internal void BuildGUI(GUIDictionaryFieldBase parent, GUILayout parentLayout, int rowIdx, int depth)
|
|
internal void BuildGUI(GUIDictionaryFieldBase parent, GUILayout parentLayout, int rowIdx, int depth)
|
|
|
{
|
|
{
|
|
|
|
|
+ if (rowLayout != null)
|
|
|
|
|
+ rowLayout.Destroy();
|
|
|
|
|
+
|
|
|
this.parent = parent;
|
|
this.parent = parent;
|
|
|
this.rowIdx = rowIdx;
|
|
this.rowIdx = rowIdx;
|
|
|
this.depth = depth;
|
|
this.depth = depth;
|
|
|
|
|
|
|
|
- if (rowLayout == null)
|
|
|
|
|
- rowLayout = parentLayout.AddLayoutY();
|
|
|
|
|
-
|
|
|
|
|
- if (keyRowLayout == null)
|
|
|
|
|
- keyRowLayout = rowLayout.AddLayoutX();
|
|
|
|
|
-
|
|
|
|
|
- if (keyLayout == null)
|
|
|
|
|
- keyLayout = keyRowLayout.AddLayoutY();
|
|
|
|
|
-
|
|
|
|
|
- if (valueLayout == null)
|
|
|
|
|
- valueLayout = rowLayout.AddLayoutY();
|
|
|
|
|
|
|
+ rowLayout = parentLayout.AddLayoutY();
|
|
|
|
|
+ keyRowLayout = rowLayout.AddLayoutX();
|
|
|
|
|
+ keyLayout = keyRowLayout.AddLayoutY();
|
|
|
|
|
+ valueLayout = rowLayout.AddLayoutY();
|
|
|
|
|
|
|
|
GUILayoutX externalTitleLayout = CreateKeyGUI(keyLayout);
|
|
GUILayoutX externalTitleLayout = CreateKeyGUI(keyLayout);
|
|
|
CreateValueGUI(valueLayout);
|
|
CreateValueGUI(valueLayout);
|
|
@@ -889,6 +1133,10 @@ namespace BansheeEditor
|
|
|
titleLayout.AddElement(deleteBtn);
|
|
titleLayout.AddElement(deleteBtn);
|
|
|
titleLayout.AddSpace(10);
|
|
titleLayout.AddSpace(10);
|
|
|
titleLayout.AddElement(editBtn);
|
|
titleLayout.AddElement(editBtn);
|
|
|
|
|
+
|
|
|
|
|
+ EditMode = editMode;
|
|
|
|
|
+
|
|
|
|
|
+ Refresh();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|