215 lines
7.5 KiB
C#
215 lines
7.5 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using JetBrains.Annotations;
|
||
|
using Unity.Cloud.Collaborate.Assets;
|
||
|
using Unity.Cloud.Collaborate.UserInterface;
|
||
|
using UnityEditor;
|
||
|
using UnityEngine.UIElements;
|
||
|
|
||
|
namespace Unity.Cloud.Collaborate.Components
|
||
|
{
|
||
|
[UsedImplicitly]
|
||
|
internal class AlertBox : VisualElement
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Describes the severity of the alert. Used to set the icon.
|
||
|
/// </summary>
|
||
|
public enum AlertLevel
|
||
|
{
|
||
|
Info,
|
||
|
Warning,
|
||
|
Alert
|
||
|
}
|
||
|
|
||
|
public const string UssClassName = "alert-box";
|
||
|
public const string IconUssClassName = UssClassName + "__icon";
|
||
|
public const string TextUssClassName = UssClassName + "__text";
|
||
|
public const string ButtonUssClassName = UssClassName + "__button";
|
||
|
|
||
|
static readonly string k_LayoutPath = $"{CollaborateWindow.LayoutPath}/{nameof(AlertBox)}.uxml";
|
||
|
static readonly string k_StylePath = $"{CollaborateWindow.StylePath}/{nameof(AlertBox)}.uss";
|
||
|
|
||
|
readonly Button m_Button;
|
||
|
readonly VisualElement m_Icon;
|
||
|
readonly TextElement m_Text;
|
||
|
|
||
|
// Uss classes to set which icon is displayed.
|
||
|
const string k_UssIconInfo = "icon-info";
|
||
|
const string k_UssIconWarning = "icon-warning";
|
||
|
const string k_UssIconAlert = "icon-alert";
|
||
|
|
||
|
/// <summary>
|
||
|
/// Queue of alerts to be displayed.
|
||
|
/// </summary>
|
||
|
readonly SortedSet<AlertEntry> m_AlertEntryList;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initialize the box and hide it.
|
||
|
/// </summary>
|
||
|
public AlertBox()
|
||
|
{
|
||
|
// Get the layout
|
||
|
AddToClassList(UssClassName);
|
||
|
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(k_LayoutPath).CloneTree(this);
|
||
|
styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(k_StylePath));
|
||
|
|
||
|
// Initialise fields
|
||
|
m_Icon = this.Q<VisualElement>(className: IconUssClassName);
|
||
|
m_Text = this.Q<TextElement>(className: TextUssClassName);
|
||
|
m_Button = this.Q<Button>(className: ButtonUssClassName);
|
||
|
|
||
|
m_AlertEntryList = new SortedSet<AlertEntry>();
|
||
|
|
||
|
// No alerts to display, so this hides the box.
|
||
|
UpdateAlertBox();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Queue an alert to be displayed. Displayed immediately if there is currently none. If there is an existing
|
||
|
/// alert with the same name, it will be replaced with the latest one.
|
||
|
/// </summary>
|
||
|
/// <param name="id">Unique ID for the queued alert.</param>
|
||
|
/// <param name="level">Level of important of the alert.</param>
|
||
|
/// <param name="message">Message to be displayed.</param>
|
||
|
/// <param name="button">Info to populate optional button.</param>
|
||
|
public void QueueAlert([NotNull] string id, AlertLevel level, [NotNull] string message, (string text, Action action)? button = null)
|
||
|
{
|
||
|
// Search for existing alert.
|
||
|
var oldActive = m_AlertEntryList.Count == 0 ? (AlertEntry?)null : m_AlertEntryList.Max;
|
||
|
|
||
|
// Create new alert entry.
|
||
|
var entry = new AlertEntry(id, level, message, button == null
|
||
|
? (AlertEntry.AlertButton?)null
|
||
|
: new AlertEntry.AlertButton { Text = button.Value.text, Action = button.Value.action });
|
||
|
|
||
|
m_AlertEntryList.Add(entry);
|
||
|
UpdateAlertBox(oldActive?.Button?.Action);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Remove existing alert. If current active one, switch to next one, or hide if none queued.
|
||
|
/// </summary>
|
||
|
/// <param name="id">Unique ID for the alert.</param>
|
||
|
public void DequeueAlert([NotNull] string id)
|
||
|
{
|
||
|
var oldAlert = m_AlertEntryList.Max;
|
||
|
|
||
|
m_AlertEntryList.RemoveWhere(e => e.Id == id);
|
||
|
|
||
|
UpdateAlertBox(oldAlert.Button?.Action);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Display alert at the front of the queue, or hide if there are none.
|
||
|
/// </summary>
|
||
|
void UpdateAlertBox(Action previousButtonAction = null)
|
||
|
{
|
||
|
// Remove old event if it exists.
|
||
|
if (previousButtonAction != null)
|
||
|
{
|
||
|
m_Button.clickable.clicked -= previousButtonAction;
|
||
|
}
|
||
|
|
||
|
if (m_AlertEntryList.Count == 0)
|
||
|
{
|
||
|
m_Button.text = string.Empty;
|
||
|
m_Button.AddToClassList(UiConstants.ussHidden);
|
||
|
AddToClassList(UiConstants.ussHidden);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var activeAlert = m_AlertEntryList.Max;
|
||
|
|
||
|
m_Text.text = activeAlert.Message;
|
||
|
// Update state of optional button
|
||
|
if (activeAlert.Button?.Action != null)
|
||
|
{
|
||
|
m_Button.clickable.clicked += activeAlert.Button.Value.Action;
|
||
|
m_Button.text = activeAlert.Button.Value.Text;
|
||
|
m_Button.RemoveFromClassList(UiConstants.ussHidden);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_Button.text = string.Empty;
|
||
|
m_Button.AddToClassList(UiConstants.ussHidden);
|
||
|
}
|
||
|
|
||
|
SetAlertLevel(activeAlert.Level);
|
||
|
RemoveFromClassList(UiConstants.ussHidden);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Set the icon to the given severity level.
|
||
|
/// </summary>
|
||
|
/// <param name="level">Level of severity to make the icon.</param>
|
||
|
void SetAlertLevel(AlertLevel level)
|
||
|
{
|
||
|
// Remove old level
|
||
|
m_Icon.RemoveFromClassList(k_UssIconInfo);
|
||
|
m_Icon.RemoveFromClassList(k_UssIconWarning);
|
||
|
m_Icon.RemoveFromClassList(k_UssIconAlert);
|
||
|
|
||
|
// Set new one
|
||
|
switch (level)
|
||
|
{
|
||
|
case AlertLevel.Info:
|
||
|
m_Icon.AddToClassList(k_UssIconInfo);
|
||
|
break;
|
||
|
case AlertLevel.Warning:
|
||
|
m_Icon.AddToClassList(k_UssIconWarning);
|
||
|
break;
|
||
|
case AlertLevel.Alert:
|
||
|
m_Icon.AddToClassList(k_UssIconAlert);
|
||
|
break;
|
||
|
default:
|
||
|
throw new ArgumentOutOfRangeException(nameof(level), level, null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct AlertEntry : IComparable<AlertEntry>
|
||
|
{
|
||
|
public readonly string Id;
|
||
|
public readonly AlertLevel Level;
|
||
|
public readonly string Message;
|
||
|
public AlertButton? Button;
|
||
|
|
||
|
public AlertEntry(string id, AlertLevel level, string message, AlertButton? button)
|
||
|
{
|
||
|
Id = id;
|
||
|
Level = level;
|
||
|
Message = message;
|
||
|
Button = button;
|
||
|
}
|
||
|
|
||
|
public struct AlertButton
|
||
|
{
|
||
|
public string Text;
|
||
|
public Action Action;
|
||
|
}
|
||
|
|
||
|
public override int GetHashCode()
|
||
|
{
|
||
|
return Id.GetHashCode();
|
||
|
}
|
||
|
|
||
|
public override bool Equals(object obj)
|
||
|
{
|
||
|
return obj is AlertEntry other && Id == other.Id;
|
||
|
}
|
||
|
|
||
|
public int CompareTo(AlertEntry other)
|
||
|
{
|
||
|
var value = Level.CompareTo(other.Level);
|
||
|
// If same level, compare by id.
|
||
|
return value != 0
|
||
|
? value
|
||
|
: string.Compare(Id, other.Id, StringComparison.Ordinal);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[UsedImplicitly]
|
||
|
public new class UxmlFactory : UxmlFactory<AlertBox> { }
|
||
|
}
|
||
|
}
|