com.alicizax.unity.ui.exten.../Runtime/UXComponent/Group/UXGroup.cs

397 lines
10 KiB
C#
Raw Normal View History

using UnityEngine;
using UnityEngine.EventSystems;
2025-10-13 20:20:01 +08:00
2025-12-09 20:30:11 +08:00
namespace UnityEngine.UI
2025-04-11 17:26:28 +08:00
{
[AddComponentMenu("UI/UXGroup", 31)]
2025-12-09 20:30:11 +08:00
[DisallowMultipleComponent]
public class UXGroup : UIBehaviour
{
[SerializeField] private bool m_AllowSwitchOff = false;
2025-04-11 17:26:28 +08:00
[SerializeField]
private UXToggle[] m_Toggles = new UXToggle[0];
[SerializeField]
private UXToggle m_DefaultToggle;
2025-04-11 17:26:28 +08:00
private int m_ToggleCount;
private UXToggle m_CurrentToggle;
private int m_CurrentIndex = -1;
public bool allowSwitchOff
{
get { return m_AllowSwitchOff; }
set
{
m_AllowSwitchOff = value;
EnsureValidState();
}
}
public UXToggle defaultToggle
2025-04-11 17:26:28 +08:00
{
get { return m_DefaultToggle; }
set
{
m_DefaultToggle = ContainsToggle(value) ? value : null;
EnsureValidState();
}
2025-04-11 17:26:28 +08:00
}
2025-07-28 13:04:49 +08:00
protected UXGroup()
2025-12-09 20:30:11 +08:00
{
}
2025-10-13 20:20:01 +08:00
protected override void Start()
{
EnsureValidState();
base.Start();
2025-04-11 17:26:28 +08:00
}
2025-12-09 20:30:11 +08:00
protected override void OnEnable()
2025-04-11 17:26:28 +08:00
{
EnsureValidState();
base.OnEnable();
2025-04-11 17:26:28 +08:00
}
public void NotifyToggleOn(UXToggle toggle, bool sendCallback = true)
{
EnsureStorage();
int index = IndexOfToggle(toggle);
if (index < 0)
return;
m_CurrentToggle = toggle;
m_CurrentIndex = index;
for (int i = 0; i < m_ToggleCount; i++)
2025-12-09 20:30:11 +08:00
{
UXToggle item = m_Toggles[i];
if (item == null || item == toggle)
continue;
if (sendCallback)
item.isOn = false;
else
item.SetIsOnWithoutNotify(false);
2025-12-09 20:30:11 +08:00
}
}
public void UnregisterToggle(UXToggle toggle)
{
EnsureStorage();
int index = IndexOfToggle(toggle);
if (index < 0)
return;
RemoveAt(index);
if (m_DefaultToggle == toggle)
m_DefaultToggle = null;
if (m_CurrentToggle == toggle)
{
m_CurrentToggle = null;
m_CurrentIndex = -1;
2025-12-09 20:30:11 +08:00
}
EnsureSingleSelection();
2025-04-11 17:26:28 +08:00
}
2025-07-28 13:04:49 +08:00
public void RegisterToggle(UXToggle toggle)
2025-12-09 20:30:11 +08:00
{
EnsureStorage();
if (toggle == null || ContainsToggle(toggle))
return;
2025-10-13 20:20:01 +08:00
EnsureCapacity(m_ToggleCount + 1);
m_Toggles[m_ToggleCount] = toggle;
m_ToggleCount++;
if (toggle.isOn)
NotifyToggleOn(toggle);
else
EnsureSingleSelection();
}
public bool ContainsToggle(UXToggle toggle)
{
EnsureStorage();
return IndexOfToggle(toggle) >= 0;
}
public void EnsureValidState()
{
EnsureStorage();
CompactNulls();
SyncToggleGroups();
EnsureDefaultToggle();
EnsureSingleSelection();
}
2026-03-18 16:01:18 +08:00
public bool AnyTogglesOn()
{
return FindFirstActiveIndex() >= 0;
}
public UXToggle GetFirstActiveToggle()
{
int index = FindFirstActiveIndex();
return index >= 0 ? m_Toggles[index] : null;
}
public void SetAllTogglesOff(bool sendCallback = true)
{
bool oldAllowSwitchOff = m_AllowSwitchOff;
m_AllowSwitchOff = true;
for (int i = 0; i < m_ToggleCount; i++)
2025-12-09 20:30:11 +08:00
{
UXToggle toggle = m_Toggles[i];
if (toggle == null)
continue;
2026-03-18 16:01:18 +08:00
if (sendCallback)
toggle.isOn = false;
else
toggle.SetIsOnWithoutNotify(false);
2025-12-09 20:30:11 +08:00
}
m_CurrentToggle = null;
m_CurrentIndex = -1;
m_AllowSwitchOff = oldAllowSwitchOff;
}
2025-12-09 20:30:11 +08:00
public void Next()
{
SelectAdjacent(true);
}
2026-03-18 16:01:18 +08:00
public void Previous()
{
SelectAdjacent(false);
2025-12-09 20:30:11 +08:00
}
2025-04-11 17:26:28 +08:00
internal UXToggle GetToggleAt(int index)
2025-12-09 20:30:11 +08:00
{
return index >= 0 && index < m_ToggleCount ? m_Toggles[index] : null;
}
internal int ToggleCount
{
get { return m_ToggleCount; }
}
private void EnsureStorage()
{
if (m_Toggles == null)
m_Toggles = new UXToggle[0];
if (m_ToggleCount == 0)
{
int count = 0;
for (int i = 0; i < m_Toggles.Length; i++)
{
if (m_Toggles[i] != null)
count = i + 1;
}
2025-08-08 20:56:31 +08:00
m_ToggleCount = count;
}
if (m_ToggleCount > m_Toggles.Length)
m_ToggleCount = m_Toggles.Length;
}
private void CompactNulls()
{
int write = 0;
for (int read = 0; read < m_Toggles.Length; read++)
{
UXToggle toggle = m_Toggles[read];
if (toggle == null)
continue;
m_Toggles[write] = toggle;
write++;
}
for (int i = write; i < m_Toggles.Length; i++)
m_Toggles[i] = null;
m_ToggleCount = write;
}
private void SyncToggleGroups()
{
for (int i = 0; i < m_ToggleCount; i++)
{
UXToggle toggle = m_Toggles[i];
if (toggle != null && toggle.group != this)
toggle.SetToggleGroupInternal(this, true);
}
}
private void EnsureDefaultToggle()
{
if (m_ToggleCount == 0)
{
m_DefaultToggle = null;
return;
}
if (m_DefaultToggle != null && IndexOfToggle(m_DefaultToggle) >= 0)
return;
if (!m_AllowSwitchOff)
m_DefaultToggle = m_Toggles[0];
else
m_DefaultToggle = null;
}
private void EnsureSingleSelection()
{
int selectedIndex = FindSelectedIndex();
if (selectedIndex < 0 && !m_AllowSwitchOff && m_ToggleCount > 0)
{
selectedIndex = GetDefaultIndex();
if (selectedIndex < 0)
selectedIndex = 0;
UXToggle toggle = m_Toggles[selectedIndex];
if (toggle != null)
toggle.isOn = true;
}
if (selectedIndex >= 0)
{
m_CurrentToggle = m_Toggles[selectedIndex];
m_CurrentIndex = selectedIndex;
for (int i = 0; i < m_ToggleCount; i++)
{
UXToggle toggle = m_Toggles[i];
if (toggle != null && i != selectedIndex && toggle.isOn)
toggle.SetIsOnWithoutNotify(false);
}
}
else
{
m_CurrentToggle = null;
m_CurrentIndex = -1;
}
2025-12-09 20:30:11 +08:00
}
2025-08-08 20:56:31 +08:00
private int FindSelectedIndex()
2025-12-09 20:30:11 +08:00
{
int defaultIndex = GetDefaultIndex();
if (defaultIndex >= 0 && m_Toggles[defaultIndex].isOn)
return defaultIndex;
return FindFirstActiveIndex();
2025-12-09 20:30:11 +08:00
}
2025-08-08 20:56:31 +08:00
private int FindFirstActiveIndex()
{
for (int i = 0; i < m_ToggleCount; i++)
{
UXToggle toggle = m_Toggles[i];
if (toggle != null && toggle.isOn)
return i;
}
return -1;
}
2025-08-08 20:56:31 +08:00
private int GetDefaultIndex()
2025-08-11 14:45:27 +08:00
{
if (m_DefaultToggle == null)
return -1;
return IndexOfToggle(m_DefaultToggle);
2025-08-08 20:56:31 +08:00
}
2025-08-11 14:45:27 +08:00
private int IndexOfToggle(UXToggle toggle)
2025-12-09 20:30:11 +08:00
{
if (toggle == null || m_Toggles == null)
return -1;
2025-12-09 20:30:11 +08:00
for (int i = 0; i < m_ToggleCount; i++)
2025-12-09 20:30:11 +08:00
{
if (m_Toggles[i] == toggle)
return i;
2025-12-09 20:30:11 +08:00
}
return -1;
}
private void EnsureCapacity(int capacity)
{
if (m_Toggles == null)
m_Toggles = new UXToggle[capacity < 4 ? 4 : capacity];
if (m_Toggles.Length >= capacity)
return;
int newCapacity = m_Toggles.Length == 0 ? 4 : m_Toggles.Length * 2;
while (newCapacity < capacity)
newCapacity *= 2;
UXToggle[] newToggles = new UXToggle[newCapacity];
for (int i = 0; i < m_ToggleCount; i++)
newToggles[i] = m_Toggles[i];
m_Toggles = newToggles;
}
private void RemoveAt(int index)
{
int lastIndex = m_ToggleCount - 1;
for (int i = index; i < lastIndex; i++)
m_Toggles[i] = m_Toggles[i + 1];
if (lastIndex >= 0)
m_Toggles[lastIndex] = null;
m_ToggleCount = lastIndex;
}
private void SelectAdjacent(bool forward)
{
if (m_ToggleCount == 0)
return;
int index = ResolveCurrentIndex();
if (index < 0)
index = forward ? -1 : 0;
for (int step = 0; step < m_ToggleCount; step++)
{
index = forward ? index + 1 : index - 1;
if (index >= m_ToggleCount)
index = 0;
else if (index < 0)
index = m_ToggleCount - 1;
UXToggle toggle = m_Toggles[index];
if (toggle == null || !toggle.IsActive() || !toggle.IsInteractable())
continue;
toggle.isOn = true;
return;
}
2025-12-09 20:30:11 +08:00
}
private int ResolveCurrentIndex()
{
if (m_CurrentIndex >= 0 && m_CurrentIndex < m_ToggleCount && m_Toggles[m_CurrentIndex] == m_CurrentToggle)
return m_CurrentIndex;
m_CurrentIndex = FindFirstActiveIndex();
m_CurrentToggle = m_CurrentIndex >= 0 ? m_Toggles[m_CurrentIndex] : null;
return m_CurrentIndex;
}
2025-08-08 20:56:31 +08:00
}
2025-04-11 17:26:28 +08:00
}