using System;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.UserInterface;
using Unity.Cloud.Collaborate.Utilities;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace Unity.Cloud.Collaborate.Components.Menus
{
///
/// Global element that is used to display dialogues throughout the window.
///
internal class FloatingDialogue : VisualElement
{
///
/// USS class name of elements of this type.
///
public const string UssClassName = "unity-floating-dialogue";
///
/// Location the uss file for this element is stored.
///
static readonly string k_StylePath = $"{CollaborateWindow.StylePath}/{nameof(FloatingDialogue)}.uss";
///
/// Backing field for the singleton.
///
static FloatingDialogue s_Instance;
///
/// Singleton for accessing the global FloatingDialogue
///
public static FloatingDialogue Instance => s_Instance ?? (s_Instance = new FloatingDialogue());
///
/// Constructor used by the singleton.
///
FloatingDialogue()
{
AddToClassList(UssClassName);
AddToClassList(UiConstants.ussHidden);
styleSheets.Add(AssetDatabase.LoadAssetAtPath(k_StylePath));
}
///
/// Allows focus to be set when the window opens. Allows for the options to be next up for tab.
///
public override bool canGrabFocus => true;
///
/// Set the position of the dialogue.
///
/// World x coordinate.
/// World y coordinate.
/// Content of the window.
/// Direction to open the dialogue towards.
void SetPosition(float x, float y, VisualElement content, MenuUtilities.OpenDirection openDirection)
{
// Correct for the top left corner of the element based on the direction it opens.
switch (openDirection)
{
case MenuUtilities.OpenDirection.UpLeft:
x -= content.worldBound.width;
y -= content.worldBound.height;
break;
case MenuUtilities.OpenDirection.UpRight:
y -= content.worldBound.height;
break;
case MenuUtilities.OpenDirection.DownLeft:
x -= content.worldBound.width;
break;
case MenuUtilities.OpenDirection.DownRight:
break;
default:
throw new ArgumentOutOfRangeException(nameof(openDirection), openDirection, null);
}
// Set the position.
style.top = y - parent.worldBound.yMin;
style.left = x - parent.worldBound.xMin;
style.right = new StyleLength(StyleKeyword.Auto);
style.bottom = new StyleLength(StyleKeyword.Auto);
}
///
/// Open the dialogue.
///
/// World x coordinate.
/// World y coordinate.
/// Content to display.
/// Direction to open the dialogue towards.
internal void Open(float x, float y, VisualElement content, MenuUtilities.OpenDirection openDirection = MenuUtilities.OpenDirection.DownLeft)
{
// Set new content
Clear();
Add(content);
// Set visible and give focus
RemoveFromClassList(UiConstants.ussHidden);
Focus();
BringToFront();
// Catch scrolling to avoid menu becoming detached.
parent.RegisterCallback(OnScroll, TrickleDown.TrickleDown);
// Catch clicks outside of the dialogue and close it.
parent.RegisterCallback(OnMouseDown, TrickleDown.TrickleDown);
// Catch window size changes so that the menu position can be fixed.
parent.RegisterCallback(OnGeometryChange, TrickleDown.TrickleDown);
content.RegisterCallback(SizeKnownCallback);
void SizeKnownCallback(GeometryChangedEvent _)
{
// Unregister now that we know it has a size.
content.UnregisterCallback(SizeKnownCallback);
// Set the position in the window
SetPosition(x, y, content, openDirection);
}
}
///
/// Close the dialogue.
///
internal void Close()
{
AddToClassList(UiConstants.ussHidden);
Clear();
// Avoid wasting extra cycles when closed.
parent.UnregisterCallback(OnScroll, TrickleDown.TrickleDown);
parent.UnregisterCallback(OnMouseDown, TrickleDown.TrickleDown);
}
///
/// On scroll event.
///
/// Event data.
void OnScroll(WheelEvent evt)
{
// Close the window if the user scrolls outside the dialogue.
if (!worldBound.Contains(evt.mousePosition))
{
Close();
}
}
///
/// Mouse down event.
///
/// Event data.
void OnMouseDown(MouseDownEvent evt)
{
// Close the window if the user clicks outside the dialogue.
if (!worldBound.Contains(evt.mousePosition))
{
Close();
}
}
///
/// Geometry changed event.
///
/// Event data.
void OnGeometryChange(GeometryChangedEvent evt)
{
// Close to avoid the dialogue getting detached.
Close();
}
}
}