Compare commits

..

10 Commits

Author SHA1 Message Date
Carlos Zamora
c64b2433da code format 2026-05-15 13:28:47 -07:00
Carlos Zamora
17538eb499 Convert Navigators to SettingsCards 2026-05-15 13:28:30 -07:00
Carlos Zamora
edfc599d6d Consolidate SettingContainer styles 2026-05-15 12:50:53 -07:00
Carlos Zamora
acc1a59367 Port SettingsCard/Expander from WCT; have SettingContainer use it 2026-05-14 19:31:22 -07:00
aarushi singh
12e3455bb2 Make DECSTR cursor restore behave like xterm in alt screen buffer (#20032)
There is some disagreement among terminal emulators as to whether DECSTR
(and therefore, soft reset) restores the cursor in the active buffer or
in all buffers.

We had previously chosen to restore the cursor in all buffers.

xterm restores the cursor only in the active buffer.

Closes #19918
2026-05-14 17:04:58 +00:00
Lucas Trzesniewski
fb71a0462e Add safeUriSchemes setting (#20207)
This adds a `safeUriSchemes` global setting which lets you define
hyperlink URI schemes which the user considers safe. No confirmation
dialog will be shown when trying to open hyperlinks which use these
schemes.

- This solves the root issue, but doesn't introduce any UI or
  documentation changes. I wanted to validate the approach and
  implementation with you first.
- I closely followed the code handling the `disabledProfileSources`
  setting, which is of the same type.
- This feature does not change the behavior of `http`, `https` and
  `file` schemes.

Validation

I ran the dev terminal, and tested the behavior by clicking on `vscode`
hyperlinks generated by ripgrep with various `safeUriSchemes` settings:

- Setting not defined - asks for confirmation
- `["vscode"]` - does not ask for confirmation
- `["foo", "vscode"]` - does not ask for confirmation
- `["foo"]` - asks for confirmation
- `null` - asks for confirmation
- `[]` - asks for confirmation
- `[""]` - asks for confirmation
- `[{"foo": "bar"}]` - fails to deserialize (as expected)

A few uinit tests failed, but they seem unrelated to these changes:
- `KeyBindingTests` in `UnitTests_SettingsModel`, probably because I use
  an AZERTY keyboard.
- A few `Conhost` tests, but I didn't touch this part

Refs #20065
Closes #20191
2026-05-12 21:32:56 +00:00
Dustin L. Howett
c829d4ca54 sixel: prevent allocating an absurd amount of memory or writing OOB (#20213)
This commit implements two fixes for the integer overflow/out-of-bounds
write reported in #20149.

First, it catches any exception generated in sixel char processing
(which will prevent `out_of_memory` or `bad_alloc` from being ignored
sight-unseen, and will prevent the consumption of further DCS content).

Second, it prevents us from allocating memory for an image which will
never be displayed (because it exceeds the height of the display.)

This supersedes prior work in #20153 for the same issues.

Closes #20149
Closes #20153

Co-authored-by: James Holderness <j4_james@hotmail.com>
2026-05-12 18:15:19 +02:00
Dustin L. Howett
b991eb048e Only set startingTitle once, clear up title fallback handling (#20214)
This commit ensures that we only set the starting title once when we
open a new terminal pane.

It also consolidates all title selection into Terminal::GetConsoleTitle,
which is now used in the TitleChanged event.

TitleChanged no longer stores a separate copy of the starting title if
an application attempts to _clear_ the title; that seems like a poorer
implementation of what we already had.

This supersedes work in #20204.

Closes #19340
Closes #20204

Co-authored-by: imsh <im.shaedar@gmail.com>
2026-05-12 00:08:57 +00:00
Dustin L. Howett
3e3b3ad883 Reject DTTERM Window Manipulation resizes with the current size (#20183)
This may help #20182 and #20112
Closes #19310
2026-05-11 17:42:18 -05:00
Windows Console Service Bot
d3f76e7acf Localization Updates - main - 05/07/2026 03:04:15 (#20196)
Co-authored-by: Console Service Bot <consvc@microsoft.com>
2026-05-07 14:03:13 -05:00
47 changed files with 1718 additions and 2869 deletions

View File

@@ -21,10 +21,6 @@ parameters:
displayName: "Build Everything (Overrides all other build options)"
type: boolean
default: false
- name: enableRustBuild
displayName: "Build src/tools/wtr (requires RustInstaller extension + microsoft/Dart Azure Artifact feeds)"
type: boolean
default: true
- name: pgoBuildMode
type: string
default: None
@@ -170,80 +166,6 @@ jobs:
.\build\scripts\Generate-ThirdPartyNotices.ps1 -MarkdownNoticePath .\NOTICE.md -OutputPath .\src\cascadia\CascadiaPackage\NOTICE.html
displayName: Generate NOTICE.html from NOTICE.md
- ${{ if eq(parameters.enableRustBuild, true) }}:
# ────────────────────────────────────────────────────────────────────────
# Rust toolchain for src/tools/wtr (1ES-compliant pattern).
# Placed early — before vcpkg/nuget/VC-tools setup — so that Rust pipeline
# validation is independent of any C++ build environment problems.
#
# Pipeline lives at: https://microsoft.visualstudio.com/Dart/
# Reference for this pattern: microsoft/sudo (.pipelines/OneBranch.Common.yml)
#
# Compliance notes (per 1ES Rust workflow guidance):
# 1. Toolchain comes from the org-level `RustTools` Azure Artifact NuGet
# feed (already exists on `microsoft` ADO org; same feed used by
# microsoft/sudo, microsoft/vscode, microsoft/edit, microsoft/qdk).
# 2. Crates resolve through a project-level Azure Artifact Cargo feed
# named `Cargo` in microsoft/Dart (with crates.io upstream).
# 3. The Rust version uses the `ms-prod-X.YY` channel, pinned in
# `src/tools/wtr/rust-toolchain.toml`.
# 4. Component Governance scanning of Cargo.lock is auto-injected by
# 1ES PT (no extra task needed here).
#
# Prerequisites — until in place, leave `enableRustBuild` parameter false:
# - Azure Artifact Cargo feed `Cargo` exists in microsoft/Dart with
# `crates.io` upstream. (Project-scope, terminal team can create it.)
# - The CI account ("Project Collection Build Service (microsoft)") has
# at least Collaborator permission on the `RustTools` feed.
# ────────────────────────────────────────────────────────────────────────
- task: RustInstaller@1
displayName: Install Rust toolchain (MSRustup)
inputs:
rustVersion: ms-prod-1.93
toolchainFeed: https://pkgs.dev.azure.com/microsoft/_packaging/RustTools/nuget/v3/index.json
# NOTE: we deliberately do NOT set `cratesIoFeedOverride` here. That
# parameter injects an env-var registry override named `ms_crates_io`
# which CargoAuthenticate cannot see (it scans config files, not env
# vars), so the override registry ends up unauthenticated and Azure
# Artifacts returns 404 on its sparse-index endpoint.
# Instead, the registry + source replacement lives in
# src/tools/wtr/.cargo/config.toml — CargoAuthenticate@0 (next step)
# tokens it, and cargo's normal config-file resolution picks it up.
additionalTargets: 'x86_64-pc-windows-msvc i686-pc-windows-msvc aarch64-pc-windows-msvc'
# CargoAuthenticate issues a Bearer token (using System.AccessToken) for
# every Azure Artifacts Cargo feed referenced in the config file.
# Required: without it, Azure Artifacts returns 404 on the sparse index
# endpoint and cargo reports "config.json not found in registry".
# This is the canonical 1ES Rust auth pattern (mirrors microsoft/edit).
- task: CargoAuthenticate@0
displayName: Authenticate Cargo feeds
inputs:
configFile: src/tools/wtr/.cargo/config.toml
# Separate fetch step: some 1ES pools disable network during the build
# phase. Fetching upfront keeps `cargo build --frozen` fully offline.
- script: cargo fetch --manifest-path src/tools/wtr/Cargo.toml --locked
displayName: Fetch crates (offline-friendly)
- pwsh: |-
$ErrorActionPreference = 'Stop'
$triple = switch ('$(BuildPlatform)') {
'x64' { 'x86_64-pc-windows-msvc' }
'x86' { 'i686-pc-windows-msvc' }
'arm64' { 'aarch64-pc-windows-msvc' }
default { 'x86_64-pc-windows-msvc' }
}
$config = '$(BuildConfiguration)'
Write-Host "Building wtr for $triple ($config)"
if ($config -eq 'Release') {
cargo build --manifest-path src/tools/wtr/Cargo.toml --target $triple --frozen --release
} else {
cargo build --manifest-path src/tools/wtr/Cargo.toml --target $triple --frozen
}
if ($LASTEXITCODE -ne 0) { throw "cargo build failed with exit code $LASTEXITCODE" }
displayName: Build src/tools/wtr (Cargo)
- template: .\steps-install-vcpkg.yml
- template: .\steps-restore-nuget.yml

View File

@@ -10,30 +10,13 @@ $VCToolsRoot = Join-Path $VSRoot "VC\Tools\MSVC"
# differs from the version on the files themselves. We might as well check
# whether the version we just found _actually exists_ before we use it.
# We'll use whichever highest version exists.
#
# Some pool images report a package version (e.g. 14.44.35208) but ship only
# package metadata, not the actual toolchain — the version folder either
# doesn't exist or is empty. Detect both cases by also requiring a non-empty
# `bin` subdirectory (where cl.exe / link.exe live).
$PackageVCToolPath = Join-Path $VCToolsRoot $LatestVCToolsVersion
$PackageVCBinPath = Join-Path $PackageVCToolPath "bin"
$PackageIsValid = ($Null -Ne (Get-Item $PackageVCToolPath -ErrorAction:Ignore)) -And `
($Null -Ne (Get-Item $PackageVCBinPath -ErrorAction:Ignore))
If (-Not $PackageIsValid) {
$VCToolsVersions = Get-ChildItem $VCToolsRoot -Directory -ErrorAction:Ignore | Where-Object {
# Only consider directories that actually contain a populated `bin`.
$binDir = Join-Path $_.FullName "bin"
(Test-Path $binDir) -And ((Get-ChildItem $binDir -Recurse -File -ErrorAction:Ignore | Select-Object -First 1) -Ne $Null)
} | ForEach-Object {
If ($Null -Eq (Get-Item $PackageVCToolPath -ErrorAction:Ignore)) {
$VCToolsVersions = Get-ChildItem $VCToolsRoot | ForEach-Object {
[Version]$_.Name
} | Sort -Descending
$LatestActualVCToolsVersion = $VCToolsVersions | Select -First 1
If ($Null -Eq $LatestActualVCToolsVersion) {
Write-Error "No usable VC Tools installation found under $VCToolsRoot. Package version was $LatestVCToolsVersion. This typically indicates a partial/broken pool image (DD-1541167)."
Exit 1
}
If ([Version]$LatestVCToolsVersion -Ne $LatestActualVCToolsVersion) {
Write-Output "VC Tools Mismatch: Directory = $LatestActualVCToolsVersion, Package = $LatestVCToolsVersion"
$LatestVCToolsVersion = $LatestActualVCToolsVersion.ToString(3)

View File

@@ -2472,6 +2472,13 @@
},
"type": "array"
},
"safeUriSchemes": {
"description": "Specifies a list of URI schemes that are considered safe. No confirmation will be required to open URIs with these schemes.",
"items": {
"type": "string"
},
"type": "array"
},
"rendering.graphicsAPI": {
"description": "Direct3D 11 provides a more performant and feature-rich experience, whereas Direct2D is more stable. The default option \"Automatic\" will pick the API that best fits your graphics hardware. If you experience significant issues, consider using Direct2D.",
"type": "string",

View File

@@ -24,21 +24,6 @@
<EntryPointProjectUniqueName>..\WindowsTerminal\WindowsTerminal.vcxproj</EntryPointProjectUniqueName>
<DebuggerType>NativeOnly</DebuggerType>
</PropertyGroup>
<!-- Map MSBuild Platform to Rust target triple for wtr.exe inclusion. -->
<PropertyGroup>
<WtrRustTarget Condition="'$(Platform)'=='x64'">x86_64-pc-windows-msvc</WtrRustTarget>
<WtrRustTarget Condition="'$(Platform)'=='ARM64'">aarch64-pc-windows-msvc</WtrRustTarget>
<WtrRustProfile Condition="'$(Configuration)'=='Debug'">debug</WtrRustProfile>
<WtrRustProfile Condition="'$(Configuration)'!='Debug'">release</WtrRustProfile>
</PropertyGroup>
<ItemGroup>
<Content Include="$(SolutionDir)src\tools\wtr\target\$(WtrRustTarget)\$(WtrRustProfile)\wtr.exe"
Condition="Exists('$(SolutionDir)src\tools\wtr\target\$(WtrRustTarget)\$(WtrRustProfile)\wtr.exe')">
<Link>wtr.exe</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<PropertyGroup Condition="!Exists('CascadiaPackage_TemporaryKey.pfx')">
<AppxPackageSigningEnabled>false</AppxPackageSigningEnabled>
<AppxBundle>Never</AppxBundle>

View File

@@ -496,12 +496,48 @@
<value>第三方通知</value>
<comment>A hyperlink name for the Terminal's third-party notices</comment>
</data>
<data name="ConfirmCloseDialog_Cancel" xml:space="preserve">
<value>取消</value>
</data>
<data name="ConfirmCloseDialog_CloseAllTitle" xml:space="preserve">
<value>是否要关闭所有窗口?</value>
</data>
<data name="ConfirmCloseDialog_CloseAllPrimary" xml:space="preserve">
<value>全部关闭</value>
</data>
<data name="ConfirmCloseDialog_WindowTitle" xml:space="preserve">
<value>是否要关闭所有标签页?</value>
</data>
<data name="ConfirmCloseDialog_WindowPrimary" xml:space="preserve">
<value>全部关闭</value>
</data>
<data name="ConfirmCloseDialog_TabTitle" xml:space="preserve">
<value>是否要关闭此选项卡?</value>
</data>
<data name="ConfirmCloseDialog_TabPrimary" xml:space="preserve">
<value>关闭选项卡</value>
</data>
<data name="ConfirmCloseDialog_PaneTitle" xml:space="preserve">
<value>是否要关闭此窗格?</value>
</data>
<data name="ConfirmCloseDialog_PanePrimary" xml:space="preserve">
<value>关闭窗格</value>
</data>
<data name="ConfirmCloseDialog_MultipleTabsTitle" xml:space="preserve">
<value>是否要关闭这些选项卡?</value>
</data>
<data name="ConfirmCloseDialog_MultipleTabsPrimary" xml:space="preserve">
<value>关闭选项卡</value>
</data>
<data name="ConfirmCloseDialog_MultiplePanesTitle" xml:space="preserve">
<value>是否要关闭这些窗格?</value>
</data>
<data name="ConfirmCloseDialog_MultiplePanesPrimary" xml:space="preserve">
<value>关闭窗格</value>
</data>
<data name="DontAskAgainCheckBox.Content" xml:space="preserve">
<value>不再询问</value>
</data>
<data name="CloseReadOnlyDialog.CloseButtonText" xml:space="preserve">
<value>取消</value>
</data>

View File

@@ -3307,13 +3307,15 @@ namespace winrt::TerminalApp::implementation
return true;
}
bool TerminalPage::_IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri)
bool TerminalPage::_IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri) const
{
if (parsedUri.SchemeName() == L"http" || parsedUri.SchemeName() == L"https")
const auto& schemeName = parsedUri.SchemeName();
if (schemeName == L"http" || schemeName == L"https")
{
return true;
}
if (parsedUri.SchemeName() == L"file")
if (schemeName == L"file")
{
static const auto pathext{ wil::TryGetEnvironmentVariableW<std::wstring>(L"PATHEXT") };
const auto filename = parsedUri.Path();
@@ -3327,6 +3329,16 @@ namespace winrt::TerminalApp::implementation
return true;
}
if (const auto& safeSchemes = _settings.GlobalSettings().SafeUriSchemes())
{
for (const auto& scheme : safeSchemes)
{
if (til::equals_insensitive_ascii(schemeName, scheme))
{
return true;
}
}
}
return false;
}

View File

@@ -438,7 +438,7 @@ namespace winrt::TerminalApp::implementation
safe_void_coroutine _OpenHyperlinkHandler(const IInspectable sender, const Microsoft::Terminal::Control::OpenHyperlinkEventArgs eventArgs);
static bool _IsUriSupported(const winrt::Windows::Foundation::Uri& parsedUri);
static bool _IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri);
bool _IsUriConsideredSomewhatSafe(const winrt::Windows::Foundation::Uri& parsedUri) const;
void _ShowCouldNotOpenDialog(winrt::hstring reason, winrt::hstring uri);
bool _CopyText(bool dismissSelection, bool singleLine, bool withControlSequences, Microsoft::Terminal::Control::CopyFormat formats);

View File

@@ -98,7 +98,6 @@ void Terminal::UpdateSettings(ICoreSettings settings)
_answerbackMessage = settings.AnswerbackMessage();
_wordDelimiters = settings.WordDelimiters();
_suppressApplicationTitle = settings.SuppressApplicationTitle();
_startingTitle = settings.StartingTitle();
_trimBlockSelection = settings.TrimBlockSelection();
_autoMarkPrompts = settings.AutoMarkPrompts();
_rainbowSuggestions = settings.RainbowSuggestions();
@@ -124,6 +123,11 @@ void Terminal::UpdateSettings(ICoreSettings settings)
// Save the changes made above and in UpdateAppearance as the new default render settings.
GetRenderSettings().SaveDefaultSettings();
if (!_startingTitle)
{
_startingTitle = settings.StartingTitle();
}
if (!_startingTabColor && settings.StartingTabColor())
{
_startingTabColor = settings.StartingTabColor().Value();

View File

@@ -349,7 +349,7 @@ private:
::Microsoft::Console::VirtualTerminal::TerminalInput _terminalInput;
std::optional<std::wstring> _title;
std::wstring _startingTitle;
std::optional<std::wstring> _startingTitle;
std::optional<til::color> _startingTabColor;
std::vector<til::point_span> _searchHighlights;

View File

@@ -91,8 +91,12 @@ void Terminal::SetWindowTitle(const std::wstring_view title)
_assertLocked();
if (!_suppressApplicationTitle)
{
_title.emplace(title.empty() ? _startingTitle : title);
_pfnTitleChanged(_title.value());
_title.reset();
if (!title.empty())
{
_title.emplace(title);
}
_pfnTitleChanged(GetConsoleTitle());
}
}
@@ -112,6 +116,13 @@ bool Terminal::ResizeWindow(const til::CoordType width, const til::CoordType hei
return false;
}
const auto currentDimensions = _GetMutableViewport().Dimensions();
if (width == currentDimensions.width && height == currentDimensions.height)
{
return false;
}
if (_pfnWindowSizeChanged)
{
_pfnWindowSizeChanged(width, height);

View File

@@ -184,11 +184,18 @@ void Terminal::SelectNewRegion(const til::point coordStart, const til::point coo
std::wstring_view Terminal::GetConsoleTitle() const noexcept
{
_assertLocked();
if (_title.has_value())
if (_title)
{
return *_title;
}
return _startingTitle;
if (_startingTitle)
{
return *_startingTitle;
}
return {};
}
// Method Description:

View File

@@ -81,7 +81,7 @@
CurrentValueAccessibleName="{x:Bind Appearance.CurrentColorScheme.Name, Mode=OneWay}"
HasSettingValue="{x:Bind Appearance.HasDarkColorSchemeName, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.DarkColorSchemeNameOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
Style="{StaticResource ExpanderSettingContainerStyle}">
<local:SettingContainer.CurrentValue>
<ComboBox Padding="4"
ItemsSource="{x:Bind Appearance.SchemesList, Mode=OneWay}"
@@ -220,7 +220,7 @@
CurrentValueTemplate="{StaticResource ColorPreviewTemplate}"
HasSettingValue="{x:Bind Appearance.HasForeground, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.ForegroundOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
Style="{StaticResource ExpanderSettingContainerStyle}">
<local:NullableColorPicker x:Uid="Profile_Foreground_NullableColorPicker"
ColorSchemeVM="{x:Bind Appearance.CurrentColorScheme, Mode=OneWay}"
CurrentColor="{x:Bind Appearance.Foreground, Mode=TwoWay}"
@@ -236,7 +236,7 @@
CurrentValueTemplate="{StaticResource ColorPreviewTemplate}"
HasSettingValue="{x:Bind Appearance.HasBackground, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.BackgroundOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
Style="{StaticResource ExpanderSettingContainerStyle}">
<local:NullableColorPicker x:Uid="Profile_Background_NullableColorPicker"
ColorSchemeVM="{x:Bind Appearance.CurrentColorScheme, Mode=OneWay}"
CurrentColor="{x:Bind Appearance.Background, Mode=TwoWay}"
@@ -252,7 +252,7 @@
CurrentValueTemplate="{StaticResource ColorPreviewTemplate}"
HasSettingValue="{x:Bind Appearance.HasSelectionBackground, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.SelectionBackgroundOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
Style="{StaticResource ExpanderSettingContainerStyle}">
<local:NullableColorPicker x:Uid="Profile_SelectionBackground_NullableColorPicker"
ColorSchemeVM="{x:Bind Appearance.CurrentColorScheme, Mode=OneWay}"
CurrentColor="{x:Bind Appearance.SelectionBackground, Mode=TwoWay}"
@@ -536,7 +536,7 @@
CurrentValueTemplate="{StaticResource ColorPreviewTemplate}"
HasSettingValue="{x:Bind Appearance.HasCursorColor, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.CursorColorOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
Style="{StaticResource ExpanderSettingContainerStyle}">
<local:NullableColorPicker x:Uid="Profile_CursorColor_NullableColorPicker"
ColorSchemeVM="{x:Bind Appearance.CurrentColorScheme, Mode=OneWay}"
CurrentColor="{x:Bind Appearance.CursorColor, Mode=TwoWay}"

View File

@@ -8,8 +8,9 @@
xmlns:mtu="using:Microsoft.Terminal.UI"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls">
<!-- Merge SettingContainerStyle here to give every page access to the SettingContainer -->
<!-- Merge SettingsControls and SettingContainer styles here to give every page access to them -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="SettingsControlsStyle.xaml" />
<ResourceDictionary Source="SettingContainerStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
@@ -1200,87 +1201,6 @@
</Setter>
</Style>
<Style x:Key="NavigatorButtonStyle"
TargetType="Button">
<Setter Property="Background" Value="{ThemeResource ExpanderHeaderBackground}" />
<Setter Property="MinWidth" Value="{ThemeResource FlyoutThemeMinWidth}" />
<Setter Property="MinHeight" Value="64" />
<Setter Property="BorderThickness" Value="{ThemeResource ExpanderHeaderBorderThickness}" />
<Setter Property="BorderBrush" Value="{ThemeResource ExpanderHeaderBorderBrush}" />
<Setter Property="Padding" Value="16,0,8,0" />
<Setter Property="Margin" Value="0,4,0,0" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid x:Name="Grid"
Padding="{TemplateBinding Padding}"
AutomationProperties.AccessibilityView="Raw"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="ContentPresenter"
Grid.Column="0"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}" />
<FontIcon Grid.Column="1"
Margin="20,0,8,0"
HorizontalAlignment="Right"
FontSize="10"
FontWeight="Black"
Glyph="&#xE76C;" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource ToggleButtonBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource ToggleButtonBorderBrushPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource ToggleButtonBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Grid"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource ToggleButtonBorderBrushPressed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="NewInfoBadge"
TargetType="muxc:InfoBadge">
<Setter Property="Padding" Value="5,1,5,2" />

View File

@@ -79,7 +79,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Extensions::ExtensionNavigator_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/)
{
const auto extPkgVM = sender.as<Controls::Button>().Tag().as<Editor::ExtensionPackageViewModel>();
const auto extPkgVM = sender.as<FrameworkElement>().Tag().as<Editor::ExtensionPackageViewModel>();
_ViewModel.CurrentExtensionPackage(extPkgVM);
}

View File

@@ -127,78 +127,58 @@
<DataTemplate x:Key="DefaultExtensionNavigatorTemplate"
x:DataType="local:ExtensionPackageViewModel">
<Button AutomationProperties.Name="{x:Bind AccessibleName}"
Click="ExtensionNavigator_Click"
Style="{StaticResource NavigatorButtonStyle}"
Tag="{x:Bind}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Content="{x:Bind}"
ContentTemplate="{StaticResource DefaultExtensionIdentifierTemplate}" />
<ToggleSwitch Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
AutomationProperties.Name="{x:Bind AccessibleName}"
IsOn="{x:Bind Enabled, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</Grid>
</Button>
<local:SettingsCard AutomationProperties.Name="{x:Bind AccessibleName}"
Click="ExtensionNavigator_Click"
Header="{x:Bind Package.Source}"
IsClickEnabled="True"
Tag="{x:Bind}">
<local:SettingsCard.HeaderIcon>
<FontIcon Glyph="&#xE74C;" />
</local:SettingsCard.HeaderIcon>
<ToggleSwitch HorizontalAlignment="Right"
VerticalAlignment="Center"
AutomationProperties.Name="{x:Bind AccessibleName}"
IsOn="{x:Bind Enabled, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingsCard>
</DataTemplate>
<DataTemplate x:Key="ComplexExtensionNavigatorTemplate"
x:DataType="local:ExtensionPackageViewModel">
<Button AutomationProperties.Name="{x:Bind AccessibleName}"
Click="ExtensionNavigator_Click"
Style="{StaticResource NavigatorButtonStyle}"
Tag="{x:Bind}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Content="{x:Bind}"
ContentTemplate="{StaticResource ComplexExtensionIdentifierTemplate}" />
<ToggleSwitch Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
AutomationProperties.Name="{x:Bind AccessibleName}"
IsOn="{x:Bind Enabled, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</Grid>
</Button>
<local:SettingsCard AutomationProperties.Name="{x:Bind AccessibleName}"
Click="ExtensionNavigator_Click"
Description="{x:Bind Package.Source}"
Header="{x:Bind Package.DisplayName}"
IsClickEnabled="True"
Tag="{x:Bind}">
<local:SettingsCard.HeaderIcon>
<IconSourceElement IconSource="{x:Bind mtu:IconPathConverter.IconSourceWUX(Package.Icon)}" />
</local:SettingsCard.HeaderIcon>
<ToggleSwitch HorizontalAlignment="Right"
VerticalAlignment="Center"
AutomationProperties.Name="{x:Bind AccessibleName}"
IsOn="{x:Bind Enabled, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingsCard>
</DataTemplate>
<DataTemplate x:Key="ComplexExtensionNavigatorTemplateWithFontIcon"
x:DataType="local:ExtensionPackageViewModel">
<Button AutomationProperties.Name="{x:Bind AccessibleName}"
Click="ExtensionNavigator_Click"
Style="{StaticResource NavigatorButtonStyle}"
Tag="{x:Bind}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Content="{x:Bind}"
ContentTemplate="{StaticResource ComplexExtensionIdentifierTemplateWithFontIcon}" />
<ToggleSwitch Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
AutomationProperties.Name="{x:Bind AccessibleName}"
IsOn="{x:Bind Enabled, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</Grid>
</Button>
<local:SettingsCard AutomationProperties.Name="{x:Bind AccessibleName}"
Click="ExtensionNavigator_Click"
Description="{x:Bind Package.Source}"
Header="{x:Bind Package.DisplayName}"
IsClickEnabled="True"
Tag="{x:Bind}">
<local:SettingsCard.HeaderIcon>
<FontIcon Glyph="{x:Bind Package.Icon}" />
</local:SettingsCard.HeaderIcon>
<ToggleSwitch HorizontalAlignment="Right"
VerticalAlignment="Center"
AutomationProperties.Name="{x:Bind AccessibleName}"
IsOn="{x:Bind Enabled, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingsCard>
</DataTemplate>
<DataTemplate x:Key="FragmentProfileViewModelTemplate"
@@ -502,9 +482,11 @@
<!-- Scope -->
<local:SettingContainer x:Name="Scope"
x:Uid="Extensions_Scope"
Content="{x:Bind ViewModel.CurrentExtensionPackage.Scope, Mode=OneWay}"
IsTabStop="False"
Style="{StaticResource SettingContainerWithTextContent}" />
IsTabStop="False">
<TextBlock VerticalAlignment="Center"
Style="{ThemeResource SecondaryTextBlockStyle}"
Text="{x:Bind ViewModel.CurrentExtensionPackage.Scope, Mode=OneWay}" />
</local:SettingContainer>
<!-- JSON -->
<ItemsControl IsTabStop="False"
ItemTemplate="{StaticResource JsonTemplate}"

View File

@@ -173,6 +173,15 @@
<ClInclude Include="SettingContainer.h">
<DependentUpon>SettingContainer.idl</DependentUpon>
</ClInclude>
<ClInclude Include="SettingsCard.h">
<DependentUpon>SettingsCard.idl</DependentUpon>
</ClInclude>
<ClInclude Include="SettingsExpander.h">
<DependentUpon>SettingsExpander.idl</DependentUpon>
</ClInclude>
<ClInclude Include="StringDefaultTemplateSelector.h">
<DependentUpon>StringDefaultTemplateSelector.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Utils.h" />
<ClInclude Include="PreviewConnection.h" />
<ClInclude Include="$(GeneratedFilesDir)GeneratedSettingsIndex.g.h" />
@@ -252,6 +261,9 @@
<Page Include="SettingContainerStyle.xaml">
<Type>DefaultStyle</Type>
</Page>
<Page Include="SettingsControlsStyle.xaml">
<Type>DefaultStyle</Type>
</Page>
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
@@ -385,6 +397,15 @@
<ClCompile Include="SettingContainer.cpp">
<DependentUpon>SettingContainer.idl</DependentUpon>
</ClCompile>
<ClCompile Include="SettingsCard.cpp">
<DependentUpon>SettingsCard.idl</DependentUpon>
</ClCompile>
<ClCompile Include="SettingsExpander.cpp">
<DependentUpon>SettingsExpander.idl</DependentUpon>
</ClCompile>
<ClCompile Include="StringDefaultTemplateSelector.cpp">
<DependentUpon>StringDefaultTemplateSelector.idl</DependentUpon>
</ClCompile>
<ClCompile Include="Utils.cpp" />
<ClCompile Include="PreviewConnection.cpp">
<DependentUpon>PreviewConnection.h</DependentUpon>
@@ -492,6 +513,15 @@
<Midl Include="SettingContainer.idl">
<SubType>Code</SubType>
</Midl>
<Midl Include="SettingsCard.idl">
<SubType>Code</SubType>
</Midl>
<Midl Include="SettingsExpander.idl">
<SubType>Code</SubType>
</Midl>
<Midl Include="StringDefaultTemplateSelector.idl">
<SubType>Code</SubType>
</Midl>
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>

View File

@@ -30,6 +30,9 @@
<Midl Include="LaunchViewModel.idl" />
<Midl Include="EnumEntry.idl" />
<Midl Include="SettingContainer.idl" />
<Midl Include="SettingsCard.idl" />
<Midl Include="SettingsExpander.idl" />
<Midl Include="StringDefaultTemplateSelector.idl" />
<Midl Include="TerminalColorConverters.idl" />
<Midl Include="NewTabMenuViewModel.idl" />
</ItemGroup>
@@ -52,6 +55,7 @@
<Page Include="Actions.xaml" />
<Page Include="EditAction.xaml" />
<Page Include="SettingContainerStyle.xaml" />
<Page Include="SettingsControlsStyle.xaml" />
<Page Include="AddProfile.xaml" />
<Page Include="KeyChordListener.xaml" />
<Page Include="NullableColorPicker.xaml" />

View File

@@ -332,7 +332,7 @@
<local:SettingContainer x:Name="CurrentFolderIcon"
x:Uid="NewTabMenu_CurrentFolderIcon"
CurrentValueAccessibleName="{x:Bind ViewModel.CurrentFolderLocalizedIcon, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
Style="{StaticResource ExpanderSettingContainerStyle}">
<local:SettingContainer.CurrentValue>
<Grid>
<ContentControl Width="16"
@@ -378,8 +378,7 @@
<!-- Add Profile -->
<local:SettingContainer x:Name="AddProfile"
x:Uid="NewTabMenu_AddProfile"
FontIconGlyph="&#xE756;"
Style="{StaticResource SettingContainerWithIcon}">
FontIconGlyph="&#xE756;">
<StackPanel Orientation="Horizontal"
Spacing="4">
@@ -428,8 +427,7 @@
<!-- Add Separator -->
<local:SettingContainer x:Name="AddSeparator"
x:Uid="NewTabMenu_AddSeparator"
FontIconGlyph="&#xE76f;"
Style="{StaticResource SettingContainerWithIcon}">
FontIconGlyph="&#xE76f;">
<Button x:Name="AddSeparatorButton"
x:Uid="NewTabMenu_AddSeparatorButton"
HorizontalAlignment="Stretch"
@@ -445,8 +443,7 @@
<!-- Add Folder -->
<local:SettingContainer x:Name="AddFolder"
x:Uid="NewTabMenu_AddFolder"
FontIconGlyph="&#xF12B;"
Style="{StaticResource SettingContainerWithIcon}">
FontIconGlyph="&#xF12B;">
<StackPanel Orientation="Horizontal"
Spacing="5">
<TextBox x:Name="FolderNameTextBox"
@@ -473,7 +470,7 @@
<local:SettingContainer x:Name="AddMatchProfiles"
x:Uid="NewTabMenu_AddMatchProfiles"
FontIconGlyph="&#xE748;"
Style="{StaticResource ExpanderSettingContainerStyleWithIcon}">
Style="{StaticResource ExpanderSettingContainerStyle}">
<StackPanel Spacing="8">
<HyperlinkButton x:Uid="NewTabMenu_AddMatchProfiles_Help"
NavigateUri="https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference" />
@@ -500,8 +497,7 @@
<!-- Add Remaining Profiles -->
<local:SettingContainer x:Name="AddRemainingProfiles"
x:Uid="NewTabMenu_AddRemainingProfiles"
FontIconGlyph="&#xE902;"
Style="{StaticResource SettingContainerWithIcon}">
FontIconGlyph="&#xE902;">
<Button x:Name="AddRemainingProfilesButton"
x:Uid="NewTabMenu_AddRemainingProfilesButton"
HorizontalAlignment="Stretch"

View File

@@ -22,9 +22,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Automation::AutomationProperties::SetFullDescription(StartingDirectoryUseParentCheckbox(), unbox_value<hstring>(startingDirCheckboxTooltip));
Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"Profile_DeleteButton/Text"));
AppearanceNavigator().Content(box_value(RS_(L"Profile_Appearance/Header")));
TerminalNavigator().Content(box_value(RS_(L"Profile_Terminal/Header")));
AdvancedNavigator().Content(box_value(RS_(L"Profile_Advanced/Header")));
AppearanceNavigator().Header(box_value(RS_(L"Profile_Appearance/Header")));
TerminalNavigator().Header(box_value(RS_(L"Profile_Terminal/Header")));
AdvancedNavigator().Header(box_value(RS_(L"Profile_Advanced/Header")));
}
void Profiles_Base::OnNavigatedTo(const NavigationEventArgs& e)

View File

@@ -106,7 +106,7 @@
CurrentValueAccessibleName="{x:Bind Profile.LocalizedIcon, Mode=OneWay}"
HasSettingValue="{x:Bind Profile.HasIcon, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.IconOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
Style="{StaticResource ExpanderSettingContainerStyle}">
<local:SettingContainer.CurrentValue>
<Grid>
<ContentControl Width="16"
@@ -150,7 +150,7 @@
CurrentValueTemplate="{StaticResource ColorPreviewTemplate}"
HasSettingValue="{x:Bind Profile.HasTabColor, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.TabColorOverrideSource, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
Style="{StaticResource ExpanderSettingContainerStyle}">
<local:NullableColorPicker x:Uid="Profile_TabColor_NullableColorPicker"
ColorSchemeVM="{x:Bind Profile.DefaultAppearance.CurrentColorScheme, Mode=OneWay}"
CurrentColor="{x:Bind Profile.TabColor, Mode=TwoWay}"
@@ -179,15 +179,15 @@
Margin="0,32,0,4"
Style="{StaticResource TextBlockSubHeaderStyle}" />
<Button x:Name="AppearanceNavigator"
Click="Appearance_Click"
Style="{StaticResource NavigatorButtonStyle}" />
<Button x:Name="TerminalNavigator"
Click="Terminal_Click"
Style="{StaticResource NavigatorButtonStyle}" />
<Button x:Name="AdvancedNavigator"
Click="Advanced_Click"
Style="{StaticResource NavigatorButtonStyle}" />
<local:SettingsCard x:Name="AppearanceNavigator"
Click="Appearance_Click"
IsClickEnabled="True" />
<local:SettingsCard x:Name="TerminalNavigator"
Click="Terminal_Click"
IsClickEnabled="True" />
<local:SettingsCard x:Name="AdvancedNavigator"
Click="Advanced_Click"
IsClickEnabled="True" />
<!-- Delete Button -->
<Border MaxWidth="{StaticResource StandardControlMaxWidth}">
<Button x:Name="DeleteButton"

View File

@@ -2201,10 +2201,26 @@
<value>关闭多个选项卡时发出警告</value>
<comment>Header for a control to toggle whether to show a confirm dialog box when closing the application with multiple tabs open.</comment>
</data>
<data name="Globals_ConfirmOnClose.Header" xml:space="preserve">
<value>关闭时发出警告</value>
<comment>Header for a dropdown controlling when to show a confirmation dialog before closing.</comment>
</data>
<data name="Globals_ConfirmOnClose.HelpText" xml:space="preserve">
<value>控制在关闭选项卡或窗口之前何时显示确认对话框。“始终”在关闭任何窗格时显示对话框。</value>
<comment>Help text associated with Globals_ConfirmOnClose. "Always" refers to Globals_ConfirmOnCloseAlways.Content.</comment>
</data>
<data name="Globals_ConfirmOnCloseNever.Content" xml:space="preserve">
<value>从不</value>
<comment>Option associated with Globals_ConfirmOnClose. "Never" means that the system will never display a warning when closing.</comment>
</data>
<data name="Globals_ConfirmOnCloseAlways.Content" xml:space="preserve">
<value>始终</value>
<comment>Option associated with Globals_ConfirmOnClose. "Always" means that the system will always display a warning when closing.</comment>
</data>
<data name="Globals_ConfirmOnCloseAutomatic.Content" xml:space="preserve">
<value>多个选项卡或窗格</value>
<comment>Option associated with Globals_ConfirmOnClose. The system will display a warning when multiple tabs or panes are present.</comment>
</data>
<data name="Globals_InputServiceWarning.Header" xml:space="preserve">
<value>禁用“触摸键盘和手写面板服务”时发出警告</value>
</data>

View File

@@ -3,6 +3,7 @@
#include "pch.h"
#include "SettingContainer.h"
#include "SettingsExpander.h"
#include "SettingContainer.g.cpp"
using namespace winrt::Windows::UI::Xaml;
@@ -54,7 +55,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
L"FontIconGlyph",
xaml_typename<hstring>(),
xaml_typename<Editor::SettingContainer>(),
PropertyMetadata{ box_value(L"") });
PropertyMetadata{ box_value(L""), PropertyChangedCallback{ &SettingContainer::_OnFontIconGlyphChanged } });
}
if (!_CurrentValueProperty)
{
@@ -139,7 +140,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
base.reserve(2);
if (const auto& child{ GetTemplateChild(L"Expander") })
{
if (const auto& expander{ child.try_as<Microsoft::UI::Xaml::Controls::Expander>() })
if (const auto& expander{ child.try_as<Editor::SettingsExpander>() })
{
base.push_back(child);
}
@@ -223,13 +224,47 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_UpdateOverrideSystem();
_UpdateHelpText();
_UpdateHeaderIcon();
}
void SettingContainer::_UpdateHeaderIcon()
{
Controls::IconElement icon{ nullptr };
if (const auto glyph{ FontIconGlyph() }; !glyph.empty())
{
Controls::FontIcon fi;
fi.Glyph(glyph);
icon = fi;
}
if (const auto& cardChild{ GetTemplateChild(L"Card") })
{
if (const auto& card{ cardChild.try_as<Editor::SettingsCard>() })
{
card.HeaderIcon(icon);
return;
}
}
if (const auto& expanderChild{ GetTemplateChild(L"Expander") })
{
if (const auto& expander{ expanderChild.try_as<Editor::SettingsExpander>() })
{
expander.HeaderIcon(icon);
}
}
}
void SettingContainer::_OnFontIconGlyphChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*args*/)
{
const auto& obj{ d.try_as<Editor::SettingContainer>() };
get_self<SettingContainer>(obj)->_UpdateHeaderIcon();
}
void SettingContainer::SetExpanded(bool expanded)
{
if (const auto& child{ GetTemplateChild(L"Expander") })
{
if (const auto& expander{ child.try_as<Microsoft::UI::Xaml::Controls::Expander>() })
if (const auto& expander{ child.try_as<Editor::SettingsExpander>() })
{
expander.IsExpanded(expanded);
}
@@ -271,7 +306,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
if (const auto& child{ GetTemplateChild(L"Expander") })
{
if (const auto& expander{ child.try_as<Microsoft::UI::Xaml::Controls::Expander>() })
if (const auto& expander{ child.try_as<Editor::SettingsExpander>() })
{
Automation::AutomationProperties::SetName(expander, _GenerateAccessibleName());
}

View File

@@ -48,11 +48,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
static void _OnCurrentValueChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
static void _OnHasSettingValueChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
static void _OnHelpTextChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
static void _OnFontIconGlyphChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
static hstring _GenerateOverrideMessage(const IInspectable& settingOrigin);
hstring _GenerateAccessibleName();
void _UpdateOverrideSystem();
void _UpdateHelpText();
void _UpdateCurrentValueAutoProp();
void _UpdateHeaderIcon();
};
}

View File

@@ -18,8 +18,6 @@
</Style>
<SolidColorBrush x:Key="SubgroupHeaderBrush"
Color="{StaticResource TextFillColorSecondary}" />
<StaticResource x:Key="ExpanderHeaderBorderBrush"
ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="SettingContainerErrorSeverityBackgroundBrush"
ResourceKey="SystemFillColorCriticalBackgroundBrush" />
@@ -43,16 +41,12 @@
<StaticResource x:Key="SettingContainerResetButtonIconForeground"
ResourceKey="SystemAccentColorDark2" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<Style x:Key="SecondaryTextBlockStyle"
TargetType="TextBlock" />
<!-- Do not mess with the foreground color for High Contrast. Let it ride as is. -->
<SolidColorBrush x:Key="SubgroupHeaderBrush"
Color="{ThemeResource SystemColorWindowTextColor}" />
<StaticResource x:Key="ExpanderHeaderBorderBrush"
ResourceKey="SystemColorButtonTextColorBrush" />
<StaticResource x:Key="SettingContainerErrorSeverityBackgroundBrush"
ResourceKey="SystemColorWindowColorBrush" />
@@ -84,8 +78,6 @@
</Style>
<SolidColorBrush x:Key="SubgroupHeaderBrush"
Color="{StaticResource TextFillColorSecondary}" />
<StaticResource x:Key="ExpanderHeaderBorderBrush"
ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="SettingContainerErrorSeverityBackgroundBrush"
ResourceKey="SystemFillColorCriticalBackgroundBrush" />
@@ -121,12 +113,6 @@
<Thickness x:Key="SettingContainerIconMargin">0,4,8,4</Thickness>
<x:Double x:Key="SettingContainerIconFontSize">16</x:Double>
<Style x:Key="StackPanelInExpanderStyle"
TargetType="StackPanel">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Padding" Value="0,12,0,12" />
</Style>
<Style x:Key="SettingContainerResetButtonStyle"
BasedOn="{StaticResource DefaultButtonStyle}"
TargetType="Button">
@@ -145,18 +131,6 @@
<Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}" />
</Style>
<Style x:Key="NonExpanderGrid"
TargetType="Grid">
<Setter Property="Background" Value="{ThemeResource ExpanderHeaderBackground}" />
<Setter Property="MinWidth" Value="{ThemeResource FlyoutThemeMinWidth}" />
<Setter Property="MinHeight" Value="64" />
<Setter Property="BorderThickness" Value="{ThemeResource ExpanderHeaderBorderThickness}" />
<Setter Property="BorderBrush" Value="{ThemeResource ExpanderHeaderBorderBrush}" />
<Setter Property="Padding" Value="16,0,8,0" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
</Style>
<Style x:Key="SettingsPageItemHeaderStyle"
BasedOn="{StaticResource BodyTextBlockStyle}"
TargetType="TextBlock">
@@ -178,7 +152,7 @@
BasedOn="{StaticResource SettingsPageItemDescriptionStyle}"
TargetType="TextBlock">
<Setter Property="MaxWidth" Value="248" />
<Setter Property="Margin" Value="0,0,-16,0" />
<Setter Property="Margin" Value="0" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets" />
@@ -189,7 +163,9 @@
Text="{Binding}" />
</DataTemplate>
<!-- A setting container for a setting that has no additional options -->
<local:StringDefaultTemplateSelector x:Key="ExpanderPreviewTemplateSelector"
StringTemplate="{StaticResource ExpanderSettingContainerStringPreviewTemplate}" />
<Style TargetType="local:SettingContainer">
<Setter Property="Margin" Value="0,4,0,0" />
<Setter Property="IsTabStop" Value="False" />
@@ -197,14 +173,9 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<Grid AutomationProperties.Name="{TemplateBinding Header}"
Style="{StaticResource NonExpanderGrid}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Style="{StaticResource StackPanelInExpanderStyle}">
<local:SettingsCard x:Name="Card"
Content="{TemplateBinding Content}">
<local:SettingsCard.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
Text="{TemplateBinding Header}" />
@@ -214,257 +185,61 @@
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
</StackPanel>
</local:SettingsCard.Header>
<local:SettingsCard.Description>
<TextBlock x:Name="HelpTextBlock"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
<ContentPresenter Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="{TemplateBinding Content}" />
</Grid>
</local:SettingsCard.Description>
</local:SettingsCard>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- A basic setting container displaying immutable text as content -->
<Style x:Key="SettingContainerWithTextContent"
TargetType="local:SettingContainer">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<Grid AutomationProperties.Name="{TemplateBinding Header}"
Style="{StaticResource NonExpanderGrid}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
Text="{TemplateBinding Header}" />
<Button x:Name="ResetButton"
Style="{StaticResource SettingContainerResetButtonStyle}">
<FontIcon Glyph="&#xE845;"
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
</StackPanel>
<TextBlock x:Name="HelpTextBlock"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
<TextBlock Grid.Column="1"
Margin="0,0,8,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Style="{ThemeResource SecondaryTextBlockStyle}"
Text="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--
A setting container for a setting that has no additional options.
Includes space for an icon on the left side of the header.
XAML applies the margin/padding of the icon regardless of its
existence (which caused inconsistent padding). It's easier to just create
another style and move on.
-->
<Style x:Key="SettingContainerWithIcon"
TargetType="local:SettingContainer">
<Setter Property="Margin" Value="0,4,0,0" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="MaxWidth" Value="1000" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<Grid AutomationProperties.Name="{TemplateBinding Header}"
Style="{StaticResource NonExpanderGrid}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FontIcon Grid.Column="0"
Glyph="{TemplateBinding FontIconGlyph}" />
<StackPanel Grid.Column="1"
Padding="14,12,0,12"
Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
Text="{TemplateBinding Header}" />
<Button x:Name="ResetButton"
Style="{StaticResource SettingContainerResetButtonStyle}">
<FontIcon Glyph="&#xE845;"
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
</StackPanel>
<TextBlock x:Name="HelpTextBlock"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
<ContentPresenter Grid.Column="2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- A setting container which can expand -->
<Style x:Key="ExpanderSettingContainerStyle"
TargetType="local:SettingContainer">
<Setter Property="Margin" Value="0,4,0,0" />
<Setter Property="MaxWidth" Value="1000" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="AutomationProperties.AccessibilityView" Value="Raw" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<muxc:Expander x:Name="Expander"
Margin="0,4,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Content="{TemplateBinding Content}"
IsExpanded="{TemplateBinding StartExpanded}">
<muxc:Expander.Header>
<Grid MinHeight="64">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{TemplateBinding Header}" />
<Button x:Name="ResetButton"
Style="{StaticResource SettingContainerResetButtonStyle}">
<FontIcon Glyph="&#xE845;"
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
</StackPanel>
<TextBlock x:Name="HelpTextBlock"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
<ContentPresenter Grid.Column="1"
Content="{TemplateBinding CurrentValue}"
ContentTemplate="{StaticResource ExpanderSettingContainerStringPreviewTemplate}" />
</Grid>
</muxc:Expander.Header>
</muxc:Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--
A setting container which can expand. Includes space for an icon on the left side of the header.
XAML applies the margin/padding of the icon regardless of its
existence (which caused inconsistent padding). It's easier to just create
another style and move on.
-->
<Style x:Key="ExpanderSettingContainerStyleWithIcon"
TargetType="local:SettingContainer">
<Setter Property="MaxWidth" Value="1000" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="AutomationProperties.AccessibilityView" Value="Raw" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<muxc:Expander x:Name="Expander"
Margin="0,4,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Content="{TemplateBinding Content}"
IsExpanded="{TemplateBinding StartExpanded}">
<muxc:Expander.Header>
<Grid MinHeight="64">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FontIcon Grid.Column="0"
Glyph="{TemplateBinding FontIconGlyph}" />
<StackPanel Grid.Column="1"
Padding="14,12,0,12"
Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
Text="{TemplateBinding Header}" />
<Button x:Name="ResetButton"
Style="{StaticResource SettingContainerResetButtonStyle}">
<FontIcon Glyph="&#xE845;"
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
</StackPanel>
<TextBlock x:Name="HelpTextBlock"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
<ContentPresenter Grid.Column="2"
Content="{TemplateBinding CurrentValue}"
ContentTemplate="{StaticResource ExpanderSettingContainerStringPreviewTemplate}" />
</Grid>
</muxc:Expander.Header>
</muxc:Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- A setting container which can expand. Supports data template override for preview -->
<Style x:Key="ExpanderSettingContainerStyleWithComplexPreview"
TargetType="local:SettingContainer">
<Setter Property="MaxWidth" Value="1000" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="AutomationProperties.AccessibilityView" Value="Raw" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<muxc:Expander x:Name="Expander"
Margin="0,4,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Content="{TemplateBinding Content}"
IsExpanded="{TemplateBinding StartExpanded}">
<muxc:Expander.Header>
<Grid MinHeight="64">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
Text="{TemplateBinding Header}" />
<Button x:Name="ResetButton"
Style="{StaticResource SettingContainerResetButtonStyle}">
<FontIcon Glyph="&#xE845;"
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
</StackPanel>
<TextBlock x:Name="HelpTextBlock"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
<ContentPresenter Grid.Column="1"
MaxWidth="248"
Margin="0,0,-16,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="{TemplateBinding CurrentValue}"
ContentTemplate="{TemplateBinding CurrentValueTemplate}" />
</Grid>
</muxc:Expander.Header>
</muxc:Expander>
<local:SettingsExpander x:Name="Expander"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsExpanded="{TemplateBinding StartExpanded}">
<local:SettingsExpander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
Text="{TemplateBinding Header}" />
<Button x:Name="ResetButton"
Style="{StaticResource SettingContainerResetButtonStyle}">
<FontIcon Glyph="&#xE845;"
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
</StackPanel>
</local:SettingsExpander.Header>
<local:SettingsExpander.Description>
<TextBlock x:Name="HelpTextBlock"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</local:SettingsExpander.Description>
<local:SettingsExpander.Content>
<ContentControl MaxWidth="248"
VerticalAlignment="Center"
HorizontalContentAlignment="Right"
Content="{TemplateBinding CurrentValue}"
ContentTemplate="{TemplateBinding CurrentValueTemplate}"
ContentTemplateSelector="{StaticResource ExpanderPreviewTemplateSelector}"
IsTabStop="False" />
</local:SettingsExpander.Content>
<local:SettingsExpander.ItemsHeader>
<ContentPresenter Padding="16,12,16,16"
Content="{TemplateBinding Content}" />
</local:SettingsExpander.ItemsHeader>
</local:SettingsExpander>
</ControlTemplate>
</Setter.Value>
</Setter>
@@ -479,14 +254,8 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<Grid AutomationProperties.Name="{TemplateBinding Header}"
Background="{ThemeResource SettingContainerWarningSeverityBackgroundBrush}"
Style="{StaticResource NonExpanderGrid}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Style="{StaticResource StackPanelInExpanderStyle}">
<local:SettingsCard Background="{ThemeResource SettingContainerWarningSeverityBackgroundBrush}">
<local:SettingsCard.Header>
<StackPanel Orientation="Horizontal">
<Grid>
<TextBlock x:Name="IconBackground"
@@ -498,7 +267,6 @@
FontSize="{StaticResource SettingContainerIconFontSize}"
Foreground="{ThemeResource SettingContainerWarningSeverityIconBackground}"
Text="{StaticResource SettingContainerIconBackgroundGlyph}" />
<TextBlock x:Name="StandardIcon"
Grid.Column="0"
Margin="{StaticResource SettingContainerIconMargin}"
@@ -514,12 +282,14 @@
Style="{StaticResource SettingsPageItemHeaderStyle}"
Text="{TemplateBinding Header}" />
</StackPanel>
</local:SettingsCard.Header>
<local:SettingsCard.Description>
<TextBlock x:Name="HelpTextBlock"
Foreground="{ThemeResource SettingContainerMessageForeground}"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
</Grid>
</local:SettingsCard.Description>
</local:SettingsCard>
</ControlTemplate>
</Setter.Value>
</Setter>
@@ -534,14 +304,8 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<Grid AutomationProperties.Name="{TemplateBinding Header}"
Background="{ThemeResource SettingContainerErrorSeverityBackgroundBrush}"
Style="{StaticResource NonExpanderGrid}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Style="{StaticResource StackPanelInExpanderStyle}">
<local:SettingsCard Background="{ThemeResource SettingContainerErrorSeverityBackgroundBrush}">
<local:SettingsCard.Header>
<StackPanel Orientation="Horizontal">
<Grid>
<TextBlock x:Name="IconBackground"
@@ -553,7 +317,6 @@
FontSize="{StaticResource SettingContainerIconFontSize}"
Foreground="{ThemeResource SettingContainerErrorSeverityIconBackground}"
Text="{StaticResource SettingContainerIconBackgroundGlyph}" />
<TextBlock x:Name="StandardIcon"
Grid.Column="0"
Margin="{StaticResource SettingContainerIconMargin}"
@@ -569,12 +332,14 @@
Style="{StaticResource SettingsPageItemHeaderStyle}"
Text="{TemplateBinding Header}" />
</StackPanel>
</local:SettingsCard.Header>
<local:SettingsCard.Description>
<TextBlock x:Name="HelpTextBlock"
Foreground="{ThemeResource SettingContainerMessageForeground}"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</StackPanel>
</Grid>
</local:SettingsCard.Description>
</local:SettingsCard>
</ControlTemplate>
</Setter.Value>
</Setter>

View File

@@ -0,0 +1,472 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "SettingsCard.h"
#include "SettingsCard.g.cpp"
#include "SettingsCardAutomationPeer.g.cpp"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::System;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Automation;
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Controls::Primitives;
using namespace winrt::Windows::UI::Xaml::Input;
using namespace winrt::Windows::UI::Xaml::Media;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
DependencyProperty SettingsCard::_HeaderProperty{ nullptr };
DependencyProperty SettingsCard::_DescriptionProperty{ nullptr };
DependencyProperty SettingsCard::_HeaderIconProperty{ nullptr };
DependencyProperty SettingsCard::_ActionIconProperty{ nullptr };
DependencyProperty SettingsCard::_ActionIconToolTipProperty{ nullptr };
DependencyProperty SettingsCard::_IsClickEnabledProperty{ nullptr };
DependencyProperty SettingsCard::_IsActionIconVisibleProperty{ nullptr };
DependencyProperty SettingsCard::_ContentAlignmentProperty{ nullptr };
static constexpr std::wstring_view NormalState{ L"Normal" };
static constexpr std::wstring_view PointerOverState{ L"PointerOver" };
static constexpr std::wstring_view PressedState{ L"Pressed" };
static constexpr std::wstring_view DisabledState{ L"Disabled" };
static constexpr std::wstring_view RightState{ L"Right" };
static constexpr std::wstring_view LeftState{ L"Left" };
static constexpr std::wstring_view VerticalState{ L"Vertical" };
static constexpr std::wstring_view ActionIconPresenterHolder{ L"PART_ActionIconPresenterHolder" };
static constexpr std::wstring_view HeaderPresenter{ L"PART_HeaderPresenter" };
static constexpr std::wstring_view DescriptionPresenter{ L"PART_DescriptionPresenter" };
static constexpr std::wstring_view HeaderIconPresenterHolder{ L"PART_HeaderIconPresenterHolder" };
SettingsCard::SettingsCard()
{
_InitializeProperties();
}
void SettingsCard::_InitializeProperties()
{
if (!_HeaderProperty)
{
_HeaderProperty = DependencyProperty::Register(
L"Header",
xaml_typename<IInspectable>(),
xaml_typename<Editor::SettingsCard>(),
PropertyMetadata{ nullptr, PropertyChangedCallback{ &SettingsCard::_OnHeaderChanged } });
}
if (!_DescriptionProperty)
{
_DescriptionProperty = DependencyProperty::Register(
L"Description",
xaml_typename<IInspectable>(),
xaml_typename<Editor::SettingsCard>(),
PropertyMetadata{ nullptr, PropertyChangedCallback{ &SettingsCard::_OnDescriptionChanged } });
}
if (!_HeaderIconProperty)
{
_HeaderIconProperty = DependencyProperty::Register(
L"HeaderIcon",
xaml_typename<IconElement>(),
xaml_typename<Editor::SettingsCard>(),
PropertyMetadata{ nullptr, PropertyChangedCallback{ &SettingsCard::_OnHeaderIconChanged } });
}
if (!_ActionIconProperty)
{
_ActionIconProperty = DependencyProperty::Register(
L"ActionIcon",
xaml_typename<IInspectable>(),
xaml_typename<Editor::SettingsCard>(),
PropertyMetadata{ nullptr });
}
if (!_ActionIconToolTipProperty)
{
_ActionIconToolTipProperty = DependencyProperty::Register(
L"ActionIconToolTip",
xaml_typename<hstring>(),
xaml_typename<Editor::SettingsCard>(),
PropertyMetadata{ box_value(hstring{}) });
}
if (!_IsClickEnabledProperty)
{
_IsClickEnabledProperty = DependencyProperty::Register(
L"IsClickEnabled",
xaml_typename<bool>(),
xaml_typename<Editor::SettingsCard>(),
PropertyMetadata{ box_value(false), PropertyChangedCallback{ &SettingsCard::_OnIsClickEnabledChanged } });
}
if (!_IsActionIconVisibleProperty)
{
_IsActionIconVisibleProperty = DependencyProperty::Register(
L"IsActionIconVisible",
xaml_typename<bool>(),
xaml_typename<Editor::SettingsCard>(),
PropertyMetadata{ box_value(true), PropertyChangedCallback{ &SettingsCard::_OnIsActionIconVisibleChanged } });
}
if (!_ContentAlignmentProperty)
{
_ContentAlignmentProperty = DependencyProperty::Register(
L"ContentAlignment",
xaml_typename<Editor::SettingsCardContentAlignment>(),
xaml_typename<Editor::SettingsCard>(),
PropertyMetadata{ box_value(Editor::SettingsCardContentAlignment::Right), PropertyChangedCallback{ &SettingsCard::_OnContentAlignmentChanged } });
}
}
AutomationPeer SettingsCard::OnCreateAutomationPeer()
{
return winrt::make<implementation::SettingsCardAutomationPeer>(*this);
}
void SettingsCard::OnApplyTemplate()
{
// Drop any handlers from a previous template.
_isEnabledChangedRevoker.revoke();
_DisableButtonInteraction();
if (_contentChangedToken != 0)
{
UnregisterPropertyChangedCallback(ContentControl::ContentProperty(), _contentChangedToken);
_contentChangedToken = 0;
}
_UpdateActionIconVisibility();
_UpdateHeaderVisibility();
_UpdateDescriptionVisibility();
_UpdateHeaderIconVisibility();
// Initial visual states.
_CheckInitialVisualState();
_SetAccessibleContentName();
// Watch for Content changing later (we may need to refresh the AutomationProperties.Name on it).
_contentChangedToken = RegisterPropertyChangedCallback(ContentControl::ContentProperty(), [weakThis = get_weak()](auto&&, auto&&) {
if (const auto strongThis = weakThis.get())
{
strongThis->_SetAccessibleContentName();
}
});
// Apply click-interaction state.
if (IsClickEnabled())
{
_EnableButtonInteraction();
}
_isEnabledChangedRevoker = IsEnabledChanged(winrt::auto_revoke, [weakThis = get_weak()](auto&&, auto&&) {
if (const auto strongThis = weakThis.get())
{
strongThis->_GoToCommonState(strongThis->IsEnabled() ? NormalState : DisabledState, true);
}
});
}
void SettingsCard::_CheckInitialVisualState()
{
VisualStateManager::GoToState(*this, IsEnabled() ? hstring{ NormalState } : hstring{ DisabledState }, true);
_UpdateContentAlignmentState();
}
void SettingsCard::_SetAccessibleContentName()
{
// If Header is a string and the inner Content lacks an AutomationProperties.Name, propagate the header
// into the content so screen readers can announce something meaningful when focus lands there.
const auto headerObj{ Header() };
if (!headerObj)
{
return;
}
const auto headerString{ unbox_value_or<hstring>(headerObj, hstring{}) };
if (headerString.empty())
{
return;
}
const auto contentObj{ Content() };
if (const auto element{ contentObj.try_as<UIElement>() })
{
if (Automation::AutomationProperties::GetName(element).empty())
{
// Don't override ButtonBase content (would clobber its own name) or plain text blocks.
if (!element.try_as<ButtonBase>() && !element.try_as<TextBlock>())
{
Automation::AutomationProperties::SetName(element, headerString);
}
}
}
}
void SettingsCard::_EnableButtonInteraction()
{
if (_interactionEnabled)
{
return;
}
_interactionEnabled = true;
IsTabStop(true);
_pointerEnteredRevoker = PointerEntered(winrt::auto_revoke, [weakThis = get_weak()](auto&&, auto&&) {
if (const auto strongThis = weakThis.get())
{
strongThis->_GoToCommonState(PointerOverState, true);
}
});
_pointerExitedRevoker = PointerExited(winrt::auto_revoke, [weakThis = get_weak()](auto&&, auto&&) {
if (const auto strongThis = weakThis.get())
{
strongThis->_GoToCommonState(NormalState, true);
}
});
_pointerCaptureLostRevoker = PointerCaptureLost(winrt::auto_revoke, [weakThis = get_weak()](auto&&, auto&&) {
if (const auto strongThis = weakThis.get())
{
strongThis->_GoToCommonState(NormalState, true);
}
});
_pointerCanceledRevoker = PointerCanceled(winrt::auto_revoke, [weakThis = get_weak()](auto&&, auto&&) {
if (const auto strongThis = weakThis.get())
{
strongThis->_GoToCommonState(NormalState, true);
}
});
_previewKeyDownRevoker = PreviewKeyDown(winrt::auto_revoke, [weakThis = get_weak()](auto&&, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e) {
const auto strongThis = weakThis.get();
if (!strongThis)
{
return;
}
const auto key = e.Key();
if (key == Windows::System::VirtualKey::Enter || key == Windows::System::VirtualKey::Space || key == Windows::System::VirtualKey::GamepadA)
{
const auto focused{ strongThis->_GetFocusedElement() };
if (focused && focused.try_as<Editor::SettingsCard>() == strongThis.as<Editor::SettingsCard>())
{
strongThis->_GoToCommonState(PressedState, true);
}
}
});
_previewKeyUpRevoker = PreviewKeyUp(winrt::auto_revoke, [weakThis = get_weak()](auto&&, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e) {
const auto strongThis = weakThis.get();
if (!strongThis)
{
return;
}
const auto key = e.Key();
if (key == Windows::System::VirtualKey::Enter || key == Windows::System::VirtualKey::Space || key == Windows::System::VirtualKey::GamepadA)
{
strongThis->_GoToCommonState(NormalState, true);
}
});
}
void SettingsCard::_DisableButtonInteraction()
{
_interactionEnabled = false;
IsTabStop(false);
_pointerEnteredRevoker.revoke();
_pointerExitedRevoker.revoke();
_pointerCaptureLostRevoker.revoke();
_pointerCanceledRevoker.revoke();
_previewKeyDownRevoker.revoke();
_previewKeyUpRevoker.revoke();
}
void SettingsCard::_GoToCommonState(const std::wstring_view& state, bool useTransitions)
{
VisualStateManager::GoToState(*this, hstring{ state }, useTransitions);
}
FrameworkElement SettingsCard::_GetFocusedElement()
{
if (const auto root{ XamlRoot() })
{
return FocusManager::GetFocusedElement(root).try_as<FrameworkElement>();
}
return FocusManager::GetFocusedElement().try_as<FrameworkElement>();
}
void SettingsCard::_UpdateActionIconVisibility()
{
if (const auto child{ GetTemplateChild(hstring{ ActionIconPresenterHolder }) })
{
if (const auto frameworkChild{ child.try_as<FrameworkElement>() })
{
frameworkChild.Visibility((IsClickEnabled() && IsActionIconVisible()) ? Visibility::Visible : Visibility::Collapsed);
}
}
}
// Returns true if the given object is null, or is a string that is empty.
// Non-string non-null objects (e.g. a TextBlock) are considered "non-empty".
static bool _isNullOrEmpty(const winrt::Windows::Foundation::IInspectable& obj)
{
if (!obj)
{
return true;
}
if (const auto pv{ obj.try_as<IPropertyValue>() }; pv && pv.Type() == PropertyType::String)
{
return unbox_value_or<hstring>(obj, hstring{}).empty();
}
return false;
}
void SettingsCard::_UpdateHeaderVisibility()
{
if (const auto child{ GetTemplateChild(hstring{ HeaderPresenter }) })
{
if (const auto frameworkChild{ child.try_as<FrameworkElement>() })
{
frameworkChild.Visibility(_isNullOrEmpty(Header()) ? Visibility::Collapsed : Visibility::Visible);
}
}
}
void SettingsCard::_UpdateDescriptionVisibility()
{
if (const auto child{ GetTemplateChild(hstring{ DescriptionPresenter }) })
{
if (const auto frameworkChild{ child.try_as<FrameworkElement>() })
{
frameworkChild.Visibility(_isNullOrEmpty(Description()) ? Visibility::Collapsed : Visibility::Visible);
}
}
}
void SettingsCard::_UpdateHeaderIconVisibility()
{
if (const auto child{ GetTemplateChild(hstring{ HeaderIconPresenterHolder }) })
{
if (const auto frameworkChild{ child.try_as<FrameworkElement>() })
{
frameworkChild.Visibility(HeaderIcon() ? Visibility::Visible : Visibility::Collapsed);
}
}
}
void SettingsCard::_UpdateContentAlignmentState()
{
std::wstring_view state{ RightState };
switch (ContentAlignment())
{
case Editor::SettingsCardContentAlignment::Left:
state = LeftState;
break;
case Editor::SettingsCardContentAlignment::Vertical:
state = VerticalState;
break;
default:
state = RightState;
break;
}
VisualStateManager::GoToState(*this, hstring{ state }, true);
}
void SettingsCard::_OnHeaderChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*e*/)
{
const auto obj{ d.try_as<Editor::SettingsCard>() };
const auto self = get_self<SettingsCard>(obj);
self->_UpdateHeaderVisibility();
self->_SetAccessibleContentName();
}
void SettingsCard::_OnDescriptionChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*e*/)
{
const auto obj{ d.try_as<Editor::SettingsCard>() };
get_self<SettingsCard>(obj)->_UpdateDescriptionVisibility();
}
void SettingsCard::_OnHeaderIconChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*e*/)
{
const auto obj{ d.try_as<Editor::SettingsCard>() };
get_self<SettingsCard>(obj)->_UpdateHeaderIconVisibility();
}
void SettingsCard::_OnIsClickEnabledChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*e*/)
{
const auto obj{ d.try_as<Editor::SettingsCard>() };
const auto self = get_self<SettingsCard>(obj);
self->_UpdateActionIconVisibility();
if (self->IsClickEnabled())
{
self->_EnableButtonInteraction();
}
else
{
self->_DisableButtonInteraction();
}
}
void SettingsCard::_OnIsActionIconVisibleChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*e*/)
{
const auto obj{ d.try_as<Editor::SettingsCard>() };
get_self<SettingsCard>(obj)->_UpdateActionIconVisibility();
}
void SettingsCard::_OnContentAlignmentChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*e*/)
{
const auto obj{ d.try_as<Editor::SettingsCard>() };
get_self<SettingsCard>(obj)->_UpdateContentAlignmentState();
}
SettingsCardAutomationPeer::SettingsCardAutomationPeer(const Editor::SettingsCard& owner) :
SettingsCardAutomationPeerT<SettingsCardAutomationPeer>(owner)
{
}
AutomationControlType SettingsCardAutomationPeer::GetAutomationControlTypeCore() const
{
if (const auto card{ Owner().try_as<Editor::SettingsCard>() })
{
if (card.IsClickEnabled())
{
return AutomationControlType::Button;
}
}
return AutomationControlType::Group;
}
hstring SettingsCardAutomationPeer::GetClassNameCore() const
{
return hstring{ L"SettingsCard" };
}
hstring SettingsCardAutomationPeer::GetNameCore() const
{
if (const auto card{ Owner().try_as<Editor::SettingsCard>() })
{
if (card.IsClickEnabled())
{
if (const auto manualName{ AutomationProperties::GetName(card) }; !manualName.empty())
{
return manualName;
}
if (const auto headerString{ unbox_value_or<hstring>(card.Header(), hstring{}) }; !headerString.empty())
{
return headerString;
}
}
// Not clickable, or no header text: fall back to AutomationProperties.Name (matching
// FrameworkElementAutomationPeer's default behavior).
return AutomationProperties::GetName(card);
}
return {};
}
winrt::Windows::Foundation::IInspectable SettingsCardAutomationPeer::GetPatternCore(PatternInterface patternInterface) const
{
if (patternInterface == PatternInterface::Invoke)
{
if (const auto card{ Owner().try_as<Editor::SettingsCard>() })
{
if (card.IsClickEnabled())
{
// Only provide Invoke pattern if the card is clickable.
return *this;
}
}
return nullptr;
}
// ButtonBaseAutomationPeer only provides Invoke; everything else returns null.
return nullptr;
}
}

View File

@@ -0,0 +1,95 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- SettingsCard
Abstract:
- A base control for building consistent settings experiences. Based
on the Windows Community Toolkit's SettingsCard.
Author(s):
- Carlos Zamora - 2026 May
--*/
#pragma once
#include "SettingsCard.g.h"
#include "SettingsCardAutomationPeer.g.h"
#include "Utils.h"
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct SettingsCard : SettingsCardT<SettingsCard>
{
public:
SettingsCard();
void OnApplyTemplate();
// Automation peer override.
Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer();
DEPENDENCY_PROPERTY(Windows::Foundation::IInspectable, Header);
DEPENDENCY_PROPERTY(Windows::Foundation::IInspectable, Description);
DEPENDENCY_PROPERTY(Windows::UI::Xaml::Controls::IconElement, HeaderIcon);
DEPENDENCY_PROPERTY(Windows::Foundation::IInspectable, ActionIcon);
DEPENDENCY_PROPERTY(hstring, ActionIconToolTip);
DEPENDENCY_PROPERTY(bool, IsClickEnabled);
DEPENDENCY_PROPERTY(bool, IsActionIconVisible);
DEPENDENCY_PROPERTY(Editor::SettingsCardContentAlignment, ContentAlignment);
private:
static void _InitializeProperties();
static void _OnHeaderChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
static void _OnDescriptionChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
static void _OnHeaderIconChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
static void _OnIsClickEnabledChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
static void _OnIsActionIconVisibleChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
static void _OnContentAlignmentChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
void _EnableButtonInteraction();
void _DisableButtonInteraction();
void _GoToCommonState(const std::wstring_view& state, bool useTransitions);
void _UpdateActionIconVisibility();
void _UpdateHeaderVisibility();
void _UpdateDescriptionVisibility();
void _UpdateHeaderIconVisibility();
void _UpdateContentAlignmentState();
void _CheckInitialVisualState();
void _SetAccessibleContentName();
Windows::UI::Xaml::FrameworkElement _GetFocusedElement();
bool _interactionEnabled{ false };
Windows::UI::Xaml::Controls::Control::IsEnabledChanged_revoker _isEnabledChangedRevoker;
Windows::UI::Xaml::UIElement::PointerEntered_revoker _pointerEnteredRevoker;
Windows::UI::Xaml::UIElement::PointerExited_revoker _pointerExitedRevoker;
Windows::UI::Xaml::UIElement::PointerCaptureLost_revoker _pointerCaptureLostRevoker;
Windows::UI::Xaml::UIElement::PointerCanceled_revoker _pointerCanceledRevoker;
Windows::UI::Xaml::UIElement::PreviewKeyDown_revoker _previewKeyDownRevoker;
Windows::UI::Xaml::UIElement::PreviewKeyUp_revoker _previewKeyUpRevoker;
int64_t _contentChangedToken{ 0 };
};
// AutomationPeer for SettingsCard. Mirrors the Community Toolkit's
// SettingsCardAutomationPeer: only exposes Invoke + Button control type when
// the card has IsClickEnabled=true; otherwise reports as a Group.
struct SettingsCardAutomationPeer : SettingsCardAutomationPeerT<SettingsCardAutomationPeer>
{
public:
SettingsCardAutomationPeer(const Editor::SettingsCard& owner);
Windows::UI::Xaml::Automation::Peers::AutomationControlType GetAutomationControlTypeCore() const;
hstring GetClassNameCore() const;
hstring GetNameCore() const;
winrt::Windows::Foundation::IInspectable GetPatternCore(Windows::UI::Xaml::Automation::Peers::PatternInterface patternInterface) const;
};
}
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
{
BASIC_FACTORY(SettingsCard);
BASIC_FACTORY(SettingsCardAutomationPeer);
}

View File

@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace Microsoft.Terminal.Settings.Editor
{
enum SettingsCardContentAlignment
{
Right,
Left,
Vertical
};
[default_interface] runtimeclass SettingsCard : Windows.UI.Xaml.Controls.Primitives.ButtonBase
{
SettingsCard();
Object Header;
static Windows.UI.Xaml.DependencyProperty HeaderProperty { get; };
Object Description;
static Windows.UI.Xaml.DependencyProperty DescriptionProperty { get; };
Windows.UI.Xaml.Controls.IconElement HeaderIcon;
static Windows.UI.Xaml.DependencyProperty HeaderIconProperty { get; };
Object ActionIcon;
static Windows.UI.Xaml.DependencyProperty ActionIconProperty { get; };
String ActionIconToolTip;
static Windows.UI.Xaml.DependencyProperty ActionIconToolTipProperty { get; };
Boolean IsClickEnabled;
static Windows.UI.Xaml.DependencyProperty IsClickEnabledProperty { get; };
Boolean IsActionIconVisible;
static Windows.UI.Xaml.DependencyProperty IsActionIconVisibleProperty { get; };
SettingsCardContentAlignment ContentAlignment;
static Windows.UI.Xaml.DependencyProperty ContentAlignmentProperty { get; };
};
[default_interface] runtimeclass SettingsCardAutomationPeer : Windows.UI.Xaml.Automation.Peers.ButtonBaseAutomationPeer
{
SettingsCardAutomationPeer(SettingsCard owner);
};
}

View File

@@ -0,0 +1,409 @@
<!--
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information.
Default styles for SettingsCard and SettingsExpander.
These were ported from the Windows Community Toolkit
(components/SettingsControls) and trimmed for use in this project:
- The Card's responsive RightWrapped/RightWrappedNoIcon visual states are
omitted (they depend on tk:ControlSizeTrigger).
- The Expander uses muxc:Expander's built-in template rather than the
custom 500+ line template the toolkit ships.
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<StaticResource x:Key="SettingsCardBackground"
ResourceKey="CardBackgroundFillColorDefaultBrush" />
<StaticResource x:Key="SettingsCardBackgroundPointerOver"
ResourceKey="ControlFillColorSecondaryBrush" />
<StaticResource x:Key="SettingsCardBackgroundPressed"
ResourceKey="ControlFillColorTertiaryBrush" />
<StaticResource x:Key="SettingsCardBackgroundDisabled"
ResourceKey="ControlFillColorDisabledBrush" />
<StaticResource x:Key="SettingsCardForeground"
ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="SettingsCardForegroundPointerOver"
ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="SettingsCardForegroundPressed"
ResourceKey="TextFillColorSecondaryBrush" />
<StaticResource x:Key="SettingsCardForegroundDisabled"
ResourceKey="TextFillColorDisabledBrush" />
<StaticResource x:Key="SettingsCardBorderBrush"
ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="SettingsCardBorderBrushPointerOver"
ResourceKey="ControlElevationBorderBrush" />
<StaticResource x:Key="SettingsCardBorderBrushPressed"
ResourceKey="ControlStrokeColorDefaultBrush" />
<StaticResource x:Key="SettingsCardBorderBrushDisabled"
ResourceKey="ControlStrokeColorDefaultBrush" />
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<StaticResource x:Key="SettingsCardBackground"
ResourceKey="CardBackgroundFillColorDefaultBrush" />
<StaticResource x:Key="SettingsCardBackgroundPointerOver"
ResourceKey="ControlFillColorSecondaryBrush" />
<StaticResource x:Key="SettingsCardBackgroundPressed"
ResourceKey="ControlFillColorTertiaryBrush" />
<StaticResource x:Key="SettingsCardBackgroundDisabled"
ResourceKey="ControlFillColorDisabledBrush" />
<StaticResource x:Key="SettingsCardForeground"
ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="SettingsCardForegroundPointerOver"
ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="SettingsCardForegroundPressed"
ResourceKey="TextFillColorSecondaryBrush" />
<StaticResource x:Key="SettingsCardForegroundDisabled"
ResourceKey="TextFillColorDisabledBrush" />
<StaticResource x:Key="SettingsCardBorderBrush"
ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="SettingsCardBorderBrushPointerOver"
ResourceKey="ControlElevationBorderBrush" />
<StaticResource x:Key="SettingsCardBorderBrushPressed"
ResourceKey="ControlStrokeColorDefaultBrush" />
<StaticResource x:Key="SettingsCardBorderBrushDisabled"
ResourceKey="ControlStrokeColorDefaultBrush" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<StaticResource x:Key="SettingsCardBackground"
ResourceKey="SystemColorButtonFaceColorBrush" />
<StaticResource x:Key="SettingsCardBackgroundPointerOver"
ResourceKey="SystemColorHighlightTextColorBrush" />
<StaticResource x:Key="SettingsCardBackgroundPressed"
ResourceKey="SystemColorHighlightTextColorBrush" />
<StaticResource x:Key="SettingsCardBackgroundDisabled"
ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="SettingsCardForeground"
ResourceKey="SystemColorButtonTextColorBrush" />
<StaticResource x:Key="SettingsCardForegroundPointerOver"
ResourceKey="SystemColorHighlightColorBrush" />
<StaticResource x:Key="SettingsCardForegroundPressed"
ResourceKey="SystemColorHighlightColorBrush" />
<StaticResource x:Key="SettingsCardForegroundDisabled"
ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="SettingsCardBorderBrush"
ResourceKey="SystemColorButtonTextColorBrush" />
<StaticResource x:Key="SettingsCardBorderBrushPointerOver"
ResourceKey="SystemColorHighlightColorBrush" />
<StaticResource x:Key="SettingsCardBorderBrushPressed"
ResourceKey="SystemColorHighlightTextColorBrush" />
<StaticResource x:Key="SettingsCardBorderBrushDisabled"
ResourceKey="SystemControlDisabledTransparentBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Thickness x:Key="SettingsCardBorderThickness">1</Thickness>
<Thickness x:Key="SettingsCardPadding">16,16,16,16</Thickness>
<x:Double x:Key="SettingsCardMinWidth">148</x:Double>
<x:Double x:Key="SettingsCardMinHeight">68</x:Double>
<x:Double x:Key="SettingsCardDescriptionFontSize">12</x:Double>
<x:Double x:Key="SettingsCardHeaderIconMaxSize">20</x:Double>
<x:Double x:Key="SettingsCardContentMinWidth">120</x:Double>
<Thickness x:Key="SettingsCardHeaderIconMargin">2,0,20,0</Thickness>
<Thickness x:Key="SettingsCardActionIconMargin">14,0,0,0</Thickness>
<x:Double x:Key="SettingsCardActionIconMaxSize">13</x:Double>
<Thickness x:Key="SettingsExpanderItemPadding">58,8,44,8</Thickness>
<Thickness x:Key="SettingsExpanderItemBorderThickness">0,1,0,0</Thickness>
<x:Double x:Key="SettingsExpanderContentMinHeight">16</x:Double>
<!-- ============ SettingsCard ============ -->
<!--
Card style for items that live inside a SettingsExpander:
no left/right rounded corners, a 1-pixel top border between siblings,
deeper left padding to line up with the parent expander's content.
-->
<Style x:Key="SettingsExpanderItemCardStyle"
BasedOn="{StaticResource DefaultSettingsCardStyle}"
TargetType="local:SettingsCard">
<Setter Property="BorderThickness" Value="{StaticResource SettingsExpanderItemBorderThickness}" />
<Setter Property="MinHeight" Value="52" />
<Setter Property="Padding" Value="{StaticResource SettingsExpanderItemPadding}" />
<Setter Property="CornerRadius" Value="0" />
</Style>
<Style BasedOn="{StaticResource DefaultSettingsCardStyle}"
TargetType="local:SettingsCard" />
<Style x:Key="DefaultSettingsCardStyle"
TargetType="local:SettingsCard">
<Setter Property="Background" Value="{ThemeResource SettingsCardBackground}" />
<Setter Property="Foreground" Value="{ThemeResource SettingsCardForeground}" />
<Setter Property="BorderBrush" Value="{ThemeResource SettingsCardBorderBrush}" />
<Setter Property="BorderThickness" Value="{StaticResource SettingsCardBorderThickness}" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="MinHeight" Value="{StaticResource SettingsCardMinHeight}" />
<Setter Property="MinWidth" Value="{StaticResource SettingsCardMinWidth}" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
<Setter Property="Padding" Value="{StaticResource SettingsCardPadding}" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin" Value="-3" />
<Setter Property="ActionIcon" Value="&#xE974;" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingsCard">
<Grid x:Name="PART_RootGrid"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}"
MaxWidth="{TemplateBinding MaxWidth}"
Padding="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Background="{TemplateBinding Background}"
BackgroundSizing="{TemplateBinding BackgroundSizing}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Viewbox x:Name="PART_HeaderIconPresenterHolder"
Grid.RowSpan="1"
MaxWidth="{StaticResource SettingsCardHeaderIconMaxSize}"
MaxHeight="{StaticResource SettingsCardHeaderIconMaxSize}"
Margin="{StaticResource SettingsCardHeaderIconMargin}">
<ContentPresenter x:Name="PART_HeaderIconPresenter"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding HeaderIcon}" />
</Viewbox>
<StackPanel x:Name="HeaderPanel"
Grid.Column="1"
Margin="0,0,24,0"
VerticalAlignment="Center"
Orientation="Vertical">
<ContentPresenter x:Name="PART_HeaderPresenter"
HorizontalAlignment="Left"
Content="{TemplateBinding Header}"
TextWrapping="Wrap" />
<ContentPresenter x:Name="PART_DescriptionPresenter"
Content="{TemplateBinding Description}"
FontSize="{StaticResource SettingsCardDescriptionFontSize}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap">
<ContentPresenter.Resources>
<Style BasedOn="{StaticResource CaptionTextBlockStyle}"
TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</StackPanel>
<ContentPresenter x:Name="PART_ContentPresenter"
Grid.Column="2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
Content="{TemplateBinding Content}" />
<Viewbox x:Name="PART_ActionIconPresenterHolder"
Grid.RowSpan="2"
Grid.Column="3"
MaxWidth="{StaticResource SettingsCardActionIconMaxSize}"
MaxHeight="{StaticResource SettingsCardActionIconMaxSize}"
Margin="{StaticResource SettingsCardActionIconMargin}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="Collapsed">
<ContentPresenter x:Name="PART_ActionIconPresenter"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding ActionIcon}"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
ToolTipService.ToolTip="{TemplateBinding ActionIconToolTip}" />
</Viewbox>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_RootGrid"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SettingsCardBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_RootGrid"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SettingsCardBorderBrushPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_HeaderPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SettingsCardForegroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_RootGrid"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SettingsCardBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_RootGrid"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SettingsCardBorderBrushPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_HeaderPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SettingsCardForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_DescriptionPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SettingsCardForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_ActionIconPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SettingsCardForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_HeaderIconPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SettingsCardForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_HeaderPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SettingsCardForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_DescriptionPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{ThemeResource SettingsCardForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ContentAlignmentStates">
<VisualState x:Name="Right" />
<VisualState x:Name="Left">
<VisualState.Setters>
<Setter Target="PART_HeaderIconPresenterHolder.Visibility" Value="Collapsed" />
<Setter Target="PART_HeaderPresenter.Visibility" Value="Collapsed" />
<Setter Target="PART_DescriptionPresenter.Visibility" Value="Collapsed" />
<Setter Target="PART_ContentPresenter.(Grid.Row)" Value="1" />
<Setter Target="PART_ContentPresenter.(Grid.Column)" Value="1" />
<Setter Target="PART_ContentPresenter.HorizontalAlignment" Value="Left" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Vertical">
<VisualState.Setters>
<Setter Target="PART_ContentPresenter.(Grid.Row)" Value="1" />
<Setter Target="PART_ContentPresenter.(Grid.Column)" Value="1" />
<Setter Target="PART_ContentPresenter.HorizontalAlignment" Value="Stretch" />
<Setter Target="PART_RootGrid.RowSpacing" Value="8" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ============ SettingsExpander ============ -->
<Style BasedOn="{StaticResource DefaultSettingsExpanderStyle}"
TargetType="local:SettingsExpander" />
<Style x:Key="DefaultSettingsExpanderStyle"
TargetType="local:SettingsExpander">
<Setter Property="Background" Value="{ThemeResource SettingsCardBackground}" />
<Setter Property="Foreground" Value="{ThemeResource SettingsCardForeground}" />
<Setter Property="BorderBrush" Value="{ThemeResource SettingsCardBorderBrush}" />
<Setter Property="BorderThickness" Value="{StaticResource SettingsCardBorderThickness}" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="MinHeight" Value="{StaticResource SettingsCardMinHeight}" />
<Setter Property="MinWidth" Value="{StaticResource SettingsCardMinWidth}" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingsExpander">
<muxc:Expander x:Name="PART_Expander"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"
CornerRadius="{TemplateBinding CornerRadius}"
IsExpanded="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
<muxc:Expander.Header>
<local:SettingsCard MinHeight="52"
Padding="0,8,0,8"
VerticalAlignment="Center"
Background="Transparent"
BorderThickness="0"
Content="{TemplateBinding Content}"
Description="{TemplateBinding Description}"
FontFamily="{TemplateBinding FontFamily}"
Header="{TemplateBinding Header}"
HeaderIcon="{TemplateBinding HeaderIcon}"
IsClickEnabled="False" />
</muxc:Expander.Header>
<muxc:Expander.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentPresenter Content="{TemplateBinding ItemsHeader}" />
<ContentPresenter Grid.Row="1"
Content="{TemplateBinding ItemsFooter}" />
</Grid>
</muxc:Expander.Content>
</muxc:Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,150 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "SettingsExpander.h"
#include "SettingsExpander.g.cpp"
#include "SettingsExpanderAutomationPeer.g.cpp"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Automation;
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
using namespace winrt::Windows::UI::Xaml::Controls;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
DependencyProperty SettingsExpander::_HeaderProperty{ nullptr };
DependencyProperty SettingsExpander::_DescriptionProperty{ nullptr };
DependencyProperty SettingsExpander::_HeaderIconProperty{ nullptr };
DependencyProperty SettingsExpander::_ContentProperty{ nullptr };
DependencyProperty SettingsExpander::_IsExpandedProperty{ nullptr };
DependencyProperty SettingsExpander::_ItemsHeaderProperty{ nullptr };
DependencyProperty SettingsExpander::_ItemsFooterProperty{ nullptr };
SettingsExpander::SettingsExpander()
{
_InitializeProperties();
}
void SettingsExpander::_InitializeProperties()
{
if (!_HeaderProperty)
{
_HeaderProperty = DependencyProperty::Register(
L"Header",
xaml_typename<IInspectable>(),
xaml_typename<Editor::SettingsExpander>(),
PropertyMetadata{ nullptr });
}
if (!_DescriptionProperty)
{
_DescriptionProperty = DependencyProperty::Register(
L"Description",
xaml_typename<IInspectable>(),
xaml_typename<Editor::SettingsExpander>(),
PropertyMetadata{ nullptr });
}
if (!_HeaderIconProperty)
{
_HeaderIconProperty = DependencyProperty::Register(
L"HeaderIcon",
xaml_typename<IconElement>(),
xaml_typename<Editor::SettingsExpander>(),
PropertyMetadata{ nullptr });
}
if (!_ContentProperty)
{
_ContentProperty = DependencyProperty::Register(
L"Content",
xaml_typename<IInspectable>(),
xaml_typename<Editor::SettingsExpander>(),
PropertyMetadata{ nullptr });
}
if (!_IsExpandedProperty)
{
_IsExpandedProperty = DependencyProperty::Register(
L"IsExpanded",
xaml_typename<bool>(),
xaml_typename<Editor::SettingsExpander>(),
PropertyMetadata{ box_value(false), PropertyChangedCallback{ &SettingsExpander::_OnIsExpandedChanged } });
}
if (!_ItemsHeaderProperty)
{
_ItemsHeaderProperty = DependencyProperty::Register(
L"ItemsHeader",
xaml_typename<UIElement>(),
xaml_typename<Editor::SettingsExpander>(),
PropertyMetadata{ nullptr });
}
if (!_ItemsFooterProperty)
{
_ItemsFooterProperty = DependencyProperty::Register(
L"ItemsFooter",
xaml_typename<UIElement>(),
xaml_typename<Editor::SettingsExpander>(),
PropertyMetadata{ nullptr });
}
}
AutomationPeer SettingsExpander::OnCreateAutomationPeer()
{
return winrt::make<implementation::SettingsExpanderAutomationPeer>(*this);
}
void SettingsExpander::OnApplyTemplate()
{
// No template-part lookups required: our template uses regular bindings
// through TemplateBinding / x:Bind. The DPs do their own work.
}
void SettingsExpander::_OnIsExpandedChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& e)
{
const auto obj{ d.try_as<Editor::SettingsExpander>() };
if (!obj)
{
return;
}
const auto self = get_self<SettingsExpander>(obj);
const auto newValue = unbox_value_or<bool>(e.NewValue(), false);
if (newValue)
{
self->Expanded.raise(obj, nullptr);
}
else
{
self->Collapsed.raise(obj, nullptr);
}
}
SettingsExpanderAutomationPeer::SettingsExpanderAutomationPeer(const Editor::SettingsExpander& owner) :
SettingsExpanderAutomationPeerT<SettingsExpanderAutomationPeer>(owner)
{
}
AutomationControlType SettingsExpanderAutomationPeer::GetAutomationControlTypeCore() const
{
return AutomationControlType::Group;
}
hstring SettingsExpanderAutomationPeer::GetClassNameCore() const
{
return hstring{ L"SettingsExpander" };
}
hstring SettingsExpanderAutomationPeer::GetNameCore() const
{
if (const auto expander{ Owner().try_as<Editor::SettingsExpander>() })
{
if (const auto manualName{ AutomationProperties::GetName(expander) }; !manualName.empty())
{
return manualName;
}
if (const auto headerString{ unbox_value_or<hstring>(expander.Header(), hstring{}) }; !headerString.empty())
{
return headerString;
}
}
return {};
}
}

View File

@@ -0,0 +1,67 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- SettingsExpander
Abstract:
- A collapsable container for grouping multiple SettingsCards. Based
on the Windows Community Toolkit's SettingsExpander.
Author(s):
- Carlos Zamora - 2026 (port from CommunityToolkit.WinUI.Controls.SettingsExpander)
--*/
#pragma once
#include "SettingsExpander.g.h"
#include "SettingsExpanderAutomationPeer.g.h"
#include "Utils.h"
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct SettingsExpander : SettingsExpanderT<SettingsExpander>
{
public:
SettingsExpander();
void OnApplyTemplate();
Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer();
til::typed_event<Editor::SettingsExpander, Windows::Foundation::IInspectable> Expanded;
til::typed_event<Editor::SettingsExpander, Windows::Foundation::IInspectable> Collapsed;
DEPENDENCY_PROPERTY(Windows::Foundation::IInspectable, Header);
DEPENDENCY_PROPERTY(Windows::Foundation::IInspectable, Description);
DEPENDENCY_PROPERTY(Windows::UI::Xaml::Controls::IconElement, HeaderIcon);
DEPENDENCY_PROPERTY(Windows::Foundation::IInspectable, Content);
DEPENDENCY_PROPERTY(bool, IsExpanded);
DEPENDENCY_PROPERTY(Windows::UI::Xaml::UIElement, ItemsHeader);
DEPENDENCY_PROPERTY(Windows::UI::Xaml::UIElement, ItemsFooter);
private:
static void _InitializeProperties();
static void _OnIsExpandedChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
};
// AutomationPeer for SettingsExpander. Reports class name and falls back to
// Header text for the name when AutomationProperties.Name is unset.
struct SettingsExpanderAutomationPeer : SettingsExpanderAutomationPeerT<SettingsExpanderAutomationPeer>
{
public:
SettingsExpanderAutomationPeer(const Editor::SettingsExpander& owner);
Windows::UI::Xaml::Automation::Peers::AutomationControlType GetAutomationControlTypeCore() const;
hstring GetClassNameCore() const;
hstring GetNameCore() const;
};
}
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
{
BASIC_FACTORY(SettingsExpander);
BASIC_FACTORY(SettingsExpanderAutomationPeer);
}

View File

@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace Microsoft.Terminal.Settings.Editor
{
[default_interface] runtimeclass SettingsExpander : Windows.UI.Xaml.Controls.Control
{
SettingsExpander();
Object Header;
static Windows.UI.Xaml.DependencyProperty HeaderProperty { get; };
Object Description;
static Windows.UI.Xaml.DependencyProperty DescriptionProperty { get; };
Windows.UI.Xaml.Controls.IconElement HeaderIcon;
static Windows.UI.Xaml.DependencyProperty HeaderIconProperty { get; };
Object Content;
static Windows.UI.Xaml.DependencyProperty ContentProperty { get; };
Boolean IsExpanded;
static Windows.UI.Xaml.DependencyProperty IsExpandedProperty { get; };
Windows.UI.Xaml.UIElement ItemsHeader;
static Windows.UI.Xaml.DependencyProperty ItemsHeaderProperty { get; };
Windows.UI.Xaml.UIElement ItemsFooter;
static Windows.UI.Xaml.DependencyProperty ItemsFooterProperty { get; };
event Windows.Foundation.TypedEventHandler<SettingsExpander, Object> Expanded;
event Windows.Foundation.TypedEventHandler<SettingsExpander, Object> Collapsed;
};
[default_interface] runtimeclass SettingsExpanderAutomationPeer : Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer
{
SettingsExpanderAutomationPeer(SettingsExpander owner);
};
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "StringDefaultTemplateSelector.h"
#include "StringDefaultTemplateSelector.g.cpp"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
DataTemplate StringDefaultTemplateSelector::SelectTemplateCore(const IInspectable& item, const DependencyObject& /*container*/)
{
return SelectTemplateCore(item);
}
DataTemplate StringDefaultTemplateSelector::SelectTemplateCore(const IInspectable& item)
{
if (const auto pv{ item.try_as<IPropertyValue>() }; pv && pv.Type() == PropertyType::String)
{
return _StringTemplate;
}
return nullptr;
}
}

View File

@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation
// Licensed under the MIT license.
#pragma once
#include "StringDefaultTemplateSelector.g.h"
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct StringDefaultTemplateSelector : StringDefaultTemplateSelectorT<StringDefaultTemplateSelector>
{
StringDefaultTemplateSelector() = default;
Windows::UI::Xaml::DataTemplate SelectTemplateCore(const winrt::Windows::Foundation::IInspectable& item, const winrt::Windows::UI::Xaml::DependencyObject& container);
Windows::UI::Xaml::DataTemplate SelectTemplateCore(const winrt::Windows::Foundation::IInspectable& item);
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, StringTemplate, nullptr);
};
}
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
{
BASIC_FACTORY(StringDefaultTemplateSelector);
}

View File

@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
namespace Microsoft.Terminal.Settings.Editor
{
[default_interface] runtimeclass StringDefaultTemplateSelector : Windows.UI.Xaml.Controls.DataTemplateSelector
{
StringDefaultTemplateSelector();
Windows.UI.Xaml.DataTemplate StringTemplate;
}
}

View File

@@ -102,6 +102,14 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_DisabledProfileSources->Append(src);
}
}
if (_SafeUriSchemes)
{
globals->_SafeUriSchemes = winrt::single_threaded_vector<hstring>();
for (const auto& src : *_SafeUriSchemes)
{
globals->_SafeUriSchemes->Append(src);
}
}
for (const auto& parent : _parents)
{

View File

@@ -114,6 +114,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(Boolean, EnableUnfocusedAcrylic);
INHERITABLE_SETTING(Boolean, AllowHeadless);
INHERITABLE_SETTING(String, SearchWebDefaultQueryUrl);
INHERITABLE_SETTING(IVector<String>, SafeUriSchemes);
Windows.Foundation.Collections.IMapView<String, ColorScheme> ColorSchemes();
void AddColorScheme(ColorScheme scheme);

View File

@@ -63,6 +63,7 @@ Author(s):
X(bool, MinimizeToNotificationArea, "minimizeToNotificationArea", false) \
X(bool, AlwaysShowNotificationIcon, "alwaysShowNotificationIcon", false) \
X(winrt::Windows::Foundation::Collections::IVector<winrt::hstring>, DisabledProfileSources, "disabledProfileSources", nullptr) \
X(winrt::Windows::Foundation::Collections::IVector<winrt::hstring>, SafeUriSchemes, "safeUriSchemes", nullptr) \
X(bool, ShowAdminShield, "showAdminShield", true) \
X(bool, TrimPaste, "trimPaste", true) \
X(bool, EnableColorSelection, "experimental.enableColorSelection", false) \

View File

@@ -467,6 +467,7 @@ namespace SettingsModelUnitTests
"$schema" : "https://aka.ms/terminal-profiles-schema",
"defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}",
"disabledProfileSources": [ "Windows.Terminal.Wsl" ],
"safeUriSchemes": [ "vscode" ],
"newTabMenu":
[
{

View File

@@ -123,6 +123,7 @@ class ScreenBufferTests
TEST_METHOD(VtResizePreservingAttributes);
TEST_METHOD(VtSoftResetCursorPosition);
TEST_METHOD(VtSoftResetAltBufferCursorState);
TEST_METHOD(VtScrollMarginsNewlineColor);
@@ -1510,6 +1511,30 @@ void ScreenBufferTests::VtSoftResetCursorPosition()
VERIFY_ARE_EQUAL(til::point(1, 1), cursor.GetPosition());
}
void ScreenBufferTests::VtSoftResetAltBufferCursorState()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.LockConsole(); // Lock must be taken to manipulate buffer.
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
auto& si = gci.GetActiveOutputBuffer();
auto& stateMachine = si.GetStateMachine();
Log::Comment(L"Move cursor on the main buffer.");
stateMachine.ProcessString(L"\x1b[4;7H");
VERIFY_ARE_EQUAL(til::point(6, 3), si.GetTextBuffer().GetCursor().GetPosition());
Log::Comment(L"Enter alt buffer, soft reset, and return to main buffer.");
stateMachine.ProcessString(L"\x1b[?1049h");
VERIFY_IS_TRUE(gci.GetActiveOutputBuffer()._IsAltBuffer());
stateMachine.ProcessString(L"\x1b[!p");
stateMachine.ProcessString(L"\x1b[?1049l");
VERIFY_IS_FALSE(gci.GetActiveOutputBuffer()._IsAltBuffer());
Log::Comment(L"Returning from alt buffer should restore the main cursor position.");
VERIFY_ARE_EQUAL(til::point(6, 3), gci.GetActiveOutputBuffer().GetTextBuffer().GetCursor().GetPosition());
}
void ScreenBufferTests::VtScrollMarginsNewlineColor()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();

View File

@@ -90,7 +90,15 @@ std::function<bool(wchar_t)> SixelParser::DefineImage(const VTInt macroParameter
_state = States::Normal;
_parameters.clear();
return [&](const auto ch) {
_parseCommandChar(ch);
try
{
_parseCommandChar(ch);
}
catch (...)
{
// Ignore all further content.
return false;
}
return true;
};
}
@@ -234,10 +242,18 @@ void SixelParser::_executeNextLine()
_executeCarriageReturn();
_imageLineCount++;
_maybeFlushImageBuffer();
_imageCursor.y += _sixelHeight;
_availablePixelHeight -= _sixelHeight;
_resizeImageBuffer(_sixelHeight);
_fillImageBackgroundWhenScrolled();
// If we don't have any available pixel height, that means the image has
// extended beyond the bottom of the display and we haven't triggered a
// a scroll (because sixel display mode is enabled). In this state, there
// is no point in extending the image any further, because the additional
// content will never be seen, so we'll just be wasting memory.
if (_availablePixelHeight > 0)
{
_imageCursor.y += _sixelHeight;
_availablePixelHeight -= _sixelHeight;
_resizeImageBuffer(_sixelHeight);
_fillImageBackgroundWhenScrolled();
}
}
void SixelParser::_executeMoveToHome()

View File

@@ -3002,17 +3002,15 @@ void AdaptDispatch::SoftReset()
SetGraphicsRendition({}); // Normal rendition.
SetCharacterProtectionAttribute({}); // Default (unprotected)
// Reset the saved cursor state.
// Note that XTerm only resets the main buffer state, but that
// seems likely to be a bug. Most other terminals reset both.
_savedCursorState.at(0) = {}; // Main buffer
_savedCursorState.at(1) = {}; // Alt buffer
// Reset only the active saved cursor state.
// This matches xterm behavior when DECSTR is processed while using
// the alternate screen buffer (GH#19918).
_savedCursorState.at(_usingAltBuffer ? 1 : 0) = {};
// The TerminalOutput state in these buffers must be reset to
// The TerminalOutput state in this buffer must be reset to
// the same state as the _termOutput instance, which is not
// necessarily equivalent to a full reset.
_savedCursorState.at(0).TermOutput = _termOutput;
_savedCursorState.at(1).TermOutput = _termOutput;
_savedCursorState.at(_usingAltBuffer ? 1 : 0).TermOutput = _termOutput;
// Soft reset the Sixel parser if in use.
if (_sixelParser)

View File

@@ -1,12 +0,0 @@
# Cargo source replacement: route crates-io fetches through an Azure Artifact
# Cargo feed (1ES compliance — disallow direct public crates.io access).
#
# Reusing the sudo team's `sudo_public_cargo` feed in microsoft/Dart for now.
# TODO(wtr): replace with our own `microsoft/Dart` Cargo feed once provisioned.
[registries]
WindowsTerminalCargo = { index = "sparse+https://pkgs.dev.azure.com/microsoft/Dart/_packaging/sudo_public_cargo/Cargo/index/" }
[source.crates-io]
replace-with = "WindowsTerminalCargo"

View File

@@ -1 +0,0 @@
/target/

2208
src/tools/wtr/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +0,0 @@
[package]
name = "wtr"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "wtr"
path = "src/main.rs"
[dependencies]
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7", features = ["compat"] }
async-trait = "0.1"
anyhow = "1"
serde_json = "1"
clap = { version = "4", features = ["derive"] }
ratatui = "0.30"
crossterm = { version = "0.29", features = ["event-stream"] }
futures = "0.3"
unicode-width = "0.2"
textwrap = "0.16"
base64 = "0.22"
serde = { version = "1", features = ["derive"] }
windows-sys = { version = "0.61", features = [
"Win32_Foundation",
"Win32_System_Environment",
"Win32_System_Registry",
"Win32_System_Threading",
] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
tracing-appender = "0.2"

View File

@@ -1,4 +0,0 @@
# Pin the Rust toolchain channel for both local dev and 1ES CI.
# Use the MSRustup `ms-prod-X.YY` channel for compliance.
[toolchain]
channel = "ms-prod-1.93"

View File

@@ -1 +0,0 @@
fn main() {}