unity-vendor-packages/osa/Com.ForbiddenByte/OSA/Utilities/Scripts/Util/DiscreteScrollbar.cs

105 lines
3.2 KiB
C#
Raw Normal View History

2025-08-18 09:22:24 +08:00
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using frame8.Logic.Misc.Other.Extensions;
using frame8.Logic.Misc.Visual.UI;
using frame8.Logic.Misc.Visual.UI.MonoBehaviours;
using Com.ForbiddenByte.OSA.Core;
namespace Com.ForbiddenByte.OSA.Util
{
/// <summary>
/// Important note: if used with ScrollbarFixer8 (which is true in the most cases,
/// make sure <see cref="ScrollbarFixer8.minSize"/> is not too small
/// </summary>
public class DiscreteScrollbar : MonoBehaviour
{
public RectTransform slotPrefab;
public RectTransform slotsParent;
public UnityIntEvent OnSlotSelected;
public Func<int> getItemsCountFunc;
Scrollbar _Scrollbar;
RectTransform[] slots = new RectTransform[0];
RectTransform _ScrollbarPanelRT;
IScrollRectProxy _ScrollRectProxy;
int _OneIfVert_ZeroIfHor;
const int MAX_COUNT = 100;
bool _UpdatePending;
void Awake()
{
// Get in parent, but ignore self
_ScrollRectProxy = transform.parent.GetComponentInParent<IScrollRectProxy>();
if (_ScrollRectProxy == null)
throw new OSAException(GetType().Name + ": No IScrollRectProxy component found in parent");
_Scrollbar = GetComponent<Scrollbar>();
_ScrollbarPanelRT = _Scrollbar.transform as RectTransform;
_OneIfVert_ZeroIfHor = _ScrollRectProxy.IsHorizontal ? 0 : 1;
}
void OnEnable() { _UpdatePending = false; }
public void OnScrollbarSizeChanged()
{
StartCoroutine(UpdateSize());
}
IEnumerator UpdateSize()
{
while (_UpdatePending) // wait for prev request to complete
yield return null;
_UpdatePending = true;
yield return null;
if (getItemsCountFunc == null)
throw new OSAException(GetType().Name + "getItemsCountFunc==null. Please specify a count provider");
_UpdatePending = true;
int count = getItemsCountFunc();
if (count > MAX_COUNT)
throw new OSAException(GetType().Name + ": count is " + count + ". Bigger than MAX_COUNT=" + MAX_COUNT + ". Are you sure you want to use a discrete scrollbar?");
Rebuild(count);
_UpdatePending = false;
}
public void Rebuild(int numSlots)
{
slotPrefab.gameObject.SetActive(true);
// Clear prev
if (slots != null)
foreach (var slot in slots)
Destroy(slot.gameObject);
// Add new
slots = new RectTransform[numSlots];
float sizesCumu = 0;
float slotSize = _ScrollbarPanelRT.rect.size[_OneIfVert_ZeroIfHor] / numSlots; // not using the handle's size because of rounding errors with higher <numSlots>
RectTransform.Edge edgeToInsetFrom = _OneIfVert_ZeroIfHor == 1 ? RectTransform.Edge.Top : RectTransform.Edge.Left;
for (int i = 0; i < numSlots; i++)
{
var slot = (Instantiate(slotPrefab.gameObject) as GameObject).GetComponent<RectTransform>();
slots[i] = slot;
slot.SetParent(slotsParent, false);
slot.SetInsetAndSizeFromParentEdgeWithCurrentAnchors(edgeToInsetFrom, sizesCumu, slotSize);
sizesCumu += slotSize;
int copyOfI = i;
slot.GetComponentInChildren<Button>().onClick.AddListener(() => { if (OnSlotSelected != null) OnSlotSelected.Invoke(copyOfI); });
}
slotPrefab.gameObject.SetActive(false);
}
[Serializable]
public class UnityIntEvent : UnityEvent<int> { }
}
}