mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-30 09:47:58 +01:00
* Add comprehensive unit tests for WindowsKeyConverter - Implement 118 parallelizable unit tests for WindowsKeyConverter - Cover ToKey and ToKeyInfo methods with full bidirectional testing - Test basic characters, modifiers, special keys, function keys - Test VK_PACKET Unicode/IME input - Test OEM keys, NumPad keys, and lock states - Include round-trip conversion tests - All tests passing successfully Fixes #4389 * Rename `start` parameter to `viewportXOffset` for clarity The `start` parameter in several methods and interfaces has been renamed to `viewportXOffset` to better reflect its purpose as the horizontal offset of the viewport during string rendering. - Updated method signatures in `ListViewWithSelection` to use `viewportXOffset` instead of `start`, including default values. - Modified the `RenderUstr` method in `ListViewWithSelection` to use `viewportXOffset` for calculating the starting index. - Renamed the `start` parameter to `viewportXOffset` in the `IListDataSource` interface and updated its documentation. - Replaced all occurrences of `start` with `viewportXOffset` in the `ListWrapper<T>` class, including method calls and logic. - Updated the `RenderUstr` method in `ListWrapper<T>` to use `viewportXOffset` for substring calculations. - Adjusted the test method in `ListViewTests.cs` to reflect the parameter name change. These changes improve code readability and make the parameter's role in rendering logic more explicit. * Remove WindowsKeyConverterTests class that was added by mistake * Modernized ListView and IListDataSource - Tons of new unit tests Refactored `ListView` and `IListDataSource` to improve readability, maintainability, and functionality. Introduced `ListWrapper<T>` as a default implementation of `IListDataSource` for easier integration with standard collections. Enhanced `ListView` with better handling of marking, selection, and scrolling. Replaced `viewportXOffset` with `viewportX` for horizontal scrolling. Added `EnsureSelectedItemVisible` to maintain visibility of the selected item. Updated `IListDataSource` with detailed XML documentation and added `SuspendCollectionChangedEvent` for bulk updates. Improved null safety with nullable reference types. Added comprehensive unit tests for `ListWrapper<T>` and `IListDataSource` to ensure robustness. Modernized the codebase with C# features like expression-bodied members and pattern matching. Fixed bugs related to `SelectedItem` validation and rendering artifacts. * Improve index validation in ComboBox and ListView Enhance robustness by adding stricter checks for valid indices in ComboBox and ListView. Updated conditions in the `_listview.SelectedItemChanged` event handler to ensure `e.Item` is non-negative before accessing `_searchSet`. Modified the `SetValue` method to use `e.Item` instead of `_listview.SelectedItem`. In ListView, updated the `OnSelectedChanged` method to validate that `SelectedItem` is non-negative (`>= 0`) before accessing the `Source` list. These changes prevent potential out-of-range errors and improve code safety. * Refactor and enhance test coverage across modules Refactored and added new tests to improve coverage, readability, and consistency across multiple test files. Key changes include: - **ShortcutTests.cs**: Added tests for `BindKeyToApplication` and removed redundant tests. - **SourcesManagerTests.cs**: Renamed `Update_*` tests to `Load_*` for clarity. - **ArrangementTests.cs**: Reintroduced `MouseGrabHandler` tests, added `ViewArrangement` flag tests, and improved structure. - **NeedsDrawTests.cs**: Replaced `Application.Screen.Size` with fixed dimensions for better isolation. - **DimAutoTests.cs**: Updated layout tests to use fixed dimensions. - **FrameTests.cs**: Standardized object initialization and validated frame behavior. - **SubViewTests.cs**: Improved formatting and modernized event handling. - **NumericUpDownTests.cs**: Decoupled layout tests from screen size. General improvements: - Enhanced formatting and removed redundant tests. - Added comments for clarity. - Introduced `ITestOutputHelper` for better debugging in `ArrangementTests`. * Refactor to use nullable types for better null safety Enabled nullable reference types across the codebase to improve null safety and prevent potential null reference issues. Refactored `SelectedItem` and related properties from `int` to `int?` to represent no selection with `null` instead of `-1`. Updated logic, event arguments, and method signatures to handle nullable values consistently. Simplified object initialization using modern C# syntax and improved code readability with interpolated strings. Added null checks and early returns to prevent runtime errors. Enhanced error handling by throwing `ArgumentOutOfRangeException` for invalid values. Updated tests to reflect the changes, replacing assertions for `-1` with `null` and ensuring proper handling of nullable values. Cleaned up redundant code and improved formatting for better maintainability. * on` functionality has been deprecated, refactored, or removed from the `Shortcut` class. * Refactor: Transition to instance-based architecture Updated `Run-LocalCoverage.ps1` to increase `--blame-hang-timeout` from 10s to 60s. Improved null safety in `GuiTestContext` by adding null-conditional operators. Commented out problematic code in `SetupFakeApplicationAttribute.cs` to prevent test hangs. Excluded `ViewBase` files from `UnitTests.Parallelizable.csproj` and removed redundant folder declarations. Simplified event handling in `IListDataSourceTests.cs` and updated `ListViewTests.cs` to use nullable reference types. Enhanced documentation to emphasize the transition to an instance-based application architecture. Updated examples in `application.md`, `multitasking.md`, and `navigation.md` to reflect the use of `Application.Create()` and `View.App`. Clarified the obsolescence of the static `Application` class. Revised table of contents in `toc.yml` to include new sections like "Application Deep Dive" and "Scheme Deep Dive." Added `dotnet-tools.json` for tool configuration. These changes improve maintainability, testability, and alignment with modern C# practices. * Refactor ListViewTests to use Terminal.Gui framework The `ListViewTests` class has been refactored to replace the `AutoInitShutdown` attribute with explicit application lifecycle management using `IApplication` and `app.Init()` from the `Terminal.Gui` framework. Key changes include: - Rewriting tests to use `Terminal.Gui`'s application lifecycle. - Adding a private `_output` field for logging test output via `ITestOutputHelper`. - Updating `DriverAssert.AssertDriverContentsWithFrameAre` to include `app.Driver` for UI verification. - Rewriting tests like `Clicking_On_Border_Is_Ignored`, `EnsureSelectedItemVisible_SelectedItem`, and others to align with the new framework. - Adding explicit calls to `app.Shutdown()` for proper cleanup. - Enabling nullable reference types with `#nullable enable`. - Updating `using` directives and `namespace` to reflect the new structure. These changes improve test maintainability, compatibility, and diagnostics. * Update Terminal.Gui/Views/CollectionNavigation/CollectionNavigatorBase.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Terminal.Gui/Views/CollectionNavigation/CollectionNavigatorBase.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Examples/UICatalog/UICatalogTop.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Terminal.Gui/Views/ListWrapper.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Terminal.Gui/Views/ListWrapper.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Updated the `SetMark` method to return `Source.IsMarked(SelectedItem.Value)` for consistency and removed an outdated comment questioning its correctness. Enhanced the exception message in the `SelectedItem` property setter to provide clearer guidance when the value is out of range. * Add comprehensive ListView behavior test coverage Added multiple test methods to validate `ListView` behavior: - `Vertical_ScrollBar_Hides_And_Shows_As_Needed`: Ensures the vertical scrollbar auto-hides/shows based on content height. - `Mouse_Wheel_Scrolls`: Verifies vertical scrolling with the mouse wheel updates `TopItem`. - `SelectedItem_With_Source_Null_Does_Nothing`: Confirms no exceptions occur when setting `SelectedItem` with a `null` source. - `Horizontal_Scroll`: Tests horizontal scrolling, including programmatic and mouse wheel interactions, ensuring `LeftItem` updates correctly. - `SetSourceAsync_SetsSource`: Validates the asynchronous `SetSourceAsync` method updates the source and item count. - `AllowsMultipleSelection_Set_To_False_Unmarks_All_But_Selected`: Ensures disabling multiple selection unmarks all but the selected item. - `Source_CollectionChanged_Remove`: Confirms `SelectedItem` and source count update correctly when items are removed from the source collection. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
972 lines
24 KiB
Markdown
972 lines
24 KiB
Markdown
# Configuration Management Deep Dive
|
|
|
|
Terminal.Gui provides a comprehensive configuration system that allows users and developers to customize application behavior and appearance through JSON configuration files. The [ConfigurationManager](~/api/Terminal.Gui.Configuration.ConfigurationManager.yml) enables persistent settings, themes, and application-specific preferences.
|
|
|
|
## Table of Contents
|
|
|
|
- [Overview](#overview)
|
|
- [Getting Started](#getting-started)
|
|
- [Configuration Scopes](#configuration-scopes)
|
|
- [Configuration Locations and Precedence](#configuration-locations-and-precedence)
|
|
- [Themes and Schemes](#themes-and-schemes)
|
|
- [Defining Configuration Properties](#defining-configuration-properties)
|
|
- [Loading and Applying Configuration](#loading-and-applying-configuration)
|
|
- [Events](#events)
|
|
- [What Can Be Configured](#what-can-be-configured)
|
|
- [Configuration File Format](#configuration-file-format)
|
|
- [Best Practices](#best-practices)
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
The [ConfigurationManager](~/api/Terminal.Gui.Configuration.ConfigurationManager.yml) provides:
|
|
|
|
- **Persistent Settings** - User preferences stored in JSON files
|
|
- **Theme System** - Named collections of visual settings
|
|
- **Scheme Management** - Color and text style definitions
|
|
- **Configuration Precedence** - Layered configuration from multiple sources
|
|
- **Runtime Configuration** - In-memory configuration without files
|
|
- **AOT Compatible** - Works with Native AOT compilation
|
|
|
|
### Key Features
|
|
|
|
- JSON-based configuration with schema validation
|
|
- Multiple configuration locations (user home, app directory, resources)
|
|
- Process-wide settings using static properties
|
|
- Built-in themes (Default, Dark, Light, etc.)
|
|
- Custom glyphs and Unicode characters
|
|
- Event-driven configuration changes
|
|
|
|
---
|
|
|
|
## Getting Started
|
|
|
|
### Enabling Configuration
|
|
|
|
**ConfigurationManager is disabled by default** and must be explicitly enabled:
|
|
|
|
```csharp
|
|
using Terminal.Gui.Configuration;
|
|
|
|
class Program
|
|
{
|
|
static void Main()
|
|
{
|
|
// Enable configuration with all sources
|
|
ConfigurationManager.Enable(ConfigLocations.All);
|
|
|
|
Application.Init();
|
|
// ... rest of app
|
|
}
|
|
}
|
|
```
|
|
|
|
### Quick Example
|
|
|
|
```csharp
|
|
// Enable configuration
|
|
ConfigurationManager.Enable(ConfigLocations.All);
|
|
|
|
// Listen for configuration changes
|
|
ConfigurationManager.Applied += (sender, e) =>
|
|
{
|
|
Console.WriteLine("Configuration applied!");
|
|
};
|
|
|
|
// Switch themes
|
|
ThemeManager.Theme = "Dark";
|
|
ConfigurationManager.Apply();
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration Scopes
|
|
|
|
Terminal.Gui uses three configuration scopes, each serving a different purpose:
|
|
|
|
### 1. SettingsScope
|
|
|
|
System-level settings that affect Terminal.Gui behavior. Only Terminal.Gui library developers can define [SettingsScope](~/api/Terminal.Gui.Configuration.SettingsScope.yml) properties.
|
|
|
|
```csharp
|
|
[ConfigurationProperty(Scope = typeof(SettingsScope))]
|
|
public static bool Force16Colors { get; set; } = false;
|
|
```
|
|
|
|
**Examples:**
|
|
- `Application.QuitKey` - Default key to quit applications
|
|
- `Application.Force16Colors` - Force 16-color mode
|
|
- `Key.Separator` - Character separating keys in key combinations
|
|
|
|
### 2. ThemeScope
|
|
|
|
Visual appearance settings that can be themed. Only Terminal.Gui library developers can define [ThemeScope](~/api/Terminal.Gui.Configuration.ThemeScope.yml) properties.
|
|
|
|
```csharp
|
|
[ConfigurationProperty(Scope = typeof(ThemeScope))]
|
|
public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
|
|
```
|
|
|
|
**Examples:**
|
|
- `Window.DefaultBorderStyle` - Default border style for windows
|
|
- `Dialog.DefaultShadow` - Default shadow style for dialogs
|
|
- `Schemes` - Color schemes for the theme
|
|
|
|
### 3. AppSettingsScope (Default)
|
|
|
|
Application-specific settings. Application developers can define [AppSettingsScope](~/api/Terminal.Gui.Configuration.AppSettingsScope.yml) properties for their apps.
|
|
|
|
```csharp
|
|
[ConfigurationProperty] // AppSettingsScope is default
|
|
public static string MyAppSetting { get; set; } = "default value";
|
|
```
|
|
|
|
**Important:**
|
|
- App developers **cannot** define `SettingsScope` or `ThemeScope` properties
|
|
- AppSettings property names must be globally unique (automatically prefixed with class name)
|
|
|
|
---
|
|
|
|
## Configuration Locations and Precedence
|
|
|
|
Configuration is loaded from multiple locations with increasing precedence (higher numbers override lower):
|
|
|
|
### ConfigLocations Enum
|
|
|
|
[ConfigLocations](~/api/Terminal.Gui.Configuration.ConfigLocations.yml) specifies where configuration can be loaded from:
|
|
|
|
1. **[ConfigLocations.HardCoded](~/api/Terminal.Gui.Configuration.ConfigLocations.yml)** (Lowest Precedence)
|
|
- Default values in code (static property initializers)
|
|
- Always available, even when ConfigurationManager is disabled
|
|
|
|
2. **[ConfigLocations.LibraryResources](~/api/Terminal.Gui.Configuration.ConfigLocations.yml)**
|
|
- Settings in `Terminal.Gui.dll` resources (`Terminal.Gui.Resources.config.json`)
|
|
- Defines default themes and settings for the library
|
|
|
|
3. **[ConfigLocations.Runtime](~/api/Terminal.Gui.Configuration.ConfigLocations.yml)**
|
|
- Settings in [ConfigurationManager.RuntimeConfig](~/api/Terminal.Gui.Configuration.ConfigurationManager.yml#Terminal_Gui_Configuration_ConfigurationManager_RuntimeConfig) string property
|
|
- In-memory configuration without files
|
|
|
|
4. **[ConfigLocations.AppResources](~/api/Terminal.Gui.Configuration.ConfigLocations.yml)**
|
|
- App-specific resources (`MyApp.Resources.config.json` or `Resources/config.json`)
|
|
- Embedded in the application assembly
|
|
|
|
5. **[ConfigLocations.AppHome](~/api/Terminal.Gui.Configuration.ConfigLocations.yml)**
|
|
- App-specific file in user's home directory (`~/.tui/MyApp.config.json`)
|
|
|
|
6. **[ConfigLocations.AppCurrent](~/api/Terminal.Gui.Configuration.ConfigLocations.yml)**
|
|
- App-specific file in current directory (`./.tui/MyApp.config.json`)
|
|
|
|
7. **[ConfigLocations.GlobalHome](~/api/Terminal.Gui.Configuration.ConfigLocations.yml)**
|
|
- Global file in user's home directory (`~/.tui/config.json`)
|
|
|
|
8. **[ConfigLocations.GlobalCurrent](~/api/Terminal.Gui.Configuration.ConfigLocations.yml)** (Highest Precedence)
|
|
- Global file in current directory (`./.tui/config.json`)
|
|
|
|
### Precedence Diagram
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[1. Hard-coded Defaults] --> B[2. Library Resources]
|
|
B --> C[3. Runtime Config]
|
|
C --> D[4. App Resources]
|
|
D --> E[5. App Home Directory]
|
|
E --> F[6. App Current Directory]
|
|
F --> G[7. Global Home Directory]
|
|
G --> H[8. Global Current Directory]
|
|
|
|
style A fill:#f9f9f9
|
|
style H fill:#90EE90
|
|
```
|
|
|
|
### File Locations
|
|
|
|
**Global Settings** (`config.json`):
|
|
- Windows: `C:\Users\username\.tui\config.json`
|
|
- macOS/Linux: `~/.tui/config.json` or `./.tui/config.json`
|
|
|
|
**App-Specific Settings** (`AppName.config.json`):
|
|
- Windows: `C:\Users\username\.tui\UICatalog.config.json`
|
|
- macOS/Linux: `~/.tui/UICatalog.config.json` or `./.tui/UICatalog.config.json`
|
|
|
|
---
|
|
|
|
## Themes and Schemes
|
|
|
|
### Theme System
|
|
|
|
A **Theme** is a named collection of visual settings bundled together. Terminal.Gui includes several built-in themes.
|
|
|
|
#### Built-in Themes
|
|
|
|
- **Default** - The default Terminal.Gui theme (matches hard-coded defaults)
|
|
- **Dark** - Dark color scheme with heavy borders
|
|
- **Light** - Light color scheme
|
|
- **TurboPascal 5** - Classic Turbo Pascal IDE colors
|
|
- **And more** - See `Terminal.Gui/Resources/config.json` for all built-in themes
|
|
|
|
#### Using Themes
|
|
|
|
```csharp
|
|
// Get current theme
|
|
ThemeScope currentTheme = ThemeManager.GetCurrentTheme();
|
|
|
|
// Get all available themes
|
|
Dictionary<string, ThemeScope> themes = ThemeManager.GetThemes();
|
|
|
|
// Get theme names
|
|
ImmutableList<string> themeNames = ThemeManager.GetThemeNames();
|
|
|
|
// Switch themes
|
|
ThemeManager.Theme = "Dark";
|
|
ConfigurationManager.Apply();
|
|
|
|
// Listen for theme changes
|
|
ThemeManager.ThemeChanged += (sender, e) =>
|
|
{
|
|
// Update UI based on new theme
|
|
};
|
|
```
|
|
|
|
### Scheme System
|
|
|
|
A **Scheme** defines the colors and text styles for a specific UI context (e.g., Dialog, Menu, TopLevel).
|
|
|
|
See the [Scheme Deep Dive](scheme.md) for complete details on the scheme system.
|
|
|
|
#### Built-in Schemes
|
|
|
|
[Schemes](~/api/Terminal.Gui.Drawing.Schemes.yml) enum defines the standard schemes:
|
|
|
|
- **TopLevel** - Top-level application windows
|
|
- **Base** - Default for most views
|
|
- **Dialog** - Dialogs and message boxes
|
|
- **Menu** - Menus and status bars
|
|
- **Error** - Error messages and dialogs
|
|
|
|
#### Working with Schemes
|
|
|
|
```csharp
|
|
// Get all schemes for current theme
|
|
Dictionary<string, Scheme> schemes = SchemeManager.GetCurrentSchemes();
|
|
|
|
// Get specific scheme
|
|
Scheme dialogScheme = SchemeManager.GetScheme(Schemes.Dialog);
|
|
|
|
// Get scheme names
|
|
ImmutableList<string> schemeNames = SchemeManager.GetSchemeNames();
|
|
|
|
// Add custom scheme
|
|
SchemeManager.AddScheme("MyScheme", new Scheme
|
|
{
|
|
Normal = new Attribute(Color.White, Color.Blue),
|
|
Focus = new Attribute(Color.Black, Color.Cyan)
|
|
});
|
|
|
|
// Listen for scheme changes
|
|
SchemeManager.CollectionChanged += (sender, e) =>
|
|
{
|
|
// Handle scheme changes
|
|
};
|
|
```
|
|
|
|
#### Scheme Structure
|
|
|
|
Each [Scheme](~/api/Terminal.Gui.Drawing.Scheme.yml) maps [VisualRole](~/api/Terminal.Gui.Drawing.VisualRole.yml) to [Attribute](~/api/Terminal.Gui.Drawing.Attribute.yml):
|
|
|
|
```json
|
|
{
|
|
"TopLevel": {
|
|
"Normal": {
|
|
"Foreground": "BrightGreen",
|
|
"Background": "Black",
|
|
"Style": "None"
|
|
},
|
|
"Focus": {
|
|
"Foreground": "White",
|
|
"Background": "Cyan",
|
|
"Style": "Bold"
|
|
},
|
|
"HotNormal": {
|
|
"Foreground": "Yellow",
|
|
"Background": "Black"
|
|
},
|
|
"HotFocus": {
|
|
"Foreground": "Blue",
|
|
"Background": "Cyan",
|
|
"Style": "Underline"
|
|
},
|
|
"Disabled": {
|
|
"Foreground": "DarkGray",
|
|
"Background": "Black",
|
|
"Style": "Faint"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Defining Configuration Properties
|
|
|
|
### Basic Property Definition
|
|
|
|
Application developers define settings using the [ConfigurationPropertyAttribute](~/api/Terminal.Gui.Configuration.ConfigurationPropertyAttribute.yml):
|
|
|
|
```csharp
|
|
public class MyApp
|
|
{
|
|
[ConfigurationProperty]
|
|
public static string MySetting { get; set; } = "Default Value";
|
|
|
|
[ConfigurationProperty]
|
|
public static int MaxItems { get; set; } = 100;
|
|
}
|
|
```
|
|
|
|
**Requirements:**
|
|
- Must be `public` or `internal`
|
|
- Must be `static`
|
|
- Must be a property (not a field)
|
|
- Must have a default value
|
|
|
|
### Property Naming
|
|
|
|
AppSettings properties are automatically prefixed with the class name to ensure global uniqueness:
|
|
|
|
```csharp
|
|
// Code
|
|
public class MyApp
|
|
{
|
|
[ConfigurationProperty]
|
|
public static string MySetting { get; set; } = "value";
|
|
}
|
|
|
|
// JSON
|
|
{
|
|
"AppSettings": {
|
|
"MyApp.MySetting": "value"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Scope Specification
|
|
|
|
Use the `Scope` parameter to specify non-default scopes (Terminal.Gui library only):
|
|
|
|
```csharp
|
|
// SettingsScope - Library-wide settings
|
|
[ConfigurationProperty(Scope = typeof(SettingsScope))]
|
|
public static bool Force16Colors { get; set; } = false;
|
|
|
|
// ThemeScope - Visual settings
|
|
[ConfigurationProperty(Scope = typeof(ThemeScope))]
|
|
public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
|
|
|
|
// AppSettingsScope - Application settings (default)
|
|
[ConfigurationProperty] // or explicitly: Scope = typeof(AppSettingsScope)
|
|
public static string MyAppSetting { get; set; } = "default";
|
|
```
|
|
|
|
### Omit Class Name (Advanced)
|
|
|
|
For library developers only, use `OmitClassName = true` for cleaner JSON:
|
|
|
|
```csharp
|
|
[ConfigurationProperty(Scope = typeof(ThemeScope), OmitClassName = true)]
|
|
public static Dictionary<string, Scheme> Schemes { get; set; } = new();
|
|
```
|
|
|
|
---
|
|
|
|
## Loading and Applying Configuration
|
|
|
|
### Enable with Load and Apply
|
|
|
|
The simplest approach - enable and load in one call:
|
|
|
|
```csharp
|
|
ConfigurationManager.Enable(ConfigLocations.All);
|
|
```
|
|
|
|
This:
|
|
1. Enables ConfigurationManager
|
|
2. Loads configuration from all locations
|
|
3. Applies settings to the application
|
|
|
|
### Granular Control
|
|
|
|
For more control, use [Load](~/api/Terminal.Gui.Configuration.ConfigurationManager.yml#Terminal_Gui_Configuration_ConfigurationManager_Load_Terminal_Gui_Configuration_ConfigLocations_) and [Apply](~/api/Terminal.Gui.Configuration.ConfigurationManager.yml#Terminal_Gui_Configuration_ConfigurationManager_Apply) separately:
|
|
|
|
```csharp
|
|
// Enable without loading
|
|
ConfigurationManager.Enable(ConfigLocations.None);
|
|
|
|
// Load from specific locations
|
|
ConfigurationManager.Load(ConfigLocations.GlobalHome | ConfigLocations.AppResources);
|
|
|
|
// Apply settings
|
|
ConfigurationManager.Apply();
|
|
```
|
|
|
|
### Runtime Configuration
|
|
|
|
Set configuration directly in code without files:
|
|
|
|
```csharp
|
|
ConfigurationManager.RuntimeConfig = @"
|
|
{
|
|
""Application.QuitKey"": ""Ctrl+Q"",
|
|
""Application.Force16Colors"": true
|
|
}";
|
|
|
|
ConfigurationManager.Enable(ConfigLocations.Runtime);
|
|
```
|
|
|
|
### Reset to Defaults
|
|
|
|
Reset all settings to hard-coded defaults:
|
|
|
|
```csharp
|
|
ConfigurationManager.ResetToHardCodedDefaults();
|
|
```
|
|
|
|
---
|
|
|
|
## Events
|
|
|
|
The ConfigurationManager provides events to track configuration changes:
|
|
|
|
### Applied Event
|
|
|
|
Raised after configuration is applied to the application:
|
|
|
|
```csharp
|
|
ConfigurationManager.Applied += (sender, e) =>
|
|
{
|
|
// Configuration has been applied
|
|
// Update UI or refresh views
|
|
};
|
|
```
|
|
|
|
### ThemeChanged Event
|
|
|
|
Raised when the active theme changes:
|
|
|
|
```csharp
|
|
ThemeManager.ThemeChanged += (sender, e) =>
|
|
{
|
|
// Theme has changed
|
|
// Refresh all views to use new theme
|
|
// From within a View, use: App?.Current?.SetNeedsDraw();
|
|
// Or access via IApplication instance: app.Current?.SetNeedsDraw();
|
|
};
|
|
```
|
|
|
|
### CollectionChanged Event
|
|
|
|
Raised when schemes collection changes:
|
|
|
|
```csharp
|
|
SchemeManager.CollectionChanged += (sender, e) =>
|
|
{
|
|
// Schemes have changed
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## What Can Be Configured
|
|
|
|
### Application Settings
|
|
|
|
System-wide settings from [SettingsScope](~/api/Terminal.Gui.Configuration.SettingsScope.yml):
|
|
|
|
```json
|
|
{
|
|
"Application.QuitKey": "Esc",
|
|
"Application.Force16Colors": false,
|
|
"Application.IsMouseDisabled": false,
|
|
"Application.ArrangeKey": "Ctrl+F5",
|
|
"Application.NextTabKey": "Tab",
|
|
"Application.PrevTabKey": "Shift+Tab",
|
|
"Application.NextTabGroupKey": "F6",
|
|
"Application.PrevTabGroupKey": "Shift+F6",
|
|
"Key.Separator": "+"
|
|
}
|
|
```
|
|
|
|
### View-Specific Settings
|
|
|
|
Settings for individual View types from [ThemeScope](~/api/Terminal.Gui.Configuration.ThemeScope.yml):
|
|
|
|
```json
|
|
{
|
|
"Window.DefaultBorderStyle": "Single",
|
|
"Window.DefaultShadow": "None",
|
|
"Dialog.DefaultBorderStyle": "Heavy",
|
|
"Dialog.DefaultShadow": "Transparent",
|
|
"Dialog.DefaultButtonAlignment": "End",
|
|
"FrameView.DefaultBorderStyle": "Rounded",
|
|
"Button.DefaultShadow": "None",
|
|
"PopoverMenu.DefaultKey": "Shift+F10",
|
|
"FileDialog.MaxSearchResults": 10000
|
|
}
|
|
```
|
|
|
|
### Glyphs
|
|
|
|
Customize the Unicode characters used for drawing:
|
|
|
|
```json
|
|
{
|
|
"Glyphs.RightArrow": "►",
|
|
"Glyphs.LeftArrow": "U+25C4",
|
|
"Glyphs.DownArrow": "\\u25BC",
|
|
"Glyphs.UpArrow": 965010,
|
|
"Glyphs.LeftBracket": "[",
|
|
"Glyphs.RightBracket": "]",
|
|
"Glyphs.Checked": "☑",
|
|
"Glyphs.UnChecked": "☐",
|
|
"Glyphs.Selected": "◉",
|
|
"Glyphs.UnSelected": "○"
|
|
}
|
|
```
|
|
|
|
Glyphs can be specified as:
|
|
- Unicode character: `"►"`
|
|
- U+ format: `"U+25C4"`
|
|
- UTF-16 format: `"\\u25BC"`
|
|
- Decimal codepoint: `965010`
|
|
|
|
### Discovering Configuration Properties
|
|
|
|
To find all available configuration properties:
|
|
|
|
```csharp
|
|
// Get hard-coded configuration
|
|
SettingsScope hardCoded = ConfigurationManager.GetHardCodedConfig();
|
|
|
|
// Iterate through all properties
|
|
foreach (var property in hardCoded)
|
|
{
|
|
Console.WriteLine($"{property.Key} = {property.Value}");
|
|
}
|
|
```
|
|
|
|
Or search the source code for `[ConfigurationProperty]` attributes.
|
|
|
|
---
|
|
|
|
## Themes and Schemes
|
|
|
|
### Theme Structure
|
|
|
|
A theme is a named collection bundling visual settings and schemes:
|
|
|
|
```json
|
|
{
|
|
"Themes": [
|
|
{
|
|
"Dark": {
|
|
"Dialog.DefaultBorderStyle": "Heavy",
|
|
"Dialog.DefaultShadow": "Transparent",
|
|
"Window.DefaultBorderStyle": "Single",
|
|
"Button.DefaultShadow": "Opaque",
|
|
"Schemes": [
|
|
{
|
|
"TopLevel": {
|
|
"Normal": { "Foreground": "BrightGreen", "Background": "Black" },
|
|
"Focus": { "Foreground": "White", "Background": "Cyan" }
|
|
},
|
|
"Dialog": {
|
|
"Normal": { "Foreground": "Black", "Background": "Gray" }
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Creating Custom Themes
|
|
|
|
Custom themes can be defined in configuration files:
|
|
|
|
```json
|
|
{
|
|
"Themes": [
|
|
{
|
|
"MyCustomTheme": {
|
|
"Window.DefaultBorderStyle": "Double",
|
|
"Dialog.DefaultShadow": "Opaque",
|
|
"Schemes": [
|
|
{
|
|
"Base": {
|
|
"Normal": {
|
|
"Foreground": "Cyan",
|
|
"Background": "Black",
|
|
"Style": "Bold"
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Then activate the theme:
|
|
|
|
```csharp
|
|
ThemeManager.Theme = "MyCustomTheme";
|
|
ConfigurationManager.Apply();
|
|
```
|
|
|
|
### Theme Inheritance
|
|
|
|
Themes only override specified properties. To build on an existing theme:
|
|
|
|
```csharp
|
|
// Start with default theme
|
|
ThemeManager.Theme = "Default";
|
|
ConfigurationManager.Apply();
|
|
|
|
// Apply custom theme (overrides only what's specified)
|
|
ThemeManager.Theme = "MyCustomTheme";
|
|
ConfigurationManager.Apply();
|
|
```
|
|
|
|
### TextStyle in Schemes
|
|
|
|
Each [Attribute](~/api/Terminal.Gui.Drawing.Attribute.yml) in a scheme now includes [TextStyle](~/api/Terminal.Gui.Drawing.TextStyle.yml):
|
|
|
|
```json
|
|
{
|
|
"Normal": {
|
|
"Foreground": "White",
|
|
"Background": "Blue",
|
|
"Style": "Bold, Underline"
|
|
}
|
|
}
|
|
```
|
|
|
|
Available styles (combinable):
|
|
- `None`
|
|
- `Bold`
|
|
- `Faint`
|
|
- `Italic`
|
|
- `Underline`
|
|
- `Blink`
|
|
- `Reverse`
|
|
- `Strikethrough`
|
|
|
|
---
|
|
|
|
## Configuration File Format
|
|
|
|
### Schema
|
|
|
|
All configuration files must conform to the JSON schema:
|
|
|
|
**Schema URL:** https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json
|
|
|
|
### Root Structure
|
|
|
|
```json
|
|
{
|
|
"$schema": "https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json",
|
|
|
|
// SettingsScope properties
|
|
"Application.QuitKey": "Esc",
|
|
"Application.Force16Colors": false,
|
|
|
|
// Current theme name
|
|
"Theme": "Dark",
|
|
|
|
// Theme definitions
|
|
"Themes": [
|
|
{
|
|
"Dark": {
|
|
// ThemeScope properties
|
|
"Window.DefaultBorderStyle": "Single",
|
|
// Schemes
|
|
"Schemes": [ ... ]
|
|
}
|
|
}
|
|
],
|
|
|
|
// AppSettings
|
|
"AppSettings": {
|
|
"MyApp.MySetting": "value"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Complete Example
|
|
|
|
See the default configuration file:
|
|
|
|
[!code-json[config.json](../../Terminal.Gui/Resources/config.json)]
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### For Application Developers
|
|
|
|
**1. Enable Early**
|
|
|
|
Enable ConfigurationManager at the start of `Main()`, before `Application.Init()`:
|
|
|
|
```csharp
|
|
static void Main()
|
|
{
|
|
ConfigurationManager.Enable(ConfigLocations.All);
|
|
Application.Init();
|
|
// ...
|
|
}
|
|
```
|
|
|
|
**2. Use AppSettings for App Configuration**
|
|
|
|
```csharp
|
|
public class MyApp
|
|
{
|
|
[ConfigurationProperty]
|
|
public static bool ShowWelcomeMessage { get; set; } = true;
|
|
|
|
[ConfigurationProperty]
|
|
public static string DefaultDirectory { get; set; } = "";
|
|
}
|
|
```
|
|
|
|
**3. Ship Default Configuration as Resource**
|
|
|
|
Include a `Resources/config.json` file in your app:
|
|
|
|
```xml
|
|
<ItemGroup>
|
|
<EmbeddedResource Include="Resources\config.json" />
|
|
</ItemGroup>
|
|
```
|
|
|
|
**4. Handle Configuration Changes**
|
|
|
|
```csharp
|
|
ConfigurationManager.Applied += (sender, e) =>
|
|
{
|
|
// Refresh UI when configuration changes
|
|
RefreshAllViews();
|
|
};
|
|
```
|
|
|
|
### For Library Developers
|
|
|
|
**1. Use Appropriate Scopes**
|
|
|
|
- `SettingsScope` - For system-wide behavior
|
|
- `ThemeScope` - For visual appearance that should be themeable
|
|
- Don't use `AppSettingsScope` in library code
|
|
|
|
**2. Provide Meaningful Defaults**
|
|
|
|
```csharp
|
|
[ConfigurationProperty(Scope = typeof(ThemeScope))]
|
|
public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
|
|
```
|
|
|
|
**3. Document Configuration Properties**
|
|
|
|
```csharp
|
|
/// <summary>
|
|
/// Gets or sets the default border style for all Windows.
|
|
/// </summary>
|
|
[ConfigurationProperty(Scope = typeof(ThemeScope))]
|
|
public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
|
|
```
|
|
|
|
### Process-Wide Settings
|
|
|
|
> [!IMPORTANT]
|
|
> Configuration settings are applied at the **process level**.
|
|
>
|
|
> Since configuration properties are static, changes affect all applications in the same process. This is typically not an issue for normal applications, but can affect scenarios with:
|
|
> - Multiple Terminal.Gui apps in the same process
|
|
> - Unit tests running in parallel
|
|
> - Hot reload scenarios
|
|
|
|
---
|
|
|
|
## Advanced Topics
|
|
|
|
### JSON Error Handling
|
|
|
|
Control how JSON parsing errors are handled:
|
|
|
|
```json
|
|
{
|
|
"ConfigurationManager.ThrowOnJsonErrors": true
|
|
}
|
|
```
|
|
|
|
- `false` (default) - Silent failures, errors logged
|
|
- `true` - Throws exceptions on JSON parsing errors
|
|
|
|
### Manually Trigger Updates
|
|
|
|
Update ConfigurationManager to reflect current static property values:
|
|
|
|
```csharp
|
|
// Change a setting programmatically
|
|
Application.QuitKey = Key.Q.WithCtrl;
|
|
|
|
// Update ConfigurationManager to reflect the change
|
|
ConfigurationManager.UpdateToCurrentValues();
|
|
|
|
// Save to file (if needed)
|
|
string json = ConfigurationManager.Serialize();
|
|
File.WriteAllText("my-config.json", json);
|
|
```
|
|
|
|
### Disable ConfigurationManager
|
|
|
|
Disable and optionally reset to defaults:
|
|
|
|
```csharp
|
|
// Disable but keep current settings
|
|
ConfigurationManager.Disable(resetToHardCodedDefaults: false);
|
|
|
|
// Disable and reset to hard-coded defaults
|
|
ConfigurationManager.Disable(resetToHardCodedDefaults: true);
|
|
```
|
|
|
|
### File System Watching
|
|
|
|
Watch for configuration file changes:
|
|
|
|
```csharp
|
|
var watcher = new FileSystemWatcher(
|
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".tui"));
|
|
watcher.Filter = "*.json";
|
|
watcher.Changed += (s, e) =>
|
|
{
|
|
ConfigurationManager.Load(ConfigLocations.GlobalHome);
|
|
ConfigurationManager.Apply();
|
|
};
|
|
watcher.EnableRaisingEvents = true;
|
|
```
|
|
|
|
See UICatalog's `ConfigurationEditor` scenario for a complete example.
|
|
|
|
---
|
|
|
|
## Examples
|
|
|
|
### Example 1: Simple Theme Switching
|
|
|
|
```csharp
|
|
using Terminal.Gui;
|
|
using Terminal.Gui.Configuration;
|
|
|
|
ConfigurationManager.Enable(ConfigLocations.All);
|
|
Application.Init();
|
|
|
|
var themeSelector = new ComboBox
|
|
{
|
|
X = 1,
|
|
Y = 1,
|
|
Width = 20
|
|
};
|
|
themeSelector.SetSource(ThemeManager.GetThemeNames());
|
|
themeSelector.SelectedItemChanged += (s, e) =>
|
|
{
|
|
ThemeManager.Theme = e.Value.ToString();
|
|
ConfigurationManager.Apply();
|
|
};
|
|
|
|
Application.Run(new Window { Title = "Theme Demo" }).Add(themeSelector);
|
|
Application.Shutdown();
|
|
```
|
|
|
|
### Example 2: Custom Application Settings
|
|
|
|
```csharp
|
|
public class MyApp
|
|
{
|
|
[ConfigurationProperty]
|
|
public static string LastOpenedFile { get; set; } = "";
|
|
|
|
[ConfigurationProperty]
|
|
public static int WindowWidth { get; set; } = 80;
|
|
|
|
[ConfigurationProperty]
|
|
public static int WindowHeight { get; set; } = 25;
|
|
}
|
|
|
|
// Enable and use
|
|
ConfigurationManager.Enable(ConfigLocations.All);
|
|
|
|
// Settings are automatically loaded and applied
|
|
var window = new Window
|
|
{
|
|
Width = MyApp.WindowWidth,
|
|
Height = MyApp.WindowHeight
|
|
};
|
|
|
|
// Later, save updated settings
|
|
MyApp.WindowWidth = 100;
|
|
ConfigurationManager.UpdateToCurrentValues();
|
|
// Could save to file here
|
|
```
|
|
|
|
### Example 3: Runtime Configuration
|
|
|
|
```csharp
|
|
ConfigurationManager.RuntimeConfig = @"
|
|
{
|
|
""Application.QuitKey"": ""Ctrl+Q"",
|
|
""Application.Force16Colors"": true,
|
|
""Theme"": ""Dark""
|
|
}";
|
|
|
|
ConfigurationManager.Enable(ConfigLocations.Runtime);
|
|
|
|
// Settings are now applied
|
|
// QuitKey is Ctrl+Q
|
|
// 16-color mode is forced
|
|
// Dark theme is active
|
|
```
|
|
|
|
---
|
|
|
|
## See Also
|
|
|
|
- **[Scheme Deep Dive](scheme.md)** - Color scheme details
|
|
- **[Drawing Deep Dive](drawing.md)** - Color and attribute system
|
|
- **[View Deep Dive](View.md)** - View configuration properties
|
|
- **[Theme Schema](https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json)** - JSON schema for validation
|
|
- **[Default Config](../../Terminal.Gui/Resources/config.json)** - Complete default configuration
|
|
|
|
### UICatalog Examples
|
|
|
|
The UICatalog application demonstrates configuration management:
|
|
|
|
- **Configuration Editor** - Interactive editor for configuration files
|
|
- **Themes** - Theme viewer and selector
|
|
- **File System Watcher** - Automatic reload on configuration file changes
|
|
|
|
### API Reference
|
|
|
|
- [ConfigurationManager](~/api/Terminal.Gui.Configuration.ConfigurationManager.yml)
|
|
- [ConfigLocations](~/api/Terminal.Gui.Configuration.ConfigLocations.yml)
|
|
- [SettingsScope](~/api/Terminal.Gui.Configuration.SettingsScope.yml)
|
|
- [ThemeScope](~/api/Terminal.Gui.Configuration.ThemeScope.yml)
|
|
- [AppSettingsScope](~/api/Terminal.Gui.Configuration.AppSettingsScope.yml)
|
|
- [ThemeManager](~/api/Terminal.Gui.Configuration.ThemeManager.yml)
|
|
- [SchemeManager](~/api/Terminal.Gui.Configuration.SchemeManager.yml)
|
|
- [ConfigurationPropertyAttribute](~/api/Terminal.Gui.Configuration.ConfigurationPropertyAttribute.yml)
|