// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Utilities.Solvers; using System; using System.Collections; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.UI { public abstract class Dialog : MonoBehaviour { /// /// The current state of the Dialog. /// public DialogState State { get; set; } = DialogState.Uninitialized; /// /// Called after user has clicked a button and the dialog has finished closing /// public Action OnClosed; protected DialogResult result; /// /// Can be used to monitor result instead of events /// public DialogResult Result => result; protected void Launch(DialogResult newResult) { if (State != DialogState.Uninitialized) { return; } result = newResult; StartCoroutine(RunDialogOverTime()); } /// /// Opens dialog, waits for input, then closes /// protected IEnumerator RunDialogOverTime() { // Create buttons and set up message GenerateButtons(); SetTitleAndMessage(); FinalizeLayout(); // Open dialog State = DialogState.Opening; yield return StartCoroutine(OpenDialog()); State = DialogState.WaitingForInput; // Wait for input while (State == DialogState.WaitingForInput) { UpdateDialog(); yield return null; } // Close dialog State = DialogState.Closing; yield return StartCoroutine(CloseDialog()); State = DialogState.Closed; // Callback OnClosed?.Invoke(result); // Wait a moment to give scripts a chance to respond yield return null; // Destroy ourselves Destroy(gameObject); yield break; } /// /// Opens the dialog - state must be set to WaitingForInput afterwards /// Overridden in inherited class. /// Perform any change here that you'd like to have when the dialog opened. /// protected virtual IEnumerator OpenDialog() { yield break; } /// /// Closes the dialog - state must be set to Closed afterwards /// protected virtual IEnumerator CloseDialog() { yield break; } /// /// Perform any updates (animation, tagalong, etc) here /// This will be called every frame while waiting for input /// protected virtual void UpdateDialog() { return; } /// /// Generates buttons - Must parent them under buttonParent! /// protected abstract void GenerateButtons(); /// /// This is called after the buttons are generated and /// the title and message have been set. /// Perform here any operations that you'd like /// Lays out the buttons on the dialog /// E.g. using an ObjectCollection /// protected abstract void FinalizeLayout(); /// /// Set the title and message using the result /// E.g. using TextMesh components /// protected abstract void SetTitleAndMessage(); /// /// Function to destroy the Dialog. /// public abstract void DismissDialog(); /// /// Instantiates a dialog and passes it a result /// /// Dialog prefab /// DialogResult class object which contains information such as title and description text public static Dialog Open(GameObject dialogPrefab, DialogResult result) { GameObject dialogGo = GameObject.Instantiate(dialogPrefab) as GameObject; Dialog dialog = dialogGo.GetComponent(); dialog.Launch(result); return dialog; } /// /// Instantiates a dialog and passes a generated result /// /// Dialog prefab /// button configuration type which is defined in DialogButtonType enum /// Title text of the dialog /// Description text of the dialog /// Object with additional variable public static Dialog Open(GameObject dialogPrefab, DialogButtonType buttons, string title, string message, bool placeForNearInteraction, System.Object variable = null) { GameObject dialogGameObject = GameObject.Instantiate(dialogPrefab) as GameObject; if (placeForNearInteraction == true) { // For HoloLens 2, place the dialog at 45cm from the user for the near hand interactions. // Size is maintained by ConstantViewSize solver Follow followSolver = dialogGameObject.GetComponent(); followSolver.MinDistance = 0.3f; followSolver.MaxDistance = 0.9f; followSolver.DefaultDistance = 0.7f; } else { // For HoloLens 1 and other platforms, place the dialog for far interactions with gaze or pointers. // Size is maintained by ConstantViewSize solver Follow followSolver = dialogGameObject.GetComponent(); followSolver.MinDistance = 1.5f; followSolver.MaxDistance = 2.0f; followSolver.DefaultDistance = 1.8f; } Dialog dialog = dialogGameObject.GetComponent(); DialogResult result = new DialogResult { Buttons = buttons, Title = title, Message = message, Variable = variable }; dialog.Launch(result); return dialog; } } }