first commit
This commit is contained in:
218
NitroxClient/MonoBehaviours/Gui/Modals/Modal.cs
Normal file
218
NitroxClient/MonoBehaviours/Gui/Modals/Modal.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UWE;
|
||||
|
||||
namespace NitroxClient.MonoBehaviours.Gui.Modals;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for Modal components, which are dialog boxes that appear in the middle of the screen
|
||||
/// </summary>
|
||||
public abstract class Modal
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a Modal by its type at any time (static)
|
||||
/// </summary>
|
||||
public static Dictionary<Type, Modal> Modals = new();
|
||||
/// <summary>
|
||||
/// Current modal that is visible on the screen
|
||||
/// </summary>
|
||||
public static Modal CurrentModal;
|
||||
|
||||
private GameObject modalSubWindow;
|
||||
private TextMeshProUGUI text;
|
||||
|
||||
// All the properties that will be overriden by new instances that inherit this class
|
||||
public string SubWindowName { get; init; }
|
||||
public string ModalText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Makes it possible to dismiss the modal by clicking outside of the modal or pressing escape (default false).
|
||||
/// </summary>
|
||||
public bool IsAvoidable { get; init; }
|
||||
public bool HideNoButton { get; init; }
|
||||
|
||||
public string YesButtonText { get; init; }
|
||||
public string NoButtonText { get; init; }
|
||||
|
||||
public bool FreezeGame { get; init; }
|
||||
|
||||
public float Transparency { get; init; }
|
||||
|
||||
public float Height { get; init; }
|
||||
|
||||
// Is useful for calling IngameMenu::OnDeselect() from a modal class (in Hide() for example)
|
||||
public bool IsAvoidableBypass = false;
|
||||
|
||||
public Modal(string yesButtonText = "YES", bool hideNoButton = true, string noButtonText = "NO", string modalText = "", bool isAvoidable = false, bool freezeGame = false, float transparency = 0.392f, float height = 195f)
|
||||
{
|
||||
Type type = GetType();
|
||||
if (Modals.ContainsKey(type))
|
||||
{
|
||||
throw new NotSupportedException($"You cannot set two modals to have the same Type");
|
||||
}
|
||||
|
||||
SubWindowName = GetType().Name;
|
||||
YesButtonText = yesButtonText;
|
||||
HideNoButton = hideNoButton;
|
||||
NoButtonText = noButtonText;
|
||||
ModalText = modalText;
|
||||
IsAvoidable = isAvoidable;
|
||||
FreezeGame = freezeGame;
|
||||
Transparency = transparency; // 0.392 is the default transparency for Subnautica's modal
|
||||
Height = height;
|
||||
|
||||
Log.Debug($"Registered Modal {SubWindowName} of type {type}");
|
||||
Modals.Add(type, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Modal to the screen
|
||||
/// </summary>
|
||||
public void Show()
|
||||
{
|
||||
CoroutineHost.StartCoroutine(ShowAsync());
|
||||
}
|
||||
|
||||
public IEnumerator ShowAsync()
|
||||
{
|
||||
CurrentModal?.Hide();
|
||||
CurrentModal = this;
|
||||
yield return ShowImplementation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the Modal from the screen
|
||||
/// </summary>
|
||||
public void Hide()
|
||||
{
|
||||
CurrentModal = null;
|
||||
if (FreezeGame)
|
||||
{
|
||||
FreezeTime.End(FreezeTime.Id.Quit);
|
||||
}
|
||||
if (IsAvoidable)
|
||||
{
|
||||
IngameMenu.main.OnDeselect();
|
||||
}
|
||||
else
|
||||
{
|
||||
IsAvoidableBypass = true;
|
||||
IngameMenu.main.OnDeselect();
|
||||
IsAvoidableBypass = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when this modal is deselected (only when pressing outside of the modal)
|
||||
/// </summary>
|
||||
public virtual void OnDeselect() { }
|
||||
|
||||
/// <summary>
|
||||
/// This creates the modal when showing it for the first time, you can't modify it afterwards
|
||||
/// </summary>
|
||||
private void InitSubWindow()
|
||||
{
|
||||
if (!IngameMenu.main)
|
||||
{
|
||||
throw new NotSupportedException($"Cannot show ingame subwindow {SubWindowName} because the ingame window does not exist.");
|
||||
}
|
||||
|
||||
if (!modalSubWindow)
|
||||
{
|
||||
GameObject derivedSubWindow = IngameMenu.main.transform.Find("QuitConfirmation").gameObject;
|
||||
modalSubWindow = UnityEngine.Object.Instantiate(derivedSubWindow, IngameMenu.main.transform, false);
|
||||
modalSubWindow.name = SubWindowName;
|
||||
|
||||
// Styling.
|
||||
RectTransform main = modalSubWindow.GetComponent<RectTransform>();
|
||||
main.sizeDelta = new Vector2(700, Height);
|
||||
|
||||
RectTransform messageTransform = modalSubWindow.FindChild("Header").GetComponent<RectTransform>();
|
||||
messageTransform.sizeDelta = new Vector2(700, Height);
|
||||
messageTransform.anchoredPosition = new Vector2(0, 50 - Height / 2);
|
||||
}
|
||||
|
||||
modalSubWindow.GetComponent<Image>().color = Color.white.WithAlpha(Transparency);
|
||||
|
||||
// Will happen either it's initialized or not
|
||||
UpdateModal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the modal with informations that may change from one Show() to another
|
||||
/// </summary>
|
||||
private void UpdateModal()
|
||||
{
|
||||
text = modalSubWindow.FindChild("Header").GetComponent<TextMeshProUGUI>();
|
||||
text.text = ModalText;
|
||||
|
||||
GameObject buttonYesObject = modalSubWindow.FindChild("ButtonYes");
|
||||
GameObject buttonNoObject = modalSubWindow.FindChild("ButtonNo");
|
||||
Button yesButton = buttonYesObject.GetComponent<Button>();
|
||||
|
||||
// We need to reinitialize onClick to avoid keeping Persisted Events (which are set manually inside Unity's Editor)
|
||||
yesButton.onClick = new Button.ButtonClickedEvent();
|
||||
yesButton.onClick.AddListener(ClickYes);
|
||||
buttonYesObject.GetComponentInChildren<TextMeshProUGUI>().text = YesButtonText;
|
||||
RectTransform yesButtonTransform = buttonYesObject.GetComponent<RectTransform>();
|
||||
yesButtonTransform.anchoredPosition = new Vector2(yesButtonTransform.anchoredPosition.x, 50f - Height);
|
||||
|
||||
|
||||
// TODO: fix yes and no button positions
|
||||
if (HideNoButton)
|
||||
{
|
||||
UnityEngine.Object.Destroy(buttonNoObject);
|
||||
buttonYesObject.transform.position = new Vector3(modalSubWindow.transform.position.x / 2, buttonYesObject.transform.position.y, buttonYesObject.transform.position.z); // Center Button
|
||||
return;
|
||||
}
|
||||
|
||||
if (buttonNoObject)
|
||||
{
|
||||
Button noButton = buttonNoObject.GetComponent<Button>();
|
||||
noButton.onClick = new Button.ButtonClickedEvent();
|
||||
noButton.onClick.AddListener(ClickNo);
|
||||
buttonNoObject.GetComponentInChildren<TextMeshProUGUI>().text = NoButtonText;
|
||||
RectTransform noButtonTransform = buttonNoObject.GetComponent<RectTransform>();
|
||||
noButtonTransform.anchoredPosition = new Vector2(noButtonTransform.anchoredPosition.x, 50f - Height);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClickYes() { }
|
||||
public virtual void ClickNo() { }
|
||||
|
||||
private IEnumerator ShowImplementation()
|
||||
{
|
||||
// Execute frame-by-frame to allow UI scripts to initialize.
|
||||
InitSubWindow();
|
||||
yield return new WaitForEndOfFrame();
|
||||
// Equivalent of IngameMenu.main.Open() but without minding for the freeze
|
||||
IngameMenu.main.gameObject.SetActive(true);
|
||||
IngameMenu.main.Select();
|
||||
yield return new WaitForEndOfFrame();
|
||||
IngameMenu.main.ChangeSubscreen(SubWindowName);
|
||||
yield return new WaitForEndOfFrame();
|
||||
if (FreezeGame)
|
||||
{
|
||||
FreezeTime.Begin(FreezeTime.Id.Quit);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lets you get any existing Modal by its Type
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the modal to get</typeparam>
|
||||
/// <returns>An existing instance of the modal if it already exists, else, a new one</returns>
|
||||
public static T Get<T>() where T : Modal
|
||||
{
|
||||
if (Modals.TryGetValue(typeof(T), out Modal modal))
|
||||
{
|
||||
return (T)modal;
|
||||
}
|
||||
// No need to add entry in dictionary as it's done in constructor
|
||||
return (T)Activator.CreateInstance(typeof(T));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user