using System.Collections.Concurrent;
namespace Terminal.Gui.App;
///
/// Implementation of core methods using the modern
/// main loop architecture with component factories for different platforms.
///
public partial class ApplicationImpl : IApplication
{
///
/// INTERNAL: Creates a new instance of the Application backend.
///
internal ApplicationImpl () { }
///
/// INTERNAL: Creates a new instance of the Application backend.
///
///
internal ApplicationImpl (IComponentFactory componentFactory) { _componentFactory = componentFactory; }
#region Singleton
///
/// Tracks which application model has been used in this process.
///
private static ApplicationModelUsage _modelUsage = ApplicationModelUsage.None;
///
/// Configures the singleton instance of to use the specified backend implementation.
///
///
public static void SetInstance (IApplication? app) { _instance = app; }
// Private static readonly Lazy instance of Application
private static IApplication? _instance;
///
/// Gets the currently configured backend implementation of gateway methods.
///
public static IApplication Instance
{
get
{
// If an instance already exists, return it without fence checking
// This allows for cleanup/reset operations
if (_instance is { })
{
return _instance;
}
// Only check the fence when creating a new instance
if (_modelUsage == ApplicationModelUsage.InstanceBased)
{
throw new InvalidOperationException (
"Cannot use legacy static Application model (Application.Init/ApplicationImpl.Instance) after using modern instance-based model (Application.Create). " +
"Use only one model per process.");
}
_modelUsage = ApplicationModelUsage.LegacyStatic;
return _instance = new ApplicationImpl ();
}
}
///
/// INTERNAL: Marks that the instance-based model has been used. Called by Application.Create().
///
internal static void MarkInstanceBasedModelUsed ()
{
if (_modelUsage == ApplicationModelUsage.LegacyStatic)
{
throw new InvalidOperationException (
"Cannot use modern instance-based model (Application.Create) after using legacy static Application model (Application.Init/ApplicationImpl.Instance). " +
"Use only one model per process.");
}
_modelUsage = ApplicationModelUsage.InstanceBased;
}
///
/// INTERNAL: Resets the model usage tracking. Only for testing purposes.
///
internal static void ResetModelUsageTracking ()
{
_modelUsage = ApplicationModelUsage.None;
_instance = null;
}
#endregion Singleton
///
/// Defines the different application usage models.
///
private enum ApplicationModelUsage
{
/// No model has been used yet.
None,
/// Legacy static model (Application.Init/ApplicationImpl.Instance).
LegacyStatic,
/// Modern instance-based model (Application.Create).
InstanceBased
}
private string? _driverName;
#region Input
private IMouse? _mouse;
///
/// Handles mouse event state and processing.
///
public IMouse Mouse
{
get
{
_mouse ??= new MouseImpl { App = this };
return _mouse;
}
set => _mouse = value ?? throw new ArgumentNullException (nameof (value));
}
private IKeyboard? _keyboard;
///
/// Handles keyboard input and key bindings at the Application level
///
public IKeyboard Keyboard
{
get
{
_keyboard ??= new KeyboardImpl { App = this };
return _keyboard;
}
set => _keyboard = value ?? throw new ArgumentNullException (nameof (value));
}
#endregion Input
#region View Management
private ApplicationPopover? _popover;
///
public ApplicationPopover? Popover
{
get
{
_popover ??= new () { App = this };
return _popover;
}
set => _popover = value;
}
private ApplicationNavigation? _navigation;
///
public ApplicationNavigation? Navigation
{
get
{
_navigation ??= new () { App = this };
return _navigation;
}
set => _navigation = value ?? throw new ArgumentNullException (nameof (value));
}
private Toplevel? _topRunnable;
///
public Toplevel? TopRunnable
{
get => _topRunnable;
set
{
_topRunnable = value;
if (_topRunnable is { })
{
_topRunnable.App = this;
}
}
}
// BUGBUG: Technically, this is not the full lst of sessions. There be dragons here, e.g. see how Toplevel.Id is used. What
///
public ConcurrentStack SessionStack { get; } = new ();
///
public Toplevel? CachedSessionTokenToplevel { get; set; }
///
public ConcurrentStack? RunnableSessionStack { get; } = new ();
///
public IRunnable? FrameworkOwnedRunnable { get; set; }
#endregion View Management
///
public new string ToString () => Driver?.ToString () ?? string.Empty;
}