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 () {