2025-11-05 17:34:40 +08:00

316 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
using static UnityEditor.Toast;
namespace UnityEditor
{
internal static class ToastManager
{
private const float NOTIFICATION_MARGIN = 5f;
private const double TARGET_FRAME_TIME = 1.0 / 30.0;
private static readonly List<Toast> topLeftNotifications = new();
private static readonly List<Toast> topRightNotifications = new();
private static readonly List<Toast> topCenterNotifications = new();
private static readonly List<Toast> bottomLeftNotifications = new();
private static readonly List<Toast> bottomRightNotifications = new();
private static readonly List<Toast> bottomCenterNotifications = new();
private static int notificationCount = 0;
[InitializeOnLoadMethod]
private static void Init()
{
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeDomainReload;
EnsureUpdateHook();
EditorApplication.QueuePlayerLoopUpdate();
}
private static void EnsureUpdateHook()
{
EditorApplication.update -= CustomUpdateLoop;
EditorApplication.update += CustomUpdateLoop;
}
private static void OnBeforeDomainReload()
{
//close all notifications on domain reload
EditorApplication.update -= CustomUpdateLoop;
for (var index = topLeftNotifications.Count - 1; index >= 0; index--)
{
var notification = topLeftNotifications[index];
RemoveNotification(notification);
notification.Close();
}
for (var index = topRightNotifications.Count - 1; index >= 0; index--)
{
var notification = topRightNotifications[index];
RemoveNotification(notification);
notification.Close();
}
for (var index = topCenterNotifications.Count - 1; index >= 0; index--)
{
var notification = topCenterNotifications[index];
RemoveNotification(notification);
notification.Close();
}
for (var index = bottomLeftNotifications.Count - 1; index >= 0; index--)
{
var notification = bottomLeftNotifications[index];
RemoveNotification(notification);
notification.Close();
}
for (var index = bottomRightNotifications.Count - 1; index >= 0; index--)
{
var notification = bottomRightNotifications[index];
RemoveNotification(notification);
notification.Close();
}
for (var index = bottomCenterNotifications.Count - 1; index >= 0; index--)
{
var notification = bottomCenterNotifications[index];
RemoveNotification(notification);
notification.Close();
}
}
private static void CustomUpdateLoop()
{
if (notificationCount <= 0) return;
UpdateNotifications(TARGET_FRAME_TIME);
if (!EditorApplication.isPlaying || notificationCount > 0)
{
EditorApplication.delayCall += () =>
{
EditorWindow.focusedWindow?.Repaint();
SceneView.RepaintAll();
};
}
}
private static void UpdateNotifications(double deltaTime)
{
CheckNotificationLifetimes();
UpdateNotificationPositions(deltaTime);
}
private static void CheckNotificationLifetimes()
{
for (var index = topLeftNotifications.Count - 1; index >= 0; index--)
{
var notification = topLeftNotifications[index];
if (!notification.IsLifetimeOver()) continue;
RemoveNotification(notification);
notification.Close();
}
for (var index = topRightNotifications.Count - 1; index >= 0; index--)
{
var notification = topRightNotifications[index];
if (!notification.IsLifetimeOver()) continue;
RemoveNotification(notification);
notification.Close();
}
for (var index = topCenterNotifications.Count - 1; index >= 0; index--)
{
var notification = topCenterNotifications[index];
if (!notification.IsLifetimeOver()) continue;
RemoveNotification(notification);
notification.Close();
}
for (var index = bottomLeftNotifications.Count - 1; index >= 0; index--)
{
var notification = bottomLeftNotifications[index];
if (!notification.IsLifetimeOver()) continue;
RemoveNotification(notification);
notification.Close();
}
for (var index = bottomRightNotifications.Count - 1; index >= 0; index--)
{
var notification = bottomRightNotifications[index];
if (!notification.IsLifetimeOver()) continue;
RemoveNotification(notification);
notification.Close();
}
for (var index = bottomCenterNotifications.Count - 1; index >= 0; index--)
{
var notification = bottomCenterNotifications[index];
if (!notification.IsLifetimeOver()) continue;
RemoveNotification(notification);
notification.Close();
}
}
private static void UpdateNotificationPositions(double deltaTime)
{
UpdateNotificationPositions(ToastPosition.TopLeft, deltaTime);
UpdateNotificationPositions(ToastPosition.TopRight, deltaTime);
UpdateNotificationPositions(ToastPosition.TopCenter, deltaTime);
UpdateNotificationPositions(ToastPosition.BottomLeft, deltaTime);
UpdateNotificationPositions(ToastPosition.BottomRight, deltaTime);
UpdateNotificationPositions(ToastPosition.BottomCenter, deltaTime);
}
public static void ShowNotification(ToastArgs toastArgs, Vector2 windowSize = default)
{
EnsureUpdateHook();
//Create the window
var notification = ScriptableObject.CreateInstance<Toast>();
notification.titleContent = new GUIContent($"Notification - {toastArgs.Title}");
notification.minSize = windowSize == default ? new Vector2(250, 100) : windowSize;
notification.maxSize = notification.minSize;
notification.position = new Rect(0, 0, notification.minSize.x, notification.minSize.y);
notification.ShowPopup();
notification.OnClose += RemoveNotification;
//Set position
notification.SetupWindow(new ToastData
{
ToastArgs = toastArgs,
TimeCreated = Time.time,
});
//Update other notifications
AddNotification(notification);
}
private static void AddNotification(Toast toast)
{
switch (toast.Args.ToastPosition)
{
case ToastPosition.TopLeft:
topLeftNotifications.Insert(0, toast);
break;
case ToastPosition.TopRight:
topRightNotifications.Insert(0, toast);
break;
case ToastPosition.TopCenter:
topCenterNotifications.Insert(0, toast);
break;
case ToastPosition.BottomLeft:
bottomLeftNotifications.Insert(0, toast);
break;
case ToastPosition.BottomRight:
bottomRightNotifications.Insert(0, toast);
break;
case ToastPosition.BottomCenter:
bottomCenterNotifications.Insert(0, toast);
break;
default:
throw new ArgumentOutOfRangeException(null);
}
notificationCount++;
}
private static void RemoveNotification(Toast toast)
{
toast.OnClose -= RemoveNotification;
switch (toast.Args.ToastPosition)
{
case ToastPosition.TopLeft:
topLeftNotifications.Remove(toast);
break;
case ToastPosition.TopRight:
topRightNotifications.Remove(toast);
break;
case ToastPosition.TopCenter:
topCenterNotifications.Remove(toast);
break;
case ToastPosition.BottomLeft:
bottomLeftNotifications.Remove(toast);
break;
case ToastPosition.BottomRight:
bottomRightNotifications.Remove(toast);
break;
case ToastPosition.BottomCenter:
bottomCenterNotifications.Remove(toast);
break;
default:
throw new ArgumentOutOfRangeException(null);
}
notificationCount--;
}
private static void UpdateNotificationPositions(ToastPosition toastPosition, double deltaTime)
{
var currentHeightOffset = 0f;
switch (toastPosition)
{
case ToastPosition.TopLeft:
foreach (var currentNotification in topLeftNotifications)
{
var positionRect = currentNotification.GetEditorWindowPosition(toastPosition);
currentNotification.UpdatePosition(positionRect, currentHeightOffset, deltaTime);
currentHeightOffset += currentNotification.GetHeight() + NOTIFICATION_MARGIN;
}
break;
case ToastPosition.TopRight:
foreach (var currentNotification in topRightNotifications)
{
var positionRect = currentNotification.GetEditorWindowPosition(toastPosition);
currentNotification.UpdatePosition(positionRect, currentHeightOffset, deltaTime);
currentHeightOffset += currentNotification.GetHeight() + NOTIFICATION_MARGIN;
}
break;
case ToastPosition.TopCenter:
foreach (var currentNotification in topCenterNotifications)
{
var positionRect = currentNotification.GetEditorWindowPosition(toastPosition);
currentNotification.UpdatePosition(positionRect, currentHeightOffset, deltaTime);
currentHeightOffset += currentNotification.GetHeight() + NOTIFICATION_MARGIN;
}
break;
case ToastPosition.BottomLeft:
foreach (var currentNotification in bottomLeftNotifications)
{
var positionRect = currentNotification.GetEditorWindowPosition(toastPosition);
currentNotification.UpdatePosition(positionRect, currentHeightOffset, deltaTime);
currentHeightOffset -= currentNotification.GetHeight() + NOTIFICATION_MARGIN;
}
break;
case ToastPosition.BottomRight:
foreach (var currentNotification in bottomRightNotifications)
{
var positionRect = currentNotification.GetEditorWindowPosition(toastPosition);
currentNotification.UpdatePosition(positionRect, currentHeightOffset, deltaTime);
currentHeightOffset -= currentNotification.GetHeight() + NOTIFICATION_MARGIN;
}
break;
case ToastPosition.BottomCenter:
foreach (var currentNotification in bottomCenterNotifications)
{
var positionRect = currentNotification.GetEditorWindowPosition(toastPosition);
currentNotification.UpdatePosition(positionRect, currentHeightOffset, deltaTime);
currentHeightOffset -= currentNotification.GetHeight() + NOTIFICATION_MARGIN;
}
break;
default:
throw new ArgumentOutOfRangeException(nameof(toastPosition), toastPosition, null);
}
}
}
}