From dc14ec8da90cc3ef2cbb23ac318e52aadf635811 Mon Sep 17 00:00:00 2001 From: journaltraces Date: Sat, 15 Aug 2026 22:17:32 +0300 Subject: [PATCH 1/2] docs: update AddASetting guide --- doc/cascadia/AddASetting.md | 165 ++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 75 deletions(-) diff --git a/doc/cascadia/AddASetting.md b/doc/cascadia/AddASetting.md index 2e6c99c211b..1e082116182 100644 --- a/doc/cascadia/AddASetting.md +++ b/doc/cascadia/AddASetting.md @@ -6,83 +6,82 @@ Adding a setting to Windows Terminal is fairly straightforward. This guide serve The Terminal Settings Model (`Microsoft.Terminal.Settings.Model`) is responsible for (de)serializing and exposing settings. -### `INHERITABLE_SETTING` macro +### `INHERITABLE_SETTING` and `INHERITABLE_PROFILE_SETTING` macros -The `INHERITABLE_SETTING` macro can be used to implement inheritance for your new setting and store the setting in the settings model. It takes three parameters: -- `type`: the type that the setting will be stored as -- `name`: the name of the variable for storage -- `defaultValue`: the value to use if the user does not define the setting anywhere +The Terminal Settings Model uses inheritable settings to expose properties that can be overridden by a more specific settings object. + +There are two related macros with different purposes: + +- `INHERITABLE_SETTING(Type, Name)` is defined in `IInheritable.idl.h` and is used in IDL to expose a setting property together with its `Has()` and `Clear()` methods. +- `INHERITABLE_SETTING(projectedType, type, name, ...)` is defined in `IInheritable.h` and provides the C++ implementation for an inheritable setting. + +Profile settings use `INHERITABLE_PROFILE_SETTING(Type, Name)` in `Profile.idl`. This is a specialized profile-setting macro that exposes the setting and its override source. + +The standard profile settings are listed in the `MTSM_PROFILE_SETTINGS` X-macro in `src/cascadia/TerminalSettingsModel/MTSMSettings.h`. Each entry uses the following format: + +```text +(type, name, jsonKey, defaultArgs) +``` + +The `defaultArgs` are optional. + +`Profile.idl` and `MTSM_PROFILE_SETTINGS` have different roles: + +- `Profile.idl` defines the WinRT API surface for the setting. +- `MTSM_PROFILE_SETTINGS` provides the metadata used by the common C++ settings-model implementation. ### Adding a Profile setting -This tutorial will add `CloseOnExitMode CloseOnExit` as a profile setting. +Most profile settings use the standard `MTSM_PROFILE_SETTINGS` pattern. -1. In `Profile.h`, declare/define the setting: +1. Add the setting to `MTSM_PROFILE_SETTINGS` in `src/cascadia/TerminalSettingsModel/MTSMSettings.h`. + +For example: ```c++ -INHERITABLE_SETTING(CloseOnExitMode, CloseOnExit, CloseOnExitMode::Graceful) +X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Automatic) ``` -2. In `Profile.idl`, expose the setting via WinRT: +2. Add the corresponding property to `src/cascadia/TerminalSettingsModel/Profile.idl` with `INHERITABLE_PROFILE_SETTING`. ```c++ -Boolean HasCloseOnExit(); -void ClearCloseOnExit(); -CloseOnExitMode CloseOnExit; +INHERITABLE_PROFILE_SETTING(CloseOnExitMode, CloseOnExit); ``` -3. In `Profile.cpp`, add (de)serialization and copy logic: +The IDL type must use the spelling appropriate for `Profile.idl`. -```c++ -// Top of file: -// - Add the serialization key -static constexpr std::string_view CloseOnExitKey{ "closeOnExit" }; +3. Do not add the normal copy, JSON layering, or JSON serialization code to `Profile.cpp` manually. -// CopySettings() or Copy(): -// - The setting is exposed in the Settings UI -profile->_CloseOnExit = source->_CloseOnExit; +`Profile.cpp` applies `MTSM_PROFILE_SETTINGS` to generate the common copy, JSON layering, and JSON serialization logic for settings in the list. -// LayerJson(): -// - get the value from the JSON -JsonUtils::GetValueForKey(json, CloseOnExitKey, _CloseOnExit); +4. If the setting requires custom JSON handling, follow the implementation of an existing setting with similar requirements. -// ToJson(): -// - write the value to the JSON -JsonUtils::SetValueForKey(json, CloseOnExitKey, _CloseOnExit); -``` +Settings that do not fit the standard `MTSM_PROFILE_SETTINGS` pattern may require setting-specific handling in `Profile.h` and/or `Profile.cpp`. Examples include settings that are optional or require custom parsing. - - If the setting is not a primitive type, in `TerminalSettingsSerializationHelpers.h` add (de)serialization logic for the accepted values: +Examples of settings with special handling include: -```c++ -// For enum values... -JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::CloseOnExitMode) -{ - JSON_MAPPINGS(3) = { - pair_type{ "always", ValueType::Always }, - pair_type{ "graceful", ValueType::Graceful }, - pair_type{ "never", ValueType::Never }, - }; -}; +- `Name` +- `Source` +- `Hidden` +- `Guid` +- `Padding` +- `TabColor` +- `UnfocusedAppearance` -// For enum flag values... -JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::TerminalControl::CopyFormat) -{ - JSON_MAPPINGS(5) = { - pair_type{ "none", AllClear }, - pair_type{ "html", ValueType::HTML }, - pair_type{ "rtf", ValueType::RTF }, - pair_type{ "all", AllSet }, - }; -}; +Do not add special-case settings to `MTSM_PROFILE_SETTINGS` unless their implementation follows the standard settings-list pattern. -// NOTE: This is also where you can add functionality for... -// - overloaded type support (i.e. accept a bool and an enum) -// - custom (de)serialization logic (i.e. coordinates) -``` +### Adding a Global or Window setting + +Global and per-window settings are maintained by the settings lists in `src/cascadia/TerminalSettingsModel/MTSMSettings.h`. + +- Add a global-only setting to `MTSM_GLOBAL_ONLY_SETTINGS`. +- Add a per-window setting to `MTSM_WINDOW_SETTINGS`. + +`MTSM_GLOBAL_SETTINGS` is the union of those two lists and is currently used by `GlobalAppSettings`. -### Adding a Global setting +Expose the setting in `src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl` with `INHERITABLE_SETTING`. -Follow the "adding a Profile setting" instructions above, but do it on the `GlobalAppSettings` files. +For standard settings, the corresponding settings list provides the metadata used by the common settings-model implementation. Do not add duplicate copy or JSON serialization code when the common machinery already handles the setting. ### Adding an Action @@ -199,7 +198,7 @@ Both have access to a `CascadiaSettings` object, for you to read the loaded sett ### Terminal-level settings -Terminal-level settings are settings that affect a shell session. Generally, these tend to be profile settings. The `TerminalApp` project is responsible for packaging this settings from the Terminal Settings Model to the terminal instance. There are two kinds of settings here: +Terminal-level settings are settings that affect a shell session. Generally, these tend to be profile settings. The `TerminalApp` project is responsible for packaging these settings from the Terminal Settings Model to the terminal instance. There are two kinds of settings here: - `IControlSettings`: - These are settings that affect the `TerminalControl` (a XAML control that hosts a shell session). - Examples include background image customization, interactivity behavior (i.e. selection), acrylic and font customization. @@ -209,18 +208,34 @@ Terminal-level settings are settings that affect a shell session. Generally, the - Examples include initial size, history size, and cursor customization. - The `TerminalCore` project has access to these settings via a saved `ICoreSettings` member. -`TerminalApp` packages these settings into a `TerminalSettings : IControlSettings, ICoreSettings` object upon creating a new terminal instance. To do so, you must submit the following changes: -- Declare the setting in `IControlSettings.idl` or `ICoreSettings.idl` (whichever is relevant to your setting). If your setting is an enum setting, declare the enum here instead of in the `TerminalSettingsModel` project. -- In `TerminalSettings.h`, declare/define the setting... +`TerminalApp` packages these settings into a `TerminalSettings : IControlSettings, ICoreSettings` object when creating a new terminal instance. + +When a new setting is terminal-facing: +- Declare the setting in `IControlSettings.idl` or `ICoreSettings.idl`, whichever is relevant. +- Add the setting to the corresponding `CONTROL_SETTINGS` or `CORE_SETTINGS` list in `src/cascadia/inc/ControlProperties.h`. These lists are consumed by `TerminalSettings.h`. +- For profile settings, update `TerminalSettings::_ApplyProfileSettings` in `src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp`. +- For window settings, update `TerminalSettings::_ApplyWindowSettings` in `src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp`. +- If additional processing or type conversion is necessary, perform it in `TerminalSettings.cpp`. + +For example, a control-facing setting can be exposed in `IControlSettings.idl` as: + ```c++ -// The WINRT_PROPERTY macro declares/defines a getter setter for the setting. -// Like INHERITABLE_SETTING, it takes in a type, name, and defaultValue. -WINRT_PROPERTY(bool, UseAcrylic, false); +String DragDropDelimiter { get; }; ``` -- In `TerminalSettings.cpp`... - - update `_ApplyProfileSettings` for profile settings - - update `_ApplyGlobalSettings` for global settings - - If additional processing is necessary, that would happen here. For example, `backgroundImageAlignment` is stored as a `ConvergedAlignment` in the Terminal Settings Model, but converted into XAML's separate horizontal and vertical alignment enums for packaging. + +and added to the control settings list in `ControlProperties.h` as: + +```c++ +X(winrt::hstring, DragDropDelimiter, L" ") +``` + +The profile value is then passed to `TerminalSettings` from `TerminalSettings.cpp`: + +```c++ +_DragDropDelimiter = profile.DragDropDelimiter(); +``` + +`TerminalControl` can then read the setting from its `IControlSettings` object. ### Actions @@ -375,22 +390,22 @@ Continue to reference `CommonResources.xaml` for appropriate styling and wrap th #### Profile Settings -If you are specifically adding a Profile setting, in addition to the steps above, you need to make the setting observable by modifying the `Profiles` files... +If you are specifically adding a Profile setting, in addition to the steps above, you need to make the setting observable by modifying the `ProfileViewModel` files. ```c++ -// Profiles.idl --> ProfileViewModel -// - this declares the setting as observable using the type and the name of the setting -OBSERVABLE_PROJECTED_SETTING(Microsoft.Terminal.Settings.Model.CloseOnExitMode, CloseOnExit); +// ProfileViewModel.idl +// - declare the setting as observable using the profile-setting projection macro +OBSERVABLE_PROJECTED_PROFILE_SETTING(String, DragDropDelimiter); -// Profiles.h --> ProfileViewModel -// - this defines the setting as observable off of the _profile object -OBSERVABLE_PROJECTED_SETTING(_profile, CloseOnExit); +// ProfileViewModel.h +// - project the setting from the _profile object +OBSERVABLE_PROJECTED_SETTING(_profile, DragDropDelimiter); -// Profiles.h --> ProfileViewModel -// - if the setting cannot be inherited by another profile (aka missing the Clear() function), use the following macro instead: +// ProfileViewModel.h +// - use the permanent variant for a setting that cannot be inherited by another profile PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, Guid); ``` -The `ProfilePageNavigationState` holds a `ProfileViewModel`, which wraps the `Profile` object from the Terminal Settings Model. The `ProfileViewModel` makes all of the profile settings observable. +The `ProfilePageNavigationState` holds a `ProfileViewModel`, which wraps the `Profile` object from the Terminal Settings Model. The `ProfileViewModel` exposes the profile settings to the Settings UI. ### Actions From 6c5070f46121d2354bd8d546a5534db92327cf50 Mon Sep 17 00:00:00 2001 From: "Xen K." Date: Wed, 19 Aug 2026 23:32:56 +0300 Subject: [PATCH 2/2] docs: refine AddASetting guide --- doc/cascadia/AddASetting.md | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/doc/cascadia/AddASetting.md b/doc/cascadia/AddASetting.md index 1e082116182..fcbb9a2ce5d 100644 --- a/doc/cascadia/AddASetting.md +++ b/doc/cascadia/AddASetting.md @@ -34,7 +34,7 @@ The `defaultArgs` are optional. Most profile settings use the standard `MTSM_PROFILE_SETTINGS` pattern. -1. Add the setting to `MTSM_PROFILE_SETTINGS` in `src/cascadia/TerminalSettingsModel/MTSMSettings.h`. +- Add the setting to `MTSM_PROFILE_SETTINGS` in `src/cascadia/TerminalSettingsModel/MTSMSettings.h`. For example: @@ -42,7 +42,7 @@ For example: X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Automatic) ``` -2. Add the corresponding property to `src/cascadia/TerminalSettingsModel/Profile.idl` with `INHERITABLE_PROFILE_SETTING`. +- Add the corresponding property to `src/cascadia/TerminalSettingsModel/Profile.idl` with `INHERITABLE_PROFILE_SETTING`. ```c++ INHERITABLE_PROFILE_SETTING(CloseOnExitMode, CloseOnExit); @@ -50,23 +50,13 @@ INHERITABLE_PROFILE_SETTING(CloseOnExitMode, CloseOnExit); The IDL type must use the spelling appropriate for `Profile.idl`. -3. Do not add the normal copy, JSON layering, or JSON serialization code to `Profile.cpp` manually. +- Do not add the normal copy, JSON layering, or JSON serialization code to `Profile.cpp` manually. `Profile.cpp` applies `MTSM_PROFILE_SETTINGS` to generate the common copy, JSON layering, and JSON serialization logic for settings in the list. -4. If the setting requires custom JSON handling, follow the implementation of an existing setting with similar requirements. +- If the setting requires custom JSON handling, follow the implementation of an existing setting with similar requirements. -Settings that do not fit the standard `MTSM_PROFILE_SETTINGS` pattern may require setting-specific handling in `Profile.h` and/or `Profile.cpp`. Examples include settings that are optional or require custom parsing. - -Examples of settings with special handling include: - -- `Name` -- `Source` -- `Hidden` -- `Guid` -- `Padding` -- `TabColor` -- `UnfocusedAppearance` +Settings that do not fit the standard `MTSM_PROFILE_SETTINGS` pattern may require setting-specific handling in `Profile.h` and/or `Profile.cpp`. Do not add special-case settings to `MTSM_PROFILE_SETTINGS` unless their implementation follows the standard settings-list pattern. @@ -74,14 +64,8 @@ Do not add special-case settings to `MTSM_PROFILE_SETTINGS` unless their impleme Global and per-window settings are maintained by the settings lists in `src/cascadia/TerminalSettingsModel/MTSMSettings.h`. -- Add a global-only setting to `MTSM_GLOBAL_ONLY_SETTINGS`. -- Add a per-window setting to `MTSM_WINDOW_SETTINGS`. - -`MTSM_GLOBAL_SETTINGS` is the union of those two lists and is currently used by `GlobalAppSettings`. - -Expose the setting in `src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl` with `INHERITABLE_SETTING`. - -For standard settings, the corresponding settings list provides the metadata used by the common settings-model implementation. Do not add duplicate copy or JSON serialization code when the common machinery already handles the setting. +- For a global-only setting, add it to `MTSM_GLOBAL_ONLY_SETTINGS` and expose it in `GlobalAppSettings.idl` with `INHERITABLE_SETTING`. +- For a per-window setting, add it to `MTSM_WINDOW_SETTINGS` and expose it in `WindowSettings` with `INHERITABLE_SETTING`. ### Adding an Action