// 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;
}
}
}