// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System.Threading; using FlaxEngine; using FlaxEngine.GUI; namespace FlaxEditor.GUI.Dialogs { /// /// Helper class for showing user dialogs. /// /// public abstract class Dialog : ContainerControl { private string _title; /// /// Flag used to block the calling thread if it used ShowDialog option. /// protected long _isWaitingForDialog; /// /// The parent window. /// protected Window _window; /// /// The dialog result. /// protected DialogResult _result; /// /// The dialog size. /// protected Float2 _dialogSize = new Float2(300, 100); /// /// Gets the dialog result. /// public DialogResult Result => _result; /// /// Initializes a new instance of the class. /// /// The title. protected Dialog(string title) { BackgroundColor = Style.Current.Background; AnchorPreset = AnchorPresets.StretchAll; Offsets = Margin.Zero; ClipChildren = false; _title = title; _result = DialogResult.None; } /// /// Shows the dialog and waits for the result. /// /// The dialog result. public DialogResult ShowDialog() { return ShowDialog(Editor.Instance.Windows.MainWindow); } /// /// Shows the dialog and waits for the result. /// /// The parent window. /// The dialog result. public DialogResult ShowDialog(WindowRootControl parentWindow) { return ShowDialog(parentWindow?.Window); } /// /// Shows the dialog and waits for the result. /// /// The control calling. /// The dialog result. public DialogResult ShowDialog(Control control) { return ShowDialog(control?.RootWindow); } /// /// Shows the dialog and waits for the result. /// /// The parent window. /// The dialog result. public DialogResult ShowDialog(Window parentWindow) { // Show window Show(parentWindow); // Set wait flag Interlocked.Increment(ref _isWaitingForDialog); // Wait for the closing do { Thread.Sleep(1); } while (_window); // Clear wait flag Interlocked.Decrement(ref _isWaitingForDialog); return _result; } /// /// Shows the dialog. /// public void Show() { Show(Editor.Instance.Windows.MainWindow); } /// /// Shows the dialog. /// /// The parent window. public void Show(WindowRootControl parentWindow) { Show(parentWindow?.Window); } /// /// Shows the dialog. /// /// The control calling. public void Show(Control control) { Show(control?.Root.RootWindow); } /// /// Shows the dialog. /// /// The parent window. public void Show(Window parentWindow) { // Setup initial window settings CreateWindowSettings settings = CreateWindowSettings.Default; settings.Title = _title; settings.Size = _dialogSize * parentWindow.DpiScale; settings.AllowMaximize = false; settings.AllowMinimize = false; settings.HasSizingFrame = false; settings.StartPosition = WindowStartPosition.CenterParent; settings.Parent = parentWindow; SetupWindowSettings(ref settings); // Create window _window = Platform.CreateWindow(ref settings); var windowGUI = _window.GUI; // Attach events _window.Closing += OnClosing; _window.Closed += OnClosed; // Link to the window Offsets = Margin.Zero; Parent = windowGUI; // Show _window.Show(); _window.Focus(); _window.FlashWindow(); // Perform layout windowGUI.UnlockChildrenRecursive(); windowGUI.PerformLayout(); OnShow(); } private void OnClosing(ClosingReason reason, ref bool cancel) { // Check if can close window if (CanCloseWindow(reason)) { if (reason == ClosingReason.User) _result = DialogResult.Cancel; // Clean up _window = null; // Check if any thread is blocked during ShowDialog, then wait for it bool wait = true; while (wait) { wait = Interlocked.Read(ref _isWaitingForDialog) > 0; Thread.Sleep(1); } // Close window return; } // Suppress closing cancel = true; } private void OnClosed() { _window = null; } /// /// Closes this dialog. /// public void Close() { if (_window != null) { var win = _window; _window = null; win.Close(); } } /// /// Called to cancel action and close the dialog. /// public virtual void OnCancel() { Close(DialogResult.Cancel); } /// /// Closes dialog with the specified result. /// /// The result. protected void Close(DialogResult result) { _result = result; Close(); } /// /// Setups the window settings. /// /// The settings. protected virtual void SetupWindowSettings(ref CreateWindowSettings settings) { } /// /// Called when dialogs popups. /// protected virtual void OnShow() { Focus(); } /// /// Determines whether this dialog can be closed. /// /// The reason. /// true if this dialog can be closed; otherwise, false. protected virtual bool CanCloseWindow(ClosingReason reason) { return true; } /// public override void OnSubmit() { base.OnSubmit(); Close(DialogResult.OK); } /// public override bool OnKeyDown(KeyboardKeys key) { if (base.OnKeyDown(key)) return true; switch (key) { case KeyboardKeys.Return: if (Root?.FocusedControl != null) Root.SubmitFocused(); else OnSubmit(); return true; case KeyboardKeys.Escape: OnCancel(); return true; case KeyboardKeys.Tab: if (Root != null) { bool shiftDown = Root.GetKey(KeyboardKeys.Shift); Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next); } return true; } return false; } } }