* The current, stable, release of Terminal.Gui v1 is [](https://www.nuget.org/packages/Terminal.Gui).
* The current, stable, release of Terminal.Gui v1 is [](https://www.nuget.org/packages/Terminal.Gui).
* The current `prealpha` release of Terminal.Gui v2 can be found on [Nuget](https://www.nuget.org/packages/Terminal.Gui).
* The current `prealpha` release of Terminal.Gui v2 can be found on [Nuget](https://www.nuget.org/packages/Terminal.Gui).
-* Developers starting new TUI projects are encouraged to target `v2`. The API is signifcantly changed, and significantly improved. There will be breaking changes in the API before Beta, but the core API is stable.
+* Developers starting new TUI projects are encouraged to target `v2`. The API is significantly changed, and significantly improved. There will be breaking changes in the API before Beta, but the core API is stable.
* `v1` is in maintenance mode and we will only accept PRs for issues impacting existing functionality.
* `v1` is in maintenance mode and we will only accept PRs for issues impacting existing functionality.
**Terminal.Gui**: A toolkit for building rich console apps for .NET, .NET Core, and Mono that works on Windows, the Mac, and Linux/Unix.
**Terminal.Gui**: A toolkit for building rich console apps for .NET, .NET Core, and Mono that works on Windows, the Mac, and Linux/Unix.
-The above documentation matches the most recent Nuget release from the `v2_develop` branch. Get the [v1 documentation here](This is the v2 API documentation. For v1 go here: https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.html)
+The above documentation matches the most recent Nuget release from the `v2_develop` branch. Get the [v1 documentation here](https://gui-cs.github.io/Terminal.Gui/api/Terminal.Gui.html).
-See the [`Terminal.Gui/`README](https://github.com/gui-cs/Terminal.Gui/tree/master/Terminal.Gui) for an overview of how the library is structured.
+See the [`Terminal.Gui/`README](https://github.com/gui-cs/Terminal.Gui/tree/master/Terminal.Gui) for an overview of how the library is structured.
@@ -8,49 +8,7 @@ public static partial class Application // Toplevel handling
/// <summary>Holds the stack of TopLevel views.</summary>
/// <summary>Holds the stack of TopLevel views.</summary>
internal static Stack<Toplevel> TopLevels { get; } = new ();
internal static Stack<Toplevel> TopLevels { get; } = new ();
- /// <summary>The <see cref="Toplevel"/> object used for the application on startup (<seealso cref="Top"/>)</summary>
+ /// <summary>The <see cref="Toplevel"/> that is currently active.</summary>
/// <value>The top.</value>
/// <value>The top.</value>
public static Toplevel? Top { get; internal set; }
public static Toplevel? Top { get; internal set; }
-
- // TODO: Determine why this can't just return _topLevels.Peek()?
- /// <summary>
- /// The current <see cref="Toplevel"/> object. This is updated in <see cref="Application.Begin"/> enters and leaves to
- /// point to the current
- /// <see cref="Toplevel"/> .
- /// </summary>
- /// <remarks>
- /// This will only be distinct from <see cref="Application.Top"/> in scenarios where <see cref="Toplevel.IsOverlappedContainer"/> is <see langword="true"/>.
- /// </remarks>
- /// <value>The current.</value>
- public static Toplevel? Current { get; internal set; }
-
- /// <summary>
- /// If <paramref name="topLevel"/> is not already Current and visible, finds the last Modal Toplevel in the stack and makes it Current.
- // QUESTION: AdvanceFocus returns false AND sets Focused to null if no view was found to advance to. Should't we only set focusProcessed if it returned true?
- focusSet = true;
-
- if (Application.Current.SuperView?.Focused != Application.Current)
- {
- return;
- }
-
- // Either AdvanceFocus didn't set focus or the view it set focus to is not current...
- // continue...
- }
-
- currentIndex++;
-
- if (foundCurrentView && !focusSet && currentIndex == viewCount)
- {
- // One of the views is Current AND AdvanceFocus didn't set focus AND we are at the last view in the list...
- // This means we should wrap around to the first view in the list.
- indexes.First ().SetFocus ();
- }
- }
- }
-
- /// <summary>
- /// Move to the next Overlapped child from the <see cref="OverlappedTop"/> and set it as the <see cref="Application.Top"/> if
- /// it is not already.
- /// </summary>
- /// <param name="top"></param>
- /// <returns></returns>
- public static bool MoveToOverlappedChild (Toplevel? top)
- {
- ArgumentNullException.ThrowIfNull (top);
-
- if (top.Visible && OverlappedTop is { } && Application.Current?.Modal == false)
- {
- lock (Application.TopLevels)
- {
- Application.TopLevels.MoveTo (top, 0, new ToplevelEqualityComparer ());
- Application.Current = top;
- }
-
- return true;
- }
-
- return false;
- }
-
- /// <summary>Move to the next Overlapped child from the <see cref="OverlappedTop"/>.</summary>
- public static void OverlappedMoveNext ()
- {
- if (OverlappedTop is { } && !Application.Current!.Modal)
- {
- lock (Application.TopLevels)
- {
- Application.TopLevels.MoveNext ();
- var isOverlapped = false;
-
- while (Application.TopLevels.Peek () == OverlappedTop || !Application.TopLevels.Peek ().Visible)
- {
- if (!isOverlapped && Application.TopLevels.Peek () == OverlappedTop)
- {
- isOverlapped = true;
- }
- else if (isOverlapped && Application.TopLevels.Peek () == OverlappedTop)
/// The items will be arranged from end (right/bottom) to start (left/top).
/// The items will be arranged from end (right/bottom) to start (left/top).
/// </summary>
/// </summary>
- /// <remarks>
- /// Not implemented.
- /// </remarks>
EndToStart = 1,
EndToStart = 1,
/// <summary>
/// <summary>
/// At least one space will be added between items. Useful for justifying text where at least one space is needed.
/// At least one space will be added between items. Useful for justifying text where at least one space is needed.
/// </summary>
/// </summary>
/// <remarks>
/// <remarks>
- /// <para>
- /// If the total size of the items is greater than the container size, the space between items will be ignored
- /// starting from the end.
- /// </para>
+ /// If the total size of the items is greater than the container size, the space between items will be ignored
+ /// starting from the end.
/// </remarks>
/// </remarks>
AddSpaceBetweenItems = 2,
AddSpaceBetweenItems = 2,
/// <summary>
/// <summary>
- /// When aligning via <see cref="Alignment.Start"/> or <see cref="Alignment.End"/>, the item opposite to the alignment (the first or last item) will be ignored.
+ /// When aligning via <see cref="Alignment.Start"/> or <see cref="Alignment.End"/>, the item opposite to the alignment
+ /// (the first or last item) will be ignored.
/// </summary>
/// </summary>
/// <remarks>
/// <remarks>
- /// <para>
- /// If the container is smaller than the total size of the items, the end items will be clipped (their locations
- /// will be greater than the container size).
- /// </para>
+ /// If the container is smaller than the total size of the items, the end items will be clipped (their locations
@@ -97,49 +97,49 @@ public readonly partial record struct Color
)
)
{
{
return (formatString, formatProvider) switch
return (formatString, formatProvider) switch
- {
- // Null or empty string and null formatProvider - Revert to 'g' case behavior
- (null or { Length: 0 }, null) => ToString (),
-
- // Null or empty string and formatProvider is an ICustomColorFormatter - Output according to the given ICustomColorFormatted, with R, G, B, and A as typed arguments
- // Null or empty string and formatProvider is otherwise non-null but not the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
- (null or { Length: 0 }, { }) when !Equals (formatProvider, CultureInfo.InvariantCulture) =>
- // Null or empty string and formatProvider is the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
- (null or { Length: 0 }, { }) when Equals (formatProvider, CultureInfo.InvariantCulture) =>
- $"#{R:X2}{G:X2}{B:X2}",
-
- // Non-null string and non-null formatProvider - let formatProvider handle it and give it R, G, B, and A
- // g format string and null formatProvider - Output as 24-bit hex according to invariant culture rules from R, G, and B
- (['g'], null) => ToString (),
-
- // G format string and null formatProvider - Output as 32-bit hex according to invariant culture rules from Argb
- (['G'], null) => $"#{A:X2}{R:X2}{G:X2}{B:X2}",
-
- // d format string and null formatProvider - Output as 24-bit decimal rgb(r,g,b) according to invariant culture rules from R, G, and B
- (['d'], null) => $"rgb({R:D},{G:D},{B:D})",
-
- // D format string and null formatProvider - Output as 32-bit decimal rgba(r,g,b,a) according to invariant culture rules from R, G, B, and A. Non-standard: a is a decimal byte value.
- // All other cases (formatString is not null here) - Delegate to formatProvider, first, and otherwise to invariant culture, and try to format the provided string from the channels
- ({ }, _) => string.Format (
- formatProvider ?? CultureInfo.InvariantCulture,
- CompositeFormat.Parse (formatString),
- R,
- G,
- B,
- A
- ),
- _ => throw new InvalidOperationException (
- $"Unable to create string from Color with value {Argb}, using format string {formatString}"
- )
- }
+ {
+ // Null or empty string and null formatProvider - Revert to 'g' case behavior
+ (null or { Length: 0 }, null) => ToString (),
+
+ // Null or empty string and formatProvider is an ICustomColorFormatter - Output according to the given ICustomColorFormatted, with R, G, B, and A as typed arguments
+ // Null or empty string and formatProvider is otherwise non-null but not the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
+ (null or { Length: 0 }, { }) when !Equals (formatProvider, CultureInfo.InvariantCulture) =>
+ // Null or empty string and formatProvider is the invariant culture - Output according to string.Format with the given IFormatProvider and R, G, B, and A as boxed arguments, with string.Empty as the format string
+ (null or { Length: 0 }, { }) when Equals (formatProvider, CultureInfo.InvariantCulture) =>
+ $"#{R:X2}{G:X2}{B:X2}",
+
+ // Non-null string and non-null formatProvider - let formatProvider handle it and give it R, G, B, and A
+ // g format string and null formatProvider - Output as 24-bit hex according to invariant culture rules from R, G, and B
+ (['g'], null) => ToString (),
+
+ // G format string and null formatProvider - Output as 32-bit hex according to invariant culture rules from Argb
+ (['G'], null) => $"#{A:X2}{R:X2}{G:X2}{B:X2}",
+
+ // d format string and null formatProvider - Output as 24-bit decimal rgb(r,g,b) according to invariant culture rules from R, G, and B
+ (['d'], null) => $"rgb({R:D},{G:D},{B:D})",
+
+ // D format string and null formatProvider - Output as 32-bit decimal rgba(r,g,b,a) according to invariant culture rules from R, G, B, and A. Non-standard: a is a decimal byte value.
+ // All other cases (formatString is not null here) - Delegate to formatProvider, first, and otherwise to invariant culture, and try to format the provided string from the channels
+ ({ }, _) => string.Format (
+ formatProvider ?? CultureInfo.InvariantCulture,
+ CompositeFormat.Parse (formatString),
+ R,
+ G,
+ B,
+ A
+ ),
+ _ => throw new InvalidOperationException (
+ $"Unable to create string from Color with value {Argb}, using format string {formatString}"
+ )
+ }
?? throw new InvalidOperationException (
?? throw new InvalidOperationException (
$"Unable to create string from Color with value {Argb}, using format string {formatString}"
$"Unable to create string from Color with value {Argb}, using format string {formatString}"
);
);
@@ -205,7 +205,7 @@ public readonly partial record struct Color
/// <summary>Converts the provided <see langword="string"/> to a new <see cref="Color"/> value.</summary>
/// <summary>Converts the provided <see langword="string"/> to a new <see cref="Color"/> value.</summary>
/// <param name="text">
/// <param name="text">
/// The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
/// The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#ARGB", "#AARRGGBB", "rgb(r,g,b)",
- /// "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="Gui.ColorName"/> string values.
+ /// "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="ColorName16"/> string values.
/// </param>
/// </param>
/// <param name="formatProvider">
/// <param name="formatProvider">
/// If specified and not <see langword="null"/>, will be passed to
/// If specified and not <see langword="null"/>, will be passed to
@@ -246,7 +246,7 @@ public readonly partial record struct Color
/// </summary>
/// </summary>
/// <param name="text">
/// <param name="text">
/// The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#RGBA", "#AARRGGBB", "rgb(r,g,b)",
/// The text to analyze. Formats supported are "#RGB", "#RRGGBB", "#RGBA", "#AARRGGBB", "rgb(r,g,b)",
- /// "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="Gui.ColorName"/> string values.
+ /// "rgb(r,g,b,a)", "rgba(r,g,b)", "rgba(r,g,b,a)", and any of the <see cref="ColorName16"/> string values.
/// </param>
/// </param>
/// <param name="formatProvider">
/// <param name="formatProvider">
/// Optional <see cref="IFormatProvider"/> to provide parsing services for the input text.
/// Optional <see cref="IFormatProvider"/> to provide parsing services for the input text.
@@ -265,95 +265,95 @@ public readonly partial record struct Color
public static Color Parse (ReadOnlySpan<char> text, IFormatProvider? formatProvider = null)
public static Color Parse (ReadOnlySpan<char> text, IFormatProvider? formatProvider = null)
{
{
return text switch
return text switch
- {
- // Null string or empty span provided
- { IsEmpty: true } when formatProvider is null => throw new ColorParseException (
- in text,
- "The text provided was null or empty.",
- in text
- ),
-
- // A valid ICustomColorFormatter was specified and the text wasn't null or empty
- { IsEmpty: false } when formatProvider is ICustomColorFormatter f => f.Parse (text),
-
- // Input string is only whitespace
- { Length: > 0 } when text.IsWhiteSpace () => throw new ColorParseException (
- in text,
- "The text provided consisted of only whitespace characters.",
- in text
- ),
-
- // Any string too short to possibly be any supported format.
- { Length: > 0 and < 3 } => throw new ColorParseException (
- in text,
- "Text was too short to be any possible supported format.",
- in text
- ),
-
- // The various hexadecimal cases
- ['#', ..] hexString => hexString switch
- {
- // #RGB
- ['#', var rChar, var gChar, var bChar] chars when chars [1..]
- public static bool IsColorClosestToNamedColor (in Color color, in ColorName namedColor) { return color.IsClosestToNamedColor (in namedColor); }
+ public static bool IsColorClosestToNamedColor16 (in Color color, in ColorName16 namedColor) { return color.IsClosestToNamedColor16 (in namedColor); }
/// <summary>Gets the "closest" named color to this <see cref="Color"/> value.</summary>
/// <summary>Gets the "closest" named color to this <see cref="Color"/> value.</summary>
/// <param name="inputColor"></param>
/// <param name="inputColor"></param>
/// <remarks>
/// <remarks>
/// Distance is defined here as the Euclidean distance between each color interpreted as a <see cref="Vector3"/>.
/// Distance is defined here as the Euclidean distance between each color interpreted as a <see cref="Vector3"/>.
- /// <para/>
- /// The order of the values in the passed Vector3 must be
+ /// See the View Layout Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+ /// </para>
+ /// <para>
/// Negative sizes are not supported.
/// Negative sizes are not supported.
/// </para>
/// </para>
/// <para>
/// <para>
@@ -55,6 +58,9 @@ public partial class View
/// </summary>
/// </summary>
/// <remarks>a>
/// <remarks>a>
/// <para>
/// <para>
+ /// See the View Layout Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
+ /// </para>
+ /// <para>
/// If the content size was not explicitly set by <see cref="SetContentSize"/>, and the View has no visible subviews, <see cref="GetContentSize ()"/> will return the
/// If the content size was not explicitly set by <see cref="SetContentSize"/>, and the View has no visible subviews, <see cref="GetContentSize ()"/> will return the
/// size of
/// size of
/// <see cref="Viewport"/>.
/// <see cref="Viewport"/>.
@@ -85,6 +91,9 @@ public partial class View
/// size or not.
/// size or not.
/// </summary>
/// </summary>
/// <remarks>
/// <remarks>
+ /// <para>
+ /// See the View Layout Deep Dive for more information: <see href="https://gui-cs.github.io/Terminal.GuiV2Docs/docs/layout.html"/>
@@ -20,10 +20,9 @@ public enum ViewDiagnosticFlags : uint
Padding = 0b_0000_0010,
Padding = 0b_0000_0010,
/// <summary>
/// <summary>
- /// When enabled, <see cref="Adornment.OnMouseEnter(Gui.MouseEvent)"/> and <see cref="Adornment.OnMouseLeave(Gui.MouseEvent)"/>
- /// will invert the foreground and background colors.
+ /// When enabled the View's colors will be darker when the mouse is hovering over the View (See <see cref="View.MouseEnter"/> and <see cref="View.MouseLeave"/>.
- /// The view that was found at the <paramref name="location"/> coordinate.
- /// <see langword="null"/> if no view was found.
- /// </returns>
-
- // CONCURRENCY: This method is not thread-safe. Undefined behavior and likely program crashes are exposed by unsynchronized access to InternalSubviews.
- internal static View? FindDeepestView (View? start, in Point location)
- {
- Point currentLocation = location;
-
- while (start is { Visible: true } && start.Contains (currentLocation))
- {
- Adornment? found = null;
-
- if (start.Margin.Contains (currentLocation))
- {
- found = start.Margin;
- }
- else if (start.Border.Contains (currentLocation))
- {
- found = start.Border;
- }
- else if (start.Padding.Contains (currentLocation))
- {
- found = start.Padding;
- }
-
- Point viewportOffset = start.GetViewportOffsetFromFrame ();
@@ -681,32 +755,48 @@ public partial class View // Focus and cross-view navigation management (TabStop
#region Tab/Focus Handling
#region Tab/Focus Handling
/// <summary>
/// <summary>
- /// Gets TabIndexes that are scoped to the specified behavior and direction. If behavior is null, all TabIndexes are
- /// returned.
+ /// Gets the subviews and Adornments of this view that are scoped to the specified behavior and direction. If behavior is null, all focusable subviews and
+/// // Shutdown - Calling Application.Shutdown is required.
+/// Application.Shutdown ();
+/// }
/// }
/// }
-/// </code>
+/// </code>
/// </example>
/// </example>
public class Scenario : IDisposable
public class Scenario : IDisposable
{
{
@@ -93,7 +101,10 @@ public class Scenario : IDisposable
/// <returns></returns>
/// <returns></returns>
public string GetName () { return ScenarioMetadata.GetName (GetType ()); }
public string GetName () { return ScenarioMetadata.GetName (GetType ()); }
- /// <summary>Helper to get the <see cref="Application.QuitKey"/> and the <see cref="Scenario"/> Name (defined in <see cref="ScenarioMetadata"/>)</summary>
+ /// <summary>
+ /// Helper to get the <see cref="Application.QuitKey"/> and the <see cref="Scenario"/> Name (defined in
+ /// <see cref="ScenarioMetadata"/>)
+ /// </summary>
/// <returns></returns>
/// <returns></returns>
public string GetQuitKeyAndName () { return $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"; }
public string GetQuitKeyAndName () { return $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"; }
@@ -125,18 +136,7 @@ public class Scenario : IDisposable
/// Called by UI Catalog to run the <see cref="Scenario"/>. This is the main entry point for the <see cref="Scenario"/>
/// Called by UI Catalog to run the <see cref="Scenario"/>. This is the main entry point for the <see cref="Scenario"/>
/// .
/// .
/// </summary>
/// </summary>
- /// <remarks>
- /// <para>
- /// Scenario developers are encouraged to override this method as the primary way of authoring a new
- /// scenario.
- /// </para>
- /// <para>
- /// The base implementation calls <see cref="Init"/>, <see cref="Setup"/>, and <see cref="Run"/>.
- /// </para>
- /// </remarks>
- public virtual void Main ()
- {
- }
+ public virtual void Main () { }
/// <summary>Gets the Scenario Name + Description with the Description padded based on the longest known Scenario name.</summary>
/// <summary>Gets the Scenario Name + Description with the Description padded based on the longest known Scenario name.</summary>
/// <returns></returns>
/// <returns></returns>
@@ -156,8 +156,7 @@ public class Scenario : IDisposable