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(); } } }