mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2026-01-01 00:46:39 +01:00
Restructured and expanded the migration guide to provide a comprehensive resource for transitioning from Terminal.Gui v1 to v2. Key updates include: - Added a Table of Contents for easier navigation. - Summarized major architectural changes in v2, including the instance-based application model, IRunnable architecture, and 24-bit TrueColor support. - Updated examples to reflect new patterns, such as initializers replacing constructors and explicit disposal using `IDisposable`. - Documented changes to the layout system, including the removal of `Absolute`/`Computed` styles and the introduction of `Viewport`. - Standardized event patterns to use `object sender, EventArgs args`. - Detailed updates to the Keyboard, Mouse, and Navigation APIs, including configurable key bindings and viewport-relative mouse coordinates. - Replaced legacy components like `ScrollView` and `ContextMenu` with built-in scrolling and `PopoverMenu`. - Clarified disposal rules and introduced best practices for resource management. - Provided a complete migration example and a summary of breaking changes. This update aims to simplify the migration process by addressing breaking changes, introducing new features, and aligning with modern .NET conventions.
1114 lines
22 KiB
Markdown
1114 lines
22 KiB
Markdown
# Migrating From v1 To v2
|
|
|
|
This document provides a comprehensive guide for migrating applications from Terminal.Gui v1 to v2.
|
|
|
|
For detailed breaking change documentation, check out this Discussion: https://github.com/gui-cs/Terminal.Gui/discussions/2448
|
|
|
|
## Table of Contents
|
|
|
|
- [Overview of Major Changes](#overview-of-major-changes)
|
|
- [Application Architecture](#application-architecture)
|
|
- [View Construction and Initialization](#view-construction-and-initialization)
|
|
- [Layout System Changes](#layout-system-changes)
|
|
- [Color and Attribute Changes](#color-and-attribute-changes)
|
|
- [Type Changes](#type-changes)
|
|
- [Unicode and Text](#unicode-and-text)
|
|
- [Keyboard API](#keyboard-api)
|
|
- [Mouse API](#mouse-api)
|
|
- [Navigation Changes](#navigation-changes)
|
|
- [Scrolling Changes](#scrolling-changes)
|
|
- [Adornments](#adornments)
|
|
- [Event Pattern Changes](#event-pattern-changes)
|
|
- [View-Specific Changes](#view-specific-changes)
|
|
- [Disposal and Resource Management](#disposal-and-resource-management)
|
|
- [API Terminology Changes](#api-terminology-changes)
|
|
|
|
---
|
|
|
|
## Overview of Major Changes
|
|
|
|
Terminal.Gui v2 represents a major architectural evolution with these key improvements:
|
|
|
|
1. **Instance-Based Application Model** - Move from static `Application` to `IApplication` instances
|
|
2. **IRunnable Architecture** - Interface-based runnable pattern with type-safe results
|
|
3. **Simplified Layout** - Removed Absolute/Computed distinction, improved adornments
|
|
4. **24-bit TrueColor** - Full color support by default
|
|
5. **Enhanced Input** - Better keyboard and mouse APIs
|
|
6. **Built-in Scrolling** - All views support scrolling inherently
|
|
7. **Fluent API** - Method chaining for elegant code
|
|
8. **Proper Disposal** - IDisposable pattern throughout
|
|
|
|
---
|
|
|
|
## Application Architecture
|
|
|
|
### Instance-Based Application Model
|
|
|
|
**v1 Pattern (Static):**
|
|
```csharp
|
|
// v1 - static Application
|
|
Application.Init();
|
|
var top = Application.Top;
|
|
top.Add(myView);
|
|
Application.Run();
|
|
Application.Shutdown();
|
|
```
|
|
|
|
**v2 Recommended Pattern (Instance-Based):**
|
|
```csharp
|
|
// v2 - instance-based with using statement
|
|
using (var app = Application.Create().Init())
|
|
{
|
|
var top = new Window();
|
|
top.Add(myView);
|
|
app.Run(top);
|
|
top.Dispose();
|
|
} // app.Dispose() called automatically
|
|
```
|
|
|
|
**v2 Legacy Pattern (Still Works):**
|
|
```csharp
|
|
// v2 - legacy static (marked obsolete but functional)
|
|
Application.Init();
|
|
var top = new Window();
|
|
top.Add(myView);
|
|
Application.Run(top);
|
|
top.Dispose();
|
|
Application.Shutdown(); // Obsolete - use Dispose() instead
|
|
```
|
|
|
|
### IRunnable Architecture
|
|
|
|
v2 introduces `IRunnable<TResult>` for type-safe, runnable views:
|
|
|
|
```csharp
|
|
// Create a dialog that returns a typed result
|
|
public class FileDialog : Runnable<string?>
|
|
{
|
|
private TextField _pathField;
|
|
|
|
public FileDialog()
|
|
{
|
|
Title = "Select File";
|
|
_pathField = new TextField { Width = Dim.Fill() };
|
|
Add(_pathField);
|
|
|
|
var okButton = new Button { 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;
|
|
}
|
|
return base.OnIsRunningChanging(oldValue, newValue);
|
|
}
|
|
}
|
|
|
|
// Use with fluent API
|
|
using (var app = Application.Create().Init())
|
|
{
|
|
app.Run<FileDialog>();
|
|
string? result = app.GetResult<string>();
|
|
|
|
if (result is { })
|
|
{
|
|
OpenFile(result);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Key Benefits:**
|
|
- Type-safe results (no casting)
|
|
- Automatic disposal of framework-created runnables
|
|
- CWP-compliant lifecycle events
|
|
- Works with any View (not just Toplevel)
|
|
|
|
### Disposal and Resource Management
|
|
|
|
v2 requires explicit disposal:
|
|
|
|
```csharp
|
|
// ❌ v1 - Application.Shutdown() disposed everything
|
|
Application.Init();
|
|
var top = new Window();
|
|
Application.Run(top);
|
|
Application.Shutdown(); // Disposed top automatically
|
|
|
|
// ✅ v2 - Dispose views explicitly
|
|
using (var app = Application.Create().Init())
|
|
{
|
|
var top = new Window();
|
|
app.Run(top);
|
|
top.Dispose(); // Must dispose
|
|
}
|
|
|
|
// ✅ v2 - Framework-created runnables disposed automatically
|
|
using (var app = Application.Create().Init())
|
|
{
|
|
app.Run<MyDialog>(); // Dialog disposed automatically
|
|
var result = app.GetResult<MyResult>();
|
|
}
|
|
```
|
|
|
|
**Disposal Rules:**
|
|
- "Whoever creates it, owns it"
|
|
- `Run<TRunnable>()`: Framework creates → Framework disposes
|
|
- `Run(IRunnable)`: Caller creates → Caller disposes
|
|
- Always dispose `IApplication` (use `using` statement)
|
|
|
|
### View.App Property
|
|
|
|
Views now have an `App` property for accessing the application context:
|
|
|
|
```csharp
|
|
// ❌ v1 - Direct static reference
|
|
Application.Driver.Move(x, y);
|
|
|
|
// ✅ v2 - Use View.App
|
|
App?.Driver.Move(x, y);
|
|
|
|
// ✅ v2 - Dependency injection
|
|
public class MyView : View
|
|
{
|
|
private readonly IApplication _app;
|
|
|
|
public MyView(IApplication app)
|
|
{
|
|
_app = app;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## View Construction and Initialization
|
|
|
|
### Constructors → Initializers
|
|
|
|
**v1:**
|
|
```csharp
|
|
var myView = new View(new Rect(10, 10, 40, 10));
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
var myView = new View
|
|
{
|
|
X = 10,
|
|
Y = 10,
|
|
Width = 40,
|
|
Height = 10
|
|
};
|
|
```
|
|
|
|
### Initialization Pattern
|
|
|
|
v2 uses `ISupportInitializeNotification`:
|
|
|
|
```csharp
|
|
// v1 - No explicit initialization
|
|
var view = new View();
|
|
Application.Run(view);
|
|
|
|
// v2 - Automatic initialization via BeginInit/EndInit
|
|
var view = new View();
|
|
// BeginInit() called automatically when added to SuperView
|
|
// EndInit() called automatically
|
|
// Initialized event raised after EndInit()
|
|
```
|
|
|
|
---
|
|
|
|
## Layout System Changes
|
|
|
|
### Removed LayoutStyle Distinction
|
|
|
|
v1 had `Absolute` and `Computed` layout styles. v2 removed this distinction.
|
|
|
|
**v1:**
|
|
```csharp
|
|
view.LayoutStyle = LayoutStyle.Computed;
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
// No LayoutStyle - all layout is declarative via Pos/Dim
|
|
view.X = Pos.Center();
|
|
view.Y = Pos.Center();
|
|
view.Width = Dim.Percent(50);
|
|
view.Height = Dim.Fill();
|
|
```
|
|
|
|
### Frame vs Bounds
|
|
|
|
**v1:**
|
|
- `Frame` - Position/size in SuperView coordinates
|
|
- `Bounds` - Always `{0, 0, Width, Height}` (location always empty)
|
|
|
|
**v2:**
|
|
- `Frame` - Position/size in SuperView coordinates (same as v1)
|
|
- `Viewport` - Visible area in content coordinates (replaces Bounds)
|
|
- **Important**: `Viewport.Location` can now be non-zero for scrolling
|
|
|
|
```csharp
|
|
// ❌ v1
|
|
var size = view.Bounds.Size;
|
|
Debug.Assert(view.Bounds.Location == Point.Empty); // Always true
|
|
|
|
// ✅ v2
|
|
var visibleArea = view.Viewport;
|
|
var contentSize = view.GetContentSize();
|
|
|
|
// Viewport.Location can be non-zero when scrolled
|
|
view.ScrollVertical(10);
|
|
Debug.Assert(view.Viewport.Location.Y == 10);
|
|
```
|
|
|
|
### Pos and Dim API Changes
|
|
|
|
| v1 | v2 |
|
|
|----|-----|
|
|
| `Pos.At(x)` | `Pos.Absolute(x)` |
|
|
| `Dim.Sized(width)` | `Dim.Absolute(width)` |
|
|
| `Pos.Anchor()` | `Pos.GetAnchor()` |
|
|
| `Dim.Anchor()` | `Dim.GetAnchor()` |
|
|
|
|
```csharp
|
|
// ❌ v1
|
|
view.X = Pos.At(10);
|
|
view.Width = Dim.Sized(20);
|
|
|
|
// ✅ v2
|
|
view.X = Pos.Absolute(10);
|
|
view.Width = Dim.Absolute(20);
|
|
```
|
|
|
|
### View.AutoSize Removed
|
|
|
|
**v1:**
|
|
```csharp
|
|
view.AutoSize = true;
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
view.Width = Dim.Auto();
|
|
view.Height = Dim.Auto();
|
|
```
|
|
|
|
See [Dim.Auto Deep Dive](dimauto.md) for details.
|
|
|
|
---
|
|
|
|
## Adornments
|
|
|
|
v2 adds `Border`, `Margin`, and `Padding` as built-in adornments.
|
|
|
|
**v1:**
|
|
```csharp
|
|
// Custom border drawing
|
|
view.Border = new Border { /* ... */ };
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
// Built-in Border adornment
|
|
view.BorderStyle = LineStyle.Single;
|
|
view.Border.Thickness = new Thickness(1);
|
|
view.Title = "My View";
|
|
|
|
// Built-in Margin and Padding
|
|
view.Margin.Thickness = new Thickness(2);
|
|
view.Padding.Thickness = new Thickness(1);
|
|
```
|
|
|
|
See [Layout Deep Dive](layout.md) for complete details.
|
|
|
|
---
|
|
|
|
## Color and Attribute Changes
|
|
|
|
### 24-bit TrueColor Default
|
|
|
|
v2 uses 24-bit color by default.
|
|
|
|
```csharp
|
|
// v1 - Limited color palette
|
|
var color = Color.Brown;
|
|
|
|
// v2 - ANSI-compliant names + TrueColor
|
|
var color = Color.Yellow; // Brown renamed
|
|
var customColor = new Color(0xFF, 0x99, 0x00); // 24-bit RGB
|
|
```
|
|
|
|
### Attribute.Make Removed
|
|
|
|
**v1:**
|
|
```csharp
|
|
var attr = Attribute.Make(Color.BrightMagenta, Color.Blue);
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
var attr = new Attribute(Color.BrightMagenta, Color.Blue);
|
|
```
|
|
|
|
### Color Name Changes
|
|
|
|
| v1 | v2 |
|
|
|----|-----|
|
|
| `Color.Brown` | `Color.Yellow` |
|
|
|
|
---
|
|
|
|
## Type Changes
|
|
|
|
### Low-Level Types
|
|
|
|
| v1 | v2 |
|
|
|----|-----|
|
|
| `Rect` | `Rectangle` |
|
|
| `Point` | `Point` |
|
|
| `Size` | `Size` |
|
|
|
|
```csharp
|
|
// ❌ v1
|
|
Rect rect = new Rect(0, 0, 10, 10);
|
|
|
|
// ✅ v2
|
|
Rectangle rect = new Rectangle(0, 0, 10, 10);
|
|
```
|
|
|
|
---
|
|
|
|
## Unicode and Text
|
|
|
|
### NStack.ustring Removed
|
|
|
|
**v1:**
|
|
```csharp
|
|
using NStack;
|
|
ustring text = "Hello";
|
|
var width = text.Sum(c => Rune.ColumnWidth(c));
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
using System.Text;
|
|
string text = "Hello";
|
|
var width = text.GetColumns(); // Extension method
|
|
```
|
|
|
|
### Rune Changes
|
|
|
|
**v1:**
|
|
```csharp
|
|
// Implicit cast
|
|
myView.AddRune(col, row, '▄');
|
|
|
|
// Width
|
|
var width = Rune.ColumnWidth(rune);
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
// Explicit constructor
|
|
myView.AddRune(col, row, new Rune('▄'));
|
|
|
|
// Width
|
|
var width = rune.GetColumns();
|
|
```
|
|
|
|
See [Unicode](https://gui-cs.github.io/Terminal.GuiV2Docs/docs/overview.html#unicode) for details.
|
|
|
|
---
|
|
|
|
## Keyboard API
|
|
|
|
v2 has a completely redesigned keyboard API.
|
|
|
|
### Key Class
|
|
|
|
**v1:**
|
|
```csharp
|
|
KeyEvent keyEvent;
|
|
if (keyEvent.KeyCode == KeyCode.Enter) { }
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
Key key;
|
|
if (key == Key.Enter) { }
|
|
|
|
// Modifiers
|
|
if (key.Shift) { }
|
|
if (key.Ctrl) { }
|
|
|
|
// With modifiers
|
|
Key ctrlC = Key.C.WithCtrl;
|
|
Key shiftF1 = Key.F1.WithShift;
|
|
```
|
|
|
|
### Key Bindings
|
|
|
|
**v1:**
|
|
```csharp
|
|
// Override OnKeyPress
|
|
protected override bool OnKeyPress(KeyEvent keyEvent)
|
|
{
|
|
if (keyEvent.KeyCode == KeyCode.Enter)
|
|
{
|
|
// Handle
|
|
return true;
|
|
}
|
|
return base.OnKeyPress(keyEvent);
|
|
}
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
// Use KeyBindings + Commands
|
|
AddCommand(Command.Accept, HandleAccept);
|
|
KeyBindings.Add(Key.Enter, Command.Accept);
|
|
|
|
private bool HandleAccept()
|
|
{
|
|
// Handle
|
|
return true;
|
|
}
|
|
```
|
|
|
|
### Application-Wide Keys
|
|
|
|
**v1:**
|
|
```csharp
|
|
// Hard-coded Ctrl+Q
|
|
if (keyEvent.Key == Key.CtrlMask | Key.Q)
|
|
{
|
|
Application.RequestStop();
|
|
}
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
// Configurable quit key
|
|
if (key == Application.QuitKey)
|
|
{
|
|
Application.RequestStop();
|
|
}
|
|
|
|
// Change the quit key
|
|
Application.QuitKey = Key.Esc;
|
|
```
|
|
|
|
### Navigation Keys
|
|
|
|
v2 has consistent, configurable navigation keys:
|
|
|
|
| Key | Purpose |
|
|
|-----|---------|
|
|
| `Tab` | Next TabStop |
|
|
| `Shift+Tab` | Previous TabStop |
|
|
| `F6` | Next TabGroup |
|
|
| `Shift+F6` | Previous TabGroup |
|
|
|
|
```csharp
|
|
// Configurable
|
|
Application.NextTabStopKey = Key.Tab;
|
|
Application.PrevTabStopKey = Key.Tab.WithShift;
|
|
Application.NextTabGroupKey = Key.F6;
|
|
Application.PrevTabGroupKey = Key.F6.WithShift;
|
|
```
|
|
|
|
See [Keyboard Deep Dive](keyboard.md) for complete details.
|
|
|
|
---
|
|
|
|
## Mouse API
|
|
|
|
### MouseEventEventArgs → MouseEventArgs
|
|
|
|
**v1:**
|
|
```csharp
|
|
void HandleMouse(MouseEventEventArgs args) { }
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
void HandleMouse(object? sender, MouseEventArgs args) { }
|
|
```
|
|
|
|
### Mouse Coordinates
|
|
|
|
**v1:**
|
|
- Mouse coordinates were screen-relative
|
|
|
|
**v2:**
|
|
- Mouse coordinates are now **Viewport-relative**
|
|
|
|
```csharp
|
|
// v2 - Viewport-relative coordinates
|
|
view.MouseClick += (s, e) =>
|
|
{
|
|
// e.Position is relative to view's Viewport
|
|
var x = e.Position.X; // 0 = left edge of viewport
|
|
var y = e.Position.Y; // 0 = top edge of viewport
|
|
};
|
|
```
|
|
|
|
### Highlight Event
|
|
|
|
v2 adds a `Highlight` event for visual feedback:
|
|
|
|
```csharp
|
|
view.Highlight += (s, e) =>
|
|
{
|
|
// Provide visual feedback on mouse hover
|
|
};
|
|
view.HighlightStyle = HighlightStyle.Hover;
|
|
```
|
|
|
|
See [Mouse Deep Dive](mouse.md) for complete details.
|
|
|
|
---
|
|
|
|
## Navigation Changes
|
|
|
|
### Focus Properties
|
|
|
|
**v1:**
|
|
```csharp
|
|
view.CanFocus = true; // Default was true
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
view.CanFocus = true; // Default is FALSE - must opt-in
|
|
```
|
|
|
|
**Important:** In v2, `CanFocus` defaults to `false`. Views that want focus must explicitly set it.
|
|
|
|
### Focus Changes
|
|
|
|
**v1:**
|
|
```csharp
|
|
// HasFocus was read-only
|
|
bool hasFocus = view.HasFocus;
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
// HasFocus can be set
|
|
view.HasFocus = true; // Equivalent to SetFocus()
|
|
view.HasFocus = false; // Equivalent to SuperView.AdvanceFocus()
|
|
```
|
|
|
|
### TabStop Behavior
|
|
|
|
**v1:**
|
|
```csharp
|
|
view.TabStop = true; // Boolean
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
view.TabStop = TabBehavior.TabStop; // Enum with more options
|
|
|
|
// Options:
|
|
// - NoStop: Focusable but not via Tab
|
|
// - TabStop: Normal tab navigation
|
|
// - TabGroup: Advance via F6
|
|
```
|
|
|
|
### Navigation Events
|
|
|
|
**v1:**
|
|
```csharp
|
|
view.Enter += (s, e) => { }; // Gained focus
|
|
view.Leave += (s, e) => { }; // Lost focus
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
view.HasFocusChanging += (s, e) =>
|
|
{
|
|
// Before focus changes (cancellable)
|
|
if (preventFocusChange)
|
|
e.Cancel = true;
|
|
};
|
|
|
|
view.HasFocusChanged += (s, e) =>
|
|
{
|
|
// After focus changed
|
|
if (e.Value)
|
|
Console.WriteLine("Gained focus");
|
|
else
|
|
Console.WriteLine("Lost focus");
|
|
};
|
|
```
|
|
|
|
See [Navigation Deep Dive](navigation.md) for complete details.
|
|
|
|
---
|
|
|
|
## Scrolling Changes
|
|
|
|
### ScrollView Removed
|
|
|
|
**v1:**
|
|
```csharp
|
|
var scrollView = new ScrollView
|
|
{
|
|
ContentSize = new Size(100, 100),
|
|
ShowHorizontalScrollIndicator = true,
|
|
ShowVerticalScrollIndicator = true
|
|
};
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
// Built-in scrolling on every View
|
|
var view = new View();
|
|
view.SetContentSize(new Size(100, 100));
|
|
|
|
// Built-in scrollbars
|
|
view.VerticalScrollBar.Visible = true;
|
|
view.HorizontalScrollBar.Visible = true;
|
|
view.VerticalScrollBar.AutoShow = true;
|
|
```
|
|
|
|
### Scrolling API
|
|
|
|
**v2:**
|
|
```csharp
|
|
// Set content larger than viewport
|
|
view.SetContentSize(new Size(100, 100));
|
|
|
|
// Scroll by changing Viewport location
|
|
view.Viewport = view.Viewport with { Location = new Point(10, 10) };
|
|
|
|
// Or use helper methods
|
|
view.ScrollVertical(5);
|
|
view.ScrollHorizontal(3);
|
|
```
|
|
|
|
See [Scrolling Deep Dive](scrolling.md) for complete details.
|
|
|
|
---
|
|
|
|
## Event Pattern Changes
|
|
|
|
v2 standardizes all events to use `object sender, EventArgs args` pattern.
|
|
|
|
### Button.Clicked → Button.Accepting
|
|
|
|
**v1:**
|
|
```csharp
|
|
button.Clicked += () => { /* do something */ };
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
button.Accepting += (s, e) => { /* do something */ };
|
|
```
|
|
|
|
### Event Signatures
|
|
|
|
**v1:**
|
|
```csharp
|
|
// Various patterns
|
|
event Action SomeEvent;
|
|
event Action<string> OtherEvent;
|
|
event Action<EventArgs> ThirdEvent;
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
// Consistent pattern
|
|
event EventHandler<EventArgs>? SomeEvent;
|
|
event EventHandler<EventArgs<string>>? OtherEvent;
|
|
event EventHandler<CancelEventArgs<bool>>? ThirdEvent;
|
|
```
|
|
|
|
**Benefits:**
|
|
- Named parameters
|
|
- Cancellable events via `CancelEventArgs`
|
|
- Future-proof (new properties can be added)
|
|
|
|
---
|
|
|
|
## View-Specific Changes
|
|
|
|
### CheckBox
|
|
|
|
**v1:**
|
|
```csharp
|
|
var cb = new CheckBox("_Checkbox", true);
|
|
cb.Toggled += (e) => { };
|
|
cb.Toggle();
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
var cb = new CheckBox
|
|
{
|
|
Title = "_Checkbox",
|
|
CheckState = CheckState.Checked
|
|
};
|
|
cb.CheckStateChanging += (s, e) =>
|
|
{
|
|
e.Cancel = preventChange;
|
|
};
|
|
cb.AdvanceCheckState();
|
|
```
|
|
|
|
### StatusBar
|
|
|
|
**v1:**
|
|
```csharp
|
|
var statusBar = new StatusBar(
|
|
new StatusItem[]
|
|
{
|
|
new StatusItem(Application.QuitKey, "Quit", () => Quit())
|
|
}
|
|
);
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
var statusBar = new StatusBar(
|
|
new Shortcut[]
|
|
{
|
|
new Shortcut(Application.QuitKey, "Quit", Quit)
|
|
}
|
|
);
|
|
```
|
|
|
|
### PopoverMenu
|
|
|
|
v2 replaces `ContextMenu` with `PopoverMenu`:
|
|
|
|
**v1:**
|
|
```csharp
|
|
var contextMenu = new ContextMenu();
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
var popoverMenu = new PopoverMenu();
|
|
```
|
|
|
|
### MenuItem
|
|
|
|
**v1:**
|
|
```csharp
|
|
new MenuItem(
|
|
"Copy",
|
|
"",
|
|
CopyGlyph,
|
|
null,
|
|
null,
|
|
(KeyCode)Key.G.WithCtrl
|
|
)
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
new MenuItem(
|
|
"Copy",
|
|
"",
|
|
CopyGlyph,
|
|
Key.G.WithCtrl
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Disposal and Resource Management
|
|
|
|
v2 implements proper `IDisposable` throughout.
|
|
|
|
### View Disposal
|
|
|
|
```csharp
|
|
// v1 - No explicit disposal needed
|
|
var view = new View();
|
|
Application.Run(view);
|
|
Application.Shutdown();
|
|
|
|
// v2 - Explicit disposal required
|
|
var view = new View();
|
|
app.Run(view);
|
|
view.Dispose();
|
|
app.Dispose();
|
|
```
|
|
|
|
### Disposal Patterns
|
|
|
|
```csharp
|
|
// ✅ Best practice - using statement
|
|
using (var app = Application.Create().Init())
|
|
{
|
|
using (var view = new View())
|
|
{
|
|
app.Run(view);
|
|
}
|
|
}
|
|
|
|
// ✅ Alternative - explicit try/finally
|
|
var app = Application.Create();
|
|
try
|
|
{
|
|
app.Init();
|
|
var view = new View();
|
|
try
|
|
{
|
|
app.Run(view);
|
|
}
|
|
finally
|
|
{
|
|
view.Dispose();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
app.Dispose();
|
|
}
|
|
```
|
|
|
|
### SubView Disposal
|
|
|
|
When a View is disposed, it automatically disposes all SubViews:
|
|
|
|
```csharp
|
|
var container = new View();
|
|
var child1 = new View();
|
|
var child2 = new View();
|
|
|
|
container.Add(child1, child2);
|
|
|
|
// Disposes container, child1, and child2
|
|
container.Dispose();
|
|
```
|
|
|
|
See [Resource Management](#disposal-and-resource-management) for complete details.
|
|
|
|
---
|
|
|
|
## API Terminology Changes
|
|
|
|
v2 modernizes terminology for clarity:
|
|
|
|
### Application.Top → Application.TopRunnable
|
|
|
|
**v1:**
|
|
```csharp
|
|
Application.Top.SetNeedsDraw();
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
// Use TopRunnable (or TopRunnableView for View reference)
|
|
app.TopRunnable?.SetNeedsDraw();
|
|
app.TopRunnableView?.SetNeedsDraw();
|
|
|
|
// From within a view
|
|
App?.TopRunnableView?.SetNeedsDraw();
|
|
```
|
|
|
|
**Why "TopRunnable"?**
|
|
- Clearly indicates it's the top of the runnable session stack
|
|
- Aligns with `IRunnable` architecture
|
|
- Works with any `IRunnable`, not just `Toplevel`
|
|
|
|
### Application.TopLevels → Application.SessionStack
|
|
|
|
**v1:**
|
|
```csharp
|
|
foreach (var tl in Application.TopLevels)
|
|
{
|
|
// Process
|
|
}
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
foreach (var token in app.SessionStack)
|
|
{
|
|
var runnable = token.Runnable;
|
|
// Process
|
|
}
|
|
|
|
// Count of sessions
|
|
int sessionCount = app.SessionStack.Count;
|
|
```
|
|
|
|
**Why "SessionStack"?**
|
|
- Describes both content (sessions) and structure (stack)
|
|
- Aligns with `SessionToken` terminology
|
|
- Follows .NET naming patterns
|
|
|
|
### View Arrangement
|
|
|
|
**v1:**
|
|
```csharp
|
|
view.SendSubViewToBack();
|
|
view.SendSubViewBackward();
|
|
view.SendSubViewToFront();
|
|
view.SendSubViewForward();
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
// Fixed naming (methods worked opposite to their names in v1)
|
|
view.MoveSubViewToStart();
|
|
view.MoveSubViewTowardsStart();
|
|
view.MoveSubViewToEnd();
|
|
view.MoveSubViewTowardsEnd();
|
|
```
|
|
|
|
### Mdi → ViewArrangement.Overlapped
|
|
|
|
**v1:**
|
|
```csharp
|
|
Application.MdiTop = true;
|
|
toplevel.IsMdiContainer = true;
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
view.Arrangement = ViewArrangement.Overlapped;
|
|
|
|
// Additional flags
|
|
view.Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable;
|
|
```
|
|
|
|
See [Arrangement Deep Dive](arrangement.md) for complete details.
|
|
|
|
---
|
|
|
|
## Complete Migration Example
|
|
|
|
Here's a complete v1 to v2 migration:
|
|
|
|
**v1:**
|
|
```csharp
|
|
using NStack;
|
|
using Terminal.Gui;
|
|
|
|
Application.Init();
|
|
|
|
var win = new Window(new Rect(0, 0, 50, 20), "Hello");
|
|
|
|
var label = new Label(1, 1, "Name:");
|
|
|
|
var textField = new TextField(10, 1, 30, "");
|
|
|
|
var button = new Button(10, 3, "OK");
|
|
button.Clicked += () =>
|
|
{
|
|
MessageBox.Query(50, 7, "Info", $"Hello, {textField.Text}", "Ok");
|
|
};
|
|
|
|
win.Add(label, textField, button);
|
|
|
|
Application.Top.Add(win);
|
|
Application.Run();
|
|
Application.Shutdown();
|
|
```
|
|
|
|
**v2:**
|
|
```csharp
|
|
using System;
|
|
using Terminal.Gui;
|
|
|
|
using (var app = Application.Create().Init())
|
|
{
|
|
var win = new Window
|
|
{
|
|
Title = "Hello",
|
|
Width = 50,
|
|
Height = 20
|
|
};
|
|
|
|
var label = new Label
|
|
{
|
|
Text = "Name:",
|
|
X = 1,
|
|
Y = 1
|
|
};
|
|
|
|
var textField = new TextField
|
|
{
|
|
X = 10,
|
|
Y = 1,
|
|
Width = 30
|
|
};
|
|
|
|
var button = new Button
|
|
{
|
|
Text = "OK",
|
|
X = 10,
|
|
Y = 3
|
|
};
|
|
button.Accepting += (s, e) =>
|
|
{
|
|
MessageBox.Query(app, "Info", $"Hello, {textField.Text}", "Ok");
|
|
};
|
|
|
|
win.Add(label, textField, button);
|
|
|
|
app.Run(win);
|
|
win.Dispose();
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Summary of Major Breaking Changes
|
|
|
|
| Category | v1 | v2 |
|
|
|----------|----|----|
|
|
| **Application** | Static `Application` | `IApplication` instances via `Application.Create()` |
|
|
| **Disposal** | Automatic | Explicit (`IDisposable` pattern) |
|
|
| **View Construction** | Constructors with Rect | Initializers with X, Y, Width, Height |
|
|
| **Layout** | Absolute/Computed distinction | Unified Pos/Dim system |
|
|
| **Colors** | Limited palette | 24-bit TrueColor default |
|
|
| **Types** | `Rect`, `NStack.ustring` | `Rectangle`, `System.String` |
|
|
| **Keyboard** | `KeyEvent`, hard-coded keys | `Key`, configurable bindings |
|
|
| **Mouse** | Screen-relative | Viewport-relative |
|
|
| **Scrolling** | `ScrollView` | Built-in on all Views |
|
|
| **Focus** | `CanFocus` default true | `CanFocus` default false |
|
|
| **Navigation** | `Enter`/`Leave` events | `HasFocusChanging`/`HasFocusChanged` |
|
|
| **Events** | Mixed patterns | Standard `EventHandler<EventArgs>` |
|
|
| **Terminology** | `Application.Top`, `TopLevels` | `TopRunnable`, `SessionStack` |
|
|
|
|
---
|
|
|
|
## Additional Resources
|
|
|
|
- [Application Deep Dive](application.md) - Complete application architecture
|
|
- [View Deep Dive](View.md) - View system details
|
|
- [Layout Deep Dive](layout.md) - Comprehensive layout guide
|
|
- [Keyboard Deep Dive](keyboard.md) - Keyboard input handling
|
|
- [Mouse Deep Dive](mouse.md) - Mouse input handling
|
|
- [Navigation Deep Dive](navigation.md) - Focus and navigation
|
|
- [Scrolling Deep Dive](scrolling.md) - Built-in scrolling system
|
|
- [Arrangement Deep Dive](arrangement.md) - Movable/resizable views
|
|
- [Configuration Deep Dive](config.md) - Configuration system
|
|
- [What's New in v2](newinv2.md) - New features overview
|
|
|
|
---
|
|
|
|
## Getting Help
|
|
|
|
- [GitHub Discussions](https://github.com/gui-cs/Terminal.Gui/discussions)
|
|
- [GitHub Issues](https://github.com/gui-cs/Terminal.Gui/issues)
|
|
- [API Documentation](~/api/index.md) |