diff --git a/Examples/AnsiStatusLineOnly/AnsiStatusLineOnly.csproj b/Examples/AnsiStatusLineOnly/AnsiStatusLineOnly.csproj
new file mode 100644
index 0000000000..46d92f7faf
--- /dev/null
+++ b/Examples/AnsiStatusLineOnly/AnsiStatusLineOnly.csproj
@@ -0,0 +1,10 @@
+
+
+ Exe
+ enable
+ latest
+
+
+
+
+
diff --git a/Examples/AnsiStatusLineOnly/Program.cs b/Examples/AnsiStatusLineOnly/Program.cs
new file mode 100644
index 0000000000..5486e63340
--- /dev/null
+++ b/Examples/AnsiStatusLineOnly/Program.cs
@@ -0,0 +1,7 @@
+using Terminal.Gui.App;
+
+Application.AppModel = AppModel.StatusLine;
+
+using IApplication app = Application.Create ();
+app.Init ();
+app.Run ();
diff --git a/Examples/AnsiStatusLineOnly/StatusLineDemo.cs b/Examples/AnsiStatusLineOnly/StatusLineDemo.cs
new file mode 100644
index 0000000000..858cd78512
--- /dev/null
+++ b/Examples/AnsiStatusLineOnly/StatusLineDemo.cs
@@ -0,0 +1,50 @@
+#nullable enable
+using Terminal.Gui.App;
+using Terminal.Gui.ViewBase;
+using Terminal.Gui.Views;
+
+internal sealed class StatusLineDemo : Runnable
+{
+ private Label? _label;
+ private Timer? _timer;
+ private int _tick;
+
+ public StatusLineDemo ()
+ {
+ Width = Dim.Fill ();
+ Height = 1;
+
+ _label = new () { X = 0, Y = 0, Text = BuildStatusText () };
+ Add (_label);
+ }
+
+ private string BuildStatusText () => $"StatusLine-only demo tick {_tick} - press Esc to quit";
+
+ protected override void OnIsRunningChanged (bool newIsRunning)
+ {
+ base.OnIsRunningChanged (newIsRunning);
+
+ if (newIsRunning)
+ {
+ _timer = new Timer (_ => App?.Invoke (UpdateStatusText), null, 0, 1000);
+
+ return;
+ }
+
+ _timer?.Dispose ();
+ _timer = null;
+ App?.StatusLine.Clear ();
+ }
+
+ private void UpdateStatusText ()
+ {
+ _tick++;
+
+ if (_label is null)
+ {
+ return;
+ }
+
+ _label.Text = BuildStatusText ();
+ }
+}
diff --git a/Examples/UICatalog/Scenarios/AnsiStatusLine.cs b/Examples/UICatalog/Scenarios/AnsiStatusLine.cs
new file mode 100644
index 0000000000..e2a7a0e066
--- /dev/null
+++ b/Examples/UICatalog/Scenarios/AnsiStatusLine.cs
@@ -0,0 +1,93 @@
+#nullable enable
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("ANSI StatusLine", "Demonstrates pushing text to the terminal status line while a full-screen app runs.")]
+[ScenarioCategory ("Arrangement")]
+public sealed class AnsiStatusLine : Scenario
+{
+ private IApplication? _app;
+
+ public override void Main ()
+ {
+ ConfigurationManager.Enable (ConfigLocations.All);
+
+ using IApplication app = Application.Create ();
+ app.Init ();
+ _app = app;
+
+ using Window appWindow = new () { Title = GetQuitKeyAndName () };
+ appWindow.IsRunningChanged += AppWindowOnIsRunningChanged;
+
+ Label description = new ()
+ {
+ X = 1,
+ Y = 1,
+ Text = "This full-screen app writes status text to the terminal title/status-line area."
+ };
+ appWindow.Add (description);
+
+ Label capability = new ()
+ {
+ X = 1,
+ Y = Pos.Bottom (description) + 1,
+ Text = app.StatusLine.IsSupported ? "StatusLine output: available for this driver." : "StatusLine output: unavailable; calls are no-ops."
+ };
+ appWindow.Add (capability);
+
+ Label prompt = new () { X = 1, Y = Pos.Bottom (capability) + 1, Text = "Text:" };
+ appWindow.Add (prompt);
+
+ TextField statusText = new ()
+ {
+ X = Pos.Right (prompt) + 1,
+ Y = Pos.Top (prompt),
+ Width = 50,
+ Text = "Terminal.Gui status line"
+ };
+ appWindow.Add (statusText);
+
+ Button updateButton = new ()
+ {
+ X = 1,
+ Y = Pos.Bottom (statusText) + 1,
+ Text = "_Update StatusLine"
+ };
+ updateButton.Accepting += (_, _) => app.StatusLine.SetText (statusText.Text, 2);
+ appWindow.Add (updateButton);
+
+ Button clearButton = new ()
+ {
+ X = Pos.Right (updateButton) + 2,
+ Y = Pos.Top (updateButton),
+ Text = "_Clear"
+ };
+ clearButton.Accepting += (_, _) => app.StatusLine.Clear ();
+ appWindow.Add (clearButton);
+
+ Label statusLineOnlyHint = new ()
+ {
+ X = 1,
+ Y = Pos.Bottom (updateButton) + 2,
+ Text = "Run Examples/AnsiStatusLineOnly to see an entire app render there."
+ };
+ appWindow.Add (statusLineOnlyHint);
+
+ app.StatusLine.SetText (statusText.Text, 2);
+ app.Run (appWindow);
+ }
+
+ private void AppWindowOnIsRunningChanged (object? sender, EventArgs args)
+ {
+ if (args.Value)
+ {
+ return;
+ }
+
+ _app?.StatusLine.Clear ();
+
+ if (sender is Window appWindow)
+ {
+ appWindow.IsRunningChanged -= AppWindowOnIsRunningChanged;
+ }
+ }
+}
diff --git a/Terminal.Gui/App/AppModel.cs b/Terminal.Gui/App/AppModel.cs
index 93b78950b4..e54a1e6bf2 100644
--- a/Terminal.Gui/App/AppModel.cs
+++ b/Terminal.Gui/App/AppModel.cs
@@ -15,5 +15,11 @@ public enum AppModel
/// The application renders inline within the primary (scrollback) buffer, anchored
/// to the bottom of the visible terminal. No alternate screen buffer is used.
///
- Inline
+ Inline,
+
+ ///
+ /// The application renders its top-level content into the terminal status line.
+ /// No alternate screen buffer or primary screen drawing is used.
+ ///
+ StatusLine
}
diff --git a/Terminal.Gui/App/Application.cs b/Terminal.Gui/App/Application.cs
index ed82a4180d..de5f9f6e23 100644
--- a/Terminal.Gui/App/Application.cs
+++ b/Terminal.Gui/App/Application.cs
@@ -227,7 +227,8 @@ internal static List GetSupportedCultures ()
///
/// Gets or sets the rendering mode for the application. When set to ,
/// the application renders inline within the primary (scrollback) buffer instead of switching to
- /// the alternate screen buffer.
+ /// the alternate screen buffer. When set to , the application
+ /// renders its top-level content into the terminal status line or title area.
///
///
///
diff --git a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs b/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs
index c1e8a2a8ed..aae2c561fe 100644
--- a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs
+++ b/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs
@@ -76,6 +76,7 @@ public IApplication Init (string? driverName = null)
RaiseInitializedChanged (this, new EventArgs (true));
SubscribeDriverEvents ();
+ StatusLine.Flush ();
SynchronizationContext.SetSynchronizationContext (new SynchronizationContext ());
@@ -276,6 +277,7 @@ public void ResetState (bool ignoreDisposed = false)
Iteration = null;
SessionBegun = null;
SessionEnded = null;
+ StatusLine.Reset ();
StopAfterFirstIteration = false;
ClearScreenNextIteration = false;
diff --git a/Terminal.Gui/App/ApplicationImpl.Screen.cs b/Terminal.Gui/App/ApplicationImpl.Screen.cs
index 98c4492769..56ecd03f45 100644
--- a/Terminal.Gui/App/ApplicationImpl.Screen.cs
+++ b/Terminal.Gui/App/ApplicationImpl.Screen.cs
@@ -17,10 +17,23 @@ internal partial class ApplicationImpl
///
public Rectangle Screen
{
- get => _screen ?? Driver?.Screen ?? new Rectangle (new Point (0, 0), new Size (2048, 2048));
+ get
+ {
+ if (AppModel == AppModel.StatusLine && Driver is { } driver)
+ {
+ return new Rectangle (0, 0, driver.Screen.Width, 1);
+ }
+
+ return _screen ?? Driver?.Screen ?? new Rectangle (new Point (0, 0), new Size (2048, 2048));
+ }
set
{
- if (AppModel == AppModel.Inline)
+ if (AppModel == AppModel.StatusLine)
+ {
+ _screen = null;
+ Driver?.SetScreenSize (value.Width, 1);
+ }
+ else if (AppModel == AppModel.Inline)
{
// Inline mode: store the sub-rectangle independently.
// Resize the output buffer to match the inline region dimensions.
@@ -75,7 +88,11 @@ private void Driver_SizeChanged (object? sender, SizeChangedEventArgs e)
{
Size newSize = e.Size ?? Size.Empty;
- if (AppModel == AppModel.Inline && _screen is { } screen)
+ if (AppModel == AppModel.StatusLine)
+ {
+ RaiseScreenChangedEvent (new Rectangle (0, 0, newSize.Width, 1));
+ }
+ else if (AppModel == AppModel.Inline && _screen is { } screen)
{
// On resize in inline mode, reset to row 0 and clear the screen.
// The next LayoutAndDraw will re-size the inline region from scratch.
diff --git a/Terminal.Gui/App/ApplicationImpl.cs b/Terminal.Gui/App/ApplicationImpl.cs
index 169c586ba8..9bc847c27e 100644
--- a/Terminal.Gui/App/ApplicationImpl.cs
+++ b/Terminal.Gui/App/ApplicationImpl.cs
@@ -17,6 +17,7 @@ internal partial class ApplicationImpl : IApplication
internal ApplicationImpl (ITimeProvider timeProvider)
{
_timeProvider = timeProvider;
+ StatusLine = new StatusLine (() => Driver);
// Initialize TimedEvents with the time provider for testable timing
TimedEvents = new TimedEvents (timeProvider);
@@ -179,6 +180,9 @@ internal static void ResetStateStatic (bool ignoreDisposed = false)
///
public IClipboard? Clipboard { get => Driver?.Clipboard; set => Driver?.Clipboard = value; }
+ ///
+ public StatusLine StatusLine { get; }
+
#endregion Screen and Driver
#region Input (Mouse/Keyboard)
diff --git a/Terminal.Gui/App/IApplication.cs b/Terminal.Gui/App/IApplication.cs
index 64b4d44a0e..43c2ab1af0 100644
--- a/Terminal.Gui/App/IApplication.cs
+++ b/Terminal.Gui/App/IApplication.cs
@@ -566,6 +566,11 @@ public interface IApplication : IDisposable
///
IClipboard? Clipboard { get; internal set; }
+ ///
+ /// Gets the terminal status line or title-area output target for this application instance.
+ ///
+ StatusLine StatusLine { get; }
+
///
/// Forces the use of the specified driver (). If not
/// specified, the driver is selected based on the platform.
@@ -576,6 +581,7 @@ public interface IApplication : IDisposable
/// Gets or sets how the application interacts with the terminal buffer.
/// uses the alternate screen buffer (default).
/// renders inline in the primary scrollback buffer.
+ /// renders the top-level content into the terminal status line or title area.
///
AppModel AppModel { get; set; }
diff --git a/Terminal.Gui/App/Legacy/Application.Driver.cs b/Terminal.Gui/App/Legacy/Application.Driver.cs
index cb0c3d12b9..77f856ded1 100644
--- a/Terminal.Gui/App/Legacy/Application.Driver.cs
+++ b/Terminal.Gui/App/Legacy/Application.Driver.cs
@@ -6,6 +6,9 @@ public static partial class Application // Driver abstractions
[Obsolete ("The legacy static Application object is going away.")]
public static IDriver? Driver { get => ApplicationImpl.Instance.Driver; internal set => ApplicationImpl.Instance.Driver = value; }
+ ///
+ public static StatusLine StatusLine => ApplicationImpl.Instance.StatusLine;
+
/// Raised when changes.
public static event EventHandler>? ForceDriverChanged;
diff --git a/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs b/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs
index 86a757da7f..b5346930ee 100644
--- a/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs
+++ b/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs
@@ -82,7 +82,9 @@ public void Initialize (ITimedEvents timedEvents,
outputBufferImpl.InlineMode = true;
}
- OutputBuffer.SetSize (consoleOutput.GetSize ().Width, consoleOutput.GetSize ().Height);
+ Size outputSize = consoleOutput.GetSize ();
+ int outputHeight = App?.AppModel == AppModel.StatusLine ? 1 : outputSize.Height;
+ OutputBuffer.SetSize (outputSize.Width, outputHeight);
SizeMonitor = componentFactory.CreateSizeMonitor (Output, OutputBuffer);
}
diff --git a/Terminal.Gui/App/StatusLine.cs b/Terminal.Gui/App/StatusLine.cs
new file mode 100644
index 0000000000..6dbf647505
--- /dev/null
+++ b/Terminal.Gui/App/StatusLine.cs
@@ -0,0 +1,75 @@
+namespace Terminal.Gui.App;
+
+///
+/// Provides access to the terminal status line or title area.
+///
+///
+/// Status line output uses the driver's best available ANSI mechanism. The initial implementation uses OSC 0/1/2
+/// title sequences, which are ignored by legacy consoles and unsupported terminals.
+///
+public sealed class StatusLine
+{
+ private readonly Func _driverGetter;
+ private bool _hasPendingWrite;
+
+ internal StatusLine (Func driverGetter) => _driverGetter = driverGetter;
+
+ ///
+ /// Gets the last text requested for the status line.
+ ///
+ public string Text { get; private set; } = string.Empty;
+
+ ///
+ /// Gets the last OSC title selector requested.
+ ///
+ public int Mode { get; private set; } = 2;
+
+ ///
+ /// Gets whether the current driver can emit ANSI status line output.
+ ///
+ public bool IsSupported => _driverGetter () is { IsLegacyConsole: false };
+
+ ///
+ /// Clears the terminal status line or title area.
+ ///
+ public void Clear () => SetText (string.Empty);
+
+ ///
+ /// Writes to the terminal status line or title area.
+ ///
+ /// The text to show. is treated as an empty string.
+ ///
+ /// The OSC title selector: 0 = icon and window title, 1 = icon title, 2 = window title.
+ ///
+ public void SetText (string? text, int mode = 2)
+ {
+ Text = text ?? string.Empty;
+ Mode = Math.Clamp (mode, 0, 2);
+ _hasPendingWrite = true;
+ Flush ();
+ }
+
+ internal void Flush ()
+ {
+ if (!_hasPendingWrite)
+ {
+ return;
+ }
+
+ IDriver? driver = _driverGetter ();
+
+ if (driver is null || driver.IsLegacyConsole)
+ {
+ return;
+ }
+
+ driver.SetTerminalTitle (Text, Mode);
+ }
+
+ internal void Reset ()
+ {
+ Text = string.Empty;
+ Mode = 2;
+ _hasPendingWrite = false;
+ }
+}
diff --git a/Terminal.Gui/Drivers/AnsiDriver/AnsiOutput.cs b/Terminal.Gui/Drivers/AnsiDriver/AnsiOutput.cs
index cf8cee1270..552539e32f 100644
--- a/Terminal.Gui/Drivers/AnsiDriver/AnsiOutput.cs
+++ b/Terminal.Gui/Drivers/AnsiDriver/AnsiOutput.cs
@@ -137,7 +137,12 @@ public AnsiOutput (AppModel appModel = AppModel.FullScreen)
}
// Initialize terminal for ANSI output
- if (AppModel == AppModel.Inline)
+ if (AppModel == AppModel.StatusLine)
+ {
+ // StatusLine mode renders only via OSC/status-line sequences and does not take over
+ // the alternate screen or primary screen buffer.
+ }
+ else if (AppModel == AppModel.Inline)
{
// Inline mode: do NOT switch to alternate screen buffer.
// Stay in the primary (scrollback) buffer.
@@ -385,7 +390,11 @@ public void Dispose ()
Write (EscSeqUtils.CSI_DisableMouseEvents);
Write (EscSeqUtils.CSI_ResetAttributes);
- if (AppModel == AppModel.Inline)
+ if (AppModel == AppModel.StatusLine)
+ {
+ // StatusLine mode did not alter the screen buffer, so there is no buffer to restore.
+ }
+ else if (AppModel == AppModel.Inline)
{
// Inline mode: do NOT restore alternate buffer. Move cursor to just
// below the inline region so the shell prompt appears naturally.
diff --git a/Terminal.Gui/Drivers/DriverImpl.cs b/Terminal.Gui/Drivers/DriverImpl.cs
index 997052cdb0..075f5b75f7 100644
--- a/Terminal.Gui/Drivers/DriverImpl.cs
+++ b/Terminal.Gui/Drivers/DriverImpl.cs
@@ -91,6 +91,13 @@ public DriverImpl (IComponentFactory componentFactory,
///
public void Refresh ()
{
+ if (AppModel == AppModel.StatusLine)
+ {
+ SetTerminalTitle (GetStatusLineText (_outputBuffer), 2);
+
+ return;
+ }
+
// Hide cursor during rendering to prevent flicker
Cursor cursor = _output.GetCursor ();
@@ -219,10 +226,14 @@ public virtual void SetScreenSize (int width, int height)
// Always update the output's knowledge of terminal size
_output.SetSize (width, height);
- // In inline mode, the output buffer is sized to App.Screen (the inline region),
- // not the full terminal. Only resize the buffer in fullscreen mode.
- if (AppModel != AppModel.Inline)
+ if (AppModel == AppModel.StatusLine)
{
+ _outputBuffer.SetSize (width, 1);
+ }
+ else if (AppModel != AppModel.Inline)
+ {
+ // In inline mode, the output buffer is sized to App.Screen (the inline region),
+ // not the full terminal. Only resize the buffer in fullscreen mode.
_outputBuffer.SetSize (width, height);
}
@@ -443,6 +454,30 @@ public void SetTerminalTitle (string title, int mode = 0)
///
public string ToAnsi () => _output.ToAnsi (_outputBuffer);
+ private static string GetStatusLineText (IOutputBuffer outputBuffer)
+ {
+ if (outputBuffer.Rows <= 0 || outputBuffer.Cols <= 0)
+ {
+ return string.Empty;
+ }
+
+ StringBuilder builder = new ();
+
+ for (int col = 0; col < outputBuffer.Cols; col++)
+ {
+ Cell cell = outputBuffer.Contents! [0, col];
+ string grapheme = cell.Grapheme;
+ builder.Append (grapheme);
+
+ if (grapheme.GetColumns () > 1 && col + 1 < outputBuffer.Cols)
+ {
+ col++;
+ }
+ }
+
+ return builder.ToString ().TrimEnd ();
+ }
+
#endregion Drawing and Rendering
#region Cursor
diff --git a/Terminal.sln b/Terminal.sln
index de4c68a853..0dd97cb88c 100644
--- a/Terminal.sln
+++ b/Terminal.sln
@@ -158,6 +158,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Terminal.Gui.Analyzers.Inte
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PerformanceTests", "Tests\PerformanceTests\PerformanceTests.csproj", "{6E98BACA-E6B6-47A0-B45C-B624F8E74EC2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnsiStatusLineOnly", "Examples\AnsiStatusLineOnly\AnsiStatusLineOnly.csproj", "{BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -468,6 +470,18 @@ Global
{6E98BACA-E6B6-47A0-B45C-B624F8E74EC2}.Release|x64.Build.0 = Release|Any CPU
{6E98BACA-E6B6-47A0-B45C-B624F8E74EC2}.Release|x86.ActiveCfg = Release|Any CPU
{6E98BACA-E6B6-47A0-B45C-B624F8E74EC2}.Release|x86.Build.0 = Release|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Debug|x64.Build.0 = Debug|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Debug|x86.Build.0 = Debug|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Release|x64.ActiveCfg = Release|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Release|x64.Build.0 = Release|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Release|x86.ActiveCfg = Release|Any CPU
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -499,6 +513,7 @@ Global
{70802F77-F259-44C6-9522-46FCE2FD754E} = {3DD033C0-E023-47BF-A808-9CCE30873C3E}
{3116547F-A8F2-4189-BC22-0B47C757164C} = {3DD033C0-E023-47BF-A808-9CCE30873C3E}
{6E98BACA-E6B6-47A0-B45C-B624F8E74EC2} = {A589126F-C71A-4FEE-B7EA-2DCA1ADF6A46}
+ {BFE8FF68-9E23-4A26-AE50-4D9379BBDED6} = {3DD033C0-E023-47BF-A808-9CCE30873C3E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9F8F8A4D-7B8D-4C2A-AC5E-CD7117F74C03}
diff --git a/Tests/UnitTestsParallelizable/Application/StatusLineTests.cs b/Tests/UnitTestsParallelizable/Application/StatusLineTests.cs
new file mode 100644
index 0000000000..11d5c50bcd
--- /dev/null
+++ b/Tests/UnitTestsParallelizable/Application/StatusLineTests.cs
@@ -0,0 +1,78 @@
+// Copilot
+
+namespace ApplicationTests;
+
+[Collection ("Application Tests")]
+public class StatusLineTests
+{
+ [Fact]
+ public void StatusLine_SetText_WritesOscTitle ()
+ {
+ using IApplication app = Application.Create ();
+ app.Init (DriverRegistry.Names.ANSI);
+
+ app.StatusLine.SetText ("Ready", 2);
+
+ Assert.True (app.StatusLine.IsSupported);
+ Assert.Equal ("Ready", app.StatusLine.Text);
+ Assert.Contains (EscSeqUtils.OSC_SetWindowTitle ("Ready", 2), app.Driver!.GetOutput ().GetLastOutput (), StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void StatusLine_SetText_BeforeInit_FlushesAfterInit ()
+ {
+ using IApplication app = Application.Create ();
+
+ app.StatusLine.SetText ("Pending", 2);
+ app.Init (DriverRegistry.Names.ANSI);
+
+ Assert.Contains (EscSeqUtils.OSC_SetWindowTitle ("Pending", 2), app.Driver!.GetOutput ().GetLastOutput (), StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void StatusLine_SetText_LegacyConsole_DoesNotWriteOsc ()
+ {
+ using IApplication app = Application.Create ();
+ app.Init (DriverRegistry.Names.ANSI);
+ app.Driver!.IsLegacyConsole = true;
+
+ app.StatusLine.SetText ("Legacy", 2);
+
+ Assert.False (app.StatusLine.IsSupported);
+ Assert.DoesNotContain (EscSeqUtils.OSC_SetWindowTitle ("Legacy", 2), app.Driver.GetOutput ().GetLastOutput (), StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void StatusLine_AppModel_ConstrainsScreenToSingleRow ()
+ {
+ using IApplication app = Application.Create ();
+ app.AppModel = AppModel.StatusLine;
+ app.Init (DriverRegistry.Names.ANSI);
+
+ app.Driver!.SetScreenSize (30, 10);
+
+ Assert.Equal (30, app.Screen.Width);
+ Assert.Equal (1, app.Screen.Height);
+ Assert.Equal (1, app.Driver.GetOutputBuffer ().Rows);
+ }
+
+ [Fact]
+ public void StatusLine_AppModel_RendersTopRowToOscTitle ()
+ {
+ using IApplication app = Application.Create ();
+ app.AppModel = AppModel.StatusLine;
+ app.Init (DriverRegistry.Names.ANSI);
+ app.Driver!.SetScreenSize (30, 10);
+
+ using Runnable runnable = new () { Width = Dim.Fill (), Height = 1 };
+ Label label = new () { Text = "Status OK", X = 0, Y = 0 };
+ runnable.Add (label);
+
+ app.StopAfterFirstIteration = true;
+ app.Run (runnable);
+
+ string output = app.Driver.GetOutput ().GetLastOutput ();
+ Assert.Contains (EscSeqUtils.OSC_SetWindowTitle ("Status OK", 2), output, StringComparison.Ordinal);
+ Assert.DoesNotContain (EscSeqUtils.CSI_SaveCursorAndActivateAltBufferNoBackscroll, output, StringComparison.Ordinal);
+ }
+}
diff --git a/Tests/UnitTestsParallelizable/Drivers/Output/AppModelTests.cs b/Tests/UnitTestsParallelizable/Drivers/Output/AppModelTests.cs
index e6b4ce6d76..cf12daa9bc 100644
--- a/Tests/UnitTestsParallelizable/Drivers/Output/AppModelTests.cs
+++ b/Tests/UnitTestsParallelizable/Drivers/Output/AppModelTests.cs
@@ -22,6 +22,13 @@ public void AppModel_Inline_IsDefined ()
Assert.True (Enum.IsDefined (typeof (AppModel), AppModel.Inline));
}
+ [Fact]
+ public void AppModel_StatusLine_IsDefined ()
+ {
+ // Assert
+ Assert.True (Enum.IsDefined (typeof (AppModel), AppModel.StatusLine));
+ }
+
[Fact]
public void AppModel_FullScreen_IsDefined ()
{