AlertBox.cs
7.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
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> { }
}
}