OldBlueWater/BlueWater/Assets/Doozy/Runtime/UIManager/Components/UIToggle.cs

370 lines
15 KiB
C#

// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved.
// This code can only be used under the standard Unity Asset Store End User License Agreement
// A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms
using System.Collections.Generic;
using System.Linq;
using Doozy.Runtime.Common.Attributes;
using Doozy.Runtime.Common.Events;
using Doozy.Runtime.Common.Utils;
using Doozy.Runtime.Mody;
using Doozy.Runtime.Signals;
using Doozy.Runtime.UIManager.Events;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
// ReSharper disable MemberCanBeProtected.Global
namespace Doozy.Runtime.UIManager.Components
{
/// <summary>
/// Toggle component based on UISelectable with category/name id identifier.
/// </summary>
[RequireComponent(typeof(RectTransform))]
[AddComponentMenu("Doozy/UI/Components/UIToggle")]
[SelectionBase]
public partial class UIToggle : UISelectable, IPointerClickHandler, ISubmitHandler
{
#if UNITY_EDITOR
[UnityEditor.MenuItem("GameObject/Doozy/UI/Components/UIToggle", false, 8)]
private static void CreateComponent(UnityEditor.MenuCommand menuCommand)
{
GameObjectUtils.AddToScene<UIToggle>("UIToggle", false, true);
}
#endif
/// <summary> UIToggles database </summary>
public static HashSet<UIToggle> database { get; private set; } = new HashSet<UIToggle>();
[ExecuteOnReload]
private static void OnReload()
{
database = new HashSet<UIToggle>();
}
[ClearOnReload]
private static SignalStream s_stream;
/// <summary> UIToggle signal stream </summary>
public static SignalStream stream => s_stream ?? (s_stream = SignalsService.GetStream(k_StreamCategory, nameof(UIToggle)));
/// <summary> All toggles that are active and enabled </summary>
public static IEnumerable<UIToggle> availableToggles => database.Where(item => item.isActiveAndEnabled);
/// <summary> TRUE is this selectable is selected by EventSystem.current, FALSE otherwise </summary>
public bool isSelected => EventSystem.current.currentSelectedGameObject == gameObject;
/// <summary> Type of selectable </summary>
public override SelectableType selectableType => SelectableType.Toggle;
/// <summary> UIToggle identifier </summary>
public UIToggleId Id = new UIToggleId();
/// <summary> Toggle became ON - executed when isOn becomes TRUE </summary>
public ModyEvent OnToggleOnCallback = new ModyEvent(nameof(OnToggleOnCallback));
// <summary> Toggle became ON with instant animations - executed when isOn becomes TRUE </summary>
public ModyEvent OnInstantToggleOnCallback = new ModyEvent(nameof(OnInstantToggleOnCallback));
/// <summary> Toggle became OFF - executed when isOn becomes FALSE </summary>
public ModyEvent OnToggleOffCallback = new ModyEvent(nameof(OnToggleOffCallback));
/// <summary> Toggle became OFF with instant animations - executed when isOn becomes FALSE </summary>
public ModyEvent OnInstantToggleOffCallback = new ModyEvent(nameof(OnInstantToggleOffCallback));
/// <summary> Toggle changed its value - executed when isOn changes its value </summary>
public BoolEvent OnValueChangedCallback = new BoolEvent();
/// <summary> Toggle value changed callback. This special callback also sends when the event happened, the previousValue and the newValue </summary>
public UnityAction<ToggleValueChangedEvent> onToggleValueChangedCallback { get; set; }
/// <summary> Returns TRUE if this toggle has a toggle group reference </summary>
public bool inToggleGroup => ToggleGroup != null && ToggleGroup.toggles.Contains(this);
[SerializeField] private UIToggleGroup ToggleGroup;
/// <summary> Reference to the toggle group that this toggle belongs to </summary>
public UIToggleGroup toggleGroup
{
get => ToggleGroup;
internal set => ToggleGroup = value;
}
/// <summary> TRUE if the toggle is on, FALSE otherwise </summary>
public override bool isOn
{
get => IsOn;
set
{
if (isLocked) return;
bool previousValue = IsOn;
IsOn = value;
if (inToggleGroup)
{
toggleGroup.ToggleChangedValue(toggle: this, animateChange: true);
return;
}
ValueChanged(previousValue: previousValue, newValue: value, animateChange: true, triggerValueChanged: true);
}
}
[SerializeField] protected bool IsLocked;
/// <summary> TRUE if the toggle is locked, FALSE otherwise. A locked toggle cannot be toggled and will maintain its current isOn value even if the user clicks on it </summary>
public bool isLocked
{
get => IsLocked;
set => IsLocked = value;
}
/// <summary> Internal flag to track if the toggle has been initialized </summary>
protected bool toggleInitialized { get; set; }
protected override void Awake()
{
toggleInitialized = false;
if (!Application.isPlaying) return;
database.Add(this);
base.Awake();
}
protected override void OnEnable()
{
if (!Application.isPlaying) return;
StopCooldown();
database.Remove(null);
base.OnEnable();
InitializeToggle();
}
protected override void OnDisable()
{
StopCooldown();
database.Remove(null);
base.OnDisable();
}
protected override void OnDestroy()
{
database.Remove(null);
database.Remove(this);
base.OnDestroy();
}
/// <summary> Initializes the toggle </summary>
protected virtual void InitializeToggle()
{
if (toggleInitialized) return;
AddToToggleGroup(toggleGroup);
toggleInitialized = true;
if (inToggleGroup) return;
ValueChanged(isOn, isOn, false, false);
}
/// <summary> Called when on pointer click event is sent by the IPointerClickHandler </summary>
/// <param name="eventData"> Pointer event data </param>
public virtual void OnPointerClick(PointerEventData eventData)
{
if (inCooldown) return;
if (eventData.button != PointerEventData.InputButton.Left)
return;
if (!IsActive() || !IsInteractable())
return;
ToggleValue();
}
/// <summary> Called when on submit event is sent by the ISubmitHandler </summary>
/// <param name="eventData"> Event data </param>
public virtual void OnSubmit(BaseEventData eventData)
{
if (inCooldown) return;
if (!IsActive() || !IsInteractable())
return;
ToggleValue();
if (!inputSettings.submitTriggersPointerClick) return;
behaviours.GetBehaviour(UIBehaviour.Name.PointerClick)?.Execute();
behaviours.GetBehaviour(UIBehaviour.Name.PointerLeftClick)?.Execute();
}
/// <summary> Toggle the toggle's value </summary>
protected virtual void ToggleValue()
{
if (isLocked) return; //if the toggle is locked, we don't toggle it
isOn = !isOn; //toggle the toggle's value
StartCooldown(); //start the cooldown
}
/// <summary> Adds this toggle to the specified toggle group </summary>
/// <param name="targetToggleGroup"> Target toggle group </param>
public void AddToToggleGroup(UIToggleGroup targetToggleGroup)
{
if (targetToggleGroup == null)
return;
if (inToggleGroup && targetToggleGroup != toggleGroup) //if the toggle is already in a toggle group, we remove it from that group
RemoveFromToggleGroup(); //remove from the previous toggle group
if (isLocked) //check if the toggle is locked because we don't want to add locked toggles to a toggle group
isLocked = false; // if the toggle is locked, we unlock it to allow the toggle group to take control of it
targetToggleGroup.AddToggle(this);
}
/// <summary> Removes this toggle from its assigned toggle group </summary>
public void RemoveFromToggleGroup()
{
if (toggleGroup == null)
return;
toggleGroup.RemoveToggle(this);
}
/// <summary> Called when the value of the toggle is changed by a toggle group </summary>
/// <param name="newValue"> New value of the toggle </param>
/// <param name="animateChange"> TRUE if the change should be animated, FALSE otherwise </param>
/// <param name="triggerValueChanged"> TRUE if the value changed callback should be triggered, FALSE otherwise </param>
protected internal virtual void UpdateValueFromGroup(bool newValue, bool animateChange, bool triggerValueChanged = true)
{
if (isLocked) isLocked = false; //if the toggle is locked, we unlock it to allow the toggle group to take control of it
bool previousValue = IsOn;
IsOn = newValue;
ValueChanged(previousValue, newValue, animateChange, triggerValueChanged);
}
/// <summary> Send a signal to the toggle signal stream with the new value for this toggle </summary>
/// <param name="newValue"> New value of the toggle </param>
internal void SendSignal(bool newValue)
{
stream.SendSignal(new UIToggleSignalData(Id.Category, Id.Name, newValue ? CommandToggle.On : CommandToggle.Off, playerIndex, this));
}
/// <summary> Called when the value of the toggle changes </summary>
/// <param name="previousValue"> Previous value of the toggle </param>
/// <param name="newValue"> New value of the toggle </param>
/// <param name="animateChange"> TRUE if the change should be animated, FALSE otherwise </param>
/// <param name="triggerValueChanged"> TRUE if the value changed callback should be triggered, FALSE otherwise </param>
internal virtual void ValueChanged(bool previousValue, bool newValue, bool animateChange, bool triggerValueChanged)
{
RefreshState();
switch (newValue)
{
case true:
switch (animateChange)
{
case true:
OnToggleOnCallback?.Execute();
break;
default:
OnInstantToggleOnCallback?.Execute();
break;
}
break;
case false:
switch (animateChange)
{
case true:
OnToggleOffCallback?.Execute();
break;
default:
OnInstantToggleOffCallback?.Execute();
break;
}
break;
}
if (!triggerValueChanged)
return;
SendSignal(newValue);
OnValueChangedCallback?.Invoke(newValue);
onToggleValueChangedCallback?.Invoke(new ToggleValueChangedEvent(previousValue, newValue, animateChange));
}
#region Static Methods
/// <summary> Get all the registered toggles with the given category and name </summary>
/// <param name="category"> UIToggle category </param>
/// <param name="name"> UIToggle name (from the given category) </param>
public static IEnumerable<UIToggle> GetToggles(string category, string name) =>
database.Where(toggle => toggle.Id.Category.Equals(category)).Where(toggle => toggle.Id.Name.Equals(name));
/// <summary> Get all the registered toggles with the given category </summary>
/// <param name="category"> UIToggle category </param>
public static IEnumerable<UIToggle> GetAllTogglesInCategory(string category) =>
database.Where(toggle => toggle.Id.Category.Equals(category));
/// <summary> Get all the toggles that are active and enabled (all the visible/available toggles) </summary>
public static IEnumerable<UIToggle> GetAvailableToggles() =>
database.Where(toggle => toggle.isActiveAndEnabled);
/// <summary> Get the selected toggle (if a toggle is not selected, this method returns null) </summary>
public static UIToggle GetSelectedToggle() =>
database.FirstOrDefault(toggle => toggle.isSelected);
/// <summary> Select the toggle with the given category and name (if it is active and enabled) </summary>
/// <param name="category"> UIToggle category </param>
/// <param name="name"> UIToggle name (from the given category) </param>
public static bool SelectToggle(string category, string name)
{
UIToggle toggle = availableToggles.FirstOrDefault(b => b.Id.Category.Equals(category) & b.Id.Name.Equals(name));
if (toggle == null) return false;
toggle.Select();
return true;
}
#endregion
}
public static class UIToggleExtensions
{
/// <summary> Set the toggle value to the given value </summary>
/// <param name="target"> Target toggle </param>
/// <param name="newValue"> New value of the toggle </param>
/// <param name="animateChange"> TRUE if the change should be animated, FALSE otherwise </param>
/// <param name="triggerValueChanged"> TRUE if the value changed callback should be triggered, FALSE otherwise </param>
public static T SetIsOn<T>(this T target, bool newValue, bool animateChange = true, bool triggerValueChanged = true) where T : UIToggle
{
if (target.isLocked) return target;
bool previousValue = target.isOn;
target.IsOn = newValue;
if (target.inToggleGroup)
{
target.toggleGroup.ToggleChangedValue(target, animateChange, triggerValueChanged);
return target;
}
target.ValueChanged(previousValue, newValue, animateChange, triggerValueChanged);
return target;
}
/// <summary> Lock the toggle from having its isOn value changed, preventing it from being changed </summary>
/// <param name="target"> Target toggle </param>
public static T Lock<T>(this T target) where T : UIToggle
{
target.isLocked = true;
return target;
}
/// <summary>
/// Unlock the toggle from having its isOn value changed, allowing it to be changed again
/// </summary>
/// <param name="target"> Target toggle </param>
public static T Unlock<T>(this T target) where T : UIToggle
{
target.isLocked = false;
return target;
}
}
}