mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-30 09:47:58 +01:00
* touching publish.yml * ColorScheme->Scheme * ColorScheme->Scheme 2 * Prototype of GetAttributeForRole * Badly broke CM * Further Badly broke CM * Refactored CM big-time. View still broken * All unit test pass again. Tons added. CM is still WIP, but Schemes is not mostly refactored and working. * Actually: All unit test pass again. Tons added. CM is still WIP, but Schemes is not mostly refactored and working. * Bug fixes. DeepMemberWiseClone cleanup * Further cleanup of Scope<T>, ConfigProperty, etc. * Made ConfigManager thread safe. * WIP: Broken * WIP: new deep clone impl * WIP: new deep clone impl is done. Now fixing CM * WIP: - config.md - Working on AOT clean up - Core CM is broken; but known. * WIP * Merged. Removed CM from Application.Init * WIP * More WIP; Less broke * All CM unit tests pass... Not sure if it actually works though * All unit tests pass... Themes are broken though in UI Cat * CM Ready for review? * Fixed failures due to TextStyles PR * Working on Scheme/Attribute * Working on Scheme/Attribute 2 * Working on Scheme/Attribute 3 * Working on Scheme/Attribute 4 * Working on Scheme/Attribute 5 * Working on Scheme/Attribute 6 * Added test to show how awful memory usage is * Improved schema. Updated config.json * Nade Scope<T> concurrentdictionary and added test to prove * Made Themes ConcrurrentDictionary. Added bunches of tests * Code cleanup * Code cleanup 2 * Code cleanup 3 * Tweaking Scheme * ClearJsonErrors * ClearJsonErrors2 * Updated Attribute API * It all (mostly) works! * Skip odd unit test * Messed with Themes * Theme tweaks * Code reorg. New .md stuff * Fixed Enabled. Added mock driver * Fixed a bunch of View.Enabled related issues * Scheme -> Get/SetScheme() * Cleanup * Cleanup2 * Broke something * Fixed everything * Made CM.Enable better * Text Style Scenario * Added comments * Fixed UI Catalog Theme Changing * Fixed more dynamic CM update stuff * Warning cleanup * New Default Theme * fixed unit test * Refactoring Scheme and Attribute to fix inheritance * more unit tests * ConfigProperty is not updating schemes correctly * All unit tests pass. Code cleanup * All unit tests pass. Code cleanup2 * Fixed unit tests * Upgraded TextField and TextView * Fixed TextView !Enabled bug * More updates to TextView. More unit tests for SchemeManager * Upgraded CharMap * API docs * Fixe HexView API * upgrade HexView * Fixed shortcut KeyView * Fixed more bugs. Added new themes * updated themes * upgraded Border * Fixed themes memory usage...mostly * Fixed themes memory usage...mostly2 * Fixed themes memory usage...2 * Fixed themes memory usage...3 * Added new colors * Fixed GetHardCodedConfig bug * Added Themes Scenario - WIP * Added Themes Scenario * Tweaked Themes Scenario * Code cleanup * Fixed json schmea * updated deepdives * updated deepdives * Tweaked Themes Scenario * Made Schemes a concurrent dict * Test cleanup * Thread safe ConfigProperty tests * trying to make things more thread safe * more trying to make things more thread safe * Fixing bugs in shadowview * Fixing bugs in shadowview 2 * Refactored GetViewsUnderMouse to GetViewsUnderLocation etc... * Fixed dupe unit tests? * Added better description of layout and coordiantes to deep dive * Added better description of layout and coordiantes to deep dive * Modified tests that call v2.AddTimeout; they were returning true which means restart the timer! This was causing mac/linux unit test failures. I think * Fixed auto scheme. Broke TextView/TextField selection * Realized Attribute.IsExplicitlySet is stupid; just use nullable * Fixed Attribute. Simplified. MOre theme testing * Updated themes again * GetViewsUnderMouse to GetViewsUnderLocation broke TransparentMouse. * Fixing mouseunder bugs * rewriting... * All working again. Shadows are now slick as snot. GetViewsUnderLocation is rewritten to actually work and be readable. Tons more low-level unit tests. Margin is now actually ViewportSettings.Transparent. * Code cleanup * Code cleanup * Code cleanup of color apis * Fixed Hover/Highlight * Update Examples/UICatalog/Scenarios/AllViewsTester.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Examples/UICatalog/Scenarios/CharacterMap/CharacterMap.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update Examples/UICatalog/Scenarios/Clipping.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fixed race condition? * reverted * Simplified Attribute API by removing events from SetAttributeForRole * Removed recursion from GetViewsAtLocation * Removed unneeded code * Code clean up. Fixed Scheme bug. * reverted temporary disable * Adjusted scheme algo * Upgraded TextValidateField * Fixed TextValidate bugs * Tweaks * Frameview rounded border by default * API doc cleanup * Readme fix * Addressed tznind feeback * Fixed more unit test issues by protecting Application statics from being set if Application.Initialized is not true * Fixed more unit test issues by protecting Application statics from being set if Application.Initialized is not true 2 * cleanup --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
98 lines
8.6 KiB
Markdown
98 lines
8.6 KiB
Markdown
# Scheme Deep Dive
|
|
|
|
See [Drawing](drawing.md) for an overview of the drawing system and [Configuration](config.md) for an overview of the configuration system.
|
|
|
|
## Scheme Overview
|
|
|
|
A Scheme is named a mapping from `VisualRole`s (e.g. `VisualRole.Focus`) to `Attribute`s, defining how a `View` should look based on its purpose (e.g. Menu or Dialog). @Terminal.Gui.SchemeManager.Schemes is a dictionary of `Scheme`s, indexed by name.
|
|
|
|
A Scheme defines how Views look based on their semantic purpose. The following schemes are supported:
|
|
|
|
| Scheme Name | Description |
|
|
|:-----|:--------|
|
|
| **Base** | The base scheme used for most Views. |
|
|
| **Dialog** | The dialog scheme; used for Dialog, MessageBox, and other views dialog-like views. |
|
|
| **Error** | The scheme for showing errors, such as in `ErrorQuery`. |
|
|
| **Menu** | The menu scheme; used for Terminal.Gui.Menu, MenuBar, and StatusBar. |
|
|
| **TopLevel** | The application Toplevel scheme; used for the Toplevel View. |
|
|
|
|
@Terminal.Gui.SchemeManager manages the set of available schemes and provides a set of convenience methods for getting the current scheme and for overriding the default values for these schemes.
|
|
|
|
```csharp
|
|
Scheme dialogScheme = SchemeManager.GetScheme (Schemes.Dialog);
|
|
```
|
|
|
|
[ConfigurationManager](config.md) can be used to override the default values for these schemes and add additional schemes.
|
|
|
|
### Scheme Inheritance
|
|
|
|
A `Scheme` enables consistent, semantic theming of UI elements by associating each visual state with a specific style. Each property (e.g., `Normal` or `Focus`) is an @Terminal.Gui.Attribute.
|
|
|
|
Only `Normal` is required. If other properties are not explicitly set, its value is derived from other roles (typically `Normal`) using well-defined inheritance rules. See the source code for the `Scheme` class for more details.
|
|
|
|
### Flexible Scheme Management in `Terminal.Gui.View`
|
|
|
|
A `View`'s appearance is primarily determined by its `Scheme`, which maps semantic `VisualRole`s (like `Normal`, `Focus`, `Disabled`) to specific `Attribute`s (foreground color, background color, and text style). `Terminal.Gui` provides a flexible system for managing these schemes:
|
|
|
|
1. **Scheme Inheritance (Default Behavior)**:
|
|
* By default, if a `View` does not have a `Scheme` explicitly set, it inherits the `Scheme` from its `SuperView` (its parent in the view hierarchy).
|
|
* This cascading behavior allows for consistent styling across related views. If no `SuperView` has a scheme, (e.g., if the view is a top-level view), it ultimately falls back to the "Base" scheme defined in `SchemeManager.GetCurrentSchemes()`.
|
|
* The `GetScheme()` method implements this logic:
|
|
* It first checks if a scheme has been explicitly set via the `_scheme` field (see point 2).
|
|
* If not, and if `SchemeName` is set, it tries to resolve the scheme by name from `SchemeManager`.
|
|
* If still no scheme, it recursively calls `SuperView.GetScheme()`.
|
|
* As a final fallback, it uses `SchemeManager.GetCurrentSchemes()["Base"]`.
|
|
|
|
2. **Explicit Scheme Assignment**:
|
|
* You can directly assign a `Scheme` object to a `View` using the `View.Scheme` property (which calls `SetScheme(value)`). This overrides any inherited scheme. The `HasScheme` property will then return `true`.
|
|
* Alternatively, you can set the `View.SchemeName` property to the name of a scheme registered in `SchemeManager`. If `Scheme` itself hasn't been directly set, `GetScheme()` will use `SchemeName` to look up the scheme. This is useful for declarative configurations (e.g., from a JSON file).
|
|
* The `SetScheme(Scheme? scheme)` method updates the internal `_scheme` field. If the new scheme is different from the current one, it marks the view for redraw (`SetNeedsDraw()`) to reflect the visual change. It also handles a special case for `Border` to ensure its scheme is updated if it `HasScheme`.
|
|
|
|
3. **Event-Driven Customization**:
|
|
The scheme resolution and application process includes events that allow for fine-grained control and customization:
|
|
|
|
* **`GettingScheme` Event (`View.Scheme.cs`)**:
|
|
* This event is raised within `GetScheme()` *before* the default logic (inheritance, `SchemeName` lookup, or explicit `_scheme` usage) fully determines the scheme.
|
|
* Subscribers (which could be the `SuperView`, a `SubView`, or any other interested component) can handle this event.
|
|
* In the event handler, you can:
|
|
* **Modify the scheme**: Set `args.NewScheme` to a different `Scheme` object.
|
|
* **Cancel default resolution**: Set `args.Cancel = true`. If canceled, the `Scheme` provided in `args.NewScheme` (which might have been modified by the handler) is returned directly by `GetScheme()`.
|
|
* The `OnGettingScheme(out Scheme? scheme)` virtual method is called first, allowing derived classes to provide a scheme directly.
|
|
|
|
* **`SettingScheme` Event (`View.Scheme.cs`)**:
|
|
* This event is raised within `SetScheme(Scheme? scheme)` *before* the `_scheme` field is actually updated.
|
|
* Subscribers can cancel the scheme change by setting `args.Cancel = true` in the event handler.
|
|
* The `OnSettingScheme(in Scheme? scheme)` virtual method is called first, allowing derived classes to prevent the scheme from being set.
|
|
|
|
4. **Retrieving and Applying Attributes for Visual Roles (`View.Attribute.cs`)**:
|
|
Once a `View` has determined its active `Scheme` (via `GetScheme()`), it uses this scheme to get specific `Attribute`s for rendering different parts of itself based on their `VisualRole`.
|
|
|
|
* **`GetAttributeForRole(VisualRole role)`**:
|
|
* This method first retrieves the base `Attribute` for the given `role` from the `View`'s current `Scheme` (`GetScheme()!.GetAttributeForRole(role)`).
|
|
* It then raises the `GettingAttributeForRole` event (and calls the `OnGettingAttributeForRole` virtual method).
|
|
* Subscribers to `GettingAttributeForRole` can:
|
|
* **Modify the attribute**: Change the `args.NewValue` (which is passed by `ref` as `schemeAttribute` to the event).
|
|
* **Cancel default behavior**: Set `args.Cancel = true`. The (potentially modified) `args.NewValue` is then returned.
|
|
* Crucially, if the `View` is `Enabled == false` and the requested `role` is *not* `VisualRole.Disabled`, this method will recursively call itself to get the `Attribute` for `VisualRole.Disabled`. This ensures disabled views use their designated disabled appearance.
|
|
|
|
* **`SetAttributeForRole(VisualRole role)`**:
|
|
* This method is used to tell the `ConsoleDriver` which `Attribute` to use for subsequent drawing operations (like `AddRune` or `AddStr`).
|
|
* It first determines the appropriate `Attribute` for the `role` from the current `Scheme` by calling `GetAttributeForRole`.
|
|
|
|
* **`SetAttribute(Attribute attribute)`**:
|
|
* This is a more direct way to set the driver's current attribute, bypassing the scheme and role system. It's generally preferred to use `SetAttributeForRole` to maintain consistency with the `Scheme`.
|
|
|
|
### Impact of SuperViews and SubViews via Events
|
|
|
|
* **SuperView Influence**: A `SuperView` can subscribe to its `SubView`'s `GettingScheme` or `GettingAttributeForRole` events. This would allow a `SuperView` to dynamically alter how its children determine their schemes or specific attributes, perhaps based on the `SuperView`'s state or other application logic. For example, a container view might want all its children to adopt a slightly modified version of its own scheme under certain conditions.
|
|
|
|
* **SubView Influence (Less Common for Scheme of Parent)**: While a `SubView` *could* subscribe to its `SuperView`'s scheme events, this is less typical for influencing the `SuperView`'s *own* scheme. It's more common for a `SubView` to react to changes in its `SuperView`'s scheme if needed, or to manage its own scheme independently.
|
|
|
|
* **General Event Usage**: These events are powerful for scenarios where:
|
|
* A specific `View` instance needs a unique, dynamically calculated appearance that isn't easily captured by a static `Scheme` object.
|
|
* External logic needs to intercept and modify appearance decisions.
|
|
* Derived `View` classes want to implement custom scheme or attribute resolution logic by overriding the `On...` methods.
|
|
|
|
In summary, `Terminal.Gui` offers a layered approach to scheme management: straightforward inheritance and explicit setting for common cases, and a robust event system for advanced customization and dynamic control over how views derive and apply their visual attributes. This allows developers to achieve a wide range of visual styles and behaviors.
|
|
|