Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preps for and partially Fixes #2491 - Refactors Toplevel and Application Navigation #3634

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
f328f0c
Initial commit.
tig Jul 21, 2024
14d8bf5
Merge branch 'v2_develop' into v2_2491-Toplevel-Redesign
tig Jul 22, 2024
44ce74a
Refactored Application into smaller files.
tig Jul 22, 2024
2e0a9a7
Fixed nullable warnings
tig Jul 22, 2024
0b8e434
Applicaation Toplevel handling moved to separate file
tig Jul 22, 2024
2939108
Added Toplevel to spelling dict
tig Jul 22, 2024
250050c
Toplevel cleanup
tig Jul 22, 2024
4701080
Toplevel.cs organization
tig Jul 23, 2024
d44e8d3
More Toplevel.cs organization & docs
tig Jul 23, 2024
d813b1f
Fixed dumb enum cast in KeyBinding code
tig Jul 23, 2024
fe5cbe4
More Toplevel.cs organization & docs
tig Jul 23, 2024
f8e8aff
More Toplevel.cs organization & docs
tig Jul 23, 2024
feaf5c0
WIP (Very Broken) try to move keybindings out of Toplevel to app
tig Jul 24, 2024
c03dd32
Moved Toplevel keybindings out of Toplevel to Application.
tig Jul 24, 2024
22dcbc1
removed un needed key handling code from TextView
tig Jul 24, 2024
c088f2e
removed unneeded key handling code from Toplevel
tig Jul 24, 2024
4a56b84
removed unneeded AlternateBack/FormardKey code from Toplevel
tig Jul 24, 2024
0c56dfe
Moved view navigation out of Toplevel and into Application (via ViewN…
tig Jul 24, 2024
73a9dc3
Fixed nullable warnings 2
tig Jul 24, 2024
04dbe68
Fixed nullable warnings 3
tig Jul 24, 2024
3b35189
Fixed nullable warnings 4
tig Jul 24, 2024
689c0cd
Fixed nullable warnings 5
tig Jul 24, 2024
ff47aa2
Fixed nullable warnings 6
tig Jul 24, 2024
022050d
Fixed nullable warnings 7
tig Jul 24, 2024
f37ec5e
Moved Overlapped stuff to ApplicationOverlap static class. Fixed null…
tig Jul 24, 2024
cb3e806
Moved Overlapped stuff to ApplicationOverlap static class. Fixed null…
tig Jul 24, 2024
cbecae5
Moved Overlapped stuff to ApplicationOverlap static class. Fixed null…
tig Jul 24, 2024
331d972
nullable enable TopLevel
tig Jul 24, 2024
ca4d10b
WIP: Modify Focus logic to properly deal with ViewArrangement.Overlap…
tig Jul 25, 2024
3a40851
WIP: More - Modify Focus logic to properly deal with ViewArrangement.…
tig Jul 25, 2024
d874f56
Reorganized View source files to get my head straight
tig Jul 25, 2024
9b89fe6
Code cleanup and API docs - getting better understanding of navigatio…
tig Jul 25, 2024
ccec0ee
Documenting focus code
tig Jul 25, 2024
15e6b4e
Documenting focus code
tig Jul 25, 2024
a935ef8
Merge remote-tracking branch 'refs/remotes/origin/v2_2491-Toplevel-Re…
tig Jul 25, 2024
78f527e
Fixed post merge errors.
tig Jul 25, 2024
5b1ac0e
Merge branch 'v2_2491-Toplevel-Redesign' into v2_2491-Arrangement-Ove…
tig Jul 25, 2024
4b785c8
Prepping to reduce duplicated code in FocusNext/Prev
tig Jul 25, 2024
66f83ad
Reduced duplicated code by leverating Navigationdirection enum
tig Jul 25, 2024
911f2c6
Rewrote FocusNearestView to be understandable
tig Jul 26, 2024
aa9d42f
Renamed FocusNearestView to be understandable
tig Jul 26, 2024
3f19a6f
Added low-level Focus tests
tig Jul 26, 2024
5e28ba1
Added low-level Focus tests
tig Jul 26, 2024
ee3c48a
Progress
tig Jul 27, 2024
207266b
Fixed unit test
tig Jul 27, 2024
e41b24f
Removed coupling between TabStop and CanFocus
tig Jul 27, 2024
5d1467d
Converted TabStop to auto property
tig Jul 27, 2024
4ede064
TabStop -> now of type TabStop.
tig Jul 27, 2024
fa4b9dc
Added BUGBUGs and TODOs re TabIndex
tig Jul 27, 2024
d507426
Changed semantics of TabIndexes, TabIndex, and CanFocus relative to T…
tig Jul 27, 2024
d407683
Made View.Navigation nullable enable.
tig Jul 27, 2024
65592b4
WIP: Refining TabStop and GroupStop
tig Jul 28, 2024
f2eb9ce
WIP: More refining
tig Jul 29, 2024
cf1435a
WIP: Fixed stuff. Broke stuff. Making progress.
tig Jul 29, 2024
37f3490
WIP: More. Trying to fix TableView regression
tig Jul 31, 2024
6c889c9
Fixed FileDialog test
tig Jul 31, 2024
3d00102
Added BUGBUGs
tig Jul 31, 2024
47e1c87
Added AllViews_AtLeastOneNavKey_Leaves
tig Jul 31, 2024
38e517c
Added AllViews_AtLeastOneNavKey_Leaves
tig Jul 31, 2024
6f4c7be
Cleaned up some unit tests
tig Jul 31, 2024
469b357
Revamped Scenario
tig Jul 31, 2024
2f71fc0
Code cleanup
tig Aug 2, 2024
25018b7
Changed Shortcuts scenario to use Ctrl-F4 to exit instead of Ctrl-Z a…
tig Aug 2, 2024
9a6e09f
Merge branch 'v2_develop' into v2_2491-Refactor-TopLevel-Application-…
tig Aug 2, 2024
bb16e69
merged with v2_develop
tig Aug 2, 2024
79e50b4
Nuked AlternateFwd/BkKeys.
tig Aug 2, 2024
8da833a
Added Next/PrevTabKeys.
tig Aug 2, 2024
8cbbcd8
backported adornmentseditor
tig Aug 2, 2024
eaa5b01
backported ViewExperiments
tig Aug 2, 2024
09c1003
Enabled ViewArrangement.Overlapped zorder hack
tig Aug 2, 2024
ac9d011
Updated Window.Arrangement
tig Aug 2, 2024
4985da4
Hacked in Application.Navigation.Set/GetFocused
tig Aug 3, 2024
288e3fb
Fixed hack
tig Aug 3, 2024
8772645
Fixed KeyBinding issue @bdisp found
tig Aug 3, 2024
bbe8fbf
Merge branch 'v2_develop' into v2_2491-Refactor-TopLevel-Application-…
tig Aug 3, 2024
5f567be
Code cleanup & doc fix
tig Aug 3, 2024
18997e0
Merge branch 'v2_2491-Refactor-TopLevel-Application-And-Focus' of tig…
tig Aug 3, 2024
3072d8b
Back ported navigation.md
tig Aug 3, 2024
8c14ab3
Doc updates
tig Aug 3, 2024
fc05d06
Merge branch 'v2_develop' into v2_2491-Refactor-TopLevel-Application-…
tig Aug 4, 2024
f70e8de
Doc updates
tig Aug 4, 2024
bc1483e
Merge branch 'v2_2491-Refactor-TopLevel-Application-And-Focus' of tig…
tig Aug 4, 2024
18ac4ab
simplified test
tig Aug 4, 2024
9526b4e
Found and fixed another Shortcut bug
tig Aug 4, 2024
e86a2fc
Simplfiied app scope key setters
tig Aug 5, 2024
331ab51
Updatd keyboard.md
tig Aug 5, 2024
9865f72
Merged with v2_develop.
tig Aug 5, 2024
af9887b
Fixed merged issue
tig Aug 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CommunityToolkitExample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ private static void Main (string [] args)
Services = ConfigureServices ();
Application.Init ();
Application.Run (Services.GetRequiredService<LoginView> ());
Application.Top.Dispose();
Application.Top?.Dispose();
Application.Shutdown ();
}

Expand Down
4 changes: 2 additions & 2 deletions SelfContained/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ private static void Main (string [] args)

#region The code in this region is not intended for use in a self-contained single-file. It's just here to make sure there is no functionality break with localization in Terminal.Gui using single-file

if (Equals (Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures.Count == 0)
if (Equals (Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures?.Count == 0)
{
// Only happens if the project has <InvariantGlobalization>true</InvariantGlobalization>
Debug.Assert (Application.SupportedCultures.Count == 0);
}
else
{
Debug.Assert (Application.SupportedCultures.Count > 0);
Debug.Assert (Application.SupportedCultures?.Count > 0);
Debug.Assert (Equals (CultureInfo.CurrentCulture, Thread.CurrentThread.CurrentUICulture));
}

Expand Down
29 changes: 29 additions & 0 deletions Terminal.Gui/Application/Application.Driver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#nullable enable
namespace Terminal.Gui;

public static partial class Application // Driver abstractions
{
internal static bool _forceFakeConsole;

/// <summary>Gets the <see cref="ConsoleDriver"/> that has been selected. See also <see cref="ForceDriver"/>.</summary>
public static ConsoleDriver? Driver { get; internal set; }

/// <summary>
/// Gets or sets whether <see cref="Application.Driver"/> will be forced to output only the 16 colors defined in
/// <see cref="ColorName"/>. The default is <see langword="false"/>, meaning 24-bit (TrueColor) colors will be output
/// as long as the selected <see cref="ConsoleDriver"/> supports TrueColor.
/// </summary>
[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
public static bool Force16Colors { get; set; }

/// <summary>
/// Forces the use of the specified driver (one of "fake", "ansi", "curses", "net", or "windows"). If not
/// specified, the driver is selected based on the platform.
/// </summary>
/// <remarks>
/// Note, <see cref="Application.Init(ConsoleDriver, string)"/> will override this configuration setting if called
/// with either `driver` or `driverName` specified.
/// </remarks>
[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
public static string ForceDriver { get; set; } = string.Empty;
}
214 changes: 214 additions & 0 deletions Terminal.Gui/Application/Application.Initialization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
#nullable enable
using System.Diagnostics.CodeAnalysis;
using System.Reflection;

namespace Terminal.Gui;

public static partial class Application // Initialization (Init/Shutdown)
{
/// <summary>Initializes a new instance of <see cref="Terminal.Gui"/> Application.</summary>
/// <para>Call this method once per instance (or after <see cref="Shutdown"/> has been called).</para>
/// <para>
/// This function loads the right <see cref="ConsoleDriver"/> for the platform, Creates a <see cref="Toplevel"/>. and
/// assigns it to <see cref="Top"/>
/// </para>
/// <para>
/// <see cref="Shutdown"/> must be called when the application is closing (typically after
/// <see cref="Run{T}"/> has returned) to ensure resources are cleaned up and
/// terminal settings
/// restored.
/// </para>
/// <para>
/// The <see cref="Run{T}"/> function combines
/// <see cref="Init(Terminal.Gui.ConsoleDriver,string)"/> and <see cref="Run(Toplevel, Func{Exception, bool})"/>
/// into a single
/// call. An application cam use <see cref="Run{T}"/> without explicitly calling
/// <see cref="Init(Terminal.Gui.ConsoleDriver,string)"/>.
/// </para>
/// <param name="driver">
/// The <see cref="ConsoleDriver"/> to use. If neither <paramref name="driver"/> or
/// <paramref name="driverName"/> are specified the default driver for the platform will be used.
/// </param>
/// <param name="driverName">
/// The short name (e.g. "net", "windows", "ansi", "fake", or "curses") of the
/// <see cref="ConsoleDriver"/> to use. If neither <paramref name="driver"/> or <paramref name="driverName"/> are
/// specified the default driver for the platform will be used.
/// </param>
[RequiresUnreferencedCode ("AOT")]
[RequiresDynamicCode ("AOT")]
public static void Init (ConsoleDriver? driver = null, string? driverName = null) { InternalInit (driver, driverName); }

internal static bool IsInitialized { get; set; }
internal static int MainThreadId { get; set; } = -1;

// INTERNAL function for initializing an app with a Toplevel factory object, driver, and mainloop.
//
// Called from:
//
// Init() - When the user wants to use the default Toplevel. calledViaRunT will be false, causing all state to be reset.
// Run<T>() - When the user wants to use a custom Toplevel. calledViaRunT will be true, enabling Run<T>() to be called without calling Init first.
// Unit Tests - To initialize the app with a custom Toplevel, using the FakeDriver. calledViaRunT will be false, causing all state to be reset.
//
// calledViaRunT: If false (default) all state will be reset. If true the state will not be reset.
[RequiresUnreferencedCode ("AOT")]
[RequiresDynamicCode ("AOT")]
internal static void InternalInit (
ConsoleDriver? driver = null,
string? driverName = null,
bool calledViaRunT = false
)
{
if (IsInitialized && driver is null)
{
return;
}

if (IsInitialized)
{
throw new InvalidOperationException ("Init has already been called and must be bracketed by Shutdown.");
}

if (!calledViaRunT)
{
// Reset all class variables (Application is a singleton).
ResetState ();
}

Navigation = new ();

// For UnitTests
if (driver is { })
{
Driver = driver;
}

// Start the process of configuration management.
// Note that we end up calling LoadConfigurationFromAllSources
// multiple times. We need to do this because some settings are only
// valid after a Driver is loaded. In this case we need just
// `Settings` so we can determine which driver to use.
// Don't reset, so we can inherit the theme from the previous run.
Load ();
Apply ();

AddApplicationKeyBindings ();

// Ignore Configuration for ForceDriver if driverName is specified
if (!string.IsNullOrEmpty (driverName))
{
ForceDriver = driverName;
}

if (Driver is null)
{
PlatformID p = Environment.OSVersion.Platform;

if (string.IsNullOrEmpty (ForceDriver))
{
if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows)
{
Driver = new WindowsDriver ();
}
else
{
Driver = new CursesDriver ();
}
}
else
{
List<Type?> drivers = GetDriverTypes ();
Type? driverType = drivers.FirstOrDefault (t => t!.Name.Equals (ForceDriver, StringComparison.InvariantCultureIgnoreCase));

if (driverType is { })
{
Driver = (ConsoleDriver)Activator.CreateInstance (driverType)!;
}
else
{
throw new ArgumentException (
$"Invalid driver name: {ForceDriver}. Valid names are {string.Join (", ", drivers.Select (t => t!.Name))}"
);
}
}
}

try
{
MainLoop = Driver!.Init ();
}
catch (InvalidOperationException ex)
{
// This is a case where the driver is unable to initialize the console.
// This can happen if the console is already in use by another process or
// if running in unit tests.
// In this case, we want to throw a more specific exception.
throw new InvalidOperationException (
"Unable to initialize the console. This can happen if the console is already in use by another process or in unit tests.",
ex
);
}

Driver.SizeChanged += Driver_SizeChanged;
Driver.KeyDown += Driver_KeyDown;
Driver.KeyUp += Driver_KeyUp;
Driver.MouseEvent += Driver_MouseEvent;

SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext ());

SupportedCultures = GetSupportedCultures ();
MainThreadId = Thread.CurrentThread.ManagedThreadId;
bool init = IsInitialized = true;
InitializedChanged?.Invoke (null, new (init));
}

private static void Driver_SizeChanged (object? sender, SizeChangedEventArgs e) { OnSizeChanging (e); }
private static void Driver_KeyDown (object? sender, Key e) { OnKeyDown (e); }
private static void Driver_KeyUp (object? sender, Key e) { OnKeyUp (e); }
private static void Driver_MouseEvent (object? sender, MouseEvent e) { OnMouseEvent (e); }

/// <summary>Gets of list of <see cref="ConsoleDriver"/> types that are available.</summary>
/// <returns></returns>
[RequiresUnreferencedCode ("AOT")]
public static List<Type?> GetDriverTypes ()
{
// use reflection to get the list of drivers
List<Type?> driverTypes = new ();

foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies ())
{
foreach (Type? type in asm.GetTypes ())
{
if (type.IsSubclassOf (typeof (ConsoleDriver)) && !type.IsAbstract)
{
driverTypes.Add (type);
}
}
}

return driverTypes;
}

/// <summary>Shutdown an application initialized with <see cref="Init"/>.</summary>
/// <remarks>
/// Shutdown must be called for every call to <see cref="Init"/> or
/// <see cref="Application.Run(Toplevel, Func{Exception, bool})"/> to ensure all resources are cleaned
/// up (Disposed)
/// and terminal settings are restored.
/// </remarks>
public static void Shutdown ()
{
// TODO: Throw an exception if Init hasn't been called.
ResetState ();
PrintJsonErrors ();
bool init = IsInitialized;
InitializedChanged?.Invoke (null, new (in init));
}

/// <summary>
/// This event is raised after the <see cref="Init"/> and <see cref="Shutdown"/> methods have been called.
/// </summary>
/// <remarks>
/// Intended to support unit tests that need to know when the application has been initialized.
/// </remarks>
public static event EventHandler<EventArgs<bool>>? InitializedChanged;
}
Loading
Loading