Merge branch 'better-tag-picker' of https://github.com/Tryibion/FlaxEngine into Tryibion-better-tag-picker
This commit is contained in:
@@ -156,88 +156,126 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
pickerData.IsEditing = false;
|
||||
}
|
||||
|
||||
|
||||
private static void OnAddButtonClicked(Tree tree, TreeNode parentNode, PickerData pickerData)
|
||||
private static void OnAddTagButtonClicked(string input, Tree tree, TextBox textBox, DropPanel dropPanel, PickerData pickerData)
|
||||
{
|
||||
// Create new node
|
||||
var nodeIndent = 16.0f;
|
||||
var indentation = 0;
|
||||
var parentTag = string.Empty;
|
||||
if (parentNode.CustomArrowRect.HasValue)
|
||||
{
|
||||
indentation = (int)((parentNode.CustomArrowRect.Value.Location.X - 18) / nodeIndent) + 1;
|
||||
var parentTagValue = (Tag)parentNode.Tag;
|
||||
parentTag = parentTagValue.ToString();
|
||||
}
|
||||
var node = new TreeNodeWithAddons
|
||||
{
|
||||
ChildrenIndent = nodeIndent,
|
||||
CullChildren = false,
|
||||
ClipChildren = false,
|
||||
TextMargin = new Margin(22.0f, 2.0f, 2.0f, 2.0f),
|
||||
CustomArrowRect = new Rectangle(18 + indentation * nodeIndent, 2, 12, 12),
|
||||
BackgroundColorSelected = Color.Transparent,
|
||||
BackgroundColorHighlighted = parentNode.BackgroundColorHighlighted,
|
||||
};
|
||||
var checkbox = new CheckBox(32.0f + indentation * nodeIndent, 0)
|
||||
{
|
||||
Height = 16.0f,
|
||||
IsScrollable = false,
|
||||
Parent = node,
|
||||
};
|
||||
node.Addons.Add(checkbox);
|
||||
checkbox.StateChanged += c => OnCheckboxEdited(node, c, pickerData);
|
||||
var addButton = new Button(tree.Width - 16, 0, 14, 14)
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Add subtag within this tag namespace",
|
||||
IsScrollable = false,
|
||||
BorderColor = Color.Transparent,
|
||||
BackgroundColor = Color.Transparent,
|
||||
Parent = node,
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
};
|
||||
node.Addons.Add(addButton);
|
||||
addButton.ButtonClicked += button => OnAddButtonClicked(tree, node, pickerData);
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return;
|
||||
|
||||
// Ensure that name is unique
|
||||
if (Tags.List.Contains(input))
|
||||
return;
|
||||
|
||||
// Link
|
||||
node.Parent = parentNode;
|
||||
node.IndexInParent = 0;
|
||||
parentNode.Expand(true);
|
||||
((Panel)tree.Parent.Parent).ScrollViewTo(node);
|
||||
var subInputs = input.Split('.');
|
||||
string tagString = string.Empty;
|
||||
string lastTagString = string.Empty;
|
||||
|
||||
// Start renaming the tag
|
||||
var prefix = parentTag.Length != 0 ? parentTag + '.' : string.Empty;
|
||||
var renameArea = node.HeaderRect;
|
||||
if (renameArea.Location.X < nodeIndent)
|
||||
TreeNode parentNode = tree.GetChild<TreeNode>(); // Start at root
|
||||
int parentCount = 0;
|
||||
for (int i = 0; i < subInputs.Length; i++)
|
||||
{
|
||||
// Fix root node's child renaming
|
||||
renameArea.Location.X += nodeIndent;
|
||||
renameArea.Size.X -= nodeIndent;
|
||||
}
|
||||
var dialog = RenamePopup.Show(node, renameArea, prefix, false);
|
||||
var cursor = dialog.InputField.TextLength;
|
||||
dialog.InputField.SelectionRange = new TextRange(cursor, cursor);
|
||||
dialog.Validate = (popup, value) =>
|
||||
{
|
||||
// Ensure that name is unique
|
||||
if (Tags.List.Contains(value))
|
||||
return false;
|
||||
|
||||
// Ensure user entered direct subtag of the parent node
|
||||
if (value.StartsWith(popup.InitialValue))
|
||||
if (string.IsNullOrEmpty(subInputs[i]))
|
||||
{
|
||||
var name = value.Substring(popup.InitialValue.Length);
|
||||
if (name.Length > 0 && name.IndexOf('.') == -1)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
dialog.Renamed += popup =>
|
||||
{
|
||||
|
||||
// Check all entered subtags and create any that dont exist
|
||||
for (int j = 0; j <= i; j++)
|
||||
{
|
||||
tagString += j == 0 ? subInputs[j] : "." + subInputs[j];
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(tagString))
|
||||
{
|
||||
tagString = string.Empty;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Tags.List.Contains(tagString))
|
||||
{
|
||||
// Find next parent node
|
||||
foreach (var child in parentNode.Children)
|
||||
{
|
||||
if (!(child is TreeNode childNode))
|
||||
continue;
|
||||
|
||||
var tagValue = (Tag)childNode.Tag;
|
||||
if (!string.Equals(tagValue.ToString(), tagString))
|
||||
continue;
|
||||
|
||||
parentNode = childNode;
|
||||
parentCount += 1;
|
||||
}
|
||||
lastTagString = tagString;
|
||||
tagString = string.Empty;
|
||||
continue;
|
||||
}
|
||||
else if (subInputs.Length > 1)
|
||||
{
|
||||
// Find next parent node
|
||||
foreach (var child in parentNode.Children)
|
||||
{
|
||||
if (!(child is TreeNode childNode))
|
||||
continue;
|
||||
|
||||
var tagValue = (Tag)childNode.Tag;
|
||||
if (!string.Equals(tagValue.ToString(), lastTagString))
|
||||
continue;
|
||||
|
||||
parentNode = childNode;
|
||||
parentCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new node
|
||||
var nodeIndent = 16.0f;
|
||||
var indentation = 0;
|
||||
var parentTag = string.Empty;
|
||||
if (parentNode.CustomArrowRect.HasValue)
|
||||
{
|
||||
indentation = (int)((parentNode.CustomArrowRect.Value.Location.X - 18) / nodeIndent) + 1;
|
||||
var parentTagValue = (Tag)parentNode.Tag;
|
||||
parentTag = parentTagValue.ToString();
|
||||
}
|
||||
var node = new TreeNodeWithAddons
|
||||
{
|
||||
ChildrenIndent = nodeIndent,
|
||||
CullChildren = false,
|
||||
ClipChildren = false,
|
||||
TextMargin = new Margin(22.0f, 2.0f, 2.0f, 2.0f),
|
||||
CustomArrowRect = new Rectangle(18 + indentation * nodeIndent, 2, 12, 12),
|
||||
BackgroundColorSelected = Color.Transparent,
|
||||
BackgroundColorHighlighted = parentNode.BackgroundColorHighlighted,
|
||||
};
|
||||
var checkbox = new CheckBox(32.0f + indentation * nodeIndent, 0)
|
||||
{
|
||||
Height = 16.0f,
|
||||
IsScrollable = false,
|
||||
Parent = node,
|
||||
};
|
||||
node.Addons.Add(checkbox);
|
||||
checkbox.StateChanged += c => OnCheckboxEdited(node, c, pickerData);
|
||||
var addButton = new Button(tree.Width - 16, 0, 14, 14)
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Add subtag within this tag namespace",
|
||||
IsScrollable = false,
|
||||
BorderColor = Color.Transparent,
|
||||
BackgroundColor = Color.Transparent,
|
||||
Parent = node,
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
};
|
||||
node.Addons.Add(addButton);
|
||||
addButton.ButtonClicked += button => OnAddSubTagButtonClicked(((Tag)node.Tag).ToString(), textBox, dropPanel);
|
||||
|
||||
// Link
|
||||
node.Parent = parentNode;
|
||||
node.IndexInParent = 0;
|
||||
parentNode.Expand(true);
|
||||
((Panel)tree.Parent.Parent).ScrollViewTo(node);
|
||||
|
||||
// Get tag name
|
||||
var tagName = popup.Text;
|
||||
var tagShortName = tagName.Substring(popup.InitialValue.Length);
|
||||
var tagName = tagString;
|
||||
var tagShortName = tagName.Substring(parentCount == 0 ? lastTagString.Length : lastTagString.Length + 1);
|
||||
if (tagShortName.Length == 0)
|
||||
return;
|
||||
|
||||
@@ -256,24 +294,134 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
// Update asset
|
||||
var settingsObj = (LayersAndTagsSettings)settingsAsset.Instance;
|
||||
settingsObj.Tags.Add(tagName);
|
||||
settingsObj.Tags.Sort();
|
||||
settingsAsset.SetInstance(settingsObj);
|
||||
|
||||
// Reload editor window to reflect new tag
|
||||
assetWindow?.RefreshAsset();
|
||||
}
|
||||
};
|
||||
dialog.Closed += popup =>
|
||||
|
||||
lastTagString = tagString;
|
||||
tagString = string.Empty;
|
||||
}
|
||||
textBox.Text = string.Empty;
|
||||
}
|
||||
|
||||
private static void OnAddSubTagButtonClicked(string parentTag, TextBox textBox, DropPanel dropPanel)
|
||||
{
|
||||
if (textBox == null || dropPanel == null || string.IsNullOrEmpty(parentTag))
|
||||
{
|
||||
// Remove temporary node if renaming was canceled
|
||||
if (popup.InitialValue == popup.Text || popup.Text.Length == 0)
|
||||
node.Dispose();
|
||||
};
|
||||
return;
|
||||
}
|
||||
dropPanel.Open();
|
||||
textBox.Text = parentTag + ".";
|
||||
textBox.Focus();
|
||||
textBox.SelectionRange = new TextRange(textBox.Text.Length, textBox.Text.Length);
|
||||
}
|
||||
|
||||
internal static ContextMenuBase CreatePicker(Tag value, Tag[] values, PickerData pickerData)
|
||||
{
|
||||
// Initialize search popup
|
||||
var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree, 20.0f);
|
||||
var menu = Utilities.Utils.CreateSearchPopup(out var searchBox, out var tree, 40.0f);
|
||||
|
||||
// Add tag drop panel
|
||||
var addTagDropPanel = new DropPanel
|
||||
{
|
||||
HeaderText = "Add Tag",
|
||||
EnableDropDownIcon = true,
|
||||
ArrowImageOpened = new SpriteBrush(FlaxEngine.GUI.Style.Current.ArrowDown),
|
||||
ArrowImageClosed = new SpriteBrush(FlaxEngine.GUI.Style.Current.ArrowRight),
|
||||
Parent = menu,
|
||||
HeaderTextMargin = new Margin(2.0f),
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
Bounds = new Rectangle(2, 2, menu.Width - 4, 30),
|
||||
IsClosed = true,
|
||||
ItemsMargin = new Margin(2.0f),
|
||||
CloseAnimationTime = 0,
|
||||
};
|
||||
var tagNamePanel = new HorizontalPanel
|
||||
{
|
||||
Parent = addTagDropPanel,
|
||||
AutoSize = false,
|
||||
Bounds = new Rectangle(0, 0, addTagDropPanel.Width, 20.0f),
|
||||
};
|
||||
var nameLabel = new Label
|
||||
{
|
||||
Size = new Float2(addTagDropPanel.Width / 4, 0),
|
||||
Parent = tagNamePanel,
|
||||
Text = "Tag Name:",
|
||||
};
|
||||
var nameTextBox = new TextBox
|
||||
{
|
||||
IsMultiline = false,
|
||||
WatermarkText = "X.Y.Z",
|
||||
Size = new Float2(addTagDropPanel.Width * 0.7f, 0),
|
||||
Parent = tagNamePanel,
|
||||
EndEditOnClick = false,
|
||||
};
|
||||
|
||||
bool uniqueText = true;
|
||||
nameTextBox.TextChanged += () =>
|
||||
{
|
||||
// Ensure that name is unique
|
||||
if (Tags.List.Contains(nameTextBox.Text))
|
||||
uniqueText = false;
|
||||
else
|
||||
uniqueText = true;
|
||||
|
||||
var currentStyle = FlaxEngine.GUI.Style.Current;
|
||||
if (uniqueText)
|
||||
{
|
||||
nameTextBox.BorderColor = Color.Transparent;
|
||||
nameTextBox.BorderSelectedColor = currentStyle.BackgroundSelected;
|
||||
}
|
||||
else
|
||||
{
|
||||
var color = new Color(1.0f, 0.0f, 0.02745f, 1.0f);
|
||||
nameTextBox.BorderColor = Color.Lerp(color, currentStyle.TextBoxBackground, 0.6f);
|
||||
nameTextBox.BorderSelectedColor = color;
|
||||
}
|
||||
};
|
||||
|
||||
nameTextBox.EditEnd += () =>
|
||||
{
|
||||
if (!uniqueText)
|
||||
return;
|
||||
|
||||
if (addTagDropPanel.IsClosed)
|
||||
{
|
||||
Debug.Log("Hit");
|
||||
nameTextBox.BorderColor = Color.Transparent;
|
||||
nameTextBox.BorderSelectedColor = FlaxEngine.GUI.Style.Current.BackgroundSelected;
|
||||
return;
|
||||
}
|
||||
|
||||
OnAddTagButtonClicked(nameTextBox.Text, tree, nameTextBox, addTagDropPanel, pickerData);
|
||||
};
|
||||
|
||||
var addButtonPanel = new HorizontalPanel
|
||||
{
|
||||
Parent = addTagDropPanel,
|
||||
AutoSize = false,
|
||||
Bounds = new Rectangle(0, 0, addTagDropPanel.Width, 30.0f),
|
||||
};
|
||||
var buttonAddTag = new Button
|
||||
{
|
||||
Parent = addButtonPanel,
|
||||
Size = new Float2(100, 20),
|
||||
Text = "Add Tag",
|
||||
AnchorPreset = AnchorPresets.MiddleCenter,
|
||||
};
|
||||
buttonAddTag.Clicked += () =>
|
||||
{
|
||||
if (!uniqueText)
|
||||
return;
|
||||
|
||||
OnAddTagButtonClicked(nameTextBox.Text, tree, nameTextBox, addTagDropPanel, pickerData);
|
||||
};
|
||||
|
||||
// Used for how far everything should drop when the drop down panel is opened
|
||||
var dropPanelOpenHeight = tagNamePanel.Height + addButtonPanel.Height + 4;
|
||||
|
||||
// Create tree with tags hierarchy
|
||||
tree.Margin = new Margin(-16.0f, 0.0f, -16.0f, -0.0f); // Hide root node
|
||||
@@ -339,7 +487,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
};
|
||||
node.Addons.Add(addButton);
|
||||
addButton.ButtonClicked += button => OnAddButtonClicked(tree, node, pickerData);
|
||||
addButton.ButtonClicked += button => OnAddSubTagButtonClicked(((Tag)node.Tag).ToString(), nameTextBox, addTagDropPanel);
|
||||
|
||||
// Link to parent
|
||||
{
|
||||
@@ -360,10 +508,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
Margin = new Margin(1.0f),
|
||||
AutoSize = false,
|
||||
Bounds = new Rectangle(0, 0, menu.Width, 20.0f),
|
||||
Bounds = new Rectangle(0, 0, menu.Width - 4, 20.0f),
|
||||
Parent = menu,
|
||||
};
|
||||
var buttonsSize = new Float2((menu.Width - buttonsPanel.Margin.Width) / 4.0f - buttonsPanel.Spacing, 18.0f);
|
||||
buttonsPanel.Y += addTagDropPanel.HeaderHeight + 4;
|
||||
buttonsPanel.X += 2;
|
||||
|
||||
var buttonsSize = new Float2((menu.Width - 2 - buttonsPanel.Margin.Width) / 3.0f - buttonsPanel.Spacing, 18.0f);
|
||||
var buttonExpandAll = new Button
|
||||
{
|
||||
Size = buttonsSize,
|
||||
@@ -382,20 +533,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
root.CollapseAll(true);
|
||||
root.Expand(true);
|
||||
};
|
||||
var buttonAddTag = new Button
|
||||
var buttonClearAll = new Button
|
||||
{
|
||||
Size = buttonsSize,
|
||||
Parent = buttonsPanel,
|
||||
Text = "Add Tag",
|
||||
Text = "Clear all",
|
||||
};
|
||||
buttonAddTag.Clicked += () => OnAddButtonClicked(tree, root, pickerData);
|
||||
var buttonReset = new Button
|
||||
{
|
||||
Size = buttonsSize,
|
||||
Parent = buttonsPanel,
|
||||
Text = "Reset",
|
||||
};
|
||||
buttonReset.Clicked += () =>
|
||||
buttonClearAll.Clicked += () =>
|
||||
{
|
||||
pickerData.IsEditing = true;
|
||||
UncheckAll(root);
|
||||
@@ -404,6 +548,48 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
pickerData.SetValues?.Invoke(null);
|
||||
};
|
||||
|
||||
// Move menu children to location when drop panel is opened and closed
|
||||
addTagDropPanel.IsClosedChanged += panel =>
|
||||
{
|
||||
if (panel.IsClosed)
|
||||
{
|
||||
// Resize/ Move UI to fit space with drop down
|
||||
foreach (var child in menu.Children)
|
||||
{
|
||||
if (child == panel)
|
||||
continue;
|
||||
|
||||
// Expand panels so scrollbars work
|
||||
if (child is Panel)
|
||||
{
|
||||
child.Bounds = new Rectangle(child.Bounds.X, child.Bounds.Y - dropPanelOpenHeight, child.Width, child.Height + dropPanelOpenHeight);
|
||||
continue;
|
||||
}
|
||||
child.Y -= dropPanelOpenHeight;
|
||||
nameTextBox.Text = string.Empty;
|
||||
nameTextBox.Defocus();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resize/ Move UI to fit space with drop down
|
||||
foreach (var child in menu.Children)
|
||||
{
|
||||
if (child == panel)
|
||||
continue;
|
||||
|
||||
// Shrink panels so scrollbars work
|
||||
if (child is Panel)
|
||||
{
|
||||
child.Bounds = new Rectangle(child.Bounds.X, child.Bounds.Y + dropPanelOpenHeight, child.Width, child.Height - dropPanelOpenHeight);
|
||||
continue;
|
||||
}
|
||||
child.Y += dropPanelOpenHeight;
|
||||
nameTextBox.Text = string.Empty;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Setup search filter
|
||||
searchBox.TextChanged += delegate
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user