using System;
using System.ComponentModel;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
namespace Robust.Client.UserInterface.Controls
{
///
/// Represents an operating system-based UI window.
///
///
[Virtual]
public partial class OSWindow : Control
{
[Dependency] private IClyde _clyde = default!;
private string _title = "Window";
private WindowRoot? _root;
///
/// The window instance backing this window instance.
/// Not guaranteed to be available unless the window is currently open.
///
public IClydeWindow? ClydeWindow { get; private set; }
///
/// The window that will "own" this window.
/// Owned windows always appear on top of their owners and have some other misc behavior depending on the OS.
///
public IClydeWindow? Owner { get; set; }
///
/// Whether the window is created and currently open.
///
public bool IsOpen => ClydeWindow?.IsVisible ?? false;
///
/// The title of the window.
///
///
/// Can be changed while the window is open.
///
public string Title
{
get => _title;
set
{
_title = value;
if (ClydeWindow != null)
ClydeWindow.Title = value;
}
}
///
/// The location to place the window at when it is opened.
///
///
/// Changing this while the window is open has no effect.
///
public WindowStartupLocation StartupLocation { get; set; }
///
/// Controls whether to automatically size one or both axis of the window to fit the content.
///
///
/// Changing this while the window is open has no effect.
///
public WindowSizeToContent SizeToContent { get; set; }
///
/// Specifies window styling options for the window.
///
///
/// Changing this while the window is open has no effect.
///
public OSWindowStyles WindowStyles { get; set; }
///
/// Raised when the window is being closed
/// (via calling or the user clicking the close button).
/// Can be cancelled.
///
public event Action? Closing;
///
/// Raised when the window has been closed.
///
public event Action? Closed;
public OSWindow()
{
IoCManager.InjectDependencies(this);
}
///
/// Create the window if not already created.
/// This window is not visible by default, call to display it.
///
public IClydeWindow Create()
{
if (ClydeWindow != null)
return ClydeWindow;
var parameters = new WindowCreateParameters();
if (!float.IsNaN(SetWidth))
parameters.Width = (int)SetWidth;
if (!float.IsNaN(SetHeight))
parameters.Height = (int)SetHeight;
if (SizeToContent != WindowSizeToContent.Manual)
{
Measure(Vector2Helpers.Infinity);
if ((SizeToContent & WindowSizeToContent.Width) != 0)
parameters.Width = (int)DesiredSize.X;
if ((SizeToContent & WindowSizeToContent.Height) != 0)
parameters.Height = (int)DesiredSize.Y;
}
parameters.Title = _title;
parameters.Styles = WindowStyles;
parameters.Owner = Owner;
parameters.StartupLocation = StartupLocation;
parameters.Visible = false;
ClydeWindow = _clyde.CreateWindow(parameters);
ClydeWindow.RequestClosed += OnWindowRequestClosed;
ClydeWindow.Destroyed += OnWindowDestroyed;
ClydeWindow.Resized += OnWindowResized;
_root = UserInterfaceManager.CreateWindowRoot(ClydeWindow);
_root.CreateRootControls();
// Add ourselves *after* creating the root.
// This way root controls are valid in EnteredTree().
// We have to re-organize the controls after, of course.
_root.AddChild(this);
SetPositionFirst();
// Resize the window by our UIScale
ClydeWindow.Size = new((int)(parameters.Width * UIScale), (int)(parameters.Height * UIScale));
return ClydeWindow;
}
///
/// Show the window to the user, creating it if necessary
///
public void Show()
{
ClydeWindow = Create();
ClydeWindow.IsVisible = true;
Shown();
}
protected virtual void Shown()
{
}
///
/// Try to close the window.
///
///
/// This can be cancelled by anything handling .
///
public void Close()
{
if (ClydeWindow == null)
return;
var eventArgs = new CancelEventArgs();
Closing?.Invoke(eventArgs);
if (eventArgs.Cancel)
return;
ClydeWindow.Dispose();
}
private void OnWindowRequestClosed(WindowRequestClosedEventArgs eventArgs)
{
Close();
}
private void OnWindowDestroyed(WindowDestroyedEventArgs obj)
{
// I give it a 75% chance that some Linux user can force close the window,
// breaking GLFW,
// and forcing us to have a code path that ignores the RequestClosed code.
RealClosed();
}
private void OnWindowResized(WindowResizedEventArgs obj)
{
SetSize = obj.NewSize / UIScale;
}
private void RealClosed()
{
Orphan();
ClydeWindow = null;
_root = null;
Closed?.Invoke();
}
}
[Flags]
public enum WindowSizeToContent : byte
{
Manual = 0,
Width = 1 << 0,
Height = 1 << 1,
WidthAndHeight = Width | Height
}
}