Compare commits

..

17 Commits

Author SHA1 Message Date
Mike Griese
62c47cf8fb Merge remote-tracking branch 'origin/main' into dev/migrie/f/overview-view 2026-05-14 10:25:34 -05:00
Mike Griese
906c1fe15a Merge remote-tracking branch 'origin/main' into dev/migrie/f/overview-view 2026-05-09 14:35:28 -05:00
Mike Griese
c67adbdedb need you 2026-04-24 06:31:05 -05:00
Mike Griese
134a2d5d39 Merge remote-tracking branch 'origin/main' into dev/migrie/f/overview-view 2026-04-24 05:35:34 -05:00
Mike Griese
47950d2753 nope don't check that in 2026-04-24 05:33:44 -05:00
Mike Griese
d3c17f9d6f squad: log overview pre-dismiss before DoAction
Coordinator direct edit in TerminalPage::_KeyDownHandler: dismiss
OverviewPane before _actionDispatch->DoAction when a non-ToggleOverview
action resolves. Restores reparented tab Content to its original parent
before any tab-state-mutating action runs.

Requested by: Mike Griese.
2026-04-18 20:19:29 -05:00
Mike Griese
451bf37008 Overview: route unrecognized keys to global action dispatch
Add PreviewKeyDown=_KeyDownHandler to OverviewPaneElement in TerminalPage.xaml so global keybindings continue to work while the overview is visible. Local grid keys (Tab/arrows/Enter/Escape) remain handled by OverviewPane::_OnKeyDown. Matches the CommandPalette/SuggestionsControl pattern.
2026-04-18 20:01:05 -05:00
Mike Griese
5a3ae31453 avoid a _mild_ exception 2026-04-18 19:56:50 -05:00
Mike Griese
3dd5dadb38 squad: log overview external-dismiss fix
Dallas extracted _DismissOverviewVisuals() helper and wired
_OnTabSelectionChanged to release reparented overview content
before _UpdatedSelectedTab mounts the new tab.

Requested-by: Mike Griese
2026-04-18 06:58:18 -05:00
Mike Griese
724c519788 theme aware bg 2026-04-18 06:50:47 -05:00
Mike Griese
3cc213a2c1 mildly cleaner 2026-03-31 21:32:51 -05:00
Mike Griese
0afdc93bef i'd ship this 2026-03-30 19:55:14 -05:00
Mike Griese
6f384c59f4 Awesome, click on the previews anywhere 2026-03-30 15:57:27 -05:00
Mike Griese
66a828bc02 Weird thing: Why do we hit this all the time now? We never did before? 2026-03-30 15:36:18 -05:00
Mike Griese
e19e710e03 yea that's wacky 2026-03-30 14:51:30 -05:00
Mike Griese
54604076b9 what the fuck 2026-03-30 14:10:40 -05:00
Mike Griese
ceef4d46ed no way 2026-03-30 13:37:41 -05:00
40 changed files with 1535 additions and 1581 deletions

View File

@@ -604,6 +604,13 @@ namespace winrt::TerminalApp::implementation
args.Handled(true);
}
void TerminalPage::_HandleToggleOverview(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
ToggleOverview();
args.Handled(true);
}
void TerminalPage::_HandleSetFocusMode(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{

View File

@@ -0,0 +1,690 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "OverviewPane.h"
#include "OverviewPane.g.cpp"
using namespace winrt;
using namespace winrt::TerminalApp;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Input;
using namespace winrt::Windows::UI::Xaml::Media;
using namespace winrt::Windows::UI::Xaml::Media::Animation;
using namespace winrt::Windows::System;
namespace winrt::TerminalApp::implementation
{
static constexpr double PreviewCellWidth = 320.0;
static constexpr double PreviewCellHeight = 220.0;
static constexpr std::chrono::milliseconds EnterAnimDuration{ 400 };
static constexpr std::chrono::milliseconds ExitAnimDuration{ 100 };
OverviewPane::OverviewPane()
{
InitializeComponent();
// Apply the initial background (opaque by default, since _useMica
// starts false). Without this the XAML-declared brush stays in place
// until someone explicitly calls UseMica().
_UpdateBackgroundForMica();
// Listen for layout passes so we can start the zoom-in animation
// once the WrapGrid has measured and positioned its children.
PreviewGrid().LayoutUpdated([weakThis = get_weak()](auto&&, auto&&) {
if (auto self = weakThis.get())
{
if (!self->_pendingEnterAnimation)
{
return;
}
// Check that at least the first cell is laid out
auto items = self->PreviewGrid().Items();
if (items.Size() == 0)
{
return;
}
auto first = items.GetAt(0).try_as<FrameworkElement>();
if (!first || first.ActualWidth() <= 0)
{
return;
}
self->_pendingEnterAnimation = false;
self->_StartEnterZoomAnimation();
}
});
}
OverviewPane::~OverviewPane()
{
ClearTabContent();
}
void OverviewPane::UpdateTabContent(Windows::Foundation::Collections::IVector<TerminalApp::Tab> tabs, int32_t focusedIndex)
{
// Clear any previous state
ClearTabContent();
if (!tabs || tabs.Size() == 0)
{
return;
}
const auto itemsControl = PreviewGrid();
// Determine a reference size from the currently visible tab's content.
// Inactive tabs have zero ActualWidth/Height since they're not laid out
// in the visual tree, but all tabs share the same content area, so the
// active tab's size is the right reference for all of them.
double referenceWidth = 0;
double referenceHeight = 0;
if (focusedIndex >= 0 && focusedIndex < static_cast<int32_t>(tabs.Size()))
{
auto focusedContent = tabs.GetAt(static_cast<uint32_t>(focusedIndex)).Content();
if (focusedContent)
{
referenceWidth = focusedContent.ActualWidth();
referenceHeight = focusedContent.ActualHeight();
}
}
for (uint32_t i = 0; i < tabs.Size(); i++)
{
const auto& tab = tabs.GetAt(i);
auto cell = _BuildPreviewCell(tab, static_cast<int32_t>(i), referenceWidth, referenceHeight);
itemsControl.Items().Append(cell);
}
_selectedIndex = focusedIndex;
_UpdateSelection();
_PlayEnterAnimation();
// Focus self for keyboard input
Focus(FocusState::Programmatic);
}
void OverviewPane::ClearTabContent()
{
_pendingEnterAnimation = false;
// Stop any running exit animation
if (_exitContentStoryboard)
{
_exitContentStoryboard.Completed(_exitAnimationToken);
_exitAnimationToken = {};
_exitContentStoryboard.Stop();
_exitContentStoryboard = nullptr;
}
// Reset the zoom transform to identity
auto transform = ContentTransform();
transform.ScaleX(1.0);
transform.ScaleY(1.0);
transform.TranslateX(0.0);
transform.TranslateY(0.0);
ContentWrapper().Opacity(1.0);
// Reparent content back to original parents
for (auto& entry : _reparentedContent)
{
if (entry.content)
{
// Restore original Width/Height (NaN = auto-sizing)
entry.content.Width(entry.originalWidth);
entry.content.Height(entry.originalHeight);
// Restore original RenderTransform and origin
entry.content.RenderTransform(entry.originalRenderTransform);
entry.content.RenderTransformOrigin(entry.originalRenderTransformOrigin);
_DetachContent(entry.content);
// Put it back where it came from
if (entry.originalParent)
{
entry.originalParent.Children().Append(entry.content);
}
}
}
_reparentedContent.clear();
const auto itemsControl = PreviewGrid();
itemsControl.Items().Clear();
}
int32_t OverviewPane::SelectedIndex() const
{
return _selectedIndex;
}
void OverviewPane::SelectedIndex(int32_t value)
{
if (_selectedIndex != value)
{
_selectedIndex = value;
_UpdateSelection();
}
}
bool OverviewPane::UseMica() const
{
return _useMica;
}
void OverviewPane::UseMica(bool value)
{
if (_useMica != value)
{
_useMica = value;
_UpdateBackgroundForMica();
}
}
void OverviewPane::_UpdateBackgroundForMica()
{
auto overlay = BackgroundOverlay();
if (_useMica)
{
// Transparent background — let the Mica backdrop show through
overlay.Background(SolidColorBrush{ winrt::Windows::UI::Colors::Transparent() });
}
else
{
// Opaque background when Mica is not active.
// Use the theme-aware SolidBackgroundFillColorBaseBrush so we
// match the correct color for light / dark theme.
auto res = Application::Current().Resources();
auto brush = res.TryLookup(winrt::box_value(L"SolidBackgroundFillColorBaseBrush"));
if (brush)
{
overlay.Background(brush.as<Brush>());
}
else
{
overlay.Background(SolidColorBrush{ winrt::Windows::UI::ColorHelper::FromArgb(255, 32, 32, 32) });
}
}
}
void OverviewPane::_OnPreviewKeyDown(const IInspectable& /*sender*/, const KeyRoutedEventArgs& e)
{
if (e.OriginalKey() != VirtualKey::Tab)
{
return;
}
const auto items = PreviewGrid().Items();
const auto itemCount = static_cast<int32_t>(items.Size());
if (itemCount == 0)
{
return;
}
const auto shiftPressed = (Windows::UI::Core::CoreWindow::GetForCurrentThread().GetKeyState(VirtualKey::Shift) & Windows::UI::Core::CoreVirtualKeyStates::Down) == Windows::UI::Core::CoreVirtualKeyStates::Down;
if (shiftPressed)
{
if (_selectedIndex > 0)
{
_selectedIndex--;
_UpdateSelection();
}
}
else
{
if (_selectedIndex < itemCount - 1)
{
_selectedIndex++;
_UpdateSelection();
}
}
e.Handled(true);
}
void OverviewPane::_OnKeyDown(const IInspectable& /*sender*/, const KeyRoutedEventArgs& e)
{
const auto items = PreviewGrid().Items();
const auto itemCount = static_cast<int32_t>(items.Size());
if (itemCount == 0)
{
return;
}
auto handled = true;
switch (e.OriginalKey())
{
case VirtualKey::Left:
if (_selectedIndex > 0)
{
_selectedIndex--;
_UpdateSelection();
}
break;
case VirtualKey::Right:
if (_selectedIndex < itemCount - 1)
{
_selectedIndex++;
_UpdateSelection();
}
break;
case VirtualKey::Up:
if (_selectedIndex - _columnCount >= 0)
{
_selectedIndex -= _columnCount;
_UpdateSelection();
}
break;
case VirtualKey::Down:
if (_selectedIndex + _columnCount < itemCount)
{
_selectedIndex += _columnCount;
_UpdateSelection();
}
break;
case VirtualKey::Enter:
_OnItemClicked(_selectedIndex);
break;
case VirtualKey::Escape:
_PlayExitAnimation([weakThis = get_weak()]() {
if (auto self = weakThis.get())
{
self->Dismissed.raise(*self, nullptr);
}
});
break;
default:
handled = false;
break;
}
e.Handled(handled);
}
void OverviewPane::_OnItemClicked(int32_t index)
{
_PlayExitAnimation([weakThis = get_weak(), index]() {
if (auto self = weakThis.get())
{
self->TabSelected.raise(*self, winrt::Windows::Foundation::IReference<int32_t>{ index });
}
});
}
void OverviewPane::_UpdateSelection()
{
const auto items = PreviewGrid().Items();
const auto itemCount = static_cast<int32_t>(items.Size());
// Clamp selection
_selectedIndex = std::clamp(_selectedIndex, 0, std::max(0, itemCount - 1));
for (int32_t i = 0; i < itemCount; i++)
{
if (auto cellElement = items.GetAt(i).try_as<FrameworkElement>())
{
if (auto border = cellElement.try_as<Border>())
{
if (i == _selectedIndex)
{
// Accent-colored border for selected item
const auto accentBrush = Application::Current()
.Resources()
.Lookup(winrt::box_value(L"SystemAccentColor"))
.as<winrt::Windows::UI::Color>();
border.BorderBrush(SolidColorBrush{ accentBrush });
border.BorderThickness(ThicknessHelper::FromUniformLength(2));
// Scroll into view if needed
border.StartBringIntoView();
}
else
{
border.BorderBrush(SolidColorBrush{ winrt::Windows::UI::Colors::Transparent() });
border.BorderThickness(ThicknessHelper::FromUniformLength(2));
}
}
}
}
}
void OverviewPane::_PlayEnterAnimation()
{
// Hide the content wrapper until the LayoutUpdated callback fires
// and we can read cell positions to set up the zoom transform.
ContentWrapper().Opacity(0);
_pendingEnterAnimation = true;
}
void OverviewPane::_StartEnterZoomAnimation()
{
// Start the background fade-in together with the zoom so both
// animations are visible at the same moment (avoids opacity flash).
if (auto bgSb = Resources().Lookup(winrt::box_value(L"BackgroundFadeIn")).try_as<Storyboard>())
{
bgSb.Begin();
}
auto wrapper = ContentWrapper();
auto transform = ContentTransform();
auto zoomParams = _GetZoomParamsForCell(_selectedIndex);
if (!zoomParams)
{
wrapper.Opacity(1.0);
return;
}
auto [scale, tx, ty] = *zoomParams;
// Set the initial transform so the focused cell fills the viewport
transform.ScaleX(scale);
transform.ScaleY(scale);
transform.TranslateX(tx);
transform.TranslateY(ty);
wrapper.Opacity(1.0);
// Animate from the zoomed-in state to identity (zoom out to grid)
Storyboard storyboard;
const auto duration = DurationHelper::FromTimeSpan(EnterAnimDuration);
CubicEase easing;
easing.EasingMode(EasingMode::EaseOut);
_AddDoubleAnimation(storyboard, transform, L"ScaleX", scale, 1.0, duration, easing);
_AddDoubleAnimation(storyboard, transform, L"ScaleY", scale, 1.0, duration, easing);
_AddDoubleAnimation(storyboard, transform, L"TranslateX", tx, 0.0, duration, easing);
_AddDoubleAnimation(storyboard, transform, L"TranslateY", ty, 0.0, duration, easing);
storyboard.Begin();
}
void OverviewPane::_PlayExitAnimation(std::function<void()> onComplete)
{
// Fade out the background overlay
if (auto bgSb = Resources().Lookup(winrt::box_value(L"BackgroundFadeOut")).try_as<Storyboard>())
{
bgSb.Begin();
}
auto zoomParams = _GetZoomParamsForCell(_selectedIndex);
if (!zoomParams)
{
if (onComplete)
{
onComplete();
}
return;
}
auto [scale, tx, ty] = *zoomParams;
// Animate from the current grid view into the selected cell
auto transform = ContentTransform();
Storyboard storyboard;
const auto duration = DurationHelper::FromTimeSpan(ExitAnimDuration);
CubicEase easing;
easing.EasingMode(EasingMode::EaseIn);
_AddDoubleAnimation(storyboard, transform, L"ScaleX", 1.0, scale, duration, easing);
_AddDoubleAnimation(storyboard, transform, L"ScaleY", 1.0, scale, duration, easing);
_AddDoubleAnimation(storyboard, transform, L"TranslateX", 0.0, tx, duration, easing);
_AddDoubleAnimation(storyboard, transform, L"TranslateY", 0.0, ty, duration, easing);
// Revoke any previously registered Completed handler
if (_exitContentStoryboard)
{
_exitContentStoryboard.Completed(_exitAnimationToken);
}
_exitContentStoryboard = storyboard;
_exitAnimationToken = storyboard.Completed([weakThis = get_weak(), onComplete](auto&&, auto&&) {
if (auto self = weakThis.get())
{
if (onComplete)
{
onComplete();
}
}
});
storyboard.Begin();
}
std::optional<std::tuple<double, double, double>> OverviewPane::_GetZoomParamsForCell(int32_t index)
{
auto wrapper = ContentWrapper();
const auto vpW = wrapper.ActualWidth();
const auto vpH = wrapper.ActualHeight();
if (vpW <= 0 || vpH <= 0)
{
return std::nullopt;
}
auto items = PreviewGrid().Items();
if (index < 0 || index >= static_cast<int32_t>(items.Size()))
{
return std::nullopt;
}
auto cell = items.GetAt(static_cast<uint32_t>(index)).try_as<FrameworkElement>();
if (!cell || cell.ActualWidth() <= 0 || cell.ActualHeight() <= 0)
{
return std::nullopt;
}
const auto cellW = cell.ActualWidth();
const auto cellH = cell.ActualHeight();
// Cell center relative to ContentWrapper (accounts for scroll offset)
auto cellTransform = cell.TransformToVisual(wrapper);
auto topLeft = cellTransform.TransformPoint({ 0.0f, 0.0f });
const auto cellCX = static_cast<double>(topLeft.X) + cellW / 2.0;
const auto cellCY = static_cast<double>(topLeft.Y) + cellH / 2.0;
// Scale so the cell fits the viewport
const auto scale = std::min(vpW / cellW, vpH / cellH);
// With RenderTransformOrigin={0.5,0.5} the scale origin is the
// center of ContentWrapper. Translate so the cell center lands
// at the viewport center after scaling.
const auto translateX = (vpW / 2.0 - cellCX) * scale;
const auto translateY = (vpH / 2.0 - cellCY) * scale;
return std::tuple{ scale, translateX, translateY };
}
FrameworkElement OverviewPane::_BuildPreviewCell(const TerminalApp::Tab& tab, int32_t index, double referenceWidth, double referenceHeight)
{
// Outer border — serves as the selection indicator
Border outerBorder;
outerBorder.BorderBrush(SolidColorBrush{ winrt::Windows::UI::Colors::Transparent() });
outerBorder.BorderThickness(ThicknessHelper::FromUniformLength(2));
outerBorder.CornerRadius({ 8, 8, 8, 8 });
outerBorder.Padding(ThicknessHelper::FromUniformLength(4));
outerBorder.Margin(ThicknessHelper::FromUniformLength(6));
// Vertical stack: preview box + title
StackPanel cellStack;
cellStack.Orientation(Orientation::Vertical);
cellStack.HorizontalAlignment(HorizontalAlignment::Center);
// Preview container with a dark background
Border previewBorder;
previewBorder.Width(PreviewCellWidth);
previewBorder.Height(PreviewCellHeight);
previewBorder.CornerRadius({ 6, 6, 6, 6 });
auto bgBrush = Application::Current().Resources().TryLookup(winrt::box_value(L"SystemControlBackgroundChromeMediumBrush"));
if (bgBrush)
{
previewBorder.Background(bgBrush.as<Brush>());
}
else
{
previewBorder.Background(SolidColorBrush{ winrt::Windows::UI::ColorHelper::FromArgb(255, 30, 30, 30) });
}
// Get the tab's content and reparent it with a ScaleTransform
auto tabContent = tab.Content();
if (tabContent)
{
// Save the Width/Height *property* values (likely NaN for auto-sized
// elements). These differ from ActualWidth/Height (the rendered size).
// We need the property values to restore auto-sizing on exit.
const auto origWidthProp = tabContent.Width();
const auto origHeightProp = tabContent.Height();
const auto origRenderTransform = tabContent.RenderTransform();
const auto origRenderTransformOrigin = tabContent.RenderTransformOrigin();
// Use ActualWidth/Height if the content is currently laid out (active tab).
// Inactive tabs aren't in the visual tree and report zero — fall back
// to the reference size from the active tab's content area.
auto layoutWidth = tabContent.ActualWidth();
auto layoutHeight = tabContent.ActualHeight();
if (layoutWidth <= 0 || layoutHeight <= 0)
{
layoutWidth = referenceWidth;
layoutHeight = referenceHeight;
}
auto originalParent = VisualTreeHelper::GetParent(tabContent).try_as<Panel>();
// XAML single-parent rule: remove from current parent first
_DetachContent(tabContent);
// Lock the content to the layout size — this prevents
// TermControl from seeing a resize and reflowing its buffer
if (layoutWidth > 0 && layoutHeight > 0)
{
tabContent.Width(layoutWidth);
tabContent.Height(layoutHeight);
// Calculate uniform scale to fit in preview
const double previewWidth = PreviewCellWidth;
const double previewHeight = PreviewCellHeight;
const double scale = std::min(previewWidth / layoutWidth, previewHeight / layoutHeight);
// RenderTransform is applied AFTER layout — the content still
// thinks it's at its original size
ScaleTransform scaleTransform;
scaleTransform.ScaleX(scale);
scaleTransform.ScaleY(scale);
tabContent.RenderTransform(scaleTransform);
tabContent.RenderTransformOrigin({ 0.0f, 0.0f });
// Use a Canvas so the content is not constrained by the preview
// container's layout. Canvas gives children infinite measure
// space and arranges at desired size.
Canvas canvas;
canvas.Width(previewWidth);
canvas.Height(previewHeight);
// Clip to preview bounds so the scaled content doesn't overflow
RectangleGeometry clipGeometry;
clipGeometry.Rect({ 0, 0, static_cast<float>(previewWidth), static_cast<float>(previewHeight) });
canvas.Clip(clipGeometry);
canvas.Children().Append(tabContent);
// Layer the canvas behind a transparent overlay so
// pointer events never reach the TermControl content.
Grid previewGrid;
previewGrid.Children().Append(canvas);
Border inputOverlay;
inputOverlay.Background(SolidColorBrush{ winrt::Windows::UI::Colors::Transparent() });
previewGrid.Children().Append(inputOverlay);
previewBorder.Child(previewGrid);
}
_reparentedContent.push_back({ tabContent, originalParent, origWidthProp, origHeightProp, origRenderTransform, origRenderTransformOrigin });
}
cellStack.Children().Append(previewBorder);
// Tab title text
TextBlock titleBlock;
titleBlock.Text(tab.Title());
titleBlock.FontSize(14);
titleBlock.Foreground(SolidColorBrush{ winrt::Windows::UI::Colors::White() });
titleBlock.HorizontalAlignment(HorizontalAlignment::Center);
titleBlock.TextTrimming(TextTrimming::CharacterEllipsis);
titleBlock.MaxWidth(PreviewCellWidth);
titleBlock.Margin({ 0, 6, 0, 0 });
cellStack.Children().Append(titleBlock);
outerBorder.Child(cellStack);
// Click handler
outerBorder.PointerPressed([weakThis = get_weak(), index](auto&&, auto&&) {
if (auto self = weakThis.get())
{
self->_selectedIndex = index;
self->_UpdateSelection();
self->_OnItemClicked(index);
}
});
// Hover effect
outerBorder.PointerEntered([weakThis = get_weak(), index](auto&&, auto&&) {
if (auto self = weakThis.get())
{
self->_selectedIndex = index;
self->_UpdateSelection();
}
});
return outerBorder;
}
void OverviewPane::_AddDoubleAnimation(
const Storyboard& storyboard,
const CompositeTransform& target,
const hstring& property,
double from,
double to,
const Duration& duration,
const EasingFunctionBase& easing)
{
DoubleAnimation anim;
anim.From(from);
anim.To(to);
anim.Duration(duration);
anim.EasingFunction(easing);
Storyboard::SetTarget(anim, target);
Storyboard::SetTargetProperty(anim, property);
storyboard.Children().Append(anim);
}
void OverviewPane::_DetachContent(const FrameworkElement& content)
{
// Try removing from various XAML container types
if (auto parent = VisualTreeHelper::GetParent(content))
{
if (auto panel = parent.try_as<Panel>())
{
uint32_t idx;
if (panel.Children().IndexOf(content, idx))
{
panel.Children().RemoveAt(idx);
}
}
else if (auto border = parent.try_as<Border>())
{
border.Child(nullptr);
}
else if (auto contentControl = parent.try_as<ContentControl>())
{
contentControl.Content(nullptr);
}
else if (auto viewbox = parent.try_as<Viewbox>())
{
viewbox.Child(nullptr);
}
}
}
}

View File

@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include <limits>
#include <optional>
#include "OverviewPane.g.h"
namespace winrt::TerminalApp::implementation
{
struct OverviewPane : OverviewPaneT<OverviewPane>
{
OverviewPane();
~OverviewPane();
void UpdateTabContent(Windows::Foundation::Collections::IVector<TerminalApp::Tab> tabs, int32_t focusedIndex);
void ClearTabContent();
int32_t SelectedIndex() const;
void SelectedIndex(int32_t value);
bool UseMica() const;
void UseMica(bool value);
// Events
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::IReference<int32_t>> TabSelected;
til::typed_event<> Dismissed;
private:
friend struct OverviewPaneT<OverviewPane>; // for Xaml to bind events
void _OnKeyDown(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
void _OnPreviewKeyDown(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
void _OnItemClicked(int32_t index);
void _UpdateSelection();
void _PlayEnterAnimation();
void _PlayExitAnimation(std::function<void()> onComplete = nullptr);
void _StartEnterZoomAnimation();
std::optional<std::tuple<double, double, double>> _GetZoomParamsForCell(int32_t index);
Windows::UI::Xaml::FrameworkElement _BuildPreviewCell(const TerminalApp::Tab& tab, int32_t index, double referenceWidth, double referenceHeight);
void _DetachContent(const Windows::UI::Xaml::FrameworkElement& content);
static void _AddDoubleAnimation(
const Windows::UI::Xaml::Media::Animation::Storyboard& storyboard,
const Windows::UI::Xaml::Media::CompositeTransform& target,
const hstring& property,
double from,
double to,
const Windows::UI::Xaml::Duration& duration,
const Windows::UI::Xaml::Media::Animation::EasingFunctionBase& easing);
int32_t _selectedIndex{ 0 };
int32_t _columnCount{ 3 }; // must match WrapGrid MaximumRowsOrColumns in OverviewPane.xaml
bool _pendingEnterAnimation{ false };
bool _useMica{ false };
void _UpdateBackgroundForMica();
winrt::event_token _exitAnimationToken{};
Windows::UI::Xaml::Media::Animation::Storyboard _exitContentStoryboard{ nullptr };
struct ReparentedEntry
{
Windows::UI::Xaml::FrameworkElement content{ nullptr };
Windows::UI::Xaml::Controls::Panel originalParent{ nullptr };
double originalWidth{ std::numeric_limits<double>::quiet_NaN() };
double originalHeight{ std::numeric_limits<double>::quiet_NaN() };
Windows::UI::Xaml::Media::Transform originalRenderTransform{ nullptr };
Windows::Foundation::Point originalRenderTransformOrigin{ 0.0f, 0.0f };
};
std::vector<ReparentedEntry> _reparentedContent;
};
}
namespace winrt::TerminalApp::factory_implementation
{
BASIC_FACTORY(OverviewPane);
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "Tab.idl";
namespace TerminalApp
{
[default_interface] runtimeclass OverviewPane : Windows.UI.Xaml.Controls.UserControl
{
OverviewPane();
void UpdateTabContent(Windows.Foundation.Collections.IVector<Tab> tabs, Int32 focusedIndex);
void ClearTabContent();
Int32 SelectedIndex;
Boolean UseMica;
event Windows.Foundation.TypedEventHandler<Object, Windows.Foundation.IReference<Int32> > TabSelected;
event Windows.Foundation.TypedEventHandler<Object, Object> Dismissed;
}
}

View File

@@ -0,0 +1,88 @@
<!--
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information.
-->
<UserControl x:Class="TerminalApp.OverviewPane"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:TerminalApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
AllowFocusOnInteraction="True"
IsTabStop="True"
KeyDown="_OnKeyDown"
PreviewKeyDown="_OnPreviewKeyDown"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<Storyboard x:Key="BackgroundFadeIn">
<DoubleAnimation Duration="0:0:0.2"
Storyboard.TargetName="BackgroundOverlay"
Storyboard.TargetProperty="Opacity"
From="0.0"
To="1.0">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
<Storyboard x:Key="BackgroundFadeOut">
<DoubleAnimation Duration="0:0:0.15"
Storyboard.TargetName="BackgroundOverlay"
Storyboard.TargetProperty="Opacity"
From="1.0"
To="0.0">
<DoubleAnimation.EasingFunction>
<QuadraticEase EasingMode="EaseIn" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="RootGrid"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<!-- Semi-transparent dark overlay -->
<Border x:Name="BackgroundOverlay"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{ThemeResource SmokeFillColorDefaultBrush}" />
<!-- Zoom-animated wrapper around the scrollable content -->
<Grid x:Name="ContentWrapper"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<CompositeTransform x:Name="ContentTransform" />
</Grid.RenderTransform>
<ScrollViewer x:Name="PreviewScrollViewer"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<!-- WrapGrid-style layout using ItemsControl with WrapGrid panel -->
<ItemsControl x:Name="PreviewGrid"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="24">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid HorizontalChildrenAlignment="Left"
ItemHeight="270"
ItemWidth="332"
MaximumRowsOrColumns="3"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</Grid>
</Grid>
</UserControl>

View File

@@ -1131,6 +1131,13 @@ namespace winrt::TerminalApp::implementation
{
if (!_rearranging && !_removing)
{
// If the user clicked a tab in the tab row while the overview is
// open, the overview is still holding that tab's Content reparented
// into one of its preview cells. Tear down the overview visuals
// first so _UpdatedSelectedTab can mount the content into the
// active content area without hitting the XAML single-parent rule.
_DismissOverviewVisuals();
auto tabView = sender.as<MUX::Controls::TabView>();
auto selectedIndex = tabView.SelectedIndex();
if (selectedIndex >= 0 && selectedIndex < gsl::narrow_cast<int32_t>(_tabs.Size()))

View File

@@ -71,6 +71,9 @@
<Page Include="CommandPalette.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="OverviewPane.xaml">
<SubType>Designer</SubType>
</Page>
<Page Include="SuggestionsControl.xaml">
<SubType>Designer</SubType>
</Page>
@@ -132,6 +135,9 @@
<ClInclude Include="CommandPalette.h">
<DependentUpon>CommandPalette.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="OverviewPane.h">
<DependentUpon>OverviewPane.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="FilteredCommand.h" />
<ClInclude Include="Pane.h" />
<ClInclude Include="../fzf/fzf.h" />
@@ -243,6 +249,9 @@
<ClCompile Include="CommandPalette.cpp">
<DependentUpon>CommandPalette.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="OverviewPane.cpp">
<DependentUpon>OverviewPane.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="FilteredCommand.cpp" />
<ClCompile Include="Pane.cpp" />
<ClCompile Include="Pane.LayoutSizeNode.cpp" />
@@ -354,6 +363,10 @@
<DependentUpon>CommandPalette.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="OverviewPane.idl">
<DependentUpon>OverviewPane.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="SuggestionsControl.idl">
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
<SubType>Code</SubType>

View File

@@ -1838,6 +1838,17 @@ namespace winrt::TerminalApp::implementation
return;
}
// If the overview pane is open, most actions mutate tab state whose
// Content() is currently reparented under the overview. Tear down the
// overview's visuals first so the content is back under its original
// parent before the action runs. ToggleOverview is the exception — it
// handles its own exit.
if (_isInOverviewMode &&
cmd.ActionAndArgs().Action() != ShortcutAction::ToggleOverview)
{
_DismissOverviewVisuals();
}
if (!_actionDispatch->DoAction(cmd.ActionAndArgs()))
{
return;
@@ -4269,6 +4280,120 @@ namespace winrt::TerminalApp::implementation
AlwaysOnTopChanged.raise(*this, nullptr);
}
void TerminalPage::ToggleOverview()
{
if (_isInOverviewMode)
{
_ExitOverview(std::nullopt);
}
else
{
_EnterOverview();
}
}
void TerminalPage::_EnterOverview()
{
_isInOverviewMode = true;
// Use FindName to lazily load the OverviewPane (same pattern as CommandPalette)
auto overview = FindName(L"OverviewPaneElement").try_as<OverviewPane>();
if (!overview)
{
return;
}
const auto focusedIdx = _GetFocusedTabIndex();
const auto idx = focusedIdx.has_value() ? static_cast<int32_t>(focusedIdx.value()) : 0;
// Wire up events to handle tab selection and dismissal
_overviewTabSelectedToken = overview.TabSelected([weakThis = get_weak()](auto&&, const auto& args) {
if (auto self = weakThis.get())
{
if (args)
{
self->_ExitOverview(static_cast<uint32_t>(args.Value()));
}
}
});
_overviewDismissedToken = overview.Dismissed([weakThis = get_weak()](auto&&, auto&&) {
if (auto self = weakThis.get())
{
self->_ExitOverview(std::nullopt);
}
});
// _tabs is already IObservableVector<Tab>, which inherits from IVector<Tab>
const auto theme = _settings.GlobalSettings().CurrentTheme();
const auto windowTheme = theme ? theme.Window() : nullptr;
overview.UseMica(windowTheme ? windowTheme.UseMica() : false);
overview.UpdateTabContent(_tabs, idx);
overview.Visibility(WUX::Visibility::Visible);
overview.Focus(WUX::FocusState::Programmatic);
}
void TerminalPage::_ExitOverview(const std::optional<uint32_t>& selectedIndex)
{
auto overview = FindName(L"OverviewPaneElement").try_as<OverviewPane>();
// Determine which tab to switch to. Prefer the explicitly passed index
// (from TabSelected event), but fall back to the overview pane's
// current selection — this covers ToggleOverview and Dismiss paths so
// the user always lands on whichever tab they navigated to.
std::optional<uint32_t> tabToSelect = selectedIndex;
if (!tabToSelect.has_value() && overview)
{
const auto overviewIdx = overview.SelectedIndex();
if (overviewIdx >= 0 && overviewIdx < static_cast<int32_t>(_tabs.Size()))
{
tabToSelect = static_cast<uint32_t>(overviewIdx);
}
}
_DismissOverviewVisuals();
if (tabToSelect.has_value())
{
_SelectTab(tabToSelect.value());
}
_UpdatedSelectedTab(_GetFocusedTab());
}
// Method Description:
// - Tears down the overview's reparented content and hides the overlay,
// without changing tab selection. Safe to call when not in overview mode.
// - Used by both _ExitOverview (which then selects a tab) and by
// _OnTabSelectionChanged (where the TabView has already updated the
// selection and we just need to release the reparented content before
// _UpdatedSelectedTab tries to mount it back into the content area).
void TerminalPage::_DismissOverviewVisuals()
{
if (!_isInOverviewMode)
{
return;
}
if (auto overview = FindName(L"OverviewPaneElement").try_as<OverviewPane>())
{
// Revoke event handlers to avoid stale callbacks
overview.TabSelected(_overviewTabSelectedToken);
overview.Dismissed(_overviewDismissedToken);
_overviewTabSelectedToken = {};
_overviewDismissedToken = {};
overview.ClearTabContent();
overview.Visibility(WUX::Visibility::Collapsed);
}
_isInOverviewMode = false;
}
bool TerminalPage::OverviewMode() const
{
return _isInOverviewMode;
}
// Method Description:
// - Sets the tab split button color when a new tab color is selected
// Arguments:

View File

@@ -138,9 +138,11 @@ namespace winrt::TerminalApp::implementation
void ToggleFocusMode();
void ToggleFullscreen();
void ToggleAlwaysOnTop();
void ToggleOverview();
bool FocusMode() const;
bool Fullscreen() const;
bool AlwaysOnTop() const;
bool OverviewMode() const;
bool ShowTabsFullscreen() const;
void SetShowTabsFullscreen(bool newShowTabsFullscreen);
void SetFullscreen(bool);
@@ -250,6 +252,9 @@ namespace winrt::TerminalApp::implementation
TerminalApp::Tab _settingsTab{ nullptr };
bool _isInFocusMode{ false };
bool _isInOverviewMode{ false };
winrt::event_token _overviewTabSelectedToken{};
winrt::event_token _overviewDismissedToken{};
bool _isFullscreen{ false };
bool _isMaximized{ false };
bool _isAlwaysOnTop{ false };
@@ -375,6 +380,9 @@ namespace winrt::TerminalApp::implementation
void _SelectNextTab(const bool bMoveRight, const Windows::Foundation::IReference<Microsoft::Terminal::Settings::Model::TabSwitcherMode>& customTabSwitcherMode);
bool _SelectTab(uint32_t tabIndex);
void _EnterOverview();
void _ExitOverview(const std::optional<uint32_t>& selectedIndex);
void _DismissOverviewVisuals();
bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool _MovePane(const Microsoft::Terminal::Settings::Model::MovePaneArgs args);

View File

@@ -61,6 +61,7 @@ namespace TerminalApp
Boolean FocusMode { get; };
Boolean Fullscreen { get; };
Boolean AlwaysOnTop { get; };
Boolean OverviewMode { get; };
WindowProperties WindowProperties { get; };
void IdentifyWindow();

View File

@@ -155,6 +155,12 @@
</TextBlock>
</ContentDialog>
<local:OverviewPane x:Name="OverviewPaneElement"
Grid.Row="2"
x:Load="False"
PreviewKeyDown="_KeyDownHandler"
Visibility="Collapsed" />
<local:CommandPalette x:Name="CommandPaletteElement"
Grid.Row="2"
VerticalAlignment="Stretch"

View File

@@ -2906,6 +2906,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
else
{
// Yes, this is reachable: when a pane is split, the new TermControl
// is added to the XAML tree before its Loaded event fires, so
// _initializedTerminal is still false when layout queries MinimumSize().
// Return a small fallback — the real size will be used once initialized.
return { 10, 10 };
}
}

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 ExpanderSettingContainerStyle}">
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<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 ExpanderSettingContainerStyle}">
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<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 ExpanderSettingContainerStyle}">
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<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 ExpanderSettingContainerStyle}">
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<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 ExpanderSettingContainerStyle}">
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<local:NullableColorPicker x:Uid="Profile_CursorColor_NullableColorPicker"
ColorSchemeVM="{x:Bind Appearance.CurrentColorScheme, Mode=OneWay}"
CurrentColor="{x:Bind Appearance.CursorColor, Mode=TwoWay}"

View File

@@ -8,9 +8,8 @@
xmlns:mtu="using:Microsoft.Terminal.UI"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls">
<!-- Merge SettingsControls and SettingContainer styles here to give every page access to them -->
<!-- Merge SettingContainerStyle here to give every page access to the SettingContainer -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="SettingsControlsStyle.xaml" />
<ResourceDictionary Source="SettingContainerStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
@@ -1201,6 +1200,87 @@
</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<FrameworkElement>().Tag().as<Editor::ExtensionPackageViewModel>();
const auto extPkgVM = sender.as<Controls::Button>().Tag().as<Editor::ExtensionPackageViewModel>();
_ViewModel.CurrentExtensionPackage(extPkgVM);
}

View File

@@ -127,58 +127,78 @@
<DataTemplate x:Key="DefaultExtensionNavigatorTemplate"
x:DataType="local:ExtensionPackageViewModel">
<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>
<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>
</DataTemplate>
<DataTemplate x:Key="ComplexExtensionNavigatorTemplate"
x:DataType="local:ExtensionPackageViewModel">
<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>
<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>
</DataTemplate>
<DataTemplate x:Key="ComplexExtensionNavigatorTemplateWithFontIcon"
x:DataType="local:ExtensionPackageViewModel">
<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>
<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>
</DataTemplate>
<DataTemplate x:Key="FragmentProfileViewModelTemplate"
@@ -482,11 +502,9 @@
<!-- Scope -->
<local:SettingContainer x:Name="Scope"
x:Uid="Extensions_Scope"
IsTabStop="False">
<TextBlock VerticalAlignment="Center"
Style="{ThemeResource SecondaryTextBlockStyle}"
Text="{x:Bind ViewModel.CurrentExtensionPackage.Scope, Mode=OneWay}" />
</local:SettingContainer>
Content="{x:Bind ViewModel.CurrentExtensionPackage.Scope, Mode=OneWay}"
IsTabStop="False"
Style="{StaticResource SettingContainerWithTextContent}" />
<!-- JSON -->
<ItemsControl IsTabStop="False"
ItemTemplate="{StaticResource JsonTemplate}"

View File

@@ -173,15 +173,6 @@
<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" />
@@ -261,9 +252,6 @@
<Page Include="SettingContainerStyle.xaml">
<Type>DefaultStyle</Type>
</Page>
<Page Include="SettingsControlsStyle.xaml">
<Type>DefaultStyle</Type>
</Page>
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
@@ -397,15 +385,6 @@
<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>
@@ -513,15 +492,6 @@
<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,9 +30,6 @@
<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>
@@ -55,7 +52,6 @@
<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 ExpanderSettingContainerStyle}">
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<local:SettingContainer.CurrentValue>
<Grid>
<ContentControl Width="16"
@@ -378,7 +378,8 @@
<!-- Add Profile -->
<local:SettingContainer x:Name="AddProfile"
x:Uid="NewTabMenu_AddProfile"
FontIconGlyph="&#xE756;">
FontIconGlyph="&#xE756;"
Style="{StaticResource SettingContainerWithIcon}">
<StackPanel Orientation="Horizontal"
Spacing="4">
@@ -427,7 +428,8 @@
<!-- Add Separator -->
<local:SettingContainer x:Name="AddSeparator"
x:Uid="NewTabMenu_AddSeparator"
FontIconGlyph="&#xE76f;">
FontIconGlyph="&#xE76f;"
Style="{StaticResource SettingContainerWithIcon}">
<Button x:Name="AddSeparatorButton"
x:Uid="NewTabMenu_AddSeparatorButton"
HorizontalAlignment="Stretch"
@@ -443,7 +445,8 @@
<!-- Add Folder -->
<local:SettingContainer x:Name="AddFolder"
x:Uid="NewTabMenu_AddFolder"
FontIconGlyph="&#xF12B;">
FontIconGlyph="&#xF12B;"
Style="{StaticResource SettingContainerWithIcon}">
<StackPanel Orientation="Horizontal"
Spacing="5">
<TextBox x:Name="FolderNameTextBox"
@@ -470,7 +473,7 @@
<local:SettingContainer x:Name="AddMatchProfiles"
x:Uid="NewTabMenu_AddMatchProfiles"
FontIconGlyph="&#xE748;"
Style="{StaticResource ExpanderSettingContainerStyle}">
Style="{StaticResource ExpanderSettingContainerStyleWithIcon}">
<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" />
@@ -497,7 +500,8 @@
<!-- Add Remaining Profiles -->
<local:SettingContainer x:Name="AddRemainingProfiles"
x:Uid="NewTabMenu_AddRemainingProfiles"
FontIconGlyph="&#xE902;">
FontIconGlyph="&#xE902;"
Style="{StaticResource SettingContainerWithIcon}">
<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().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")));
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")));
}
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 ExpanderSettingContainerStyle}">
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<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 ExpanderSettingContainerStyle}">
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
<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}" />
<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" />
<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}" />
<!-- Delete Button -->
<Border MaxWidth="{StaticResource StandardControlMaxWidth}">
<Button x:Name="DeleteButton"

View File

@@ -3,7 +3,6 @@
#include "pch.h"
#include "SettingContainer.h"
#include "SettingsExpander.h"
#include "SettingContainer.g.cpp"
using namespace winrt::Windows::UI::Xaml;
@@ -55,7 +54,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
L"FontIconGlyph",
xaml_typename<hstring>(),
xaml_typename<Editor::SettingContainer>(),
PropertyMetadata{ box_value(L""), PropertyChangedCallback{ &SettingContainer::_OnFontIconGlyphChanged } });
PropertyMetadata{ box_value(L"") });
}
if (!_CurrentValueProperty)
{
@@ -140,7 +139,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<Editor::SettingsExpander>() })
if (const auto& expander{ child.try_as<Microsoft::UI::Xaml::Controls::Expander>() })
{
base.push_back(child);
}
@@ -224,47 +223,13 @@ 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<Editor::SettingsExpander>() })
if (const auto& expander{ child.try_as<Microsoft::UI::Xaml::Controls::Expander>() })
{
expander.IsExpanded(expanded);
}
@@ -306,7 +271,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
if (const auto& child{ GetTemplateChild(L"Expander") })
{
if (const auto& expander{ child.try_as<Editor::SettingsExpander>() })
if (const auto& expander{ child.try_as<Microsoft::UI::Xaml::Controls::Expander>() })
{
Automation::AutomationProperties::SetName(expander, _GenerateAccessibleName());
}

View File

@@ -48,13 +48,11 @@ 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,6 +18,8 @@
</Style>
<SolidColorBrush x:Key="SubgroupHeaderBrush"
Color="{StaticResource TextFillColorSecondary}" />
<StaticResource x:Key="ExpanderHeaderBorderBrush"
ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="SettingContainerErrorSeverityBackgroundBrush"
ResourceKey="SystemFillColorCriticalBackgroundBrush" />
@@ -41,12 +43,16 @@
<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" />
@@ -78,6 +84,8 @@
</Style>
<SolidColorBrush x:Key="SubgroupHeaderBrush"
Color="{StaticResource TextFillColorSecondary}" />
<StaticResource x:Key="ExpanderHeaderBorderBrush"
ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="SettingContainerErrorSeverityBackgroundBrush"
ResourceKey="SystemFillColorCriticalBackgroundBrush" />
@@ -113,6 +121,12 @@
<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">
@@ -131,6 +145,18 @@
<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">
@@ -152,7 +178,7 @@
BasedOn="{StaticResource SettingsPageItemDescriptionStyle}"
TargetType="TextBlock">
<Setter Property="MaxWidth" Value="248" />
<Setter Property="Margin" Value="0" />
<Setter Property="Margin" Value="0,0,-16,0" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets" />
@@ -163,9 +189,7 @@
Text="{Binding}" />
</DataTemplate>
<local:StringDefaultTemplateSelector x:Key="ExpanderPreviewTemplateSelector"
StringTemplate="{StaticResource ExpanderSettingContainerStringPreviewTemplate}" />
<!-- A setting container for a setting that has no additional options -->
<Style TargetType="local:SettingContainer">
<Setter Property="Margin" Value="0,4,0,0" />
<Setter Property="IsTabStop" Value="False" />
@@ -173,9 +197,14 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<local:SettingsCard x:Name="Card"
Content="{TemplateBinding Content}">
<local:SettingsCard.Header>
<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}" />
@@ -185,61 +214,257 @@
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
</StackPanel>
</local:SettingsCard.Header>
<local:SettingsCard.Description>
<TextBlock x:Name="HelpTextBlock"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
Text="{TemplateBinding HelpText}" />
</local:SettingsCard.Description>
</local:SettingsCard>
</StackPanel>
<ContentPresenter Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ExpanderSettingContainerStyle"
<!-- 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="MaxWidth" Value="1000" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="AutomationProperties.AccessibilityView" Value="Raw" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<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>
<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>
</ControlTemplate>
</Setter.Value>
</Setter>
@@ -254,8 +479,14 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<local:SettingsCard Background="{ThemeResource SettingContainerWarningSeverityBackgroundBrush}">
<local:SettingsCard.Header>
<Grid AutomationProperties.Name="{TemplateBinding Header}"
Background="{ThemeResource SettingContainerWarningSeverityBackgroundBrush}"
Style="{StaticResource NonExpanderGrid}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<Grid>
<TextBlock x:Name="IconBackground"
@@ -267,6 +498,7 @@
FontSize="{StaticResource SettingContainerIconFontSize}"
Foreground="{ThemeResource SettingContainerWarningSeverityIconBackground}"
Text="{StaticResource SettingContainerIconBackgroundGlyph}" />
<TextBlock x:Name="StandardIcon"
Grid.Column="0"
Margin="{StaticResource SettingContainerIconMargin}"
@@ -282,14 +514,12 @@
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}" />
</local:SettingsCard.Description>
</local:SettingsCard>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
@@ -304,8 +534,14 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SettingContainer">
<local:SettingsCard Background="{ThemeResource SettingContainerErrorSeverityBackgroundBrush}">
<local:SettingsCard.Header>
<Grid AutomationProperties.Name="{TemplateBinding Header}"
Background="{ThemeResource SettingContainerErrorSeverityBackgroundBrush}"
Style="{StaticResource NonExpanderGrid}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Style="{StaticResource StackPanelInExpanderStyle}">
<StackPanel Orientation="Horizontal">
<Grid>
<TextBlock x:Name="IconBackground"
@@ -317,6 +553,7 @@
FontSize="{StaticResource SettingContainerIconFontSize}"
Foreground="{ThemeResource SettingContainerErrorSeverityIconBackground}"
Text="{StaticResource SettingContainerIconBackgroundGlyph}" />
<TextBlock x:Name="StandardIcon"
Grid.Column="0"
Margin="{StaticResource SettingContainerIconMargin}"
@@ -332,14 +569,12 @@
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}" />
</local:SettingsCard.Description>
</local:SettingsCard>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>

View File

@@ -1,472 +0,0 @@
// 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

@@ -1,95 +0,0 @@
/*++
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

@@ -1,46 +0,0 @@
// 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

@@ -1,409 +0,0 @@
<!--
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

@@ -1,150 +0,0 @@
// 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

@@ -1,67 +0,0 @@
/*++
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

@@ -1,39 +0,0 @@
// 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

@@ -1,26 +0,0 @@
// 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

@@ -1,24 +0,0 @@
// 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

@@ -1,12 +0,0 @@
// 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

@@ -57,6 +57,7 @@ static constexpr std::string_view SuggestionsKey{ "showSuggestions" };
static constexpr std::string_view ToggleFocusModeKey{ "toggleFocusMode" };
static constexpr std::string_view SetFocusModeKey{ "setFocusMode" };
static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" };
static constexpr std::string_view ToggleOverviewKey{ "toggleOverview" };
static constexpr std::string_view SetFullScreenKey{ "setFullScreen" };
static constexpr std::string_view SetMaximizedKey{ "setMaximized" };
static constexpr std::string_view TogglePaneZoomKey{ "togglePaneZoom" };

View File

@@ -59,6 +59,7 @@
ON_ALL_ACTIONS(ToggleShaderEffects) \
ON_ALL_ACTIONS(ToggleFocusMode) \
ON_ALL_ACTIONS(ToggleFullscreen) \
ON_ALL_ACTIONS(ToggleOverview) \
ON_ALL_ACTIONS(ToggleAlwaysOnTop) \
ON_ALL_ACTIONS(OpenSettings) \
ON_ALL_ACTIONS(SetFocusMode) \

View File

@@ -466,6 +466,9 @@
<value>Toggle focus mode</value>
<comment>"Focus mode" is a mode with minimal UI elements, for a distraction-free experience</comment>
</data>
<data name="ToggleOverviewCommandKey" xml:space="preserve">
<value>Toggle overview mode</value>
</data>
<data name="EnableFocusModeCommandKey" xml:space="preserve">
<value>Enable focus mode</value>
</data>

View File

@@ -517,6 +517,7 @@
{ "command": "closeWindow", "id": "Terminal.CloseWindow" },
{ "command": "toggleFullscreen", "id": "Terminal.ToggleFullscreen" },
{ "command": "toggleFocusMode", "id": "Terminal.ToggleFocusMode" },
{ "command": "toggleOverview", "id": "Terminal.ToggleOverview" },
{ "command": "toggleAlwaysOnTop", "id": "Terminal.ToggleAlwaysOnTop" },
{ "command": "openNewTabDropdown", "id": "Terminal.OpenNewTabDropdown" },
{ "command": { "action": "openSettings", "target": "settingsUI" }, "id": "Terminal.OpenSettingsUI" },

View File

@@ -123,7 +123,6 @@ class ScreenBufferTests
TEST_METHOD(VtResizePreservingAttributes);
TEST_METHOD(VtSoftResetCursorPosition);
TEST_METHOD(VtSoftResetAltBufferCursorState);
TEST_METHOD(VtScrollMarginsNewlineColor);
@@ -1511,30 +1510,6 @@ 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

@@ -3002,15 +3002,17 @@ void AdaptDispatch::SoftReset()
SetGraphicsRendition({}); // Normal rendition.
SetCharacterProtectionAttribute({}); // Default (unprotected)
// 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) = {};
// 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
// The TerminalOutput state in this buffer must be reset to
// The TerminalOutput state in these buffers must be reset to
// the same state as the _termOutput instance, which is not
// necessarily equivalent to a full reset.
_savedCursorState.at(_usingAltBuffer ? 1 : 0).TermOutput = _termOutput;
_savedCursorState.at(0).TermOutput = _termOutput;
_savedCursorState.at(1).TermOutput = _termOutput;
// Soft reset the Sixel parser if in use.
if (_sixelParser)