Skip to content

Commit

Permalink
[BetterSceneLoader] Dropdown filter
Browse files Browse the repository at this point in the history
  • Loading branch information
Keelhauled committed Jul 17, 2024
1 parent 5b720ac commit f4adcff
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<Compile Include="$(MSBuildThisFileDirectory)ImageGrid.cs" />
<Compile Include="$(MSBuildThisFileDirectory)BetterSceneLoader.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DropdownAutoScroll.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DropdownFilter.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Resources\loadicon" />
Expand Down
227 changes: 227 additions & 0 deletions src/BetterSceneLoader.Core/DropdownFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UILib;
using UnityEngine;
using UnityEngine.UI;

namespace BetterSceneLoader
{
internal class DropdownFilter : MonoBehaviour
{
[SerializeField] private RectTransform _filterRect;
[SerializeField] private InputField _filterField;
[SerializeField] private RectTransform _content;
[SerializeField] private RectTransform _clearButtonRect;
[SerializeField] private string _persistentKeyword;

private static readonly Dictionary<string, string> _persistentFilterMap = new Dictionary<string, string>();
private static readonly float _clearButtonAnchorX = 0.16f;

private float _itemStride = 20f;

/// <summary>
/// Add filter UI to dropdown
/// </summary>
/// <param name="target">Target dropdown</param>
/// <param name="persistentKeyword">Name for persistent filter. A persisted filter will continue to hold the filter after it is closed.</param>
public static void AddFilterUI(Dropdown target, string persistentKeyword = null)
{
var menuTemplate = target.transform.Find("Template");

if(menuTemplate == null)
return;

var filter = menuTemplate.GetComponent<DropdownFilter>();

if(filter != null)
return;

filter = menuTemplate.gameObject.AddComponent<DropdownFilter>();
filter._persistentKeyword = persistentKeyword;

var filterField = UIUtility.CreateInputField("Filter", menuTemplate.transform, "Filter");
filter._filterField = filterField;

float uiMargin = 2f;
float filterUIHeight = GetItemSize(target) + uiMargin;

var templateRect = (RectTransform)menuTemplate.transform;
templateRect.anchoredPosition = new Vector2(0, -filterUIHeight + uiMargin + uiMargin);

var filterRect = (RectTransform)filterField.transform;
filter._filterRect = filterRect;

filterRect.anchorMin = new Vector2(_clearButtonAnchorX, 0f);
filterRect.anchorMax = new Vector2(1f, 0f);
filterRect.offsetMin = new Vector2(0, -filterUIHeight);
filterRect.offsetMax = new Vector2(0, 0);
filterRect.sizeDelta = new Vector2(0, filterUIHeight);

var clearButton = UIUtility.CreateButton("ClearFilter", menuTemplate.transform, "Clear");
var clearButtonRect = (RectTransform)clearButton.transform;
filter._clearButtonRect = clearButtonRect;
clearButtonRect.anchorMin = new Vector2(0f, 0);
clearButtonRect.anchorMax = new Vector2(_clearButtonAnchorX, 0);
clearButtonRect.offsetMin = new Vector2(0, -filterUIHeight);
clearButtonRect.offsetMax = new Vector2(0, 0);
clearButtonRect.sizeDelta = new Vector2(0, filterUIHeight);

var content = (RectTransform)target.transform.Find("Template/Viewport/Content");
filter._content = content;

//Add layout groups/element so that items are automatically aligned
var contentLayoutGroup = content.gameObject.AddComponent<VerticalLayoutGroup>();

contentLayoutGroup.childForceExpandWidth = true;
contentLayoutGroup.childForceExpandHeight = false;
contentLayoutGroup.childControlWidth = true;
contentLayoutGroup.childControlHeight = false;

content.Find("Item").gameObject.AddComponent<LayoutElement>();
}

private void Start()
{
if(_filterRect == null)
return;

SetUIPosition();

_filterField.onValueChanged.AddListener(OnChangeFilter);
_filterField.onEndEdit.AddListener(OnEndEditFilter);

if(_content.childCount >= 3)
{
var item1 = (RectTransform)_content.GetChild(1);
var item2 = (RectTransform)_content.GetChild(2);
_itemStride = Mathf.Abs(item1.offsetMin.y - item2.offsetMin.y);
}
else
{
_itemStride = 20f;
}

_clearButtonRect.GetComponent<Button>().onClick.AddListener(ClearFilter);
RestoreFilter();
}

private void SaveFilter()
{
if(string.IsNullOrEmpty(_persistentKeyword))
return;

_persistentFilterMap[_persistentKeyword] = _filterField.text;
}

private void RestoreFilter()
{
if(string.IsNullOrEmpty(_persistentKeyword) || !_persistentFilterMap.ContainsKey(_persistentKeyword))
return;

var filter = _persistentFilterMap[_persistentKeyword];
_filterField.text = filter;
}

private void ClearFilter()
{
_filterField.text = "";
SaveFilter();
}

private void OnEndEditFilter(string filter)
{
SaveFilter();
}

private void OnChangeFilter(string filter)
{
if(string.IsNullOrEmpty(filter))
filter = "*";

var regex = new Regex(Regex.Escape(filter).Replace("\\*", ".*").Replace("\\?", "."), RegexOptions.IgnoreCase);
int activeItems = 0;

foreach(Transform item in _content)
{
var name = item.name;

if(name.EndsWith(": Reset"))
{
++activeItems;
continue;
}

int colon = name.IndexOf(":");
if(colon < 0)
continue;

bool isShown = regex.IsMatch(name.Substring(colon + 1).Trim());
item.gameObject.SetActive(isShown);
if(isShown)
++activeItems;
}

var sizeDelta = _content.sizeDelta;
sizeDelta.y = _itemStride * activeItems + 8f;
_content.sizeDelta = sizeDelta;

var scrollbar = GetComponentInChildren<Scrollbar>();
if(scrollbar != null)
{
scrollbar.value = 1f;
scrollbar.Rebuild(CanvasUpdate.Prelayout);
}
}

/// <summary>
/// Set the position of the filter UI.
/// The drop-down UI is displayed either up or down depending on position.
/// Change the position of the filter UI accordingly.
/// </summary>
private void SetUIPosition()
{
float height = _filterRect.sizeDelta.y;
float offset = 2f;
var dropdownRect = (RectTransform)transform;

if(dropdownRect.offsetMax.y > 0)
{
_filterRect.anchorMin = new Vector2(_clearButtonAnchorX, 0f);
_filterRect.anchorMax = new Vector2(1f, 0f);
_filterRect.offsetMin = new Vector2(0, -height + offset);
_filterRect.offsetMax = new Vector2(0, offset);

_clearButtonRect.anchorMin = new Vector2(0f, 0f);
_clearButtonRect.anchorMax = new Vector2(_clearButtonAnchorX, 0f);
_clearButtonRect.offsetMin = new Vector2(0, -height + offset);
_clearButtonRect.offsetMax = new Vector2(0, offset);
}
else
{
_filterRect.anchorMin = new Vector2(_clearButtonAnchorX, 1f);
_filterRect.anchorMax = new Vector2(1f, 1f);
_filterRect.offsetMin = new Vector2(0, -offset);
_filterRect.offsetMax = new Vector2(0, height - offset);

_clearButtonRect.anchorMin = new Vector2(0, 1f);
_clearButtonRect.anchorMax = new Vector2(_clearButtonAnchorX, 1f);
_clearButtonRect.offsetMin = new Vector2(0, -offset);
_clearButtonRect.offsetMax = new Vector2(0, height - offset);
}
}

private static float GetItemSize(Dropdown dropdown)
{
if(dropdown.itemText != null)
{
return dropdown.itemText.rectTransform.rect.height;
}
else if(dropdown.itemImage != null)
{
return dropdown.itemImage.rectTransform.rect.height;
}

return 20f;
}
}
}
3 changes: 2 additions & 1 deletion src/BetterSceneLoader.Core/ImageGrid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void ShowWindow(bool flag)
{
UISystem.gameObject.SetActive(flag);
toolbarToggle?.SetValue(flag);
category?.template.SetRect(0f, 1f, 0f, 0f, 0f, 0f, dropdownWidth, mainPanel.rectTransform.rect.height / 2);
category?.template.SetRect(0f, 1f, 0f, 0f, 0f, headerSize - marginSize, dropdownWidth, mainPanel.rectTransform.rect.height / 2);
}

public void UpdateWindow()
Expand Down Expand Up @@ -141,6 +141,7 @@ public void CreateUI(string name, int sortingOrder, string titleText)
PopulateGrid();
});
DropdownAutoScroll.Setup(category);
DropdownFilter.AddFilterUI(category, "BetterSceneLoaderDropdown");

var refresh = UIUtility.CreateButton("RefreshButton", drag.transform, "Refresh");
refresh.transform.SetRect(0f, 0f, 0f, 1f, curPos, 0f, curPos+=80f);
Expand Down

0 comments on commit f4adcff

Please sign in to comment.