This document provides an in-depth overview of the new features, improvements, and architectural changes in Terminal.Gui v2 compared to v1.
For migration guidance, see the v1 To v2 Migration Guide.
Terminal.Gui v2 represents a fundamental redesign of the library's architecture, API, and capabilities. Key improvements include:
IApplication instancesTerminal.Gui v2 was designed with these core principles:
EventHandler<T> and IDisposableSee the Application Deep Dive for complete details.
v2 introduces an instance-based architecture that eliminates global state and enables multiple application contexts.
IApplication Interface:
Application.Create() returns an IApplication instanceView.App Property:
App property referencing its IApplication contextApp (driver, session management, etc.)Session Management:
SessionStack tracks all running sessions as a stackTopRunnable property references the currently active sessionBegin() and End() methods manage session lifecycle// Instance-based pattern (recommended)
IApplication app = Application.Create ().Init ();
Window window = new () { Title = "My App" };
app.Run (window);
window.Dispose ();
app.Dispose ();
// With using statement for automatic disposal
using (IApplication app = Application.Create ().Init ())
{
Window window = new () { Title = "My App" };
app.Run (window);
window.Dispose ();
} // app.Dispose() called automatically
// Access from within a view
public class MyView : View
{
public void DoWork ()
{
App?.Driver.Move (0, 0);
App?.TopRunnableView?.SetNeedsDraw ();
}
}
IApplication for unit testsv2 implements full IDisposable pattern:
// Recommended: using statement
using (IApplication app = Application.Create ().Init ())
{
app.Run<MyDialog> ();
MyResult? result = app.GetResult<MyResult> ();
}
// Ensures:
// - Input thread stopped cleanly
// - Driver resources released
// - No thread leaks in tests
Important Changes:
Shutdown() method is obsolete - use Dispose() insteadSee the Application Deep Dive for complete details.
v2 introduces IRunnable<TResult> - an interface-based pattern for runnable views with type-safe results.
Interface-Based:
IRunnable<TResult> without inheriting from RunnableType-Safe Results:
TResult parameter provides compile-time type safetynull indicates cancellation/non-acceptanceLifecycle Events (CWP-Compliant):
IsRunningChanging - Cancellable, before stack changeIsRunningChanged - Non-cancellable, after stack changeIsModalChanging - Cancellable, before modal state changeIsModalChanged - Non-cancellable, after modal state changepublic class FileDialog : Runnable<string?>
{
private TextField _pathField;
public FileDialog ()
{
Title = "Select File";
_pathField = new () { Width = Dim.Fill () };
Add (_pathField);
Button okButton = new () { Text = "OK", IsDefault = true };
okButton.Accepting += (s, e) =>
{
Result = _pathField.Text;
Application.RequestStop ();
};
AddButton (okButton);
}
protected override bool OnIsRunningChanging (bool oldValue, bool newValue)
{
if (!newValue) // Stopping - extract result before disposal
{
Result = _pathField?.Text;
// Optionally cancel stop
if (HasUnsavedChanges ())
{
return true; // Cancel
}
}
return base.OnIsRunningChanging (oldValue, newValue);
}
}
// Use with fluent API
using (IApplication app = Application.Create ().Init ())
{
app.Run<FileDialog> ();
string? path = app.GetResult<string> ();
if (path is { })
{
OpenFile (path);
}
}
v2 enables elegant method chaining:
// Concise and readable
using (IApplication app = Application.Create ().Init ())
{
app.Run<ColorPickerDialog> ();
Color? result = app.GetResult<Color> ();
}
Key Methods:
Init() - Returns IApplication for chainingRun<TRunnable>() - Creates and runs runnable, returns IApplicationGetResult<T>() - Extract typed result after runDispose() - Release all resources"Whoever creates it, owns it":
| Method | Creator | Owner | Disposal |
|---|---|---|---|
Run<TRunnable>() |
Framework | Framework | Automatic when returns |
Run(IRunnable) |
Caller | Caller | Manual by caller |
// Framework ownership - automatic disposal
app.Run<MyDialog> (); // Dialog disposed automatically
// Caller ownership - manual disposal
MyDialog dialog = new ();
app.Run (dialog);
dialog.Dispose (); // Caller must dispose
See the Drawing Deep Dive for complete details.
v2 provides full 24-bit color support by default:
Usage: Direct RGB input via Color struct
// 24-bit RGB color
Color customColor = new (0xFF, 0x99, 0x00);
// Or use named colors (ANSI-compliant)
Color color = Color.Yellow; // Was "Brown" in v1
See the Layout Deep Dive for complete details.
v2 introduces a comprehensive Adornment system:
Border Features:
Title display with alignment options
view.BorderStyle = LineStyle.Double;
view.Border.Thickness = new (1);
view.Title = "My View";
view.Margin.Thickness = new (2);
view.Padding.Thickness = new (1);
See the Configuration Deep Dive and Scheme Deep Dive for details.
v2 adds comprehensive theme support:
User Customization: End-users can personalize without code changes
// Apply a theme
ConfigurationManager.Themes.Theme = "Dark";
// Customize text style
view.Scheme.Normal = new (
Color.White,
Color.Black,
TextStyle.Bold | TextStyle.Underline
);
See the Drawing Deep Dive for complete details.
LineCanvas provides sophisticated line drawing:
Used by Border, Line, and custom views
// Line view uses LineCanvas
Line line = new () { Orientation = Orientation.Horizontal };
line.LineStyle = LineStyle.Double;
See the Drawing Deep Dive for details.
v2 adds gradient support:
Apply to borders, backgrounds, or custom elements
Gradient gradient = new (Color.Blue, Color.Cyan);
view.BackgroundGradient = new (gradient, Orientation.Vertical);
v2 consolidates redundant APIs:
EventHandler<T> patternOnHasFocusChanged)Example:
// v1 - Multiple scattered methods
View.MostFocused
View.EnsureFocus ()
View.FocusNext ()
// v2 - Centralized
Application.Navigation.GetFocused ()
view.SetFocus ()
view.AdvanceFocus ()
EventHandler<EventArgs> instead of custom delegatesv2 reduces overhead through:
NeedsDraw system (only draw what changed)Result: Snappier UIs, especially with many views or frequent updates
v2 clarifies view ownership:
Application.Run manages Runnable lifecycleSee the Scrolling Deep Dive for complete details.
Every View supports scrolling inherently:
No need for ScrollView wrapper!
// Enable scrolling
view.SetContentSize (new (100, 100));
// Scroll by changing Viewport location
view.ScrollVertical (5);
view.ScrollHorizontal (3);
// Built-in scrollbars
view.VerticalScrollBar.Visible = true;
view.HorizontalScrollBar.Visible = true;
view.VerticalScrollBar.AutoShow = true;
v2 replaces ScrollBarView with ScrollBar:
ScrollSliderSee the Layout Deep Dive and DimAuto Deep Dive for details.
Simplifies creating aligned layouts
// Auto-size based on text
label.Width = Dim.Auto ();
label.Height = Dim.Auto ();
// Anchor to bottom-right
button.X = Pos.AnchorEnd (10);
button.Y = Pos.AnchorEnd (2);
// Center align
label1.X = Pos.Center ();
label2.X = Pos.Center ();
See the Arrangement Deep Dive for complete details.
View.Arrangement enables interactive UI:
Arrangement Key: Press Ctrl+F5 (configurable via Application.ArrangeKey) to enter arrange mode
// Movable and resizable window
window.Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable;
// Overlapped windows
container.Arrangement = ViewArrangement.Overlapped;
See the Navigation Deep Dive for complete details.
v2 decouples navigation concepts:
false in v2)Navigation Keys (Configurable):
Tab / Shift+Tab - Next/previous TabStopF6 / Shift+F6 - Next/previous TabGroupArrow keys - Same as Tab navigation
// Configure navigation keys
Application.NextTabStopKey = Key.Tab;
Application.PrevTabStopKey = Key.Tab.WithShift;
Application.NextTabGroupKey = Key.F6;
Application.PrevTabGroupKey = Key.F6.WithShift;
// Set tab behavior
view.CanFocus = true;
view.TabStop = TabBehavior.TabStop; // Normal tab navigation
See the Views Overview for a complete catalog.
See the Keyboard Deep Dive and Command Deep Dive for details.
Key Class:
KeyEvent structPlatform-independent
// Check keys
if (key == Key.Enter) { }
if (key == Key.C.WithCtrl) { }
// Modifiers
if (key.Shift) { }
if (key.Ctrl) { }
Key Bindings:
Views declare supported commands via View.AddCommand
// Add command handler
view.AddCommand (Command.Accept, HandleAccept);
// Bind key to command
view.KeyBindings.Add (Key.Enter, Command.Accept);
private bool HandleAccept ()
{
// Handle command
return true; // Handled
}
Configurable Keys:
See the Mouse Deep Dive for complete details.
MouseEventEventArgsGranular Events:
Highlight and Continuous Presses:
View.WantContinuousButtonPresses - Repeat Command.Accept during button hold
// Highlight on hover
view.Highlight += (s, e) => { /* Visual feedback */ };
view.HighlightStyle = HighlightStyle.Hover;
// Continuous button presses
view.WantContinuousButtonPresses = true;
See the Configuration Deep Dive for complete details.
ConfigurationManager provides:
ConfigLocations - Where to search for configs
// Enable configuration
ConfigurationManager.Enable (ConfigLocations.All);
// Load a theme
ConfigurationManager.Themes.Theme = "Dark";
// Save current configuration
ConfigurationManager.Save ();
User Customization:
See the Logging Deep Dive for complete details.
Logging integrates with Microsoft.Extensions.Logging:
Works with standard .NET logging frameworks (Serilog, NLog, etc.)
// Configure logging
Logging.ConfigureLogging ("myapp.log", LogLevel.Debug);
// Use in code
Logging.Debug ("Rendering view {ViewId}", view.Id);
Logging.Meter provides performance metrics:
Tools: Use dotnet-counters or other metrics tools to monitor
dotnet counters monitor --name MyApp Terminal.Gui
v2 supports the Sixel protocol for rendering images:
Use Cases: Image previews, graphics in terminal apps
v2 ensures compatibility with Ahead-of-Time compilation:
Example: See Examples/NativeAot for AOT deployment
Terminal.Gui v2 represents a comprehensive modernization:
Architecture:
Features:
API:
Views:
v2 provides a robust foundation for building sophisticated, maintainable, and user-friendly terminal applications. The architectural improvements, combined with new features and enhanced APIs, enable developers to create modern terminal UIs that feel responsive and polished.
For detailed migration guidance, see the v1 To v2 Migration Guide.