Compare commits

...

62 Commits

Author SHA1 Message Date
Carlos Zamora
f3b7ff29fc PRE-MERGE #19822 Update actions page and edit actions page with better alignment 2026-02-10 10:34:17 -08:00
Carlos Zamora
9c2a507696 PRE-MERGE #19827 Allow searching through the available shortcut actions in the EditAction page 2026-02-10 10:11:23 -08:00
Carlos Zamora
425a4036d4 PRE-MERGE #19817 Implement the Kitty Keyboard Protocol 2026-02-10 10:10:51 -08:00
Carlos Zamora
408b232d13 PRE-MERGE #19519 Implement search in Settings UI 2026-02-10 10:10:32 -08:00
Carlos Zamora
9be7bf3130 PRE-MERGE #19647 Fix tab row acrylic material in unfocused windows 2026-02-10 10:10:11 -08:00
Leonard Hecker
efcc5f94f4 Fix typo 2026-02-09 22:21:38 +01:00
Leonard Hecker
877c7c6555 Address feedback 2026-02-09 22:15:29 +01:00
Leonard Hecker
59705d5905 Spel 2026-02-04 23:06:44 +01:00
Leonard Hecker
4f86d4ceca Implement the Kitty Keyboard Protocol 2026-02-04 23:02:18 +01:00
Carlos Zamora
b540827359 Merge branch 'main' into dev/cazamor/sui/search 2026-02-03 16:10:16 -08:00
Pankaj Bhojwani
d37fb80d19 add search for actions box 2026-02-03 16:02:21 -08:00
Pankaj Bhojwani
3dc426d685 format 2026-01-30 16:07:34 -08:00
Pankaj Bhojwani
a2e24f0221 remove these artifacts 2026-01-30 15:59:33 -08:00
Pankaj Bhojwani
d955d44e98 alignments 2026-01-30 15:56:01 -08:00
Carlos Zamora
09bcdc0566 Merge branch 'main' into dev/cazamor/sui/search 2026-01-29 14:29:32 -08:00
Carlos Zamora
26644183f2 Polish Extensions results 2026-01-28 10:50:22 -08:00
Carlos Zamora
8f7a26d05c Add support for searching actions 2026-01-28 09:53:41 -08:00
Carlos Zamora
7a05a7c603 create NavConstants.h to deduplicate code; spellcheck 2026-01-27 18:36:17 -08:00
Carlos Zamora
c370efbdad remove some dead code 2026-01-27 18:11:46 -08:00
Carlos Zamora
543c67771e remove help text from index 2026-01-27 17:48:39 -08:00
Carlos Zamora
5471c4c400 tune fzf scoring 2026-01-27 17:36:57 -08:00
Carlos Zamora
5d188ce593 Move search logic to SearchIndex; add fzf 2026-01-27 11:35:26 -08:00
Carlos Zamora
82a986af94 Merge branch 'main' into dev/cazamor/sui/search 2026-01-20 10:43:59 -08:00
Carlos Zamora
62a6b5eb2f address feedback from Pankaj's review 2026-01-16 17:15:17 -08:00
Carlos Zamora
6cc6fe1714 fix memory leak 2026-01-16 14:49:57 -08:00
Carlos Zamora
14e380a9b2 minor code health in _UpdateSearchIndex() 2026-01-15 15:51:35 -08:00
Carlos Zamora
9cbaa980d8 exclude Profiles_Base_Orphaned from index 2026-01-14 19:09:08 -08:00
Carlos Zamora
27d4a0b575 prevent multiple searches if we're not gonna use them 2026-01-14 18:27:13 -08:00
Carlos Zamora
e14dfec7b7 fix duplicate profile results when reloading 2026-01-14 18:04:57 -08:00
Carlos Zamora
672945c3bf Add secondary label to search results 2026-01-14 17:35:09 -08:00
Carlos Zamora
d008e80d02 fix search results + profile.defaults bugs 2026-01-14 15:27:37 -08:00
Carlos Zamora
3252b7ddf0 Merge branch 'main' into dev/cazamor/sui/search 2026-01-12 16:52:21 -08:00
Carlos Zamora
fe4069dd13 Merge branch 'main' into fix-tab-acrylic-unfocused 2026-01-12 16:16:55 -08:00
Carlos Zamora
3abee35102 Remove shouldUseAcrylic 2026-01-12 13:57:43 -08:00
Carlos Zamora
acb19efea7 Remove unused variable 2026-01-12 13:18:23 -08:00
Carlos Zamora
22ba243185 Merge branch 'main' into dev/cazamor/sui/search 2025-12-16 16:16:44 -08:00
AbhishekGiri04
018fade640 Fix tab row acrylic material in unfocused windows (#19544)
This commit fixes the issue where the tab row loses its acrylic material
when the window loses focus, even when 'Allow acrylic material in unfocused
windows' setting is enabled.

The fix modifies the _updateThemeColors() function in TerminalPage.cpp to
check both the UseAcrylicInTabRow setting AND the window focus state along
with the EnableUnfocusedAcrylic setting. Now the tab row will maintain its
acrylic effect when:
1. UseAcrylicInTabRow is enabled, AND
2. Either the window is focused OR EnableUnfocusedAcrylic is enabled

This ensures that the 'Allow acrylic material in unfocused windows' setting
is properly respected for the tab row, not just the terminal background.

Fixes #19544
2025-12-15 20:14:42 +05:30
Carlos Zamora
6d968b54f3 remove TODO CARLOS 2025-11-24 16:19:22 -08:00
Carlos Zamora
20bdc21c79 clean up more TODOs 2025-11-17 17:42:43 -08:00
Carlos Zamora
a4c69cfc6a clean up some TODOs 2025-11-17 17:35:45 -08:00
Carlos Zamora
e5ea64586d NavigateToXArgs --> NavigateToPageArgs 2025-11-17 16:49:57 -08:00
Carlos Zamora
81f881a579 Fix navigation for profile.appearance settings (global profile too) 2025-11-17 15:41:19 -08:00
Carlos Zamora
45d75e701f registerIndex macro -> lambda 2025-11-17 14:11:56 -08:00
Carlos Zamora
83aa9fd889 profiles: fix BringIntoView (partially) 2025-11-13 10:34:20 -08:00
Carlos Zamora
8c99200e96 color schemes: fix BringIntoView 2025-11-12 15:49:00 -08:00
Carlos Zamora
14bab6cc1a more polish; more bugs found; I guess it's a stalemate 2025-11-07 12:24:33 -08:00
Carlos Zamora
be2b1d30cb fix color schemes; convert APPEND_RUNTIME_OBJECT_RESULTS to lambda 2025-11-06 16:17:32 -08:00
Carlos Zamora
6fbf953fb2 clear search box 2025-11-06 15:32:35 -08:00
Carlos Zamora
cff62cc60e update selected item 2025-11-06 13:57:13 -08:00
Carlos Zamora
82536fd756 bugfix: duplicate profile 2025-11-05 15:26:54 -08:00
Carlos Zamora
bf2e4e19d7 code format and spell check 2025-11-05 15:26:34 -08:00
Carlos Zamora
2706d05491 icons 2025-11-05 14:54:07 -08:00
Carlos Zamora
1dafcef36f FilteredSearchResult::CreateNoResultsItem and CreateRuntimeObjectItem 2025-11-05 13:02:30 -08:00
Carlos Zamora
53ddd92e7f remove SearchMetadata (old attempt) 2025-11-03 17:26:08 -08:00
Carlos Zamora
e84e8d408f add language neutral search 2025-11-03 17:02:03 -08:00
Carlos Zamora
532343f1ce runtime object indexing 2025-11-03 16:27:12 -08:00
Carlos Zamora
2a41f8a57c improve index 2025-11-03 11:48:36 -08:00
Carlos Zamora
e56eb74788 improve indexing perf and include help text 2025-10-29 15:01:31 -07:00
Carlos Zamora
cac844b1e9 minimum viable product 2025-10-28 12:59:31 -07:00
Carlos Zamora
915f085b60 load runtime index for search 2025-10-23 13:32:18 -07:00
Carlos Zamora
1b8c99dff8 Load build time index for search
- Adds referential XAML names to components of interest.
- Adds a script that generates the build time entries to be loaded
- Invokes the script in Editor.vcxproj
2025-10-15 15:44:17 -07:00
Carlos Zamora
e01ff4faf0 Introduce SearchMetadata and navigation by name
- SearchMetadata is unsused, but it is designed to hold information
   so that we can navigate to a setting
- Updated all pages to...
   - have an x:Name on relevant setting containers
   - have a NavigateToXArgs used in the OnNavigatedTo() function
   - update the NavigateToXArgs to include the name of an element
      to scroll down to
   - Add BringIntoViewWhenLoaded() to HasScrollViewer<T> which
      scrolls down to the element with a given name
These components aren't fully hooked up together yet and there's a
few TODO CARLOS's throughout. Main upcoming work:
- indexing
- runtime indexing
- search box UI
- search results UI
based on 079c69b8be/doc/specs/settings-search.md
2025-10-14 11:46:31 -07:00
96 changed files with 4262 additions and 1059 deletions

View File

@@ -71,7 +71,9 @@ sustainability
sxn
Tencent
toolset
Uids
UEFI
UIDs
uiatextrange
und
vsdevcmd

View File

@@ -256,7 +256,6 @@ conterm
contsf
contypes
conwinuserrefs
coordnew
COPYCOLOR
COPYDATA
COPYDATASTRUCT
@@ -622,7 +621,6 @@ fuzzmap
fuzzwrapper
fwdecl
fwe
fwlink
fzf
gci
gcx
@@ -866,6 +864,7 @@ KILLACTIVE
KILLFOCUS
kinda
KIYEOK
KKP
KLF
KLMNO
KOK
@@ -885,6 +884,7 @@ LBUTTONDOWN
LBUTTONUP
lcb
lci
LCMAP
LCONTROL
LCTRL
lcx

View File

@@ -600,90 +600,6 @@ namespace
},
},
};
#pragma region TAEF hookup for the test case array above
struct ArrayIndexTaefAdapterRow : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom | Microsoft::WRL::InhibitFtmBase>, IDataRow>
{
HRESULT RuntimeClassInitialize(const size_t index)
{
_index = index;
return S_OK;
}
STDMETHODIMP GetTestData(BSTR /*pszName*/, SAFEARRAY** ppData) override
{
const auto indexString{ wil::str_printf<std::wstring>(L"%zu", _index) };
auto safeArray{ SafeArrayCreateVector(VT_BSTR, 0, 1) };
LONG index{ 0 };
auto indexBstr{ wil::make_bstr(indexString.c_str()) };
(void)SafeArrayPutElement(safeArray, &index, indexBstr.release());
*ppData = safeArray;
return S_OK;
}
STDMETHODIMP GetMetadataNames(SAFEARRAY** ppMetadataNames) override
{
*ppMetadataNames = nullptr;
return S_FALSE;
}
STDMETHODIMP GetMetadata(BSTR /*pszName*/, SAFEARRAY** ppData) override
{
*ppData = nullptr;
return S_FALSE;
}
STDMETHODIMP GetName(BSTR* ppszRowName) override
{
*ppszRowName = nullptr;
return S_FALSE;
}
private:
size_t _index;
};
struct ArrayIndexTaefAdapterSource : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom | Microsoft::WRL::InhibitFtmBase>, IDataSource>
{
STDMETHODIMP Advance(IDataRow** ppDataRow) override
{
if (_index < std::extent_v<decltype(testCases)>)
{
Microsoft::WRL::MakeAndInitialize<ArrayIndexTaefAdapterRow>(ppDataRow, _index++);
}
else
{
*ppDataRow = nullptr;
}
return S_OK;
}
STDMETHODIMP Reset() override
{
_index = 0;
return S_OK;
}
STDMETHODIMP GetTestDataNames(SAFEARRAY** names) override
{
auto safeArray{ SafeArrayCreateVector(VT_BSTR, 0, 1) };
LONG index{ 0 };
auto dataNameBstr{ wil::make_bstr(L"index") };
(void)SafeArrayPutElement(safeArray, &index, dataNameBstr.release());
*names = safeArray;
return S_OK;
}
STDMETHODIMP GetTestDataType(BSTR /*name*/, BSTR* type) override
{
*type = nullptr;
return S_OK;
}
private:
size_t _index{ 0 };
};
#pragma endregion
}
extern "C" HRESULT __declspec(dllexport) __cdecl ReflowTestDataSource(IDataSource** ppDataSource, void*)

View File

@@ -134,7 +134,7 @@
</ClInclude>
<ClInclude Include="FilteredCommand.h" />
<ClInclude Include="Pane.h" />
<ClInclude Include="fzf/fzf.h" />
<ClInclude Include="../fzf/fzf.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="ShortcutActionDispatch.h">
<DependentUpon>ShortcutActionDispatch.idl</DependentUpon>
@@ -204,7 +204,7 @@
<ClCompile Include="Tab.cpp">
<DependentUpon>Tab.idl</DependentUpon>
</ClCompile>
<ClCompile Include="fzf/fzf.cpp" />
<ClCompile Include="../fzf/fzf.cpp" />
<ClCompile Include="TaskbarState.cpp">
<DependentUpon>TaskbarState.idl</DependentUpon>
</ClCompile>

View File

@@ -4916,7 +4916,7 @@ namespace winrt::TerminalApp::implementation
theme.TabRow().UnfocusedBackground()) :
ThemeColor{ nullptr } };
if (_settings.GlobalSettings().UseAcrylicInTabRow())
if (_settings.GlobalSettings().UseAcrylicInTabRow() && (_activated || _settings.GlobalSettings().EnableUnfocusedAcrylic()))
{
if (tabRowBg)
{

View File

@@ -121,6 +121,7 @@ namespace Microsoft.Terminal.Core
String WordDelimiters { get; };
Boolean ForceVTInput { get; };
Boolean AllowKittyKeyboardMode { get; };
Boolean AllowVtChecksumReport { get; };
Boolean AllowVtClipboardWrite { get; };
Boolean TrimBlockSelection { get; };

View File

@@ -98,6 +98,7 @@ void Terminal::UpdateSettings(ICoreSettings settings)
}
_getTerminalInput().ForceDisableWin32InputMode(settings.ForceVTInput());
_getTerminalInput().ForceDisableKittyKeyboardProtocol(!settings.AllowKittyKeyboardMode());
if (settings.TabColor() == nullptr)
{

View File

@@ -349,6 +349,7 @@ namespace winrt::Microsoft::Terminal::Settings
_ReloadEnvironmentVariables = profile.ReloadEnvironmentVariables();
_RainbowSuggestions = profile.RainbowSuggestions();
_ForceVTInput = profile.ForceVTInput();
_AllowKittyKeyboardMode = profile.AllowKittyKeyboardMode();
_AllowVtChecksumReport = profile.AllowVtChecksumReport();
_AllowVtClipboardWrite = profile.AllowVtClipboardWrite();
_PathTranslationStyle = profile.PathTranslationStyle();

View File

@@ -4,6 +4,8 @@
#include "pch.h"
#include "Actions.h"
#include "Actions.g.cpp"
#include "NavigateToPageArgs.g.h"
#include "LibraryResources.h"
#include "../TerminalSettingsModel/AllShortcutActions.h"
using namespace winrt::Windows::UI::Xaml;
@@ -20,16 +22,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Actions::OnNavigatedTo(const NavigationEventArgs& e)
{
_ViewModel = e.Parameter().as<Editor::ActionsViewModel>();
_ViewModel.CurrentPage(ActionsSubPage::Base);
auto vmImpl = get_self<ActionsViewModel>(_ViewModel);
vmImpl->MarkAsVisited();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::ActionsViewModel>();
get_self<ActionsViewModel>(_ViewModel)->MarkAsVisited();
_layoutUpdatedRevoker = LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
// Only let this succeed once.
_layoutUpdatedRevoker.revoke();
AddNewButton().Focus(FocusState::Programmatic);
});
BringIntoViewWhenLoaded(args.ElementToFocus());
TraceLoggingWrite(
g_hTerminalSettingsEditorProvider,

View File

@@ -182,8 +182,7 @@
</Page.Resources>
<Border MaxWidth="{StaticResource StandardControlMaxWidth}">
<StackPanel MaxWidth="600"
HorizontalAlignment="Left"
<StackPanel HorizontalAlignment="Stretch"
Spacing="8"
Style="{StaticResource SettingsStackStyle}">
<HyperlinkButton x:Uid="Actions_Disclaimer"

View File

@@ -27,6 +27,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void AddProfile::OnNavigatedTo(const NavigationEventArgs& e)
{
_State = e.Parameter().as<Editor::AddProfilePageNavigationState>();
BringIntoViewWhenLoaded(_State.ElementToFocus());
TraceLoggingWrite(
g_hTerminalSettingsEditorProvider,

View File

@@ -26,8 +26,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
struct AddProfilePageNavigationState : AddProfilePageNavigationStateT<AddProfilePageNavigationState>
{
public:
AddProfilePageNavigationState(const Model::CascadiaSettings& settings) :
_Settings{ settings } {}
AddProfilePageNavigationState(const Model::CascadiaSettings& settings, const hstring& elementToFocus = {}) :
_Settings{ settings },
_ElementToFocus{ elementToFocus } {}
void RequestAddNew()
{
@@ -42,6 +43,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
til::event<AddNewArgs> AddNew;
WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr);
WINRT_PROPERTY(hstring, ElementToFocus);
};
struct AddProfile : public HasScrollViewer<AddProfile>, AddProfileT<AddProfile>

View File

@@ -8,6 +8,8 @@ namespace Microsoft.Terminal.Settings.Editor
runtimeclass AddProfilePageNavigationState
{
Microsoft.Terminal.Settings.Model.CascadiaSettings Settings;
String ElementToFocus { get; };
void RequestAddNew();
void RequestDuplicate(Guid profile);
event AddNewArgs AddNew;

View File

@@ -37,7 +37,8 @@
</Button>
</Border>
<StackPanel Margin="{StaticResource StandardControlMargin}">
<local:SettingContainer x:Uid="AddProfile_Duplicate">
<local:SettingContainer x:Name="DuplicateProfile"
x:Uid="AddProfile_Duplicate">
<ComboBox x:Name="Profiles"
AutomationProperties.AccessibilityView="Content"
ItemsSource="{x:Bind State.Settings.AllProfiles, Mode=OneWay}"

View File

@@ -1138,6 +1138,27 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
INITIALIZE_BINDABLE_ENUM_SETTING(IntenseTextStyle, IntenseTextStyle, winrt::Microsoft::Terminal::Settings::Model::IntenseStyle, L"Appearance_IntenseTextStyle", L"Content");
}
// Appearances doesn't implement HasScrollViewer<T> which normally adds this function.
void Appearances::BringIntoViewWhenLoaded(hstring elementToFocus)
{
if (elementToFocus.empty())
{
return;
}
_loadedRevoker = this->Loaded(winrt::auto_revoke, [weakThis{ get_weak() }, elementToFocus](auto&&, auto&&) {
if (const auto strongThis = weakThis.get())
{
if (const auto& controlToFocus{ strongThis->FindName(elementToFocus).try_as<Controls::Control>() })
{
controlToFocus.as<FrameworkElement>().StartBringIntoView();
controlToFocus.Focus(FocusState::Programmatic);
}
strongThis->_loadedRevoker.revoke();
}
});
}
IObservableVector<Editor::Font> Appearances::FilteredFontList()
{
if (!_filteredFonts)

View File

@@ -188,6 +188,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
struct Appearances : AppearancesT<Appearances>
{
Appearances();
void BringIntoViewWhenLoaded(hstring elementToFocus);
// CursorShape visibility logic
bool IsVintageCursor() const;
@@ -213,6 +214,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
bool IsCustomFontWeight();
til::property_changed_event PropertyChanged;
winrt::Windows::UI::Xaml::FrameworkElement::Loaded_revoker _loadedRevoker;
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Microsoft::Terminal::Settings::Editor::EnumEntry>, FontWeightList);

View File

@@ -75,7 +75,8 @@
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Color Scheme -->
<!-- This currently only display the Dark color scheme, even if the user has a pair of schemes set. -->
<local:SettingContainer x:Uid="Profile_ColorScheme"
<local:SettingContainer x:Name="ColorScheme"
x:Uid="Profile_ColorScheme"
ClearSettingValue="{x:Bind Appearance.ClearColorScheme}"
CurrentValueAccessibleName="{x:Bind Appearance.CurrentColorScheme.Name, Mode=OneWay}"
HasSettingValue="{x:Bind Appearance.HasDarkColorSchemeName, Mode=OneWay}"
@@ -284,17 +285,20 @@
IsChecked="{x:Bind ShowAllFonts, Mode=TwoWay}" />
</StackPanel>
</local:SettingContainer>
<local:SettingContainer x:Uid="Profile_MissingFontFaces"
<local:SettingContainer x:Name="MissingFontFaces"
x:Uid="Profile_MissingFontFaces"
HelpText="{x:Bind Appearance.MissingFontFaces, Mode=OneWay}"
Style="{StaticResource SettingContainerErrorStyle}"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(Appearance.MissingFontFaces), Mode=OneWay}" />
<local:SettingContainer x:Uid="Profile_ProportionalFontFaces"
<local:SettingContainer x:Name="ProportionalFontFaces"
x:Uid="Profile_ProportionalFontFaces"
HelpText="{x:Bind Appearance.ProportionalFontFaces, Mode=OneWay}"
Style="{StaticResource SettingContainerWarningStyle}"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(Appearance.ProportionalFontFaces), Mode=OneWay}" />
<!-- Font Size -->
<local:SettingContainer x:Uid="Profile_FontSize"
<local:SettingContainer x:Name="FontSize"
x:Uid="Profile_FontSize"
ClearSettingValue="{x:Bind Appearance.ClearFontSize}"
HasSettingValue="{x:Bind Appearance.HasFontSize, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.FontSizeOverrideSource, Mode=OneWay}"
@@ -310,7 +314,8 @@
</local:SettingContainer>
<!-- Line Height -->
<local:SettingContainer x:Uid="Profile_LineHeight"
<local:SettingContainer x:Name="LineHeight"
x:Uid="Profile_LineHeight"
ClearSettingValue="{x:Bind Appearance.ClearLineHeight}"
HasSettingValue="{x:Bind Appearance.HasLineHeight, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.LineHeightOverrideSource, Mode=OneWay}"
@@ -326,7 +331,8 @@
</local:SettingContainer>
<!-- Cell Width -->
<local:SettingContainer x:Uid="Profile_CellWidth"
<local:SettingContainer x:Name="CellWidth"
x:Uid="Profile_CellWidth"
ClearSettingValue="{x:Bind Appearance.ClearCellWidth}"
HasSettingValue="{x:Bind Appearance.HasCellWidth, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.CellWidthOverrideSource, Mode=OneWay}"
@@ -342,7 +348,8 @@
</local:SettingContainer>
<!-- Font Weight -->
<local:SettingContainer x:Uid="Profile_FontWeight"
<local:SettingContainer x:Name="FontWeight"
x:Uid="Profile_FontWeight"
ClearSettingValue="{x:Bind Appearance.ClearFontWeight}"
HasSettingValue="{x:Bind Appearance.HasFontWeight, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.FontWeightOverrideSource, Mode=OneWay}"
@@ -378,7 +385,8 @@
</Grid>
</StackPanel>
</local:SettingContainer>
<local:SettingContainer x:Uid="Profile_FontAxes"
<local:SettingContainer x:Name="FontAxes"
x:Uid="Profile_FontAxes"
ClearSettingValue="{x:Bind Appearance.ClearFontAxes}"
HasSettingValue="{x:Bind Appearance.HasFontAxes, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.FontAxesOverrideSource, Mode=OneWay}"
@@ -405,7 +413,8 @@
</muxc:DropDownButton>
</StackPanel>
</local:SettingContainer>
<local:SettingContainer x:Uid="Profile_FontFeatures"
<local:SettingContainer x:Name="FontFeatures"
x:Uid="Profile_FontFeatures"
ClearSettingValue="{x:Bind Appearance.ClearFontFeatures}"
HasSettingValue="{x:Bind Appearance.HasFontFeatures, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.FontFeaturesOverrideSource, Mode=OneWay}"
@@ -434,7 +443,8 @@
</local:SettingContainer>
<!-- Builtin Glyphs -->
<local:SettingContainer x:Uid="Profile_EnableBuiltinGlyphs"
<local:SettingContainer x:Name="EnableBuiltinGlyphs"
x:Uid="Profile_EnableBuiltinGlyphs"
ClearSettingValue="{x:Bind Appearance.ClearEnableBuiltinGlyphs}"
HasSettingValue="{x:Bind Appearance.HasEnableBuiltinGlyphs, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.EnableBuiltinGlyphsOverrideSource, Mode=OneWay}">
@@ -443,7 +453,8 @@
</local:SettingContainer>
<!-- Color Glyphs -->
<local:SettingContainer x:Uid="Profile_EnableColorGlyphs"
<local:SettingContainer x:Name="EnableColorGlyphs"
x:Uid="Profile_EnableColorGlyphs"
ClearSettingValue="{x:Bind Appearance.ClearEnableColorGlyphs}"
HasSettingValue="{x:Bind Appearance.HasEnableColorGlyphs, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.EnableColorGlyphsOverrideSource, Mode=OneWay}">
@@ -452,7 +463,8 @@
</local:SettingContainer>
<!-- Retro Terminal Effect -->
<local:SettingContainer x:Uid="Profile_RetroTerminalEffect"
<local:SettingContainer x:Name="RetroTerminalEffect"
x:Uid="Profile_RetroTerminalEffect"
ClearSettingValue="{x:Bind Appearance.ClearRetroTerminalEffect}"
HasSettingValue="{x:Bind Appearance.HasRetroTerminalEffect, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.RetroTerminalEffectOverrideSource, Mode=OneWay}">
@@ -461,7 +473,8 @@
</local:SettingContainer>
<!-- Adjust Indistinguishable Colors -->
<local:SettingContainer x:Uid="Profile_AdjustIndistinguishableColors"
<local:SettingContainer x:Name="AdjustIndistinguishableColors"
x:Uid="Profile_AdjustIndistinguishableColors"
ClearSettingValue="{x:Bind Appearance.ClearAdjustIndistinguishableColors}"
HasSettingValue="{x:Bind Appearance.HasAdjustIndistinguishableColors, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.AdjustIndistinguishableColorsOverrideSource, Mode=OneWay}">
@@ -479,7 +492,8 @@
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Cursor Shape -->
<local:SettingContainer x:Uid="Profile_CursorShape"
<local:SettingContainer x:Name="CursorShape"
x:Uid="Profile_CursorShape"
ClearSettingValue="{x:Bind Appearance.ClearCursorShape}"
HasSettingValue="{x:Bind Appearance.HasCursorShape, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.CursorShapeOverrideSource, Mode=OneWay}">
@@ -491,7 +505,8 @@
</local:SettingContainer>
<!-- Cursor Height -->
<local:SettingContainer x:Uid="Profile_CursorHeight"
<local:SettingContainer x:Name="CursorHeight"
x:Uid="Profile_CursorHeight"
ClearSettingValue="{x:Bind Appearance.ClearCursorHeight}"
HasSettingValue="{x:Bind Appearance.HasCursorHeight, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.CursorHeightOverrideSource, Mode=OneWay}"
@@ -563,7 +578,8 @@
</local:SettingContainer>
<!-- Background Image Stretch Mode -->
<local:SettingContainer x:Uid="Profile_BackgroundImageStretchMode"
<local:SettingContainer x:Name="BackgroundImageStretchMode"
x:Uid="Profile_BackgroundImageStretchMode"
ClearSettingValue="{x:Bind Appearance.ClearBackgroundImageStretchMode}"
HasSettingValue="{x:Bind Appearance.HasBackgroundImageStretchMode, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.BackgroundImageStretchModeOverrideSource, Mode=OneWay}"
@@ -576,7 +592,8 @@
</local:SettingContainer>
<!-- Background Image Alignment -->
<local:SettingContainer x:Uid="Profile_BackgroundImageAlignment"
<local:SettingContainer x:Name="BackgroundImageAlignment"
x:Uid="Profile_BackgroundImageAlignment"
ClearSettingValue="{x:Bind Appearance.ClearBackgroundImageAlignment}"
CurrentValue="{x:Bind Appearance.BackgroundImageAlignmentCurrentValue, Mode=OneWay}"
HasSettingValue="{x:Bind Appearance.HasBackgroundImageAlignment, Mode=OneWay}"
@@ -764,7 +781,8 @@
</local:SettingContainer>
<!-- Background Image Opacity -->
<local:SettingContainer x:Uid="Profile_BackgroundImageOpacity"
<local:SettingContainer x:Name="BackgroundImageOpacity"
x:Uid="Profile_BackgroundImageOpacity"
ClearSettingValue="{x:Bind Appearance.ClearBackgroundImageOpacity}"
HasSettingValue="{x:Bind Appearance.HasBackgroundImageOpacity, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.BackgroundImageOpacityOverrideSource, Mode=OneWay}"
@@ -790,7 +808,8 @@
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Intense is bold, bright -->
<local:SettingContainer x:Uid="Appearance_IntenseTextStyle"
<local:SettingContainer x:Name="IntenseTextStyle"
x:Uid="Appearance_IntenseTextStyle"
ClearSettingValue="{x:Bind Appearance.ClearIntenseTextStyle}"
HasSettingValue="{x:Bind Appearance.HasIntenseTextStyle, Mode=OneWay}"
SettingOverrideSource="{x:Bind Appearance.IntenseTextStyleOverrideSource, Mode=OneWay}">

View File

@@ -4,6 +4,7 @@
#include "pch.h"
#include "ColorSchemes.h"
#include "ColorTableEntry.g.cpp"
#include "NavigateToPageArgs.g.h"
#include "ColorSchemes.g.cpp"
using namespace winrt;
@@ -33,9 +34,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void ColorSchemes::OnNavigatedTo(const NavigationEventArgs& e)
{
_ViewModel = e.Parameter().as<Editor::ColorSchemesPageViewModel>();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::ColorSchemesPageViewModel>();
_ViewModel.CurrentPage(ColorSchemesSubPage::Base);
BringIntoViewWhenLoaded(args.ElementToFocus());
_layoutUpdatedRevoker = LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
// Only let this succeed once.
_layoutUpdatedRevoker.revoke();

View File

@@ -37,6 +37,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_OBSERVABLE_PROPERTY(ColorSchemesSubPage, CurrentPage, _propertyChangedHandlers, ColorSchemesSubPage::Base);
WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::ColorSchemeViewModel>, AllColorSchemes, _propertyChangedHandlers, nullptr);
WINRT_PROPERTY(hstring, ElementToFocus);
private:
Editor::ColorSchemeViewModel _CurrentScheme{ nullptr };

View File

@@ -6,6 +6,7 @@
#include "EnumEntry.h"
#include "Compatibility.g.cpp"
#include "CompatibilityViewModel.g.cpp"
#include "NavigateToPageArgs.g.h"
using namespace winrt::Windows::UI::Xaml::Navigation;
using namespace winrt::Microsoft::Terminal::Settings::Model;
@@ -54,7 +55,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Compatibility::OnNavigatedTo(const NavigationEventArgs& e)
{
_ViewModel = e.Parameter().as<Editor::CompatibilityViewModel>();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::CompatibilityViewModel>();
BringIntoViewWhenLoaded(args.ElementToFocus());
TraceLoggingWrite(
g_hTerminalSettingsEditorProvider,

View File

@@ -26,13 +26,15 @@
<StackPanel Style="{StaticResource SettingsStackStyle}">
<!-- Allow Headless -->
<local:SettingContainer x:Uid="Globals_AllowHeadless">
<local:SettingContainer x:Name="AllowHeadless"
x:Uid="Globals_AllowHeadless">
<ToggleSwitch IsOn="{x:Bind ViewModel.AllowHeadless, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Text Measurement -->
<local:SettingContainer x:Uid="Globals_TextMeasurement">
<local:SettingContainer x:Name="TextMeasurement"
x:Uid="Globals_TextMeasurement">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.TextMeasurementList}"
@@ -41,14 +43,16 @@
</local:SettingContainer>
<!-- Debug Features -->
<local:SettingContainer x:Uid="Globals_DebugFeaturesEnabled"
<local:SettingContainer x:Name="DebugFeaturesEnabled"
x:Uid="Globals_DebugFeaturesEnabled"
Visibility="{x:Bind ViewModel.DebugFeaturesAvailable}">
<ToggleSwitch IsOn="{x:Bind ViewModel.DebugFeaturesEnabled, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Reset Application State -->
<local:SettingContainer x:Uid="Settings_ResetApplicationState">
<local:SettingContainer x:Name="ResetApplicationState"
x:Uid="Settings_ResetApplicationState">
<Button x:Uid="Settings_ResetApplicationStateButton"
Style="{StaticResource DeleteButtonStyle}">
<Button.Flyout>
@@ -69,7 +73,8 @@
</local:SettingContainer>
<!-- Reset to Default Settings -->
<local:SettingContainer x:Uid="Settings_ResetToDefaultSettings">
<local:SettingContainer x:Name="ResetToDefaultSettings"
x:Uid="Settings_ResetToDefaultSettings">
<Button x:Uid="Settings_ResetToDefaultSettingsButton"
Style="{StaticResource DeleteButtonStyle}">
<Button.Flyout>

View File

@@ -8,7 +8,9 @@
#include "../TerminalSettingsModel/AllShortcutActions.h"
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Navigation;
using namespace winrt::Windows::Foundation::Collections;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
@@ -49,5 +51,82 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
CommandNameTextBox().Focus(FocusState::Programmatic);
});
// Initialize AutoSuggestBox with current action and store last valid action
if (_ViewModel.ProposedShortcutActionName())
{
const auto currentAction = winrt::unbox_value<winrt::hstring>(_ViewModel.ProposedShortcutActionName());
ShortcutActionBox().Text(currentAction);
_lastValidAction = currentAction;
}
}
void EditAction::ShortcutActionBox_GotFocus(const IInspectable& sender, const RoutedEventArgs&)
{
// Open the suggestions list with all available actions
std::vector<winrt::hstring> allActions;
for (const auto& action : _ViewModel.AvailableShortcutActions())
{
allActions.push_back(action);
}
_filteredActions = winrt::single_threaded_observable_vector(std::move(allActions));
sender.as<AutoSuggestBox>().ItemsSource(_filteredActions);
sender.as<AutoSuggestBox>().IsSuggestionListOpen(true);
}
void EditAction::ShortcutActionBox_TextChanged(const AutoSuggestBox& sender, const AutoSuggestBoxTextChangedEventArgs& args)
{
if (args.Reason() == AutoSuggestionBoxTextChangeReason::UserInput)
{
const auto searchText = sender.Text();
std::vector<winrt::hstring> filtered;
for (const auto& action : _ViewModel.AvailableShortcutActions())
{
if (til::contains_linguistic_insensitive(action, searchText))
{
filtered.push_back(action);
}
}
_filteredActions = winrt::single_threaded_observable_vector(std::move(filtered));
sender.ItemsSource(_filteredActions);
}
}
void EditAction::ShortcutActionBox_SuggestionChosen(const AutoSuggestBox& sender, const AutoSuggestBoxSuggestionChosenEventArgs& args)
{
if (const auto selectedAction = args.SelectedItem().try_as<winrt::hstring>())
{
sender.Text(*selectedAction);
}
}
void EditAction::ShortcutActionBox_QuerySubmitted(const AutoSuggestBox& sender, const AutoSuggestBoxQuerySubmittedEventArgs& args)
{
const auto submittedText = args.QueryText();
// Validate that this is a valid shortcut action
bool isValid = false;
for (const auto& action : _ViewModel.AvailableShortcutActions())
{
if (action == submittedText)
{
isValid = true;
break;
}
}
if (isValid)
{
_ViewModel.ProposedShortcutActionName(winrt::box_value(submittedText));
_lastValidAction = submittedText;
}
else
{
// Revert to the last valid action
sender.Text(_lastValidAction);
}
}
}

View File

@@ -20,11 +20,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_OBSERVABLE_PROPERTY(Editor::CommandViewModel, ViewModel, PropertyChanged.raise, nullptr);
void ShortcutActionBox_GotFocus(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
void ShortcutActionBox_TextChanged(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const winrt::Windows::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs& args);
void ShortcutActionBox_SuggestionChosen(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const winrt::Windows::UI::Xaml::Controls::AutoSuggestBoxSuggestionChosenEventArgs& args);
void ShortcutActionBox_QuerySubmitted(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const winrt::Windows::UI::Xaml::Controls::AutoSuggestBoxQuerySubmittedEventArgs& args);
private:
friend struct EditActionT<EditAction>; // for Xaml to bind events
winrt::Windows::UI::Xaml::FrameworkElement::LayoutUpdated_revoker _layoutUpdatedRevoker;
Editor::CommandViewModel::PropagateWindowRootRequested_revoker _propagateWindowRootRevoker;
Editor::CommandViewModel::FocusContainer_revoker _focusContainerRevoker;
winrt::Windows::Foundation::Collections::IObservableVector<winrt::hstring> _filteredActions{ nullptr };
winrt::hstring _lastValidAction;
};
}

View File

@@ -157,24 +157,30 @@
<Setter Property="Height" Value="{StaticResource EditButtonSize}" />
<Setter Property="Width" Value="{StaticResource EditButtonSize}" />
</Style>
<Style x:Key="TextBlockGroupingStyle"
BasedOn="{StaticResource BodyStrongTextBlockStyle}"
TargetType="TextBlock">
<Setter Property="MaxWidth" Value="{StaticResource StandardControlMaxWidth}" />
<Setter Property="Margin" Value="0,0,0,4" />
<Setter Property="FontSize" Value="16" />
</Style>
<!-- Templates -->
<DataTemplate x:Key="KeyChordTemplate"
x:DataType="local:KeyChordViewModel">
<ListViewItem IsTabStop="False"
Style="{StaticResource KeyBindingContainerStyle}">
<Grid Padding="2,0,2,0"
<Grid Padding="-4,0,0,0"
VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Background="{ThemeResource AppBarItemBackgroundThemeBrush}"
Click="{x:Bind ToggleEditMode}"
Style="{ThemeResource KeyChordBorderStyle}"
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(IsInEditMode), Mode=OneWay}">
<TextBlock FontSize="14"
Style="{ThemeResource KeyChordTextBlockStyle}"
Text="{x:Bind KeyChordText, Mode=OneWay}"
TextWrapping="WrapWholeWords" />
</Button>
@@ -214,6 +220,7 @@
</Grid>
<Button Grid.Column="1"
Margin="8,0,0,0"
HorizontalAlignment="Right"
AutomationProperties.Name="{x:Bind DeleteButtonName}"
Style="{StaticResource DeleteSmallButtonStyle}">
<Button.Content>
@@ -243,13 +250,14 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="{x:Bind Name}"
TextWrapping="WrapWholeWords" />
<muxc:NumberBox Grid.Column="1"
HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind Name}"
LargeChange="1"
Maximum="100"
@@ -267,13 +275,14 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="{x:Bind Name}"
TextWrapping="WrapWholeWords" />
<muxc:NumberBox Grid.Column="1"
HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind Name}"
LargeChange="1"
Maximum="999"
@@ -291,13 +300,14 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="{x:Bind Name}"
TextWrapping="WrapWholeWords" />
<muxc:NumberBox Grid.Column="1"
HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind Name}"
LargeChange="1"
Maximum="999"
@@ -315,13 +325,14 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="{x:Bind Name}"
TextWrapping="WrapWholeWords" />
<muxc:NumberBox Grid.Column="1"
HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind Name}"
LargeChange="1"
Maximum="999"
@@ -339,13 +350,14 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="{x:Bind Name}"
TextWrapping="WrapWholeWords" />
<muxc:NumberBox Grid.Column="1"
HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind Name}"
LargeChange="1"
Maximum="999"
@@ -363,13 +375,14 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="{x:Bind Name}"
TextWrapping="WrapWholeWords" />
<muxc:NumberBox Grid.Column="1"
HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind Name}"
LargeChange="0.2"
Maximum="1"
@@ -387,7 +400,7 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto"
<ColumnDefinition Width="*"
MinWidth="196" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
@@ -408,13 +421,14 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="{x:Bind Name}"
TextWrapping="WrapWholeWords" />
<ComboBox Grid.Column="1"
HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind Name}"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind EnumList, Mode=OneWay}"
@@ -482,13 +496,14 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="{x:Bind Name}"
TextWrapping="WrapWholeWords" />
<ToggleSwitch Grid.Column="1"
HorizontalAlignment="Right"
AutomationProperties.Name="{x:Bind Name}"
IsOn="{x:Bind UnboxBool(Value), Mode=TwoWay, BindBack=BoolOptionalBindBack}" />
</Grid>
@@ -501,13 +516,15 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="{x:Bind Name}"
TextWrapping="WrapWholeWords" />
<CheckBox Grid.Column="1"
Margin="0,0,-96,0"
HorizontalAlignment="Right"
AutomationProperties.Name="{x:Bind Name}"
IsChecked="{x:Bind UnboxBoolOptional(Value), Mode=TwoWay, BindBack=BoolOptionalBindBack}"
IsThreeState="True" />
@@ -526,13 +543,14 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="{x:Bind Name}"
TextWrapping="WrapWholeWords" />
<ComboBox Grid.Column="1"
HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind Name}"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind EnumList, Mode=OneWay}"
@@ -572,7 +590,7 @@
TextWrapping="WrapWholeWords" />
<ItemsControl Grid.Column="1"
Margin="0"
HorizontalAlignment="Left"
HorizontalAlignment="Right"
AutomationProperties.Name="{x:Bind Name}"
ItemTemplate="{StaticResource FlagItemTemplate}"
ItemsSource="{x:Bind FlagList, Mode=OneWay}" />
@@ -586,7 +604,7 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
@@ -608,7 +626,7 @@
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
@@ -646,73 +664,88 @@
<Border MaxWidth="{StaticResource StandardControlMaxWidth}"
Margin="{StaticResource SettingStackMargin}">
<Grid MaxWidth="600"
Margin="{StaticResource SettingStackMargin}"
HorizontalAlignment="Left"
<Grid Margin="{StaticResource SettingStackMargin}"
HorizontalAlignment="Stretch"
ColumnSpacing="16"
RowSpacing="8">
RowSpacing="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock x:Uid="Actions_Name"
<TextBlock x:Uid="Actions_CommandDetails"
Grid.Row="0"
Grid.Column="0"
VerticalAlignment="Center"
Style="{StaticResource TextBlockGroupingStyle}" />
<TextBlock x:Uid="Actions_Name"
Grid.Row="1"
Grid.Column="0"
VerticalAlignment="Center" />
<TextBox x:Name="CommandNameTextBox"
Grid.Row="0"
Grid.Row="1"
Grid.Column="1"
Width="300"
HorizontalAlignment="Left"
HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind ViewModel.ActionNameTextBoxAutomationPropName}"
PlaceholderText="{x:Bind ViewModel.DisplayName, Mode=OneWay}"
Text="{x:Bind ViewModel.Name, Mode=TwoWay}" />
<TextBlock x:Uid="Actions_ShortcutAction"
Grid.Row="1"
Grid.Column="0"
VerticalAlignment="Center" />
<ComboBox Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Center"
AutomationProperties.Name="{x:Bind ViewModel.ShortcutActionComboBoxAutomationPropName}"
ItemsSource="{x:Bind ViewModel.AvailableShortcutActions, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.ProposedShortcutActionName, Mode=TwoWay}" />
<TextBlock x:Uid="Actions_Arguments"
Grid.Row="2"
Grid.Column="0"
VerticalAlignment="Center"
Visibility="{x:Bind ViewModel.ActionArgsVM.HasArgs, Mode=OneWay}" />
<ItemsControl Grid.Row="2"
Grid.Column="1"
AutomationProperties.Name="{x:Bind ViewModel.AdditionalArgumentsControlAutomationPropName}"
IsTabStop="False"
ItemTemplateSelector="{StaticResource ArgsTemplateSelector}"
ItemsSource="{x:Bind ViewModel.ActionArgsVM.ArgValues, Mode=OneWay}" />
VerticalAlignment="Center" />
<AutoSuggestBox x:Name="ShortcutActionBox"
Grid.Row="2"
Grid.Column="1"
VerticalAlignment="Center"
AutomationProperties.Name="{x:Bind ViewModel.ShortcutActionComboBoxAutomationPropName}"
GotFocus="ShortcutActionBox_GotFocus"
QuerySubmitted="ShortcutActionBox_QuerySubmitted"
SuggestionChosen="ShortcutActionBox_SuggestionChosen"
TextChanged="ShortcutActionBox_TextChanged" />
<TextBlock x:Uid="Actions_Keybindings"
Grid.Row="3"
Grid.Column="0"
VerticalAlignment="Center" />
VerticalAlignment="Center"
Style="{StaticResource TextBlockGroupingStyle}" />
<ListView x:Name="KeyChordListView"
x:Uid="Actions_KeyBindingsListView"
Grid.Row="3"
Grid.Column="1"
Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="2"
ItemTemplate="{StaticResource KeyChordTemplate}"
ItemsSource="{x:Bind ViewModel.KeyChordList, Mode=OneWay}"
SelectionMode="None">
<ListView.Header>
<Button Click="{x:Bind ViewModel.AddKeybinding_Click}">
<Button Margin="0,0,0,4"
Click="{x:Bind ViewModel.AddKeybinding_Click}">
<TextBlock x:Uid="Actions_AddKeyChord" />
</Button>
</ListView.Header>
</ListView>
<Button Grid.Row="4"
<TextBlock x:Uid="Actions_Arguments"
Grid.Row="5"
Grid.Column="0"
VerticalAlignment="Center"
Style="{StaticResource TextBlockGroupingStyle}"
Visibility="{x:Bind ViewModel.ActionArgsVM.HasArgs, Mode=OneWay}" />
<ItemsControl Grid.Row="6"
Grid.Column="0"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind ViewModel.AdditionalArgumentsControlAutomationPropName}"
IsTabStop="False"
ItemTemplateSelector="{StaticResource ArgsTemplateSelector}"
ItemsSource="{x:Bind ViewModel.ActionArgsVM.ArgValues, Mode=OneWay}" />
<Button Grid.Row="7"
Grid.Column="0"
IsEnabled="{x:Bind ViewModel.IsUserAction, Mode=OneWay}"
Style="{StaticResource DeleteButtonStyle}">

View File

@@ -4,6 +4,7 @@
#include "pch.h"
#include "EditColorScheme.h"
#include "EditColorScheme.g.cpp"
#include "NavigateToPageArgs.g.h"
using namespace winrt;
using namespace winrt::Windows::UI;
@@ -38,7 +39,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void EditColorScheme::OnNavigatedTo(const NavigationEventArgs& e)
{
_ViewModel = e.Parameter().as<Editor::ColorSchemeViewModel>();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::ColorSchemeViewModel>();
BringIntoViewWhenLoaded(args.ElementToFocus());
const auto schemeName = _ViewModel.Name();
NameBox().Text(schemeName);

View File

@@ -200,7 +200,8 @@
</Grid>
</Border>
<local:SettingContainer x:Uid="ColorScheme_InboxSchemeDuplicate"
<local:SettingContainer x:Name="InboxSchemeDuplicate"
x:Uid="ColorScheme_InboxSchemeDuplicate"
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(ViewModel.IsEditable), Mode=OneWay}">
<Button x:Name="DuplicateSchemeButton"
x:Uid="ColorScheme_DuplicateButton"
@@ -208,7 +209,8 @@
Style="{StaticResource BrowseButtonStyle}" />
</local:SettingContainer>
<local:SettingContainer x:Uid="ColorScheme_ColorsHeader"
<local:SettingContainer x:Name="ColorsHeader"
x:Uid="ColorScheme_ColorsHeader"
StartExpanded="True"
Style="{StaticResource ExpanderSettingContainerStyle}"
Visibility="{x:Bind ViewModel.IsEditable, Mode=OneWay}">

View File

@@ -3,11 +3,13 @@
#include "pch.h"
#include "Extensions.h"
#include "NavigateToPageArgs.g.h"
#include "Extensions.g.cpp"
#include "ExtensionPackageViewModel.g.cpp"
#include "ExtensionsViewModel.g.cpp"
#include "FragmentProfileViewModel.g.cpp"
#include "ExtensionPackageTemplateSelector.g.cpp"
#include "NavConstants.h"
#include "..\WinRTUtils\inc\Utils.h"
@@ -33,11 +35,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Extensions::OnNavigatedTo(const NavigationEventArgs& e)
{
_ViewModel = e.Parameter().as<Editor::ExtensionsViewModel>();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::ExtensionsViewModel>();
auto vmImpl = get_self<ExtensionsViewModel>(_ViewModel);
vmImpl->ExtensionPackageIdentifierTemplateSelector(_extensionPackageIdentifierTemplateSelector);
vmImpl->LazyLoadExtensions();
BringIntoViewWhenLoaded(args.ElementToFocus());
if (vmImpl->IsExtensionView())
{
const auto currentPkgVM = vmImpl->CurrentExtensionPackage();
@@ -452,6 +458,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
hstring ExtensionPackageViewModel::DisplayName() const noexcept
{
// Fragment extensions may not have a DisplayName, so fall back to Source
const auto displayName = _package.DisplayName();
return displayName.empty() ? _package.Source() : displayName;
}
hstring ExtensionPackageViewModel::Icon() const noexcept
{
// Fragment extensions may not have an Icon, so fall back to the extensions nav icon glyph
const auto icon = _package.Icon();
return icon.empty() ? NavTagIconMap[extensionsTag] : icon;
}
hstring ExtensionPackageViewModel::Scope() const noexcept
{
return _package.Scope() == Model::FragmentScope::User ? RS_(L"Extensions_ScopeUser") : RS_(L"Extensions_ScopeSystem");

View File

@@ -93,6 +93,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void UpdateSettings(const Model::CascadiaSettings& settings);
Model::ExtensionPackage Package() const noexcept { return _package; }
hstring DisplayName() const noexcept;
hstring Icon() const noexcept;
hstring Scope() const noexcept;
bool Enabled() const;
void Enabled(bool val);

View File

@@ -40,6 +40,8 @@ namespace Microsoft.Terminal.Settings.Editor
[default_interface] runtimeclass ExtensionPackageViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
Microsoft.Terminal.Settings.Model.ExtensionPackage Package { get; };
String DisplayName { get; };
String Icon { get; };
Boolean Enabled;
String Scope { get; };
String AccessibleName { get; };

View File

@@ -500,7 +500,8 @@
<muxc:Expander.Content>
<StackPanel>
<!-- Scope -->
<local:SettingContainer x:Uid="Extensions_Scope"
<local:SettingContainer x:Name="Scope"
x:Uid="Extensions_Scope"
Content="{x:Bind ViewModel.CurrentExtensionPackage.Scope, Mode=OneWay}"
IsTabStop="False"
Style="{StaticResource SettingContainerWithTextContent}" />

View File

@@ -3,6 +3,7 @@
#include "pch.h"
#include "GlobalAppearance.h"
#include "NavigateToPageArgs.g.h"
#include "GlobalAppearance.g.cpp"
#include <WtExeUtils.h>
@@ -23,7 +24,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void GlobalAppearance::OnNavigatedTo(const NavigationEventArgs& e)
{
_ViewModel = e.Parameter().as<Editor::GlobalAppearanceViewModel>();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::GlobalAppearanceViewModel>();
BringIntoViewWhenLoaded(args.ElementToFocus());
TraceLoggingWrite(
g_hTerminalSettingsEditorProvider,

View File

@@ -5,7 +5,7 @@ import "GlobalAppearanceViewModel.idl";
namespace Microsoft.Terminal.Settings.Editor
{
[default_interface] runtimeclass GlobalAppearance : Windows.UI.Xaml.Controls.Page
runtimeclass GlobalAppearance : Windows.UI.Xaml.Controls.Page
{
GlobalAppearance();
GlobalAppearanceViewModel ViewModel { get; };

View File

@@ -28,7 +28,8 @@
<StackPanel Style="{StaticResource SettingsStackStyle}">
<!-- Theme -->
<local:SettingContainer x:Uid="Globals_Theme">
<local:SettingContainer x:Name="Theme"
x:Uid="Globals_Theme">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemsSource="{x:Bind ViewModel.ThemeList, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.CurrentTheme, Mode=TwoWay}"
@@ -42,7 +43,8 @@
</local:SettingContainer>
<!-- Position of new tab -->
<local:SettingContainer x:Uid="Globals_NewTabPosition">
<local:SettingContainer x:Name="NewTabPosition"
x:Uid="Globals_NewTabPosition">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.NewTabPositionList, Mode=OneWay}"
@@ -51,45 +53,52 @@
</local:SettingContainer>
<!-- Show Titlebar -->
<local:SettingContainer x:Uid="Globals_ShowTitlebar">
<local:SettingContainer x:Name="ShowTitlebar"
x:Uid="Globals_ShowTitlebar">
<ToggleSwitch IsOn="{x:Bind ViewModel.ShowTabsInTitlebar, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}"
Toggled="{x:Bind ViewModel.ShowTitlebarToggled}" />
</local:SettingContainer>
<!-- Always show tabs -->
<local:SettingContainer x:Uid="Globals_AlwaysShowTabs">
<local:SettingContainer x:Name="AlwaysShowTabs"
x:Uid="Globals_AlwaysShowTabs">
<ToggleSwitch IsEnabled="{x:Bind mtu:Converters.InvertBoolean(ViewModel.ShowTabsInTitlebar), Mode=OneWay}"
IsOn="{x:Bind ViewModel.AlwaysShowTabs, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Show tabs in full screen -->
<local:SettingContainer x:Uid="Globals_ShowTabsFullscreen">
<local:SettingContainer x:Name="ShowTabsFullscreen"
x:Uid="Globals_ShowTabsFullscreen">
<ToggleSwitch IsOn="{x:Bind ViewModel.ShowTabsFullscreen, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Show Acrylic in Tab Row -->
<local:SettingContainer x:Uid="Globals_AcrylicTabRow">
<local:SettingContainer x:Name="AcrylicTabRow"
x:Uid="Globals_AcrylicTabRow">
<ToggleSwitch IsOn="{x:Bind ViewModel.UseAcrylicInTabRow, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Show Title in Titlebar -->
<local:SettingContainer x:Uid="Globals_ShowTitleInTitlebar">
<local:SettingContainer x:Name="ShowTitleInTitlebar"
x:Uid="Globals_ShowTitleInTitlebar">
<ToggleSwitch IsOn="{x:Bind ViewModel.ShowTitleInTitlebar, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Always on Top -->
<local:SettingContainer x:Uid="Globals_AlwaysOnTop">
<local:SettingContainer x:Name="AlwaysOnTop"
x:Uid="Globals_AlwaysOnTop">
<ToggleSwitch IsOn="{x:Bind ViewModel.AlwaysOnTop, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Tab Width Mode -->
<local:SettingContainer x:Uid="Globals_TabWidthMode">
<local:SettingContainer x:Name="TabWidthMode"
x:Uid="Globals_TabWidthMode">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.TabWidthModeList, Mode=OneWay}"
@@ -99,37 +108,43 @@
<!-- Disable Animations -->
<!-- NOTE: the UID is "DisablePaneAnimationsReversed" not "DisablePaneAnimations". See GH#9124 for more details. -->
<local:SettingContainer x:Uid="Globals_DisableAnimationsReversed">
<local:SettingContainer x:Name="DisableAnimations"
x:Uid="Globals_DisableAnimationsReversed">
<ToggleSwitch IsOn="{x:Bind ViewModel.InvertedDisableAnimations, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Always Show Notification Icon -->
<local:SettingContainer x:Uid="Globals_AlwaysShowNotificationIcon">
<local:SettingContainer x:Name="AlwaysShowNotificationIcon"
x:Uid="Globals_AlwaysShowNotificationIcon">
<ToggleSwitch IsOn="{x:Bind ViewModel.AlwaysShowNotificationIcon, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Minimize To Notification Area -->
<local:SettingContainer x:Uid="Globals_MinimizeToNotificationArea">
<local:SettingContainer x:Name="MinimizeToNotificationArea"
x:Uid="Globals_MinimizeToNotificationArea">
<ToggleSwitch IsOn="{x:Bind ViewModel.MinimizeToNotificationArea, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Automatically hide window -->
<local:SettingContainer x:Uid="Globals_AutoHideWindow">
<local:SettingContainer x:Name="AutoHideWindow"
x:Uid="Globals_AutoHideWindow">
<ToggleSwitch IsOn="{x:Bind ViewModel.AutoHideWindow, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Show Admin Shield -->
<local:SettingContainer x:Uid="Globals_ShowAdminShield">
<local:SettingContainer x:Name="ShowAdminShield"
x:Uid="Globals_ShowAdminShield">
<ToggleSwitch IsOn="{x:Bind ViewModel.ShowAdminShield, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Enable Unfocused Acrylic -->
<local:SettingContainer x:Uid="Globals_EnableUnfocusedAcrylic">
<local:SettingContainer x:Name="EnableUnfocusedAcrylic"
x:Uid="Globals_EnableUnfocusedAcrylic">
<ToggleSwitch IsOn="{x:Bind ViewModel.EnableUnfocusedAcrylic, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>

View File

@@ -4,6 +4,7 @@
#include "pch.h"
#include "Interaction.h"
#include "Interaction.g.cpp"
#include "NavigateToPageArgs.g.h"
#include "EnumEntry.h"
@@ -21,7 +22,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Interaction::OnNavigatedTo(const NavigationEventArgs& e)
{
_ViewModel = e.Parameter().as<Editor::InteractionViewModel>();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::InteractionViewModel>();
BringIntoViewWhenLoaded(args.ElementToFocus());
TraceLoggingWrite(
g_hTerminalSettingsEditorProvider,

View File

@@ -27,13 +27,15 @@
<StackPanel>
<StackPanel Style="{StaticResource SettingsStackStyle}">
<!-- Copy On Select -->
<local:SettingContainer x:Uid="Globals_CopyOnSelect">
<local:SettingContainer x:Name="CopyOnSelect"
x:Uid="Globals_CopyOnSelect">
<ToggleSwitch IsOn="{x:Bind ViewModel.CopyOnSelect, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Copy Format -->
<local:SettingContainer x:Uid="Globals_CopyFormat">
<local:SettingContainer x:Name="CopyFormat"
x:Uid="Globals_CopyFormat">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.CopyFormatList, Mode=OneWay}"
@@ -42,19 +44,22 @@
</local:SettingContainer>
<!-- Trim Block Selection -->
<local:SettingContainer x:Uid="Globals_TrimBlockSelection">
<local:SettingContainer x:Name="TrimBlockSelection"
x:Uid="Globals_TrimBlockSelection">
<ToggleSwitch IsOn="{x:Bind ViewModel.TrimBlockSelection, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Trim Paste -->
<local:SettingContainer x:Uid="Globals_TrimPaste">
<local:SettingContainer x:Name="TrimPaste"
x:Uid="Globals_TrimPaste">
<ToggleSwitch IsOn="{x:Bind ViewModel.TrimPaste, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Word Delimiters -->
<local:SettingContainer x:Uid="Globals_WordDelimiters"
<local:SettingContainer x:Name="WordDelimiters"
x:Uid="Globals_WordDelimiters"
CurrentValue="{x:Bind ViewModel.WordDelimiters, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyle}">
<TextBox IsSpellCheckEnabled="False"
@@ -63,13 +68,15 @@
</local:SettingContainer>
<!-- Snap On Resize -->
<local:SettingContainer x:Uid="Globals_SnapToGridOnResize">
<local:SettingContainer x:Name="SnapToGridOnResize"
x:Uid="Globals_SnapToGridOnResize">
<ToggleSwitch IsOn="{x:Bind ViewModel.SnapToGridOnResize, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Tab Switcher Mode -->
<local:SettingContainer x:Uid="Globals_TabSwitcherMode">
<local:SettingContainer x:Name="TabSwitcherMode"
x:Uid="Globals_TabSwitcherMode">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.TabSwitcherModeList}"
@@ -78,31 +85,36 @@
</local:SettingContainer>
<!-- Focus Follow Mouse Mode -->
<local:SettingContainer x:Uid="Globals_FocusFollowMouse">
<local:SettingContainer x:Name="FocusFollowMouse"
x:Uid="Globals_FocusFollowMouse">
<ToggleSwitch IsOn="{x:Bind ViewModel.FocusFollowMouse, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Enable Font Size Changes with Scrolling -->
<local:SettingContainer x:Uid="Globals_ScrollToZoom">
<local:SettingContainer x:Name="ScrollToZoom"
x:Uid="Globals_ScrollToZoom">
<ToggleSwitch IsOn="{x:Bind ViewModel.ScrollToZoom, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Enable Window Opacity Changes with Scrolling -->
<local:SettingContainer x:Uid="Globals_ScrollToChangeOpacity">
<local:SettingContainer x:Name="ScrollToChangeOpacity"
x:Uid="Globals_ScrollToChangeOpacity">
<ToggleSwitch IsOn="{x:Bind ViewModel.ScrollToChangeOpacity, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Detect URLs -->
<local:SettingContainer x:Uid="Globals_DetectURLs">
<local:SettingContainer x:Name="DetectURLs"
x:Uid="Globals_DetectURLs">
<ToggleSwitch IsOn="{x:Bind ViewModel.DetectURLs, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Search Web Default Query URL -->
<local:SettingContainer x:Uid="Globals_SearchWebDefaultQueryUrl"
<local:SettingContainer x:Name="SearchWebDefaultQueryUrl"
x:Uid="Globals_SearchWebDefaultQueryUrl"
CurrentValue="{x:Bind ViewModel.SearchWebDefaultQueryUrl, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyle}">
<TextBox IsSpellCheckEnabled="False"
@@ -111,7 +123,8 @@
</local:SettingContainer>
<!-- Enable Color Selection -->
<local:SettingContainer x:Uid="Globals_EnableColorSelection">
<local:SettingContainer x:Name="EnableColorSelection"
x:Uid="Globals_EnableColorSelection">
<ToggleSwitch IsOn="{x:Bind ViewModel.EnableColorSelection, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
@@ -123,25 +136,29 @@
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Close All Tabs Warning -->
<local:SettingContainer x:Uid="Globals_ConfirmCloseAllTabs">
<local:SettingContainer x:Name="ConfirmCloseAllTabs"
x:Uid="Globals_ConfirmCloseAllTabs">
<ToggleSwitch IsOn="{x:Bind ViewModel.ConfirmCloseAllTabs, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Input Service Warning -->
<local:SettingContainer x:Uid="Globals_InputServiceWarning">
<local:SettingContainer x:Name="InputServiceWarning"
x:Uid="Globals_InputServiceWarning">
<ToggleSwitch IsOn="{x:Bind ViewModel.InputServiceWarning, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Large Paste Warning -->
<local:SettingContainer x:Uid="Globals_WarnAboutLargePaste">
<local:SettingContainer x:Name="WarnAboutLargePaste"
x:Uid="Globals_WarnAboutLargePaste">
<ToggleSwitch IsOn="{x:Bind ViewModel.WarnAboutLargePaste, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Multi Line Paste Warning -->
<local:SettingContainer x:Uid="Globals_WarnAboutMultiLinePaste">
<local:SettingContainer x:Name="WarnAboutMultiLinePaste"
x:Uid="Globals_WarnAboutMultiLinePaste">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind WarnAboutMultiLinePasteList}"

View File

@@ -38,7 +38,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Launch::OnNavigatedTo(const NavigationEventArgs& e)
{
_ViewModel = e.Parameter().as<Editor::LaunchViewModel>();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::LaunchViewModel>();
BringIntoViewWhenLoaded(args.ElementToFocus());
auto innerViewModel{ winrt::get_self<Editor::implementation::LaunchViewModel>(_ViewModel) };
/* coroutine dispatch */ innerViewModel->PrepareStartOnUserLoginSettings();

View File

@@ -44,9 +44,9 @@
<StackPanel>
<StackPanel Style="{StaticResource SettingsStackStyle}">
<!-- Default Profile -->
<local:SettingContainer x:Uid="Globals_DefaultProfile">
<ComboBox x:Name="DefaultProfile"
ItemsSource="{x:Bind ViewModel.DefaultProfiles}"
<local:SettingContainer x:Name="DefaultProfile"
x:Uid="Globals_DefaultProfile">
<ComboBox ItemsSource="{x:Bind ViewModel.DefaultProfiles}"
SelectedItem="{x:Bind ViewModel.CurrentDefaultProfile, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}">
<ComboBox.ItemTemplate>
@@ -141,7 +141,8 @@
</local:SettingContainer>
<!-- Language -->
<local:SettingContainer x:Uid="Globals_Language">
<local:SettingContainer x:Name="Language"
x:Uid="Globals_Language">
<ComboBox ItemsSource="{x:Bind ViewModel.LanguageList}"
SelectedItem="{x:Bind ViewModel.CurrentLanguage, Mode=TwoWay}"
Style="{StaticResource ComboBoxSettingStyle}">
@@ -154,7 +155,8 @@
</local:SettingContainer>
<!-- Language -->
<local:SettingContainer x:Uid="Globals_DefaultInputScope">
<local:SettingContainer x:Name="DefaultInputScope"
x:Uid="Globals_DefaultInputScope">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.DefaultInputScopeList}"
@@ -163,7 +165,8 @@
</local:SettingContainer>
<!-- Start on User Login -->
<local:SettingContainer x:Uid="Globals_StartOnUserLogin"
<local:SettingContainer x:Name="StartOnUserLogin"
x:Uid="Globals_StartOnUserLogin"
HelpText="{x:Bind ViewModel.StartOnUserLoginStatefulHelpText, Mode=OneWay}"
Visibility="{x:Bind ViewModel.StartOnUserLoginAvailable, Mode=OneTime}">
<ToggleSwitch IsEnabled="{x:Bind ViewModel.StartOnUserLoginConfigurable, Mode=OneWay}"
@@ -172,7 +175,8 @@
</local:SettingContainer>
<!-- First Window Behavior -->
<local:SettingContainer x:Uid="Globals_FirstWindowPreference">
<local:SettingContainer x:Name="FirstWindowPreference"
x:Uid="Globals_FirstWindowPreference">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.FirstWindowPreferenceList}"
@@ -181,7 +185,8 @@
</local:SettingContainer>
<!-- Windowing Behavior -->
<local:SettingContainer x:Uid="Globals_WindowingBehavior">
<local:SettingContainer x:Name="WindowingBehavior"
x:Uid="Globals_WindowingBehavior">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.WindowingBehaviorList}"
@@ -190,7 +195,8 @@
</local:SettingContainer>
<!-- Launch Size -->
<local:SettingContainer x:Uid="Globals_LaunchSize"
<local:SettingContainer x:Name="LaunchSize"
x:Uid="Globals_LaunchSize"
CurrentValue="{x:Bind ViewModel.LaunchSizeCurrentValue, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyle}">
<Grid ColumnSpacing="12"
@@ -233,7 +239,8 @@
</local:SettingContainer>
<!-- Launch Parameters -->
<local:SettingContainer x:Uid="Globals_LaunchParameters"
<local:SettingContainer x:Name="LaunchParameters"
x:Uid="Globals_LaunchParameters"
CurrentValue="{x:Bind ViewModel.LaunchParametersCurrentValue, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyle}">
<Grid RowSpacing="8">

View File

@@ -15,14 +15,18 @@
#include "GlobalAppearance.h"
#include "GlobalAppearanceViewModel.h"
#include "ColorSchemes.h"
#include "EditColorScheme.h"
#include "AddProfile.h"
#include "InteractionViewModel.h"
#include "LaunchViewModel.h"
#include "NewTabMenuViewModel.h"
#include "NewTabMenu.h"
#include "NavConstants.h"
#include "..\types\inc\utils.hpp"
#include <..\WinRTUtils\inc\Utils.h>
#include <dwmapi.h>
#include <fmt/compile.h>
namespace winrt
{
@@ -38,29 +42,45 @@ using namespace winrt::Windows::System;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::Foundation::Collections;
static const std::wstring_view openJsonTag{ L"OpenJson_Nav" };
static const std::wstring_view launchTag{ L"Launch_Nav" };
static const std::wstring_view interactionTag{ L"Interaction_Nav" };
static const std::wstring_view renderingTag{ L"Rendering_Nav" };
static const std::wstring_view compatibilityTag{ L"Compatibility_Nav" };
static const std::wstring_view actionsTag{ L"Actions_Nav" };
static const std::wstring_view newTabMenuTag{ L"NewTabMenu_Nav" };
static const std::wstring_view extensionsTag{ L"Extensions_Nav" };
static const std::wstring_view globalProfileTag{ L"GlobalProfile_Nav" };
static const std::wstring_view addProfileTag{ L"AddProfile" };
static const std::wstring_view colorSchemesTag{ L"ColorSchemes_Nav" };
static const std::wstring_view globalAppearanceTag{ L"GlobalAppearance_Nav" };
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
static WUX::Controls::FontIcon _fontIconForNavTag(const std::wstring_view navTag)
{
WUX::Controls::FontIcon icon{};
icon.Glyph(NavTagIconMap[navTag]);
icon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
icon.FontSize(16);
return icon;
}
static Editor::ProfileViewModel _viewModelForProfile(const Model::Profile& profile, const Model::CascadiaSettings& appSettings, const Windows::UI::Core::CoreDispatcher& dispatcher)
{
return winrt::make<implementation::ProfileViewModel>(profile, appSettings, dispatcher);
}
static ProfileSubPage ProfileSubPageFromBreadcrumb(BreadcrumbSubPage subPage)
{
switch (subPage)
{
case BreadcrumbSubPage::None:
return ProfileSubPage::Base;
case BreadcrumbSubPage::Profile_Appearance:
return ProfileSubPage::Appearance;
case BreadcrumbSubPage::Profile_Terminal:
return ProfileSubPage::Terminal;
case BreadcrumbSubPage::Profile_Advanced:
return ProfileSubPage::Advanced;
default:
// This should never happen
assert(false);
return ProfileSubPage::Base;
}
}
MainPage::MainPage(const CascadiaSettings& settings) :
_settingsSource{ settings },
_settingsClone{ settings.Copy() }
_settingsClone{ settings.Copy() },
_profileVMs{ single_threaded_observable_vector<Editor::ProfileViewModel>() }
{
InitializeComponent();
_UpdateBackgroundForMica();
@@ -92,16 +112,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
const auto settingName{ args.PropertyName() };
if (settingName == L"CurrentPage")
{
// extract ElementToFocus and clear it; we only want to use it once
auto vmImpl = get_self<ColorSchemesPageViewModel>(_colorSchemesPageVM);
const auto elementToFocus = vmImpl->ElementToFocus();
vmImpl->ElementToFocus({});
const auto currentScheme = _colorSchemesPageVM.CurrentScheme();
if (_colorSchemesPageVM.CurrentPage() == ColorSchemesSubPage::EditColorScheme && currentScheme)
{
contentFrame().Navigate(xaml_typename<Editor::EditColorScheme>(), currentScheme);
contentFrame().Navigate(xaml_typename<Editor::EditColorScheme>(), winrt::make<NavigateToPageArgs>(currentScheme, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(colorSchemesTag), currentScheme.Name(), BreadcrumbSubPage::ColorSchemes_Edit);
_breadcrumbs.Append(crumb);
}
else if (_colorSchemesPageVM.CurrentPage() == ColorSchemesSubPage::Base)
{
_Navigate(winrt::hstring{ colorSchemesTag }, BreadcrumbSubPage::None);
_Navigate(winrt::hstring{ colorSchemesTag }, BreadcrumbSubPage::None, elementToFocus);
}
}
else if (settingName == L"CurrentSchemeName")
@@ -141,9 +166,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
if (const auto& currentExtensionPackage = _extensionsVM.CurrentExtensionPackage())
{
const auto& pkg = currentExtensionPackage.Package();
const auto label = pkg.DisplayName().empty() ? pkg.Source() : pkg.DisplayName();
const auto crumb = winrt::make<Breadcrumb>(box_value(currentExtensionPackage), label, BreadcrumbSubPage::Extensions_Extension);
const auto crumb = winrt::make<Breadcrumb>(box_value(currentExtensionPackage), currentExtensionPackage.DisplayName(), BreadcrumbSubPage::Extensions_Extension);
_breadcrumbs.Append(crumb);
SettingsMainPage_ScrollViewer().ScrollToVerticalOffset(0);
}
@@ -154,7 +177,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
const auto crumb = winrt::make<Breadcrumb>(box_value(extensionsTag), RS_(L"Nav_Extensions/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
}
contentFrame().Navigate(xaml_typename<Editor::Extensions>(), _extensionsVM);
contentFrame().Navigate(xaml_typename<Editor::Extensions>(), winrt::make<NavigateToPageArgs>(_extensionsVM, *this));
}
});
@@ -162,6 +185,19 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// that VM into the appearance VMs within the profiles
_InitializeProfilesList();
// Apply icons to static nav items
LaunchNavItem().Icon(_fontIconForNavTag(launchTag));
InteractionNavItem().Icon(_fontIconForNavTag(interactionTag));
AppearanceNavItem().Icon(_fontIconForNavTag(globalAppearanceTag));
ColorSchemesNavItem().Icon(_fontIconForNavTag(colorSchemesTag));
RenderingNavItem().Icon(_fontIconForNavTag(renderingTag));
CompatibilityNavItem().Icon(_fontIconForNavTag(compatibilityTag));
ActionsNavItem().Icon(_fontIconForNavTag(actionsTag));
NewTabMenuNavItem().Icon(_fontIconForNavTag(newTabMenuTag));
ExtensionsNavItem().Icon(_fontIconForNavTag(extensionsTag));
BaseLayerMenuItem().Icon(_fontIconForNavTag(globalProfileTag));
OpenJsonNavItem().Icon(_fontIconForNavTag(openJsonTag));
Automation::AutomationProperties::SetHelpText(SaveButton(), RS_(L"Settings_SaveSettingsButton/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
Automation::AutomationProperties::SetHelpText(ResetButton(), RS_(L"Settings_ResetSettingsButton/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
Automation::AutomationProperties::SetHelpText(OpenJsonNavItem(), RS_(L"Nav_OpenJSON/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"));
@@ -180,6 +216,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WUX::Controls::ToolTipService::SetToolTip(OpenJsonNavItem(), box_value(RS_(L"Nav_OpenJSON/Content")));
_breadcrumbs = single_threaded_observable_vector<IInspectable>();
_UpdateSearchIndex();
extensionsVMImpl->LazyLoadExtensions();
}
// Method Description:
@@ -218,11 +256,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// Repopulate profile-related menu items
_InitializeProfilesList();
// Update the Nav State with the new version of the settings
_colorSchemesPageVM.UpdateSettings(_settingsClone);
_actionsVM.UpdateSettings(_settingsClone);
_newTabMenuPageVM.UpdateSettings(_settingsClone);
_extensionsVM.UpdateSettings(_settingsClone, _colorSchemesPageVM);
_profileDefaultsVM = nullptr; // Lazy-loaded upon navigation
// We'll update the profile in the _profilesNavState whenever we actually navigate to one
@@ -243,8 +283,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (stringTag == breadcrumbStringTag)
{
// found the one that was selected before the refresh
SettingsNav().SelectedItem(item);
_Navigate(*breadcrumbStringTag, crumb->SubPage());
SettingsNav().SelectedItem(item);
return;
}
}
@@ -254,8 +294,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
// navigate to the NewTabMenu page,
// _Navigate() will handle trying to find the right subpage
SettingsNav().SelectedItem(item);
_Navigate(breadcrumbFolderEntry, BreadcrumbSubPage::NewTabMenu_Folder);
SettingsNav().SelectedItem(item);
return;
}
}
@@ -265,8 +305,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
// navigate to the Extensions page,
// _Navigate() will handle trying to find the right subpage
SettingsNav().SelectedItem(item);
_Navigate(breadcrumbExtensionPackage, BreadcrumbSubPage::Extensions_Extension);
SettingsNav().SelectedItem(item);
return;
}
}
@@ -278,8 +318,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (profileTag->OriginalProfileGuid() == breadcrumbProfileTag->OriginalProfileGuid())
{
// found the one that was selected before the refresh
SettingsNav().SelectedItem(item);
_Navigate(*profileTag, crumb->SubPage());
SettingsNav().SelectedItem(item);
return;
}
}
@@ -293,8 +333,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// This happens when the selected item was a profile which doesn't exist in the new configuration
// We can use menuItemsSTL here because the only things they miss are profile entries.
const auto& firstItem{ _menuItemSource.GetAt(0).as<MUX::Controls::NavigationViewItem>() };
SettingsNav().SelectedItem(firstItem);
_Navigate(unbox_value<hstring>(firstItem.Tag()), BreadcrumbSubPage::None);
SettingsNav().SelectedItem(firstItem);
_UpdateSearchIndex();
}
void MainPage::SetHostingWindow(uint64_t hostingWindow) noexcept
@@ -455,31 +497,37 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
const auto settingName{ args.PropertyName() };
if (settingName == L"CurrentPage")
{
// extract ElementToFocus and clear it; we only want to use it once
auto vmImpl = get_self<ProfileViewModel>(profile);
const auto elementToFocus = vmImpl->ElementToFocus();
vmImpl->ElementToFocus({});
const auto currentPage = profile.CurrentPage();
if (currentPage == ProfileSubPage::Base)
{
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), winrt::make<NavigateToPageArgs>(profile, *this, elementToFocus));
_breadcrumbs.Clear();
const auto crumb = winrt::make<Breadcrumb>(breadcrumbTag, breadcrumbText, BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsMainPage_ScrollViewer().ScrollToVerticalOffset(0);
}
else if (currentPage == ProfileSubPage::Appearance)
{
contentFrame().Navigate(xaml_typename<Editor::Profiles_Appearance>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
contentFrame().Navigate(xaml_typename<Editor::Profiles_Appearance>(), winrt::make<NavigateToPageArgs>(profile, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(breadcrumbTag, RS_(L"Profile_Appearance/Header"), BreadcrumbSubPage::Profile_Appearance);
_breadcrumbs.Append(crumb);
SettingsMainPage_ScrollViewer().ScrollToVerticalOffset(0);
}
else if (currentPage == ProfileSubPage::Terminal)
{
contentFrame().Navigate(xaml_typename<Editor::Profiles_Terminal>(), profile);
contentFrame().Navigate(xaml_typename<Editor::Profiles_Terminal>(), winrt::make<NavigateToPageArgs>(profile, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(breadcrumbTag, RS_(L"Profile_Terminal/Header"), BreadcrumbSubPage::Profile_Terminal);
_breadcrumbs.Append(crumb);
SettingsMainPage_ScrollViewer().ScrollToVerticalOffset(0);
}
else if (currentPage == ProfileSubPage::Advanced)
{
contentFrame().Navigate(xaml_typename<Editor::Profiles_Advanced>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
contentFrame().Navigate(xaml_typename<Editor::Profiles_Advanced>(), winrt::make<NavigateToPageArgs>(profile, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(breadcrumbTag, RS_(L"Profile_Advanced/Header"), BreadcrumbSubPage::Profile_Advanced);
_breadcrumbs.Append(crumb);
SettingsMainPage_ScrollViewer().ScrollToVerticalOffset(0);
@@ -488,39 +536,44 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
});
}
void MainPage::_Navigate(hstring clickedItemTag, BreadcrumbSubPage subPage)
void MainPage::_Navigate(hstring clickedItemTag, BreadcrumbSubPage subPage, hstring elementToFocus)
{
_PreNavigateHelper();
if (clickedItemTag == launchTag)
{
contentFrame().Navigate(xaml_typename<Editor::Launch>(), winrt::make<LaunchViewModel>(_settingsClone));
contentFrame().Navigate(xaml_typename<Editor::Launch>(), winrt::make<NavigateToPageArgs>(winrt::make<LaunchViewModel>(_settingsClone), *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Launch/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsNav().SelectedItem(LaunchNavItem());
}
else if (clickedItemTag == interactionTag)
{
contentFrame().Navigate(xaml_typename<Editor::Interaction>(), winrt::make<InteractionViewModel>(_settingsClone.GlobalSettings()));
contentFrame().Navigate(xaml_typename<Editor::Interaction>(), winrt::make<NavigateToPageArgs>(winrt::make<InteractionViewModel>(_settingsClone.GlobalSettings()), *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Interaction/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsNav().SelectedItem(InteractionNavItem());
}
else if (clickedItemTag == renderingTag)
{
contentFrame().Navigate(xaml_typename<Editor::Rendering>(), winrt::make<RenderingViewModel>(_settingsClone));
contentFrame().Navigate(xaml_typename<Editor::Rendering>(), winrt::make<NavigateToPageArgs>(winrt::make<RenderingViewModel>(_settingsClone), *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Rendering/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsNav().SelectedItem(RenderingNavItem());
}
else if (clickedItemTag == compatibilityTag)
{
contentFrame().Navigate(xaml_typename<Editor::Compatibility>(), winrt::make<CompatibilityViewModel>(_settingsClone));
contentFrame().Navigate(xaml_typename<Editor::Compatibility>(), winrt::make<NavigateToPageArgs>(winrt::make<CompatibilityViewModel>(_settingsClone), *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Compatibility/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsNav().SelectedItem(CompatibilityNavItem());
}
else if (clickedItemTag == actionsTag)
{
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Actions/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
contentFrame().Navigate(xaml_typename<Editor::Actions>(), _actionsVM);
contentFrame().Navigate(xaml_typename<Editor::Actions>(), winrt::make<NavigateToPageArgs>(_actionsVM, *this, elementToFocus));
SettingsNav().SelectedItem(ActionsNavItem());
if (subPage == BreadcrumbSubPage::Actions_Edit && _actionsVM.CurrentCommand() != nullptr)
{
@@ -538,10 +591,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
else
{
// Navigate to the NewTabMenu page
contentFrame().Navigate(xaml_typename<Editor::NewTabMenu>(), winrt::make<NavigateToPageArgs>(_newTabMenuPageVM, *this));
contentFrame().Navigate(xaml_typename<Editor::NewTabMenu>(), winrt::make<NavigateToPageArgs>(_newTabMenuPageVM, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_NewTabMenu/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
}
SettingsNav().SelectedItem(NewTabMenuNavItem());
}
else if (clickedItemTag == extensionsTag)
{
@@ -553,42 +607,52 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else
{
contentFrame().Navigate(xaml_typename<Editor::Extensions>(), _extensionsVM);
contentFrame().Navigate(xaml_typename<Editor::Extensions>(), winrt::make<NavigateToPageArgs>(_extensionsVM, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Extensions/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
}
SettingsNav().SelectedItem(ExtensionsNavItem());
}
else if (clickedItemTag == globalProfileTag)
{
auto profileVM{ _viewModelForProfile(_settingsClone.ProfileDefaults(), _settingsClone, Dispatcher()) };
profileVM.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
profileVM.IsBaseLayer(true);
// lazy load profile defaults VM
if (!_profileDefaultsVM)
{
_profileDefaultsVM = _viewModelForProfile(_settingsClone.ProfileDefaults(), _settingsClone, Dispatcher());
_profileDefaultsVM.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
_profileDefaultsVM.IsBaseLayer(true);
}
_SetupProfileEventHandling(profileVM);
_SetupProfileEventHandling(_profileDefaultsVM);
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), winrt::make<implementation::NavigateToProfileArgs>(profileVM, *this));
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), winrt::make<NavigateToPageArgs>(_profileDefaultsVM, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_ProfileDefaults/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsNav().SelectedItem(BaseLayerMenuItem());
// If we were given a label, make sure we are on the correct sub-page
if (subPage == BreadcrumbSubPage::Profile_Appearance)
// Pass along the element to focus to the ProfileViewModel.
// This will work as a staging area before we navigate to the correct sub-page
auto profileVMImpl = get_self<ProfileViewModel>(_profileDefaultsVM);
profileVMImpl->ElementToFocus(elementToFocus);
// Set the profile's 'CurrentPage' to the correct one, if this requires further navigation, the
// event handler will do it
const ProfileSubPage profileSubPage = ProfileSubPageFromBreadcrumb(subPage);
const bool needsForcedRefresh = _profileDefaultsVM.CurrentPage() == profileSubPage;
_profileDefaultsVM.CurrentPage(profileSubPage);
if (needsForcedRefresh)
{
profileVM.CurrentPage(ProfileSubPage::Appearance);
}
else if (subPage == BreadcrumbSubPage::Profile_Terminal)
{
profileVM.CurrentPage(ProfileSubPage::Terminal);
}
else if (subPage == BreadcrumbSubPage::Profile_Advanced)
{
profileVM.CurrentPage(ProfileSubPage::Advanced);
// If we're already on the correct sub-page, the PropertyChanged event won't fire.
// However, we still need to pass along the ElementToFocus, so we need to force a refresh.
profileVMImpl->ForceRefreshCurrentPage();
}
}
else if (clickedItemTag == colorSchemesTag)
{
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_ColorSchemes/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
contentFrame().Navigate(xaml_typename<Editor::ColorSchemes>(), _colorSchemesPageVM);
contentFrame().Navigate(xaml_typename<Editor::ColorSchemes>(), winrt::make<NavigateToPageArgs>(_colorSchemesPageVM, *this, elementToFocus));
SettingsNav().SelectedItem(ColorSchemesNavItem());
if (subPage == BreadcrumbSubPage::ColorSchemes_Edit)
{
@@ -597,17 +661,39 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else if (clickedItemTag == globalAppearanceTag)
{
contentFrame().Navigate(xaml_typename<Editor::GlobalAppearance>(), winrt::make<GlobalAppearanceViewModel>(_settingsClone.GlobalSettings()));
contentFrame().Navigate(xaml_typename<Editor::GlobalAppearance>(), winrt::make<NavigateToPageArgs>(winrt::make<GlobalAppearanceViewModel>(_settingsClone.GlobalSettings()), *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Appearance/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsNav().SelectedItem(AppearanceNavItem());
}
else if (clickedItemTag == addProfileTag)
{
auto addProfileState{ winrt::make<AddProfilePageNavigationState>(_settingsClone) };
auto addProfileState{ winrt::make<AddProfilePageNavigationState>(_settingsClone, elementToFocus) };
addProfileState.AddNew({ get_weak(), &MainPage::_AddProfileHandler });
contentFrame().Navigate(xaml_typename<Editor::AddProfile>(), addProfileState);
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_AddNewProfile/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
// Find the "Add new profile" menu item and select it
// It's likely at the very end of the list, so we'll search backwards
for (auto i = _menuItemSource.Size() - 1; i > 0; --i)
{
const auto& item = _menuItemSource.GetAt(i);
if (const auto& menuItem{ item.try_as<MUX::Controls::NavigationViewItem>() })
{
if (const auto& tag{ menuItem.Tag() })
{
if (const auto& stringTag{ tag.try_as<hstring>() })
{
if (*stringTag == addProfileTag)
{
SettingsNav().SelectedItem(item);
break;
}
}
}
}
}
}
}
@@ -616,7 +702,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// - NOTE: this does not update the selected item.
// Arguments:
// - profile - the profile object we are getting a view of
void MainPage::_Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage)
void MainPage::_Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage, hstring elementToFocus)
{
_PreNavigateHelper();
@@ -624,44 +710,74 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (profile.Orphaned())
{
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base_Orphaned>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base_Orphaned>(), winrt::make<NavigateToPageArgs>(profile, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(profile), profile.Name(), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
profile.CurrentPage(ProfileSubPage::Base);
return;
}
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base>(), winrt::make<NavigateToPageArgs>(profile, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(profile), profile.Name(), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
if (const auto profileNavItem = _FindProfileNavItem(profile.OriginalProfileGuid()))
{
SettingsNav().SelectedItem(profileNavItem);
}
// Pass along the element to focus to the ProfileViewModel.
// This will work as a staging area before we navigate to the correct sub-page
auto profileVMImpl = get_self<ProfileViewModel>(profile);
profileVMImpl->ElementToFocus(elementToFocus);
// Set the profile's 'CurrentPage' to the correct one, if this requires further navigation, the
// event handler will do it
if (subPage == BreadcrumbSubPage::None)
const ProfileSubPage profileSubPage = ProfileSubPageFromBreadcrumb(subPage);
const bool needsForcedRefresh = profile.CurrentPage() == profileSubPage;
profile.CurrentPage(profileSubPage);
if (needsForcedRefresh)
{
profile.CurrentPage(ProfileSubPage::Base);
}
else if (subPage == BreadcrumbSubPage::Profile_Appearance)
{
profile.CurrentPage(ProfileSubPage::Appearance);
}
else if (subPage == BreadcrumbSubPage::Profile_Terminal)
{
profile.CurrentPage(ProfileSubPage::Terminal);
}
else if (subPage == BreadcrumbSubPage::Profile_Advanced)
{
profile.CurrentPage(ProfileSubPage::Advanced);
// If we're already on the correct sub-page, the PropertyChanged event won't fire.
// However, we still need to pass along the ElementToFocus, so we need to force a refresh.
profileVMImpl->ForceRefreshCurrentPage();
}
}
void MainPage::_Navigate(const Editor::NewTabMenuEntryViewModel& ntmEntryVM, BreadcrumbSubPage subPage)
void MainPage::_Navigate(const Editor::ColorSchemeViewModel& colorSchemeVM, BreadcrumbSubPage subPage, hstring elementToFocus)
{
_PreNavigateHelper();
contentFrame().Navigate(xaml_typename<Editor::NewTabMenu>(), winrt::make<NavigateToPageArgs>(_newTabMenuPageVM, *this));
const auto crumb = winrt::make<Breadcrumb>(box_value(colorSchemesTag), RS_(L"Nav_ColorSchemes/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
contentFrame().Navigate(xaml_typename<Editor::ColorSchemes>(), winrt::make<NavigateToPageArgs>(_colorSchemesPageVM, *this, elementToFocus));
SettingsNav().SelectedItem(ColorSchemesNavItem());
// Pass along the element to focus to the ColorSchemesPageViewModel.
// This will work as a staging area before we navigate to EditColorScheme
get_self<ColorSchemesPageViewModel>(_colorSchemesPageVM)->ElementToFocus(elementToFocus);
// Set CurrentScheme BEFORE the CurrentPage!
// Doing so triggers the PropertyChanged event which performs the navigation to EditColorScheme
if (subPage == BreadcrumbSubPage::None)
{
_colorSchemesPageVM.CurrentScheme(nullptr);
_colorSchemesPageVM.CurrentPage(ColorSchemesSubPage::Base);
}
else
{
_colorSchemesPageVM.CurrentScheme(colorSchemeVM);
_colorSchemesPageVM.CurrentPage(ColorSchemesSubPage::EditColorScheme);
}
}
void MainPage::_Navigate(const Editor::NewTabMenuEntryViewModel& ntmEntryVM, BreadcrumbSubPage subPage, hstring elementToFocus)
{
_PreNavigateHelper();
contentFrame().Navigate(xaml_typename<Editor::NewTabMenu>(), winrt::make<NavigateToPageArgs>(_newTabMenuPageVM, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(newTabMenuTag), RS_(L"Nav_NewTabMenu/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsNav().SelectedItem(NewTabMenuNavItem());
if (subPage == BreadcrumbSubPage::None)
{
@@ -688,13 +804,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
void MainPage::_Navigate(const Editor::ExtensionPackageViewModel& extPkgVM, BreadcrumbSubPage subPage)
void MainPage::_Navigate(const Editor::ExtensionPackageViewModel& extPkgVM, BreadcrumbSubPage subPage, hstring elementToFocus)
{
_PreNavigateHelper();
contentFrame().Navigate(xaml_typename<Editor::Extensions>(), _extensionsVM);
contentFrame().Navigate(xaml_typename<Editor::Extensions>(), winrt::make<NavigateToPageArgs>(_extensionsVM, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(extensionsTag), RS_(L"Nav_Extensions/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsNav().SelectedItem(ExtensionsNavItem());
if (subPage == BreadcrumbSubPage::None)
{
@@ -708,9 +825,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (pkgVM.Package().Source() == extPkgVM.Package().Source())
{
// Take advantage of the PropertyChanged event to navigate
// to the correct extension package and build the breadcrumbs as we go
// to the correct extension package and build the breadcrumbs as we go.
const auto wasAlreadyOnExtension = (_extensionsVM.CurrentExtensionPackage() == pkgVM);
_extensionsVM.CurrentExtensionPackage(pkgVM);
found = true;
// If CurrentExtensionPackage was already this extension, PropertyChanged won't fire,
// so we add the breadcrumb manually.
if (wasAlreadyOnExtension)
{
const auto extCrumb = winrt::make<Breadcrumb>(box_value(pkgVM), pkgVM.DisplayName(), BreadcrumbSubPage::Extensions_Extension);
_breadcrumbs.Append(extCrumb);
}
break;
}
}
@@ -722,6 +848,38 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
void MainPage::_Navigate(const Editor::CommandViewModel& commandVM, BreadcrumbSubPage subPage, hstring elementToFocus)
{
_PreNavigateHelper();
contentFrame().Navigate(xaml_typename<Editor::Actions>(), winrt::make<NavigateToPageArgs>(_actionsVM, *this, elementToFocus));
const auto crumb = winrt::make<Breadcrumb>(box_value(actionsTag), RS_(L"Nav_Actions/Content"), BreadcrumbSubPage::None);
_breadcrumbs.Append(crumb);
SettingsNav().SelectedItem(ActionsNavItem());
if (subPage == BreadcrumbSubPage::None || !commandVM)
{
_actionsVM.CurrentCommand(nullptr);
}
else
{
// Take advantage of the PropertyChanged event to navigate
// to EditAction and build the breadcrumbs as we go.
const auto wasAlreadyEdit = (_actionsVM.CurrentPage() == ActionsSubPage::Edit);
_actionsVM.CurrentCommand(commandVM);
_actionsVM.CurrentPage(ActionsSubPage::Edit);
// If CurrentPage was already Edit, PropertyChanged won't fire,
// so we navigate and add breadcrumb manually.
if (wasAlreadyEdit)
{
contentFrame().Navigate(xaml_typename<Editor::EditAction>(), winrt::make<implementation::NavigateToCommandArgs>(commandVM, *this));
const auto editCrumb = winrt::make<Breadcrumb>(box_value(actionsTag), RS_(L"Nav_EditAction/Content"), BreadcrumbSubPage::Actions_Edit);
_breadcrumbs.Append(editCrumb);
}
}
}
void MainPage::SaveButton_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
{
_settingsClone.LogSettingChanges(false);
@@ -774,10 +932,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_MoveXamlParsedNavItemsIntoItemSource();
}
// Manually create a NavigationViewItem for each profile
// Manually create a NavigationViewItem and view model for each profile
// and keep a reference to them in a map so that we
// can easily modify the correct one when the associated
// profile changes.
_profileVMs.Clear();
for (const auto& profile : _settingsClone.AllProfiles())
{
if (!profile.Deleted())
@@ -798,7 +957,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
FontIcon icon;
// This is the "Add" symbol
icon.Glyph(L"\xE710");
icon.Glyph(NavTagIconMap[addProfileTag]);
addProfileItem.Icon(icon);
_menuItemSource.Append(addProfileItem);
@@ -850,8 +1009,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
// Select and navigate to the new profile
SettingsNav().SelectedItem(navItem);
_Navigate(profileViewModel, BreadcrumbSubPage::None);
SettingsNav().SelectedItem(navItem);
}
static MUX::Controls::InfoBadge _createGlyphIconBadge(wil::zwstring_view glyph)
@@ -907,6 +1066,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
// Add an event handler for when the user wants to delete a profile.
profile.DeleteProfileRequested({ this, &MainPage::_DeleteProfile });
// Register the VM so that it appears in the search index
_profileVMs.Append(profile);
return profileNavItem;
}
@@ -932,9 +1094,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_menuItemSource.IndexOf(selectedItem, index);
_menuItemSource.RemoveAt(index);
// Remove it from the list of VMs
auto profileVM = selectedItem.as<MUX::Controls::NavigationViewItem>().Tag().as<Editor::ProfileViewModel>();
uint32_t vmIndex;
if (_menuItemSource.IndexOf(profileVM, vmIndex))
{
_profileVMs.RemoveAt(vmIndex);
}
// navigate to the profile next to this one
const auto newSelectedItem{ _menuItemSource.GetAt(index < _menuItemSource.Size() - 1 ? index : index - 1) };
SettingsNav().SelectedItem(newSelectedItem);
const auto newTag = newSelectedItem.as<MUX::Controls::NavigationViewItem>().Tag();
if (const auto profileViewModel = newTag.try_as<ProfileViewModel>())
{
@@ -947,6 +1116,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
// Since we are navigating to a new profile after deletion, scroll up to the top
SettingsMainPage_ScrollViewer().ChangeView(nullptr, 0.0, nullptr);
SettingsNav().SelectedItem(newSelectedItem);
}
}
@@ -956,6 +1126,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
void MainPage::_NavigateToProfileHandler(const IInspectable& /*sender*/, winrt::guid profileGuid)
{
if (const auto profileNavItem = _FindProfileNavItem(profileGuid))
{
_Navigate(profileNavItem.Tag().as<Editor::ProfileViewModel>(), BreadcrumbSubPage::None);
}
// Silently fail if the profile wasn't found
}
MUX::Controls::NavigationViewItem MainPage::_FindProfileNavItem(winrt::guid profileGuid) const
{
for (auto&& menuItem : _menuItemSource)
{
@@ -967,21 +1146,19 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
if (profileTag->OriginalProfileGuid() == profileGuid)
{
SettingsNav().SelectedItem(menuItem);
_Navigate(*profileTag, BreadcrumbSubPage::None);
return;
return navViewItem;
}
}
}
}
}
// Silently fail if the profile wasn't found
return nullptr;
}
void MainPage::_NavigateToColorSchemeHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/)
{
SettingsNav().SelectedItem(ColorSchemesNavItem());
_Navigate(hstring{ colorSchemesTag }, BreadcrumbSubPage::ColorSchemes_Edit);
SettingsNav().SelectedItem(ColorSchemesNavItem());
}
winrt::Windows::UI::Xaml::Media::Brush MainPage::BackgroundBrush()
@@ -1045,4 +1222,121 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
safe_void_coroutine MainPage::SettingsSearchBox_TextChanged(const AutoSuggestBox& sender, const AutoSuggestBoxTextChangedEventArgs& args)
{
if (args.Reason() != AutoSuggestionBoxTextChangeReason::UserInput)
{
// Only respond to user input, not programmatic text changes
co_return;
}
// remove leading spaces
std::wstring queryW{ sender.Text() };
const auto firstNonSpace{ queryW.find_first_not_of(L' ') };
if (firstNonSpace == std::wstring::npos)
{
// only spaces
const auto& searchBox = SettingsSearchBox();
searchBox.ItemsSource(nullptr);
searchBox.IsSuggestionListOpen(false);
co_return;
}
const hstring sanitizedQuery{ queryW.substr(firstNonSpace) };
if (sanitizedQuery.empty())
{
// empty query
const auto& searchBox = SettingsSearchBox();
searchBox.ItemsSource(nullptr);
searchBox.IsSuggestionListOpen(false);
co_return;
}
if (_currentSearch)
{
// a newer search has started, abandon this one
_currentSearch.Cancel();
co_return;
}
_currentSearch = SearchIndex::Instance().SearchAsync(sanitizedQuery,
_profileVMs.GetView(),
get_self<implementation::NewTabMenuViewModel>(_newTabMenuPageVM)->FolderTreeFlatList().GetView(),
_colorSchemesPageVM.AllColorSchemes().GetView(),
_extensionsVM.ExtensionPackages().GetView(),
_actionsVM.CommandList().GetView());
const auto results = co_await _currentSearch;
_currentSearch = nullptr;
// Update the UI with the results
const auto& searchBox = SettingsSearchBox();
searchBox.ItemsSource(results);
searchBox.IsSuggestionListOpen(true);
}
void MainPage::SettingsSearchBox_QuerySubmitted(const AutoSuggestBox& /*sender*/, const AutoSuggestBoxQuerySubmittedEventArgs& args)
{
if (args.ChosenSuggestion())
{
const auto& chosenResult{ args.ChosenSuggestion().as<FilteredSearchResult>() };
if (chosenResult->IsNoResultsPlaceholder())
{
// don't navigate anywhere
return;
}
// Navigate to the target page
const auto& indexEntry{ chosenResult->SearchIndexEntry() };
const auto& navigationArg{ chosenResult->NavigationArg() };
const auto& subpage{ indexEntry.Entry->SubPage };
const auto& elementToFocus{ indexEntry.Entry->ElementName };
if (const auto navArgString = navigationArg.try_as<hstring>())
{
_Navigate(*navArgString, subpage, elementToFocus);
}
else if (const auto& profileVM = navigationArg.try_as<Editor::ProfileViewModel>())
{
_Navigate(profileVM, subpage, elementToFocus);
}
else if (const auto& colorSchemeVM = navigationArg.try_as<Editor::ColorSchemeViewModel>())
{
_Navigate(colorSchemeVM, subpage, elementToFocus);
}
else if (const auto& ntmEntryVM = navigationArg.try_as<Editor::NewTabMenuEntryViewModel>())
{
_Navigate(ntmEntryVM, subpage, elementToFocus);
}
else if (const auto& extPkgVM = navigationArg.try_as<Editor::ExtensionPackageViewModel>())
{
_Navigate(extPkgVM, subpage, elementToFocus);
}
else if (const auto& commandVM = navigationArg.try_as<Editor::CommandViewModel>())
{
_Navigate(commandVM, subpage, elementToFocus);
}
SettingsSearchBox().Text(L"");
}
}
void MainPage::SettingsSearchBox_SuggestionChosen(const AutoSuggestBox&, const AutoSuggestBoxSuggestionChosenEventArgs&)
{
// Don't navigate on arrow keys
// Handle Enter/Click with QuerySubmitted() to instead
// AutoSuggestBox will pass the chosen item to QuerySubmitted() via args.ChosenSuggestion()
}
safe_void_coroutine MainPage::_UpdateSearchIndex()
{
auto weakThis = get_weak();
co_await winrt::resume_background();
if (auto strongThis = weakThis.get())
{
if (strongThis->_currentSearch && strongThis->_currentSearch.Status() == AsyncStatus::Started)
{
strongThis->_currentSearch.Cancel();
}
SearchIndex::Instance().Reset();
}
}
}

View File

@@ -7,6 +7,7 @@
#include "Breadcrumb.g.h"
#include "NavigateToPageArgs.g.h"
#include "Utils.h"
#include "SearchIndex.h"
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
@@ -27,16 +28,19 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
struct NavigateToPageArgs : NavigateToPageArgsT<NavigateToPageArgs>
{
public:
NavigateToPageArgs(Windows::Foundation::IInspectable viewModel, Editor::IHostedInWindow windowRoot) :
NavigateToPageArgs(Windows::Foundation::IInspectable viewModel, Editor::IHostedInWindow windowRoot, const hstring& elementToFocus = {}) :
_ViewModel(viewModel),
_WeakWindowRoot(windowRoot) {}
_WeakWindowRoot(windowRoot),
_ElementToFocus(elementToFocus) {}
Editor::IHostedInWindow WindowRoot() const noexcept { return _WeakWindowRoot.get(); }
Windows::Foundation::IInspectable ViewModel() const noexcept { return _ViewModel; }
hstring ElementToFocus() const noexcept { return _ElementToFocus; }
private:
winrt::weak_ref<Editor::IHostedInWindow> _WeakWindowRoot{ nullptr };
winrt::weak_ref<Editor::IHostedInWindow> _WeakWindowRoot;
Windows::Foundation::IInspectable _ViewModel{ nullptr };
hstring _ElementToFocus{};
};
struct MainPage : MainPageT<MainPage>
@@ -46,6 +50,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void UpdateSettings(const Model::CascadiaSettings& settings);
safe_void_coroutine SettingsSearchBox_TextChanged(const Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const Windows::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs& args);
void SettingsSearchBox_QuerySubmitted(const Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const Windows::UI::Xaml::Controls::AutoSuggestBoxQuerySubmittedEventArgs& args);
void SettingsSearchBox_SuggestionChosen(const Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const Windows::UI::Xaml::Controls::AutoSuggestBoxSuggestionChosenEventArgs& args);
void SettingsNav_Loaded(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
void SettingsNav_ItemInvoked(const Microsoft::UI::Xaml::Controls::NavigationView& sender, const Microsoft::UI::Xaml::Controls::NavigationViewItemInvokedEventArgs& args);
void SaveButton_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
@@ -84,21 +92,30 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void _SetupProfileEventHandling(const winrt::Microsoft::Terminal::Settings::Editor::ProfileViewModel profile);
void _PreNavigateHelper();
void _Navigate(hstring clickedItemTag, BreadcrumbSubPage subPage);
void _Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage);
void _Navigate(const Editor::NewTabMenuEntryViewModel& ntmEntryVM, BreadcrumbSubPage subPage);
void _Navigate(const Editor::ExtensionPackageViewModel& extPkgVM, BreadcrumbSubPage subPage);
void _Navigate(hstring clickedItemTag, BreadcrumbSubPage subPage, hstring elementToFocus = {});
void _Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage, hstring elementToFocus = {});
void _Navigate(const Editor::ColorSchemeViewModel& colorSchemeVM, BreadcrumbSubPage subPage, hstring elementToFocus = {});
void _Navigate(const Editor::NewTabMenuEntryViewModel& ntmEntryVM, BreadcrumbSubPage subPage, hstring elementToFocus = {});
void _Navigate(const Editor::ExtensionPackageViewModel& extPkgVM, BreadcrumbSubPage subPage, hstring elementToFocus = {});
void _Navigate(const Editor::CommandViewModel& commandVM, BreadcrumbSubPage subPage, hstring elementToFocus = {});
void _NavigateToProfileHandler(const IInspectable& sender, winrt::guid profileGuid);
void _NavigateToColorSchemeHandler(const IInspectable& sender, const IInspectable& args);
Microsoft::UI::Xaml::Controls::NavigationViewItem _FindProfileNavItem(winrt::guid profileGuid) const;
void _UpdateBackgroundForMica();
void _MoveXamlParsedNavItemsIntoItemSource();
safe_void_coroutine _UpdateSearchIndex();
winrt::Microsoft::Terminal::Settings::Editor::ProfileViewModel _profileDefaultsVM{ nullptr };
Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Editor::ProfileViewModel> _profileVMs{ nullptr };
winrt::Microsoft::Terminal::Settings::Editor::ColorSchemesPageViewModel _colorSchemesPageVM{ nullptr };
winrt::Microsoft::Terminal::Settings::Editor::ActionsViewModel _actionsVM{ nullptr };
winrt::Microsoft::Terminal::Settings::Editor::NewTabMenuViewModel _newTabMenuPageVM{ nullptr };
winrt::Microsoft::Terminal::Settings::Editor::ExtensionsViewModel _extensionsVM{ nullptr };
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable>> _currentSearch{ nullptr };
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _profileViewModelChangedRevoker;
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _colorSchemesPageViewModelChangedRevoker;
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _actionsViewModelChangedRevoker;

View File

@@ -16,12 +16,11 @@ namespace Microsoft.Terminal.Settings.Editor
UInt64 GetHostingWindow();
}
// This is used to pass data between pages during navigation.
// All pages will migrate to using this in GH #19519.
runtimeclass NavigateToPageArgs
{
IHostedInWindow WindowRoot { get; };
IInspectable ViewModel { get; };
String ElementToFocus { get; };
}
enum BreadcrumbSubPage

View File

@@ -52,6 +52,75 @@
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="BasicSearchResultTemplate"
x:DataType="local:FilteredSearchResult">
<Grid HorizontalAlignment="Stretch"
ColumnSpacing="8"
ToolTipService.ToolTip="{x:Bind Label}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{x:Bind Icon}" />
<TextBlock Grid.Column="1"
Text="{x:Bind Label}"
TextTrimming="CharacterEllipsis" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="ComplexSearchResultTemplate"
x:DataType="local:FilteredSearchResult">
<Grid HorizontalAlignment="Stretch"
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{x:Bind Icon}" />
<TextBlock Grid.Row="0"
Grid.Column="1"
FontSize="{ThemeResource BodyTextBlockFontSize}"
Foreground="{ThemeResource TextFillColorPrimary}"
Text="{x:Bind Label}"
TextTrimming="CharacterEllipsis" />
<TextBlock Grid.Row="1"
Grid.Column="1"
FontSize="{ThemeResource CaptionTextBlockFontSize}"
Foreground="{ThemeResource TextFillColorSecondary}"
Text="{x:Bind SecondaryLabel}"
TextTrimming="CharacterEllipsis" />
<ToolTipService.ToolTip>
<StackPanel>
<TextBlock FontSize="{ThemeResource CaptionTextBlockFontSize}"
Foreground="{ThemeResource TextFillColorPrimary}"
Text="{x:Bind Label}" />
<TextBlock FontSize="{ThemeResource CaptionTextBlockFontSize}"
Foreground="{ThemeResource TextFillColorSecondary}"
Text="{x:Bind SecondaryLabel}" />
</StackPanel>
</ToolTipService.ToolTip>
</Grid>
</DataTemplate>
<local:SearchResultTemplateSelector x:Key="SearchResultTemplateSelector"
BasicTemplate="{StaticResource BasicSearchResultTemplate}"
ComplexTemplate="{StaticResource ComplexSearchResultTemplate}" />
<SolidColorBrush x:Key="NavigationViewExpandedPaneBackground"
Color="Transparent" />
<SolidColorBrush x:Key="NavigationViewContentBackground"
@@ -99,62 +168,45 @@
</Grid>
</muxc:NavigationView.Header>
<muxc:NavigationView.MenuItems>
<muxc:NavigationView.AutoSuggestBox>
<AutoSuggestBox x:Name="SettingsSearchBox"
x:Uid="Nav_SearchBox"
ItemTemplateSelector="{StaticResource SearchResultTemplateSelector}"
QueryIcon="Find"
QuerySubmitted="SettingsSearchBox_QuerySubmitted"
SuggestionChosen="SettingsSearchBox_SuggestionChosen"
TextChanged="SettingsSearchBox_TextChanged" />
</muxc:NavigationView.AutoSuggestBox>
<muxc:NavigationView.MenuItems>
<muxc:NavigationViewItem x:Name="LaunchNavItem"
x:Uid="Nav_Launch"
Tag="Launch_Nav">
<muxc:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE7B5;" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
Tag="Launch_Nav" />
<muxc:NavigationViewItem x:Name="InteractionNavItem"
x:Uid="Nav_Interaction"
Tag="Interaction_Nav">
<muxc:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE7C9;" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
Tag="Interaction_Nav" />
<muxc:NavigationViewItem x:Name="AppearanceNavItem"
x:Uid="Nav_Appearance"
Tag="GlobalAppearance_Nav">
<muxc:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE771;" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
Tag="GlobalAppearance_Nav" />
<muxc:NavigationViewItem x:Name="ColorSchemesNavItem"
x:Uid="Nav_ColorSchemes"
Tag="ColorSchemes_Nav">
<muxc:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE790;" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
Tag="ColorSchemes_Nav" />
<muxc:NavigationViewItem x:Name="RenderingNavItem"
x:Uid="Nav_Rendering"
Tag="Rendering_Nav">
<muxc:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE7F8;" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
Tag="Rendering_Nav" />
<muxc:NavigationViewItem x:Name="CompatibilityNavItem"
x:Uid="Nav_Compatibility"
Tag="Compatibility_Nav">
<muxc:NavigationViewItem.Icon>
<FontIcon Glyph="&#xEC7A;" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
Tag="Compatibility_Nav" />
<muxc:NavigationViewItem x:Name="ActionsNavItem"
x:Uid="Nav_Actions"
Tag="Actions_Nav">
<muxc:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE765;" />
</muxc:NavigationViewItem.Icon>
<muxc:NavigationViewItem.InfoBadge>
<muxc:InfoBadge Style="{StaticResource NewInfoBadge}"
Visibility="{x:Bind ActionsVM.DisplayBadge, Mode=OneWay}" />
@@ -163,42 +215,24 @@
<muxc:NavigationViewItem x:Name="NewTabMenuNavItem"
x:Uid="Nav_NewTabMenu"
Tag="NewTabMenu_Nav">
<muxc:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE71d;" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
Tag="NewTabMenu_Nav" />
<muxc:NavigationViewItem x:Name="ExtensionsNavItem"
x:Uid="Nav_Extensions"
Tag="Extensions_Nav">
<muxc:NavigationViewItem.Icon>
<FontIcon Glyph="&#xEA86;" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
Tag="Extensions_Nav" />
<muxc:NavigationViewItemHeader x:Uid="Nav_Profiles" />
<muxc:NavigationViewItem x:Name="BaseLayerMenuItem"
x:Uid="Nav_ProfileDefaults"
Tag="GlobalProfile_Nav">
<muxc:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE81E;" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
Tag="GlobalProfile_Nav" />
</muxc:NavigationView.MenuItems>
<muxc:NavigationView.FooterMenuItems>
<!-- The OpenJson item needs both Tapped and KeyDown handler -->
<muxc:NavigationViewItem x:Name="OpenJsonNavItem"
x:Uid="Nav_OpenJSON"
SelectsOnInvoked="False"
Tag="OpenJson_Nav">
<muxc:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE713;" />
</muxc:NavigationViewItem.Icon>
</muxc:NavigationViewItem>
Tag="OpenJson_Nav" />
</muxc:NavigationView.FooterMenuItems>
<Grid>

View File

@@ -93,6 +93,8 @@
<DependentUpon>NewTabMenu.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="pch.h" />
<ClInclude Include="SearchIndex.h" />
<ClInclude Include="NavConstants.h" />
<ClInclude Include="MainPage.h">
<DependentUpon>MainPage.xaml</DependentUpon>
</ClInclude>
@@ -173,6 +175,8 @@
</ClInclude>
<ClInclude Include="Utils.h" />
<ClInclude Include="PreviewConnection.h" />
<ClInclude Include="$(GeneratedFilesDir)GeneratedSettingsIndex.g.h" />
<ClInclude Include="../fzf/fzf.h" />
</ItemGroup>
<!-- ========================= XAML files ======================== -->
<ItemGroup>
@@ -302,6 +306,7 @@
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="SearchIndex.cpp" />
<ClCompile Include="MainPage.cpp">
<DependentUpon>MainPage.xaml</DependentUpon>
</ClCompile>
@@ -384,6 +389,8 @@
<ClCompile Include="PreviewConnection.cpp">
<DependentUpon>PreviewConnection.h</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)GeneratedSettingsIndex.g.cpp" />
<ClCompile Include="../fzf/fzf.cpp" />
</ItemGroup>
<!-- ========================= idl Files ======================== -->
<ItemGroup>
@@ -440,6 +447,7 @@
<DependentUpon>Rendering.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="SearchIndex.idl" />
<Midl Include="MainPage.idl">
<DependentUpon>MainPage.xaml</DependentUpon>
</Midl>
@@ -556,4 +564,10 @@
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
<Target Name="GenerateSettingsIndex"
Inputs="@(Page);$(OpenConsoleDir)tools\GenerateSettingsIndex.ps1"
Outputs="$(GeneratedFilesDir)GeneratedSettingsIndex.g.h;$(GeneratedFilesDir)GeneratedSettingsIndex.g.cpp"
BeforeTargets="ClCompile">
<Exec Command="pwsh.exe -NoProfile -ExecutionPolicy Unrestricted &quot;$(OpenConsoleDir)tools\GenerateSettingsIndex.ps1&quot; -SourceDir &quot;$(MSBuildThisFileDirectory).&quot; -OutputDir &quot;$(MSBuildThisFileDirectory)$(GeneratedFilesDir).&quot;" />
</Target>
</Project>

View File

@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include <til/static_map.h>
// Navigation tags used to identify pages in the Settings UI NavigationView.
// These tags are stored as the Tag property on NavigationViewItems.
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
inline constexpr std::wstring_view openJsonTag{ L"OpenJson_Nav" };
inline constexpr std::wstring_view launchTag{ L"Launch_Nav" };
inline constexpr std::wstring_view interactionTag{ L"Interaction_Nav" };
inline constexpr std::wstring_view renderingTag{ L"Rendering_Nav" };
inline constexpr std::wstring_view compatibilityTag{ L"Compatibility_Nav" };
inline constexpr std::wstring_view actionsTag{ L"Actions_Nav" };
inline constexpr std::wstring_view newTabMenuTag{ L"NewTabMenu_Nav" };
inline constexpr std::wstring_view extensionsTag{ L"Extensions_Nav" };
inline constexpr std::wstring_view globalProfileTag{ L"GlobalProfile_Nav" };
inline constexpr std::wstring_view addProfileTag{ L"AddProfile" };
inline constexpr std::wstring_view colorSchemesTag{ L"ColorSchemes_Nav" };
inline constexpr std::wstring_view globalAppearanceTag{ L"GlobalAppearance_Nav" };
// Map from navigation tags to Segoe MDL2 Assets icon glyphs
inline constexpr til::static_map NavTagIconMap{
std::pair{ launchTag, L"\xE7B5" }, /* Set Lock Screen */
std::pair{ interactionTag, L"\xE7C9" }, /* Touch Pointer */
std::pair{ globalAppearanceTag, L"\xE771" }, /* Personalize */
std::pair{ colorSchemesTag, L"\xE790" }, /* Color */
std::pair{ renderingTag, L"\xE7F8" }, /* Device Laptop No Pic */
std::pair{ compatibilityTag, L"\xEC7A" }, /* Developer Tools */
std::pair{ actionsTag, L"\xE765" }, /* Keyboard Classic */
std::pair{ newTabMenuTag, L"\xE71D" }, /* All Apps */
std::pair{ extensionsTag, L"\xEA86" }, /* Puzzle */
std::pair{ globalProfileTag, L"\xE81E" }, /* Map Layers */
std::pair{ addProfileTag, L"\xE710" }, /* Add */
std::pair{ openJsonTag, L"\xE713" }, /* Settings */
};
}

View File

@@ -3,6 +3,7 @@
#include "pch.h"
#include "NewTabMenu.h"
#include "NavigateToPageArgs.g.h"
#include "NewTabMenu.g.cpp"
#include "NavigateToPageArgs.g.h"
#include "NewTabMenuEntryTemplateSelector.g.cpp"
@@ -44,6 +45,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::NewTabMenuViewModel>();
_weakWindowRoot = args.WindowRoot();
BringIntoViewWhenLoaded(args.ElementToFocus());
TraceLoggingWrite(
g_hTerminalSettingsEditorProvider,

View File

@@ -320,7 +320,8 @@
<TextBlock x:Uid="NewTabMenu_CurrentFolderTextBlock"
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Name -->
<local:SettingContainer x:Uid="NewTabMenu_CurrentFolderName"
<local:SettingContainer x:Name="CurrentFolderName"
x:Uid="NewTabMenu_CurrentFolderName"
CurrentValue="{x:Bind ViewModel.CurrentFolderName, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyle}">
<TextBox Style="{StaticResource TextBoxSettingStyle}"
@@ -354,13 +355,15 @@
</local:SettingContainer>
<!-- Inlining -->
<local:SettingContainer x:Uid="NewTabMenu_CurrentFolderInlining">
<local:SettingContainer x:Name="CurrentFolderInlining"
x:Uid="NewTabMenu_CurrentFolderInlining">
<ToggleSwitch IsOn="{x:Bind ViewModel.CurrentFolderInlining, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Allow Empty -->
<local:SettingContainer x:Uid="NewTabMenu_CurrentFolderAllowEmpty">
<local:SettingContainer x:Name="CurrentFolderAllowEmpty"
x:Uid="NewTabMenu_CurrentFolderAllowEmpty">
<ToggleSwitch IsOn="{x:Bind ViewModel.CurrentFolderAllowEmpty, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
@@ -372,7 +375,8 @@
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Add Profile -->
<local:SettingContainer x:Uid="NewTabMenu_AddProfile"
<local:SettingContainer x:Name="AddProfile"
x:Uid="NewTabMenu_AddProfile"
FontIconGlyph="&#xE756;"
Style="{StaticResource SettingContainerWithIcon}">
@@ -421,7 +425,8 @@
</local:SettingContainer>
<!-- Add Separator -->
<local:SettingContainer x:Uid="NewTabMenu_AddSeparator"
<local:SettingContainer x:Name="AddSeparator"
x:Uid="NewTabMenu_AddSeparator"
FontIconGlyph="&#xE76f;"
Style="{StaticResource SettingContainerWithIcon}">
<Button x:Name="AddSeparatorButton"
@@ -437,7 +442,8 @@
</local:SettingContainer>
<!-- Add Folder -->
<local:SettingContainer x:Uid="NewTabMenu_AddFolder"
<local:SettingContainer x:Name="AddFolder"
x:Uid="NewTabMenu_AddFolder"
FontIconGlyph="&#xF12B;"
Style="{StaticResource SettingContainerWithIcon}">
<StackPanel Orientation="Horizontal"
@@ -463,7 +469,8 @@
</local:SettingContainer>
<!-- Add Match Profiles -->
<local:SettingContainer x:Uid="NewTabMenu_AddMatchProfiles"
<local:SettingContainer x:Name="AddMatchProfiles"
x:Uid="NewTabMenu_AddMatchProfiles"
FontIconGlyph="&#xE748;"
Style="{StaticResource ExpanderSettingContainerStyleWithIcon}">
<StackPanel Spacing="8">

View File

@@ -510,6 +510,25 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
return _folderTreeCache;
}
Collections::IObservableVector<Editor::FolderEntryViewModel> NewTabMenuViewModel::FolderTreeFlatList() const
{
std::vector<Editor::FolderEntryViewModel> flatList;
_FolderTreeFlatListImpl(_rootEntries, flatList);
return single_threaded_observable_vector<Editor::FolderEntryViewModel>(std::move(flatList));
}
void NewTabMenuViewModel::_FolderTreeFlatListImpl(const Windows::Foundation::Collections::IVector<Editor::NewTabMenuEntryViewModel>& entriesToAdd, std::vector<Editor::FolderEntryViewModel>& flatList)
{
for (const auto& entry : entriesToAdd)
{
if (const auto& folderVM = entry.try_as<Editor::FolderEntryViewModel>())
{
flatList.push_back(folderVM);
_FolderTreeFlatListImpl(folderVM.Entries(), flatList);
}
}
}
// This recursively constructs the FolderTree
FolderTreeViewEntry::FolderTreeViewEntry(Editor::FolderEntryViewModel folderEntry) :
_folderEntry{ folderEntry },

View File

@@ -55,6 +55,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::Foundation::Collections::IObservableVector<Model::Profile> AvailableProfiles() const { return _Settings.AllProfiles(); }
Windows::Foundation::Collections::IObservableVector<Editor::FolderTreeViewEntry> FolderTree() const;
Windows::Foundation::Collections::IObservableVector<Editor::FolderEntryViewModel> FolderTreeFlatList() const;
Windows::Foundation::Collections::IObservableVector<Editor::NewTabMenuEntryViewModel> CurrentView() const;
VIEW_MODEL_OBSERVABLE_PROPERTY(Editor::FolderEntryViewModel, CurrentFolder, nullptr);
VIEW_MODEL_OBSERVABLE_PROPERTY(Editor::FolderTreeViewEntry, CurrentFolderTreeViewSelectedItem, nullptr);
@@ -73,6 +74,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::Foundation::Collections::IObservableVector<Editor::NewTabMenuEntryViewModel>::VectorChanged_revoker _rootEntriesChangedRevoker;
static bool _IsRemainingProfilesEntryMissing(const Windows::Foundation::Collections::IVector<Editor::NewTabMenuEntryViewModel>& entries);
static void _FolderTreeFlatListImpl(const Windows::Foundation::Collections::IVector<Editor::NewTabMenuEntryViewModel>& entriesToAdd, std::vector<Editor::FolderEntryViewModel>& flatList);
void _FolderPropertyChanged(const IInspectable& sender, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
};

View File

@@ -4,7 +4,6 @@
#pragma once
#include "DeleteProfileEventArgs.g.h"
#include "NavigateToProfileArgs.g.h"
#include "BellSoundViewModel.g.h"
#include "ProfileViewModel.g.h"
#include "Utils.h"
@@ -12,21 +11,6 @@
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct NavigateToProfileArgs : NavigateToProfileArgsT<NavigateToProfileArgs>
{
public:
NavigateToProfileArgs(ProfileViewModel profile, Editor::IHostedInWindow windowRoot) :
_Profile(profile),
_WeakWindowRoot(windowRoot) {}
Editor::IHostedInWindow WindowRoot() const noexcept { return _WeakWindowRoot ? _WeakWindowRoot.get() : nullptr; }
Editor::ProfileViewModel Profile() const noexcept { return _Profile; }
private:
winrt::weak_ref<Editor::IHostedInWindow> _WeakWindowRoot;
Editor::ProfileViewModel _Profile{ nullptr };
};
struct BellSoundViewModel : BellSoundViewModelT<BellSoundViewModel>, ViewModelHelper<BellSoundViewModel>
{
public:
@@ -55,6 +39,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void DeleteProfile();
void SetupAppearances(Windows::Foundation::Collections::IObservableVector<Editor::ColorSchemeViewModel> schemesList);
void ForceRefreshCurrentPage()
{
// Used to trigger the PropertyChanged handler in MainPage.cpp
// This forces the page to refresh
_NotifyChanges(L"CurrentPage");
}
// bell style bits
hstring BellStylePreview() const;
@@ -155,6 +145,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
OBSERVABLE_PROJECTED_SETTING(_profile, AutoMarkPrompts);
OBSERVABLE_PROJECTED_SETTING(_profile, RepositionCursorWithMouse);
OBSERVABLE_PROJECTED_SETTING(_profile, ForceVTInput);
OBSERVABLE_PROJECTED_SETTING(_profile, AllowKittyKeyboardMode);
OBSERVABLE_PROJECTED_SETTING(_profile, AllowVtChecksumReport);
OBSERVABLE_PROJECTED_SETTING(_profile, AllowVtClipboardWrite);
OBSERVABLE_PROJECTED_SETTING(_profile, AnswerbackMessage);
@@ -163,6 +154,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
WINRT_PROPERTY(bool, IsBaseLayer, false);
WINRT_PROPERTY(bool, FocusDeleteButton, false);
WINRT_PROPERTY(hstring, ElementToFocus);
GETSET_BINDABLE_ENUM_SETTING(AntiAliasingMode, Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode);
GETSET_BINDABLE_ENUM_SETTING(CloseOnExitMode, Microsoft::Terminal::Settings::Model::CloseOnExitMode, CloseOnExit);
GETSET_BINDABLE_ENUM_SETTING(ScrollState, Microsoft::Terminal::Control::ScrollbarState, ScrollState);

View File

@@ -14,12 +14,6 @@ import "ColorSchemesPageViewModel.idl";
namespace Microsoft.Terminal.Settings.Editor
{
runtimeclass NavigateToProfileArgs
{
ProfileViewModel Profile { get; };
IHostedInWindow WindowRoot { get; };
}
runtimeclass DeleteProfileEventArgs
{
Guid ProfileGuid { get; };
@@ -140,6 +134,7 @@ namespace Microsoft.Terminal.Settings.Editor
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AutoMarkPrompts);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RepositionCursorWithMouse);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, ForceVTInput);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowKittyKeyboardMode);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AllowVtChecksumReport);
OBSERVABLE_PROJECTED_PROFILE_SETTING(String, AnswerbackMessage);
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RainbowSuggestions);

View File

@@ -25,9 +25,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Profiles_Advanced::OnNavigatedTo(const NavigationEventArgs& e)
{
const auto args = e.Parameter().as<Editor::NavigateToProfileArgs>();
_Profile = args.Profile();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_Profile = args.ViewModel().as<Editor::ProfileViewModel>();
_weakWindowRoot = args.WindowRoot();
BringIntoViewWhenLoaded(args.ElementToFocus());
TraceLoggingWrite(
g_hTerminalSettingsEditorProvider,

View File

@@ -37,7 +37,8 @@
<StackPanel Grid.Row="1"
Style="{StaticResource SettingsStackStyle}">
<!-- Antialiasing Mode -->
<local:SettingContainer x:Uid="Profile_AntialiasingMode"
<local:SettingContainer x:Name="AntialiasingMode"
x:Uid="Profile_AntialiasingMode"
ClearSettingValue="{x:Bind Profile.ClearAntialiasingMode}"
HasSettingValue="{x:Bind Profile.HasAntialiasingMode, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.AntialiasingModeOverrideSource, Mode=OneWay}">
@@ -49,7 +50,8 @@
</local:SettingContainer>
<!-- AltGr Aliasing -->
<local:SettingContainer x:Uid="Profile_AltGrAliasing"
<local:SettingContainer x:Name="AltGrAliasing"
x:Uid="Profile_AltGrAliasing"
ClearSettingValue="{x:Bind Profile.ClearAltGrAliasing}"
HasSettingValue="{x:Bind Profile.HasAltGrAliasing, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.AltGrAliasingOverrideSource, Mode=OneWay}">
@@ -58,7 +60,8 @@
</local:SettingContainer>
<!-- Snap On Input -->
<local:SettingContainer x:Uid="Profile_SnapOnInput"
<local:SettingContainer x:Name="SnapOnInput"
x:Uid="Profile_SnapOnInput"
ClearSettingValue="{x:Bind Profile.ClearSnapOnInput}"
HasSettingValue="{x:Bind Profile.HasSnapOnInput, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.SnapOnInputOverrideSource, Mode=OneWay}">
@@ -67,7 +70,8 @@
</local:SettingContainer>
<!-- History Size -->
<local:SettingContainer x:Uid="Profile_HistorySize"
<local:SettingContainer x:Name="HistorySize"
x:Uid="Profile_HistorySize"
ClearSettingValue="{x:Bind Profile.ClearHistorySize}"
HasSettingValue="{x:Bind Profile.HasHistorySize, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.HistorySizeOverrideSource, Mode=OneWay}">
@@ -80,7 +84,8 @@
</local:SettingContainer>
<!-- Close On Exit -->
<local:SettingContainer x:Uid="Profile_CloseOnExit"
<local:SettingContainer x:Name="CloseOnExit"
x:Uid="Profile_CloseOnExit"
ClearSettingValue="{x:Bind Profile.ClearCloseOnExit}"
HasSettingValue="{x:Bind Profile.HasCloseOnExit, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.CloseOnExitOverrideSource, Mode=OneWay}">
@@ -92,7 +97,8 @@
</local:SettingContainer>
<!-- Bell Style -->
<local:SettingContainer x:Uid="Profile_BellStyle"
<local:SettingContainer x:Name="BellStyle"
x:Uid="Profile_BellStyle"
ClearSettingValue="{x:Bind Profile.ClearBellStyle}"
CurrentValue="{x:Bind Profile.BellStylePreview, Mode=OneWay}"
HasSettingValue="{x:Bind Profile.HasBellStyle, Mode=OneWay}"
@@ -109,7 +115,8 @@
</local:SettingContainer>
<!-- Bell Sound -->
<local:SettingContainer x:Uid="Profile_BellSound"
<local:SettingContainer x:Name="BellSound"
x:Uid="Profile_BellSound"
ClearSettingValue="{x:Bind Profile.ClearBellSound}"
CurrentValue="{x:Bind Profile.BellSoundPreview, Mode=OneWay}"
HasSettingValue="{x:Bind Profile.HasBellSound, Mode=OneWay}"
@@ -190,7 +197,8 @@
</local:SettingContainer>
<!-- RightClickContextMenu -->
<local:SettingContainer x:Uid="Profile_RightClickContextMenu"
<local:SettingContainer x:Name="RightClickContextMenu"
x:Uid="Profile_RightClickContextMenu"
ClearSettingValue="{x:Bind Profile.ClearRightClickContextMenu}"
HasSettingValue="{x:Bind Profile.HasRightClickContextMenu, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.RightClickContextMenuOverrideSource, Mode=OneWay}">
@@ -199,7 +207,8 @@
</local:SettingContainer>
<!-- ShowMarks -->
<local:SettingContainer x:Uid="Profile_ShowMarks"
<local:SettingContainer x:Name="ShowMarks"
x:Uid="Profile_ShowMarks"
ClearSettingValue="{x:Bind Profile.ClearShowMarks}"
HasSettingValue="{x:Bind Profile.HasShowMarks, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.ShowMarksOverrideSource, Mode=OneWay}"
@@ -209,7 +218,8 @@
</local:SettingContainer>
<!-- AutoMarkPrompts -->
<local:SettingContainer x:Uid="Profile_AutoMarkPrompts"
<local:SettingContainer x:Name="AutoMarkPrompts"
x:Uid="Profile_AutoMarkPrompts"
ClearSettingValue="{x:Bind Profile.ClearAutoMarkPrompts}"
HasSettingValue="{x:Bind Profile.HasAutoMarkPrompts, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.AutoMarkPromptsOverrideSource, Mode=OneWay}"
@@ -219,7 +229,8 @@
</local:SettingContainer>
<!-- ReloadEnvVars -->
<local:SettingContainer x:Uid="Profile_ReloadEnvVars"
<local:SettingContainer x:Name="ReloadEnvVars"
x:Uid="Profile_ReloadEnvVars"
ClearSettingValue="{x:Bind Profile.ClearReloadEnvironmentVariables}"
HasSettingValue="{x:Bind Profile.HasReloadEnvironmentVariables, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.ReloadEnvironmentVariablesOverrideSource, Mode=OneWay}">
@@ -228,7 +239,8 @@
</local:SettingContainer>
<!-- RepositionCursorWithMouse -->
<local:SettingContainer x:Uid="Profile_RepositionCursorWithMouse"
<local:SettingContainer x:Name="RepositionCursorWithMouse"
x:Uid="Profile_RepositionCursorWithMouse"
ClearSettingValue="{x:Bind Profile.ClearRepositionCursorWithMouse}"
HasSettingValue="{x:Bind Profile.HasRepositionCursorWithMouse, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.RepositionCursorWithMouseOverrideSource, Mode=OneWay}"
@@ -238,7 +250,8 @@
</local:SettingContainer>
<!-- RainbowSuggestions -->
<local:SettingContainer x:Uid="Profile_RainbowSuggestions"
<local:SettingContainer x:Name="RainbowSuggestions"
x:Uid="Profile_RainbowSuggestions"
ClearSettingValue="{x:Bind Profile.ClearRainbowSuggestions}"
HasSettingValue="{x:Bind Profile.HasRainbowSuggestions, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.RainbowSuggestionsOverrideSource, Mode=OneWay}">
@@ -247,7 +260,8 @@
</local:SettingContainer>
<!-- Path Translation -->
<local:SettingContainer x:Uid="Profile_PathTranslationStyle"
<local:SettingContainer x:Name="PathTranslationStyle"
x:Uid="Profile_PathTranslationStyle"
ClearSettingValue="{x:Bind Profile.ClearPathTranslationStyle}"
HasSettingValue="{x:Bind Profile.HasPathTranslationStyle, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.PathTranslationStyleOverrideSource, Mode=OneWay}">

View File

@@ -3,6 +3,7 @@
#include "pch.h"
#include "Profiles_Appearance.h"
#include "Appearances.h"
#include "ProfileViewModel.h"
#include "PreviewConnection.h"
@@ -12,6 +13,8 @@
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Navigation;
static constexpr std::wstring_view AppearanceSettingPrefix{ L"App." };
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
Profiles_Appearance::Profiles_Appearance()
@@ -22,10 +25,24 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Profiles_Appearance::OnNavigatedTo(const NavigationEventArgs& e)
{
const auto args = e.Parameter().as<Editor::NavigateToProfileArgs>();
_Profile = args.Profile();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_Profile = args.ViewModel().as<Editor::ProfileViewModel>();
_weakWindowRoot = args.WindowRoot();
// Settings are stored in Profiles_Appearance and Appearances.
// We use the "App." prefix to indicate if it's in Appearances,
// and remove it on the way to Appearances object.
const auto elementToFocus = args.ElementToFocus();
if (elementToFocus.starts_with(AppearanceSettingPrefix))
{
std::wstring correctedName{ elementToFocus.c_str() };
get_self<implementation::Appearances>(DefaultAppearanceView())->BringIntoViewWhenLoaded(hstring{ correctedName.substr(AppearanceSettingPrefix.size()) });
}
else
{
BringIntoViewWhenLoaded(elementToFocus);
}
if (!_previewControl)
{
const auto settings = winrt::get_self<implementation::ProfileViewModel>(_Profile)->TermSettings();

View File

@@ -5,11 +5,10 @@
#include <ThrottledFunc.h>
#include "Profiles_Appearance.g.h"
#include "PreviewConnection.h"
#include "Utils.h"
#include "Profiles_Appearance.g.h"
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct Profiles_Appearance : public HasScrollViewer<Profiles_Appearance>, Profiles_AppearanceT<Profiles_Appearance>

View File

@@ -75,7 +75,8 @@
CornerRadius="{StaticResource ControlCornerRadius}" />
</Border>
<local:Appearances Appearance="{x:Bind Profile.DefaultAppearance, Mode=OneWay}"
<local:Appearances x:Name="DefaultAppearanceView"
Appearance="{x:Bind Profile.DefaultAppearance, Mode=OneWay}"
SourceProfile="{x:Bind Profile, Mode=OneWay}"
WindowRoot="{x:Bind WindowRoot, Mode=OneTime}" />
@@ -85,12 +86,12 @@
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Opacity -->
<local:SettingContainer x:Name="OpacityContainer"
<local:SettingContainer x:Name="Opacity"
x:Uid="Profile_Opacity"
ClearSettingValue="{x:Bind Profile.ClearOpacity}"
HasSettingValue="{x:Bind Profile.HasOpacity, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.OpacityOverrideSource, Mode=OneWay}">
<StackPanel x:Name="OpacityControl">
<StackPanel>
<Grid Style="{StaticResource CustomSliderControlGridStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
@@ -107,12 +108,12 @@
</local:SettingContainer>
<!-- Use Acrylic -->
<local:SettingContainer x:Uid="Profile_UseAcrylic"
<local:SettingContainer x:Name="UseAcrylic"
x:Uid="Profile_UseAcrylic"
ClearSettingValue="{x:Bind Profile.ClearUseAcrylic}"
HasSettingValue="{x:Bind Profile.HasUseAcrylic, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.UseAcrylicOverrideSource, Mode=OneWay}">
<ToggleSwitch x:Name="UseAcrylicToggleSwitch"
IsOn="{x:Bind Profile.UseAcrylic, Mode=TwoWay}"
<ToggleSwitch IsOn="{x:Bind Profile.UseAcrylic, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
@@ -124,7 +125,8 @@
Style="{StaticResource TextBlockSubHeaderStyle}" />
<!-- Padding -->
<local:SettingContainer x:Uid="Profile_Padding"
<local:SettingContainer x:Name="Padding"
x:Uid="Profile_Padding"
ClearSettingValue="{x:Bind Profile.ClearPadding}"
CurrentValue="{x:Bind Profile.Padding, Mode=OneWay}"
HasSettingValue="{x:Bind Profile.HasPadding, Mode=OneWay}"
@@ -191,7 +193,8 @@
</local:SettingContainer>
<!-- Scrollbar Visibility -->
<local:SettingContainer x:Uid="Profile_ScrollbarVisibility"
<local:SettingContainer x:Name="ScrollbarVisibility"
x:Uid="Profile_ScrollbarVisibility"
ClearSettingValue="{x:Bind Profile.ClearScrollState}"
HasSettingValue="{x:Bind Profile.HasScrollState, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.ScrollStateOverrideSource, Mode=OneWay}">
@@ -207,7 +210,10 @@
Visibility="{x:Bind Profile.EditableUnfocusedAppearance, Mode=OneWay}">
<TextBlock x:Uid="Profile_UnfocusedAppearanceTextBlock"
Style="{StaticResource TextBlockSubtitleStyle}" />
<Button x:Uid="Profile_CreateUnfocusedAppearanceButton"
<!-- Create Unfocused Appearance -->
<Button x:Name="CreateUnfocusedAppearance"
x:Uid="Profile_CreateUnfocusedAppearanceButton"
Margin="8,0,0,0"
VerticalAlignment="Bottom"
Click="CreateUnfocusedAppearance_Click"
@@ -224,7 +230,10 @@
</StackPanel>
</Button.Content>
</Button>
<Button x:Uid="Profile_DeleteUnfocusedAppearanceButton"
<!-- Delete Unfocused Appearance -->
<Button x:Name="DeleteUnfocusedAppearance"
x:Uid="Profile_DeleteUnfocusedAppearanceButton"
Margin="8,0,0,0"
VerticalAlignment="Bottom"
Click="DeleteUnfocusedAppearance_Click"
@@ -241,7 +250,10 @@
</Button.Content>
</Button>
</StackPanel>
<local:Appearances Appearance="{x:Bind Profile.UnfocusedAppearance, Mode=OneWay}"
<!-- Unfocused Appearance -->
<local:Appearances x:Name="UnfocusedAppearanceView"
Appearance="{x:Bind Profile.UnfocusedAppearance, Mode=OneWay}"
SourceProfile="{x:Bind Profile, Mode=OneWay}"
Visibility="{x:Bind Profile.ShowUnfocusedAppearance, Mode=OneWay}"
WindowRoot="{x:Bind WindowRoot, Mode=OneTime}" />

View File

@@ -29,9 +29,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Profiles_Base::OnNavigatedTo(const NavigationEventArgs& e)
{
const auto args = e.Parameter().as<Editor::NavigateToProfileArgs>();
_Profile = args.Profile();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_Profile = args.ViewModel().as<Editor::ProfileViewModel>();
_weakWindowRoot = args.WindowRoot();
BringIntoViewWhenLoaded(args.ElementToFocus());
// Check the use parent directory box if the starting directory is empty
if (_Profile.StartingDirectory().empty())

View File

@@ -40,7 +40,8 @@
Additionally, the JSON stubs generated by auto-generated profiles come with a name,
so the name will always be overridden.
-->
<local:SettingContainer x:Uid="Profile_Name"
<local:SettingContainer x:Name="Name"
x:Uid="Profile_Name"
CurrentValue="{x:Bind Profile.Name, Mode=OneWay}"
Style="{StaticResource ExpanderSettingContainerStyle}"
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(Profile.IsBaseLayer), Mode=OneWay}">
@@ -49,7 +50,7 @@
</local:SettingContainer>
<!-- Commandline -->
<local:SettingContainer x:Name="CommandlineContainer"
<local:SettingContainer x:Name="Commandline"
x:Uid="Profile_Commandline"
ClearSettingValue="{x:Bind Profile.ClearCommandline}"
CurrentValue="{x:Bind Profile.Commandline, Mode=OneWay}"
@@ -70,7 +71,7 @@
</local:SettingContainer>
<!-- Starting Directory -->
<local:SettingContainer x:Name="StartingDirectoryContainer"
<local:SettingContainer x:Name="StartingDirectory"
x:Uid="Profile_StartingDirectory"
ClearSettingValue="{x:Bind Profile.ClearStartingDirectory}"
CurrentValue="{x:Bind Profile.CurrentStartingDirectoryPreview, Mode=OneWay}"
@@ -99,7 +100,8 @@
</local:SettingContainer>
<!-- Icon -->
<local:SettingContainer x:Uid="Profile_Icon"
<local:SettingContainer x:Name="Icon"
x:Uid="Profile_Icon"
ClearSettingValue="{x:Bind Profile.ClearIcon}"
CurrentValueAccessibleName="{x:Bind Profile.LocalizedIcon, Mode=OneWay}"
HasSettingValue="{x:Bind Profile.HasIcon, Mode=OneWay}"
@@ -128,7 +130,8 @@
</local:SettingContainer>
<!-- Tab Title -->
<local:SettingContainer x:Uid="Profile_TabTitle"
<local:SettingContainer x:Name="TabTitle"
x:Uid="Profile_TabTitle"
ClearSettingValue="{x:Bind Profile.ClearTabTitle}"
CurrentValue="{x:Bind Profile.TabTitlePreview, Mode=OneWay}"
HasSettingValue="{x:Bind Profile.HasTabTitle, Mode=OneWay}"
@@ -155,7 +158,8 @@
</local:SettingContainer>
<!-- Elevate -->
<local:SettingContainer x:Uid="Profile_Elevate"
<local:SettingContainer x:Name="Elevate"
x:Uid="Profile_Elevate"
ClearSettingValue="{x:Bind Profile.ClearElevate}"
HasSettingValue="{x:Bind Profile.HasElevate, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.ElevateOverrideSource, Mode=OneWay}">
@@ -164,7 +168,8 @@
</local:SettingContainer>
<!-- Hidden -->
<local:SettingContainer x:Uid="Profile_Hidden"
<local:SettingContainer x:Name="Hidden"
x:Uid="Profile_Hidden"
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(Profile.IsBaseLayer), Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind Profile.Hidden, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />

View File

@@ -22,8 +22,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Profiles_Base_Orphaned::OnNavigatedTo(const NavigationEventArgs& e)
{
const auto args = e.Parameter().as<Editor::NavigateToProfileArgs>();
_Profile = args.Profile();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_Profile = args.ViewModel().as<Editor::ProfileViewModel>();
_layoutUpdatedRevoker = LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
// This event fires every time the layout changes, but it is always the last one to fire

View File

@@ -23,7 +23,8 @@
<StackPanel Style="{StaticResource SettingsStackStyle}">
<!-- Delete Button -->
<local:SettingContainer x:Uid="Profile_Delete_Orphaned">
<local:SettingContainer x:Name="DeleteOrphaned"
x:Uid="Profile_Delete_Orphaned">
<local:SettingContainer.Content>
<Button x:Name="DeleteButton"
Click="DeleteConfirmation_Click"
@@ -40,7 +41,8 @@
</local:SettingContainer.Content>
</local:SettingContainer>
<local:SettingContainer x:Uid="Profile_Name">
<local:SettingContainer x:Name="Name"
x:Uid="Profile_Name">
<local:SettingContainer.Content>
<TextBlock FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
Style="{StaticResource SettingsPageItemDescriptionStyle}"
@@ -48,7 +50,8 @@
</local:SettingContainer.Content>
</local:SettingContainer>
<local:SettingContainer x:Uid="Profile_Source_Orphaned">
<local:SettingContainer x:Name="Source"
x:Uid="Profile_Source_Orphaned">
<local:SettingContainer.Content>
<TextBlock FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
Style="{StaticResource SettingsPageItemDescriptionStyle}"

View File

@@ -20,7 +20,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Profiles_Terminal::OnNavigatedTo(const NavigationEventArgs& e)
{
_Profile = e.Parameter().as<Editor::ProfileViewModel>();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_Profile = args.ViewModel().as<Editor::ProfileViewModel>();
BringIntoViewWhenLoaded(args.ElementToFocus());
TraceLoggingWrite(
g_hTerminalSettingsEditorProvider,

View File

@@ -32,7 +32,8 @@
Style="{StaticResource SettingsStackStyle}">
<!-- Suppress Application Title -->
<local:SettingContainer x:Uid="Profile_SuppressApplicationTitle"
<local:SettingContainer x:Name="SuppressApplicationTitle"
x:Uid="Profile_SuppressApplicationTitle"
ClearSettingValue="{x:Bind Profile.ClearSuppressApplicationTitle}"
HasSettingValue="{x:Bind Profile.HasSuppressApplicationTitle, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.SuppressApplicationTitleOverrideSource, Mode=OneWay}">
@@ -41,7 +42,8 @@
</local:SettingContainer>
<!-- Force VT Input -->
<local:SettingContainer x:Uid="Profile_ForceVTInput"
<local:SettingContainer x:Name="ForceVTInput"
x:Uid="Profile_ForceVTInput"
ClearSettingValue="{x:Bind Profile.ClearForceVTInput}"
HasSettingValue="{x:Bind Profile.HasForceVTInput, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.ForceVTInputOverrideSource, Mode=OneWay}">
@@ -49,8 +51,19 @@
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Kitty Keyboard Mode -->
<local:SettingContainer x:Name="AllowKittyKeyboardMode"
x:Uid="Profile_AllowKittyKeyboardMode"
ClearSettingValue="{x:Bind Profile.ClearAllowKittyKeyboardMode}"
HasSettingValue="{x:Bind Profile.HasAllowKittyKeyboardMode, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.AllowKittyKeyboardModeOverrideSource, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind Profile.AllowKittyKeyboardMode, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<!-- Allow VT Checksum Report -->
<local:SettingContainer x:Uid="Profile_AllowVtChecksumReport"
<local:SettingContainer x:Name="AllowVtChecksumReport"
x:Uid="Profile_AllowVtChecksumReport"
ClearSettingValue="{x:Bind Profile.ClearAllowVtChecksumReport}"
HasSettingValue="{x:Bind Profile.HasAllowVtChecksumReport, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.AllowVtChecksumReportOverrideSource, Mode=OneWay}">
@@ -59,7 +72,8 @@
</local:SettingContainer>
<!-- Allow VT Clipboard Writing -->
<local:SettingContainer x:Uid="Profile_AllowVtClipboardWrite"
<local:SettingContainer x:Name="AllowVtClipboardWrite"
x:Uid="Profile_AllowVtClipboardWrite"
ClearSettingValue="{x:Bind Profile.ClearAllowVtClipboardWrite}"
HasSettingValue="{x:Bind Profile.HasAllowVtClipboardWrite, Mode=OneWay}"
SettingOverrideSource="{x:Bind Profile.AllowVtClipboardWriteOverrideSource, Mode=OneWay}">
@@ -68,7 +82,8 @@
</local:SettingContainer>
<!-- Answerback Message -->
<local:SettingContainer x:Uid="Profile_AnswerbackMessage"
<local:SettingContainer x:Name="AnswerbackMessage"
x:Uid="Profile_AnswerbackMessage"
ClearSettingValue="{x:Bind Profile.ClearAnswerbackMessage}"
CurrentValue="{x:Bind Profile.AnswerbackMessagePreview, Mode=OneWay}"
HasSettingValue="{x:Bind Profile.HasAnswerbackMessage, Mode=OneWay}"

View File

@@ -3,6 +3,7 @@
#include "pch.h"
#include "Rendering.h"
#include "NavigateToPageArgs.g.h"
#include "Rendering.g.cpp"
using namespace winrt::Windows::UI::Xaml::Navigation;
@@ -16,7 +17,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Rendering::OnNavigatedTo(const NavigationEventArgs& e)
{
_ViewModel = e.Parameter().as<Editor::RenderingViewModel>();
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
_ViewModel = args.ViewModel().as<Editor::RenderingViewModel>();
BringIntoViewWhenLoaded(args.ElementToFocus());
TraceLoggingWrite(
g_hTerminalSettingsEditorProvider,

View File

@@ -24,7 +24,8 @@
</Page.Resources>
<StackPanel Style="{StaticResource SettingsStackStyle}">
<local:SettingContainer x:Uid="Globals_GraphicsAPI">
<local:SettingContainer x:Name="GraphicsAPI"
x:Uid="Globals_GraphicsAPI">
<ComboBox AutomationProperties.AccessibilityView="Content"
ItemTemplate="{StaticResource EnumComboBoxTemplate}"
ItemsSource="{x:Bind ViewModel.GraphicsAPIList}"
@@ -32,12 +33,14 @@
Style="{StaticResource ComboBoxSettingStyle}" />
</local:SettingContainer>
<local:SettingContainer x:Uid="Globals_DisablePartialInvalidation">
<local:SettingContainer x:Name="DisablePartialInvalidation"
x:Uid="Globals_DisablePartialInvalidation">
<ToggleSwitch IsOn="{x:Bind ViewModel.DisablePartialInvalidation, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>
<local:SettingContainer x:Uid="Globals_SoftwareRendering">
<local:SettingContainer x:Name="SoftwareRendering"
x:Uid="Globals_SoftwareRendering">
<ToggleSwitch IsOn="{x:Bind ViewModel.SoftwareRendering, Mode=TwoWay}"
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
</local:SettingContainer>

View File

@@ -546,6 +546,10 @@
<value>Use the legacy input encoding</value>
<comment>Header for a control to toggle legacy input encoding for the terminal.</comment>
</data>
<data name="Profile_AllowKittyKeyboardMode.Header" xml:space="preserve">
<value>Allow Kitty Keyboard Protocol</value>
<comment>{Locked="Kitty"}Kitty is a moniker and its Keyboard Protocol is a protocol specification.</comment>
</data>
<data name="Profile_AllowVtChecksumReport.Header" xml:space="preserve">
<value>Allow DECRQCRA (Request Checksum of Rectangular Area)</value>
<comment>{Locked="DECRQCRA"}{Locked="Request Checksum of Rectangular Area"}Header for a control to toggle support for the DECRQCRA control sequence.</comment>
@@ -1766,6 +1770,10 @@
<value>Keybindings</value>
<comment>Name for a control which contains the list of keybindings for the current command.</comment>
</data>
<data name="Actions_CommandDetails.Text" xml:space="preserve">
<value>Command details</value>
<comment>Label for the list of editable command details for the current command. This includes the command name and the shortcut action type.</comment>
</data>
<data name="Actions_Arguments.Text" xml:space="preserve">
<value>Additional arguments</value>
<comment>Label for the list of editable arguments for the currently selected action.</comment>
@@ -2583,19 +2591,19 @@
<comment>An option to choose from for the "path translation" setting.</comment>
</data>
<data name="Profile_PathTranslationStyleWsl.Content" xml:space="preserve">
<value>WSL (C:\ -> /mnt/c)</value>
<value>WSL (C:\ -&gt; /mnt/c)</value>
<comment>{Locked="WSL","C:\","/mnt/c"} An option to choose from for the "path translation" setting.</comment>
</data>
<data name="Profile_PathTranslationStyleCygwin.Content" xml:space="preserve">
<value>Cygwin (C:\ -> /cygdrive/c)</value>
<value>Cygwin (C:\ -&gt; /cygdrive/c)</value>
<comment>{Locked="Cygwin","C:\","/cygdrive/c"} An option to choose from for the "path translation" setting.</comment>
</data>
<data name="Profile_PathTranslationStyleMsys2.Content" xml:space="preserve">
<value>MSYS2 (C:\ -> /c)</value>
<value>MSYS2 (C:\ -&gt; /c)</value>
<comment>{Locked="MSYS2","C:\","/c"} An option to choose from for the "path translation" setting.</comment>
</data>
<data name="Profile_PathTranslationStyleMinGW.Content" xml:space="preserve">
<value>MinGW (C:\ -> C:/)</value>
<value>MinGW (C:\ -&gt; C:/)</value>
<comment>{Locked="MinGW","C:\","C:/"} An option to choose from for the "path translation" setting.</comment>
</data>
<data name="Profile_Delete_Orphaned.Header" xml:space="preserve">
@@ -2709,6 +2717,14 @@
<data name="Settings_ResetApplicationStateConfirmationButton.Content" xml:space="preserve">
<value>Yes, clear the cache</value>
</data>
<data name="Nav_SearchBox.PlaceholderText" xml:space="preserve">
<value>Search for settings</value>
<comment>Placeholder text for the main search box in the settings editor.</comment>
</data>
<data name="Search_NoResults" xml:space="preserve">
<value>No results for "{}"</value>
<comment>{Locked="{}"} Displayed when no results were found for a given query. "{}" will be replaced with the query.</comment>
</data>
<data name="IconPicker_BuiltInIcon.PlaceholderText" xml:space="preserve">
<value>Type to filter icons</value>
<comment>Placeholder text for a text box to filter and select an icon.</comment>

View File

@@ -0,0 +1,476 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "SearchIndex.h"
#include "FilteredSearchResult.g.cpp"
#include "SearchResultTemplateSelector.g.cpp"
#include "NavConstants.h"
#include <winrt/Windows.ApplicationModel.Resources.Core.h>
#include <ScopedResourceLoader.h>
// Weight multipliers for search result scoring.
// Higher values prioritize certain types of matches over others.
static constexpr int WeightRuntimeObjectMatch = 6; // Direct runtime object name match (e.g., "PowerShell")
static constexpr int WeightProfileDefaults = 6; // Profile Defaults setting
static constexpr int WeightRuntimeObjectSetting = 5; // Setting with runtime object context (e.g., "PowerShell: Command line")
static constexpr int WeightDisplayTextLocalized = 5; // Display text in current locale
static constexpr int WeightDisplayTextNeutral = 2; // Display text in English (fallback)
// Minimum fzf score threshold to filter out low-quality fuzzy matches
static constexpr int MinimumMatchScore = 100;
namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
namespace WUX = Windows::UI::Xaml;
}
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::System;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
// Retrieves the searchable fields from the LocalizedIndexEntry and their associated weight bonus.
// This allows us to prioritize certain fields over others when scoring search results.
std::array<std::pair<std::optional<winrt::hstring>, int>, 2> LocalizedIndexEntry::GetSearchableFields() const
{
// Profile Defaults entries (DisplayTextUid starts with "Profile_") get a higher weight
const auto weight = til::starts_with(std::wstring_view{ Entry->DisplayTextUid }, L"Profile_") ? WeightProfileDefaults : WeightDisplayTextLocalized;
return { { { std::optional<winrt::hstring>{ Entry->DisplayTextLocalized }, weight },
{ DisplayTextNeutral, WeightDisplayTextNeutral } } };
}
const ScopedResourceLoader& EnglishOnlyResourceLoader() noexcept
{
static ScopedResourceLoader loader{ GetLibraryResourceLoader().WithQualifier(L"language", L"en-US") };
return loader;
}
DataTemplate SearchResultTemplateSelector::SelectTemplateCore(const IInspectable& item, const DependencyObject& /*container*/)
{
return SelectTemplateCore(item);
}
DataTemplate SearchResultTemplateSelector::SelectTemplateCore(const IInspectable& item)
{
if (const auto searchResultItem = item.try_as<FilteredSearchResult>())
{
if (!searchResultItem->SecondaryLabel().empty())
{
return ComplexTemplate();
}
return BasicTemplate();
}
return nullptr;
}
Editor::FilteredSearchResult FilteredSearchResult::CreateNoResultsItem(const winrt::hstring& query)
{
return winrt::make<FilteredSearchResult>(nullptr, nullptr, hstring{ fmt::format(fmt::runtime(std::wstring{ RS_(L"Search_NoResults") }), query) });
}
// Creates a FilteredSearchResult with the given search index entry and runtime object.
// The resulting search result will have a label like "<ProfileName>: <display text>" or "<ProfileName>".
// This is so that we can reuse the display text from the search index, but also add additional context to which runtime object this search result maps to.
Editor::FilteredSearchResult FilteredSearchResult::CreateRuntimeObjectItem(const LocalizedIndexEntry* searchIndexEntry, const Windows::Foundation::IInspectable& runtimeObj)
{
hstring runtimeObjLabel{};
hstring runtimeObjContext{};
if (const auto profileVM = runtimeObj.try_as<Editor::ProfileViewModel>())
{
// No runtimeObjContext: profile name and icon should be enough
runtimeObjLabel = profileVM.Name();
}
else if (const auto colorSchemeVM = runtimeObj.try_as<Editor::ColorSchemeViewModel>())
{
// No runtimeObjContext: scheme name and generic icon should be enough
runtimeObjLabel = colorSchemeVM.Name();
}
else if (const auto ntmFolderEntryVM = runtimeObj.try_as<Editor::FolderEntryViewModel>())
{
runtimeObjLabel = ntmFolderEntryVM.Name();
runtimeObjContext = RS_(L"Nav_NewTabMenu/Content");
}
else if (const auto extensionPackageVM = runtimeObj.try_as<Editor::ExtensionPackageViewModel>())
{
runtimeObjLabel = extensionPackageVM.DisplayName();
runtimeObjContext = RS_(L"Nav_Extensions/Content");
}
else if (const auto commandVM = runtimeObj.try_as<Editor::CommandViewModel>())
{
runtimeObjLabel = commandVM.DisplayName();
runtimeObjContext = RS_(L"Nav_Actions/Content");
}
if (const auto& displayText = searchIndexEntry->Entry->DisplayTextLocalized; !displayText.empty())
{
// Full index entry (for settings within runtime objects)
// - primaryText: <displayText>
// - secondaryText: <runtimeObjLabel>
// navigates to setting container
return winrt::make<FilteredSearchResult>(searchIndexEntry, runtimeObj, displayText, runtimeObjLabel);
}
// Partial index entry (for runtime object main pages)
// - primaryText: <runtimeObjLabel>
// - secondaryText: <runtimeObjContext>
// "PowerShell" --> navigates to main runtime object page (i.e. Profiles_Base)
// "SSH" | "Extension" --> navigates to main runtime object page (i.e. Extensions > SSH)
return winrt::make<FilteredSearchResult>(searchIndexEntry, runtimeObj, runtimeObjLabel, runtimeObjContext);
}
winrt::hstring FilteredSearchResult::Label() const
{
if (_overrideLabel)
{
return *_overrideLabel;
}
return _SearchIndexEntry->Entry->DisplayTextLocalized;
}
bool FilteredSearchResult::IsNoResultsPlaceholder() const
{
return _overrideLabel.has_value() && !_NavigationArgOverride;
}
Windows::Foundation::IInspectable FilteredSearchResult::NavigationArg() const
{
if (_NavigationArgOverride)
{
return _NavigationArgOverride;
}
else if (_SearchIndexEntry)
{
return _SearchIndexEntry->Entry->NavigationArg;
}
return nullptr;
}
Windows::UI::Xaml::Controls::IconElement FilteredSearchResult::Icon() const
{
// We need to set the icon size here as opposed to in the XAML.
// Setting it in the XAML just crops the icon.
static constexpr double iconSize = 16;
if (auto navigationArg = NavigationArg())
{
if (const auto profileVM = navigationArg.try_as<Editor::ProfileViewModel>())
{
auto icon = UI::IconPathConverter::IconWUX(profileVM.EvaluatedIcon());
icon.Width(iconSize);
icon.Height(iconSize);
return icon;
}
else if (const auto colorSchemeVM = navigationArg.try_as<Editor::ColorSchemeViewModel>())
{
WUX::Controls::FontIcon icon{};
icon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
icon.FontSize(iconSize);
icon.Glyph(NavTagIconMap[colorSchemesTag]);
return icon;
}
else if (const auto ntmFolderEntryVM = navigationArg.try_as<Editor::FolderEntryViewModel>())
{
auto icon = UI::IconPathConverter::IconWUX(ntmFolderEntryVM.Icon());
icon.Width(iconSize);
icon.Height(iconSize);
return icon;
}
else if (const auto extensionPackageVM = navigationArg.try_as<Editor::ExtensionPackageViewModel>())
{
// TODO GH #19806: IconWUX() sets a size on the icon automatically. This is great
// for most icons, but font icons end up being a weird size.
// Check if we're using the generic font icon, and, if so, just build it ourselves
// so that it looks right.
const auto& extPkgVMIconPath = extensionPackageVM.Icon();
if (extPkgVMIconPath == NavTagIconMap[extensionsTag])
{
WUX::Controls::FontIcon icon{};
icon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
icon.FontSize(iconSize);
icon.Glyph(NavTagIconMap[extensionsTag]);
return icon;
}
auto icon = UI::IconPathConverter::IconWUX(extPkgVMIconPath);
icon.Width(iconSize);
icon.Height(iconSize);
return icon;
}
else if (const auto commandVM = navigationArg.try_as<Editor::CommandViewModel>())
{
WUX::Controls::FontIcon icon{};
icon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
icon.FontSize(iconSize);
icon.Glyph(NavTagIconMap[actionsTag]);
return icon;
}
else if (const auto stringNavArg = navigationArg.try_as<hstring>())
{
WUX::Controls::FontIcon icon{};
icon.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
icon.FontSize(iconSize);
if (_SearchIndexEntry->Entry->SubPage == BreadcrumbSubPage::ColorSchemes_Edit)
{
// If we're editing a color scheme, stringNavArg is the color scheme name.
// Use the color scheme icon.
icon.Glyph(NavTagIconMap[colorSchemesTag]);
return icon;
}
else if (const auto it = NavTagIconMap.find(*stringNavArg); it != NavTagIconMap.end())
{
// Use the font icon used by the navigation view item
icon.Glyph(it->second);
return icon;
}
}
}
return nullptr;
}
// Reset the search index to the build-time data from GeneratedSettingsIndex.g.h
void SearchIndex::Reset()
{
// copied from CommandPaletteItems.h
static bool shouldIncludeLanguageNeutralResources = [] {
try
{
const auto context{ winrt::Windows::ApplicationModel::Resources::Core::ResourceContext::GetForViewIndependentUse() };
const auto qualifiers{ context.QualifierValues() };
if (const auto language{ qualifiers.TryLookup(L"language") })
{
return !til::starts_with_insensitive_ascii(*language, L"en-");
}
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
return false;
}();
// Creates the LocalizedIndexEntry wrapper objects around the given index entries
// and adds them to the given storage vector.
auto loadLocalizedIndex = [&](const auto& indexRef) {
std::vector<LocalizedIndexEntry> localizedIndex;
localizedIndex.reserve(indexRef.size());
for (const auto& entry : indexRef)
{
LocalizedIndexEntry localizedEntry;
localizedEntry.Entry = &entry;
if (shouldIncludeLanguageNeutralResources)
{
localizedEntry.DisplayTextNeutral = EnglishOnlyResourceLoader().GetLocalizedString(entry.DisplayTextUid);
}
localizedIndex.emplace_back(std::move(localizedEntry));
}
return localizedIndex;
};
IndexData indexData{
.mainIndex = loadLocalizedIndex(LoadBuildTimeIndex()),
.profileIndex = loadLocalizedIndex(LoadProfileIndex()),
.ntmFolderIndex = loadLocalizedIndex(LoadNTMFolderIndex()),
.colorSchemeIndex = loadLocalizedIndex(LoadColorSchemeIndex())
};
// Load partial entries for runtime object main pages
// (i.e. PowerShell profile --> PartialProfileIndexEntry() --> Profile page w/ PowerShell profile VM as context)
indexData.profileIndexEntry.Entry = &PartialProfileIndexEntry();
indexData.ntmFolderIndexEntry.Entry = &PartialNTMFolderIndexEntry();
indexData.extensionIndexEntry.Entry = &PartialExtensionIndexEntry();
indexData.colorSchemeIndexEntry.Entry = &PartialColorSchemeIndexEntry();
indexData.actionIndexEntry.Entry = &PartialActionIndexEntry();
_index = std::move(indexData);
}
// Method Description:
// - Gets the search results based on the given query string. Can be cancelled.
// - Some results (i.e. profiles) are dynamically generated at runtime, so they need to be passed in here so that we can include them.
// - LOAD-BEARING: vector views CANNOT be passed by reference. We need the AddRef to avoid lifetime issues with the async operation.
// Arguments:
// - query - the search query string
// - profileVMs - the list of profile view models to search
// - ntmFolderVMs - the list of New Tab Menu folder entry view models to search
// - colorSchemeVMs - the list of color scheme view models to search
// - extensionPkgVMs - the list of extension package view models to search
// - commandVMs - the list of command view models to search
// Return value:
// - The results are sorted by score (best matches first).
// - If no results are found, a "no results" placeholder item is returned.
IAsyncOperation<IObservableVector<Windows::Foundation::IInspectable>> SearchIndex::SearchAsync(const hstring& query,
const IVectorView<Editor::ProfileViewModel> profileVMs,
const IVectorView<Editor::FolderEntryViewModel> ntmFolderVMs,
const IVectorView<Editor::ColorSchemeViewModel> colorSchemeVMs,
const IVectorView<Editor::ExtensionPackageViewModel> extensionPkgVMs,
const IVectorView<Editor::CommandViewModel> commandVMs)
{
co_await winrt::resume_background();
// The search can be cancelled at any time by the caller.
// Get a token to check for cancellation as we go.
auto cancellationToken = co_await get_cancellation_token();
const auto filter = fzf::matcher::ParsePattern(std::wstring_view{ query });
std::vector<std::pair<int, Editor::FilteredSearchResult>> scoredResults;
for (const auto& entry : _index.mainIndex)
{
if (cancellationToken())
{
break;
}
// Calculate best score across all searchable fields for entry
int bestScore = 0;
for (const auto& [searchTextOpt, weight] : entry.GetSearchableFields())
{
if (searchTextOpt.has_value())
{
if (const auto match = fzf::matcher::Match(searchTextOpt.value(), filter))
{
bestScore = std::max(bestScore, match->Score * weight);
}
}
}
if (bestScore >= MinimumMatchScore)
{
scoredResults.emplace_back(bestScore, winrt::make<FilteredSearchResult>(&entry));
}
}
// Method Description:
// - Filter and score index dependent on runtime objects (i.e. profiles)
// - Matches against combined text: "<searchable field> <runtime object name>"
// - This allows queries like "font size powershell" to find "PowerShell: Font size"
// Arguments:
// - runtimeObjectList: the list of runtime objects to search (i.e. profiles, ntm folders, extensions, etc...)
// - searchIndex: the corresponding localized search index for the runtime object type (i.e. profile -> _index.profileIndex)
// - partialSearchIndexEntry: index entry that is populated with the runtime object (i.e. profile -> _index.profileIndexEntry)
auto appendRuntimeObjectResults = [&](const auto& runtimeObjectList, const std::vector<LocalizedIndexEntry>& searchIndex, const auto& partialSearchIndexEntry) {
for (const auto& runtimeObj : runtimeObjectList)
{
if (cancellationToken())
{
break;
}
// Check for direct runtime object name match first
const auto& objName = runtimeObj.Name();
const auto objNameMatch = fzf::matcher::Match(objName, filter);
if (objNameMatch && objNameMatch->Score >= MinimumMatchScore)
{
// navigates to runtime object main page (i.e. "PowerShell" Profiles_Base page)
scoredResults.emplace_back(objNameMatch->Score * WeightRuntimeObjectMatch,
FilteredSearchResult::CreateRuntimeObjectItem(&partialSearchIndexEntry, runtimeObj));
}
for (const auto& entry : searchIndex)
{
if (cancellationToken())
{
break;
}
// Calculate best score across all searchable fields for entry
int bestScore = 0;
for (const auto& [searchTextOpt, _] : entry.GetSearchableFields())
{
if (searchTextOpt.has_value())
{
// Score for combined text: "<searchable field> <runtime object name>"
const auto combinedText = fmt::format(L"{} {}", std::wstring_view{ searchTextOpt.value() }, std::wstring_view{ objName });
const auto combinedMatch = fzf::matcher::Match(combinedText, filter);
if (!combinedMatch)
{
continue;
}
const auto settingMatch = fzf::matcher::Match(searchTextOpt.value(), filter);
// Scoring:
// 1. require EITHER the runtime object OR the setting to match the query independently,
// OR the combined match scores very well (handles "font size powershell" where neither matches alone)
// 2. only include if query matches combined text (i.e. "font size PowerShell" matches "<setting name> <profile name>")
// 3. combined match scores higher than runtime object alone (setting must contribute to the match)
// NOTE: don't compare to settingMatch! This allows "font size" to show results for all profiles
const auto hasIndependentMatch = objNameMatch || settingMatch;
const auto hasStrongCombinedMatch = combinedMatch->Score >= MinimumMatchScore * 3;
if (!hasIndependentMatch && !hasStrongCombinedMatch)
{
continue;
}
if (combinedMatch->Score > (objNameMatch ? objNameMatch->Score : 0))
{
bestScore = std::max(combinedMatch->Score,
settingMatch ? settingMatch->Score : 0);
}
}
}
if (bestScore >= MinimumMatchScore)
{
// navigates to runtime object's setting (i.e. "PowerShell: Command line")
scoredResults.emplace_back(bestScore * WeightRuntimeObjectSetting,
FilteredSearchResult::CreateRuntimeObjectItem(&entry, runtimeObj));
}
}
}
};
appendRuntimeObjectResults(profileVMs, _index.profileIndex, _index.profileIndexEntry);
appendRuntimeObjectResults(ntmFolderVMs, _index.ntmFolderIndex, _index.ntmFolderIndexEntry);
appendRuntimeObjectResults(colorSchemeVMs, _index.colorSchemeIndex, _index.colorSchemeIndexEntry);
// Simple runtime object matching (no associated search index, just match by display name)
auto appendSimpleRuntimeObjectResults = [&](const auto& runtimeObjectList, const LocalizedIndexEntry& indexEntry, auto getDisplayName) {
for (const auto& runtimeObj : runtimeObjectList)
{
if (cancellationToken())
{
break;
}
if (const auto match = fzf::matcher::Match(getDisplayName(runtimeObj), filter); match && match->Score >= MinimumMatchScore)
{
// navigates to runtime object page (i.e. "Copy Text" --> Actions > EditAction page)
scoredResults.emplace_back(match->Score * WeightRuntimeObjectMatch,
FilteredSearchResult::CreateRuntimeObjectItem(&indexEntry, runtimeObj));
}
}
};
appendSimpleRuntimeObjectResults(extensionPkgVMs, _index.extensionIndexEntry, [](const auto& ext) { return ext.DisplayName(); });
appendSimpleRuntimeObjectResults(commandVMs, _index.actionIndexEntry, [](const auto& cmd) { return cmd.DisplayName(); });
// must be IInspectable to be used as ItemsSource in XAML
std::vector<Windows::Foundation::IInspectable> results;
if (scoredResults.empty())
{
// Explicitly show "no results"
results.reserve(1);
results.emplace_back(FilteredSearchResult::CreateNoResultsItem(query));
}
else
{
// Sort results by score (descending)
results.reserve(scoredResults.size());
std::sort(scoredResults.begin(), scoredResults.end(), [](const auto& a, const auto& b) { return a.first > b.first; });
std::transform(scoredResults.begin(), scoredResults.end(), std::back_inserter(results), [](const auto& pair) { return pair.second; });
}
if (cancellationToken())
{
// Search was cancelled; do not return any results
co_return single_threaded_observable_vector<Windows::Foundation::IInspectable>();
}
co_return single_threaded_observable_vector(std::move(results));
}
}

View File

@@ -0,0 +1,117 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "FilteredSearchResult.g.h"
#include "SearchResultTemplateSelector.g.h"
#include "GeneratedSettingsIndex.g.h"
#include <til/generational.h>
#include "..\fzf\fzf.h"
class ScopedResourceLoader;
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
const ScopedResourceLoader& EnglishOnlyResourceLoader() noexcept;
// Wrapper for IndexEntry objects.
// Adds neutral locale, if necessary.
struct LocalizedIndexEntry
{
std::optional<winrt::hstring> DisplayTextNeutral = std::nullopt;
const IndexEntry* Entry = nullptr;
std::array<std::pair<std::optional<winrt::hstring>, int>, 2> GetSearchableFields() const;
};
// WinRT object representing a single filtered search result
struct FilteredSearchResult : FilteredSearchResultT<FilteredSearchResult>
{
FilteredSearchResult(const LocalizedIndexEntry* entry, const Windows::Foundation::IInspectable& navigationArgOverride = nullptr, const std::optional<hstring>& label = std::nullopt, const hstring secondaryLabel = {}) :
_SearchIndexEntry{ entry },
_NavigationArgOverride{ navigationArgOverride },
_overrideLabel{ label },
_secondaryLabel{ secondaryLabel } {}
static Editor::FilteredSearchResult CreateNoResultsItem(const winrt::hstring& query);
static Editor::FilteredSearchResult CreateRuntimeObjectItem(const LocalizedIndexEntry* searchIndexEntry, const Windows::Foundation::IInspectable& runtimeObj);
hstring ToString() { return Label(); }
winrt::hstring Label() const;
winrt::hstring SecondaryLabel() const { return _secondaryLabel; };
bool IsNoResultsPlaceholder() const;
const LocalizedIndexEntry& SearchIndexEntry() const noexcept { return *_SearchIndexEntry; }
Windows::Foundation::IInspectable NavigationArg() const;
Windows::UI::Xaml::Controls::IconElement Icon() const;
private:
const std::optional<winrt::hstring> _overrideLabel{ std::nullopt };
const winrt::hstring _secondaryLabel{};
const Windows::Foundation::IInspectable _NavigationArgOverride{ nullptr };
const LocalizedIndexEntry* _SearchIndexEntry{ nullptr };
};
struct SearchResultTemplateSelector : SearchResultTemplateSelectorT<SearchResultTemplateSelector>
{
SearchResultTemplateSelector() = default;
Windows::UI::Xaml::DataTemplate SelectTemplateCore(const Windows::Foundation::IInspectable& item, const Windows::UI::Xaml::DependencyObject& container);
Windows::UI::Xaml::DataTemplate SelectTemplateCore(const Windows::Foundation::IInspectable& item);
til::property<winrt::Windows::UI::Xaml::DataTemplate> BasicTemplate;
til::property<winrt::Windows::UI::Xaml::DataTemplate> ComplexTemplate;
};
// The main search index implemented as a singleton.
// The index loads data generated by tools\GenerateSettingsIndex.ps1 (outputs "Generated Files\GeneratedSettingsIndex.g.h" and cpp).
// Outputs FilteredSearchResult objects for UI display. UI is determined by SearchResultTemplateSelector.
class SearchIndex final
{
public:
static SearchIndex& Instance() noexcept
{
static SearchIndex instance;
return instance;
}
SearchIndex(const SearchIndex&) = delete;
SearchIndex& operator=(const SearchIndex&) = delete;
SearchIndex(SearchIndex&&) = delete;
SearchIndex& operator=(SearchIndex&&) = delete;
void Reset();
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable>> SearchAsync(const winrt::hstring& query,
const Windows::Foundation::Collections::IVectorView<Editor::ProfileViewModel> profileVMs,
const Windows::Foundation::Collections::IVectorView<Editor::FolderEntryViewModel> ntmFolderVMs,
const Windows::Foundation::Collections::IVectorView<Editor::ColorSchemeViewModel> colorSchemeVMs,
const Windows::Foundation::Collections::IVectorView<Editor::ExtensionPackageViewModel> extensionPkgVMs,
const Windows::Foundation::Collections::IVectorView<Editor::CommandViewModel> commandVMs);
private:
SearchIndex() = default;
struct IndexData
{
IndexData& operator=(const IndexData& other) = default;
// Basic index data loaded from GeneratedSettingsIndex.g.h and wrapped as LocalizedIndexEntry objects.
// Non-main indices are duplicated at runtime when searching for runtime objects.
std::vector<LocalizedIndexEntry> mainIndex;
std::vector<LocalizedIndexEntry> profileIndex;
std::vector<LocalizedIndexEntry> ntmFolderIndex;
std::vector<LocalizedIndexEntry> colorSchemeIndex;
// Links to main page; used when searching runtime objects
LocalizedIndexEntry profileIndexEntry;
LocalizedIndexEntry ntmFolderIndexEntry;
LocalizedIndexEntry colorSchemeIndexEntry;
LocalizedIndexEntry extensionIndexEntry;
LocalizedIndexEntry actionIndexEntry;
} _index;
};
}
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
{
BASIC_FACTORY(SearchResultTemplateSelector);
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "MainPage.idl";
namespace Microsoft.Terminal.Settings.Editor
{
runtimeclass FilteredSearchResult : Windows.Foundation.IStringable
{
String Label { get; };
String SecondaryLabel { get; };
Windows.UI.Xaml.Controls.IconElement Icon { get; };
}
[default_interface] runtimeclass SearchResultTemplateSelector : Windows.UI.Xaml.Controls.DataTemplateSelector
{
SearchResultTemplateSelector();
Windows.UI.Xaml.DataTemplate BasicTemplate;
Windows.UI.Xaml.DataTemplate ComplexTemplate;
}
}

View File

@@ -3,6 +3,8 @@
#pragma once
#include "SettingContainer.h"
// This macro must be used alongside GETSET_BINDABLE_ENUM_SETTING.
// Use this in your class's constructor after Initialize_Component().
// It sorts and initializes the observable list of enum entries with the enum name
@@ -116,4 +118,32 @@ struct HasScrollViewer
DismissAllPopups(uielem.XamlRoot());
}
}
// Finds the element with the given name and brings it into view
void BringIntoViewWhenLoaded(const winrt::hstring elementName)
{
if (elementName.empty())
{
return;
}
auto* pThis = static_cast<T*>(this);
_loadedRevoker = pThis->Loaded(winrt::auto_revoke, [weakThis{ pThis->get_weak() }, elementName](auto&&, auto&&) {
if (auto page{ weakThis.get() })
{
if (const auto& controlToFocus{ page->FindName(elementName).try_as<winrt::Windows::UI::Xaml::Controls::Control>() })
{
// We need to wait for the page to be loaded
// or else the call to StartBringIntoView()
// will end up doing nothing
controlToFocus.StartBringIntoView();
controlToFocus.Focus(winrt::Windows::UI::Xaml::FocusState::Programmatic);
}
page->_loadedRevoker.revoke();
}
});
}
protected:
winrt::Windows::UI::Xaml::FrameworkElement::Loaded_revoker _loadedRevoker;
};

View File

@@ -59,9 +59,8 @@ namespace winrt
namespace WARC = ::winrt::Windows::ApplicationModel::Resources::Core;
}
// Like RS_ and RS_fmt, but they use an ambient boolean named "localized" to
// determine whether to load the English version of a resource or the localized
// one.
// Like RS_ and RS_fmt, but they use an ambient context to determine
// whether to load the English version of a resource or the localized one.
#define RS_switchable_(x) RS_switchable_impl(context, USES_RESOURCE(x))
#define RS_switchable_fmt(x, ...) RS_switchable_fmt_impl(context, USES_RESOURCE(x), __VA_ARGS__)

View File

@@ -103,6 +103,7 @@ Author(s):
X(bool, ReloadEnvironmentVariables, "compatibility.reloadEnvironmentVariables", true) \
X(bool, RainbowSuggestions, "experimental.rainbowSuggestions", false) \
X(bool, ForceVTInput, "compatibility.input.forceVT", false) \
X(bool, AllowKittyKeyboardMode, "compatibility.kittyKeyboardMode", true) \
X(bool, AllowVtChecksumReport, "compatibility.allowDECRQCRA", false) \
X(bool, AllowVtClipboardWrite, "compatibility.allowOSC52", true) \
X(bool, AllowKeypadMode, "compatibility.allowDECNKM", false) \

View File

@@ -88,6 +88,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_PROFILE_SETTING(Boolean, ReloadEnvironmentVariables);
INHERITABLE_PROFILE_SETTING(Boolean, RainbowSuggestions);
INHERITABLE_PROFILE_SETTING(Boolean, ForceVTInput);
INHERITABLE_PROFILE_SETTING(Boolean, AllowKittyKeyboardMode);
INHERITABLE_PROFILE_SETTING(Boolean, AllowVtChecksumReport);
INHERITABLE_PROFILE_SETTING(Boolean, AllowKeypadMode);
INHERITABLE_PROFILE_SETTING(Boolean, AllowVtClipboardWrite);

View File

@@ -49,6 +49,7 @@
X(bool, TrimBlockSelection, true) \
X(bool, SuppressApplicationTitle) \
X(bool, ForceVTInput, false) \
X(bool, AllowKittyKeyboardMode, true) \
X(winrt::hstring, StartingTitle) \
X(bool, DetectURLs, true) \
X(bool, AutoMarkPrompts) \

View File

@@ -244,6 +244,39 @@ private:
static winrt::Windows::UI::Xaml::DependencyProperty _##name##Property;
#endif
#ifndef ATTACHED_DEPENDENCY_PROPERTY
#define ATTACHED_DEPENDENCY_PROPERTY(type, name) \
public: \
static winrt::Windows::UI::Xaml::DependencyProperty name##Property() \
{ \
return _##name##Property; \
} \
static type Get##name(winrt::Windows::UI::Xaml::DependencyObject const& target) \
{ \
auto&& temp{ target.GetValue(_##name##Property) }; \
if (temp) \
{ \
return winrt::unbox_value<type>(temp); \
} \
\
if constexpr (std::is_base_of_v<winrt::Windows::Foundation::IInspectable, type>) \
{ \
return { nullptr }; \
} \
else \
{ \
return {}; \
} \
} \
static void Set##name(winrt::Windows::UI::Xaml::DependencyObject const& target, const type& value) \
{ \
target.SetValue(_##name##Property, winrt::box_value(value)); \
} \
\
private: \
static winrt::Windows::UI::Xaml::DependencyProperty _##name##Property;
#endif
// Use this macro for quickly defining the factory_implementation part of a
// class. CppWinrt requires these for the compiler, but more often than not,
// they require no customization. See

View File

@@ -2,7 +2,7 @@
// Licensed under the MIT license.
#include "precomp.h"
#include "..\TerminalApp\fzf\fzf.h"
#include "..\fzf\fzf.h"
using namespace Microsoft::Console;
using namespace WEX::Logging;

View File

@@ -117,6 +117,14 @@ using namespace Microsoft::Console::Interactivity;
CATCH_RETURN();
}
// TODO GH#19847: Avoid translating win32im sequences to Kitty Keyboard Protocol temporarily.
// This is because as of this writing, our implementation is brand new, and Windows Terminal
// needs a toggle to disable it. That only works if ConPTY then doesn't do it anyway.
if (const auto inputBuffer = ServiceLocator::LocateGlobals().getConsoleInformation().pInputBuffer)
{
inputBuffer->GetTerminalInput().ForceDisableKittyKeyboardProtocol(true);
}
// The only way we're initialized is if the args said we're in conpty mode.
// If the args say so, then at least one of in, out, or signal was specified
_state = State::Initialized;

View File

@@ -19,6 +19,8 @@ Revision History:
#include <til/bit.h>
#include <IDataSource.h>
// Helper for declaring a variable to store a TEST_METHOD_PROPERTY and get it's value from the test metadata
#define INIT_TEST_PROPERTY(type, identifier, description) \
type identifier; \
@@ -45,6 +47,95 @@ Revision History:
namespace WEX::TestExecution
{
struct ArrayIndexTaefAdapterRow : Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom | Microsoft::WRL::InhibitFtmBase>, IDataRow>
{
HRESULT RuntimeClassInitialize(const size_t index)
{
_index = index;
return S_OK;
}
STDMETHODIMP GetTestData(BSTR /*pszName*/, SAFEARRAY** ppData) override
{
wchar_t buf[16];
swprintf_s(buf, L"%zu", _index);
LONG idx = 0;
const auto array = SafeArrayCreateVector(VT_BSTR, 0, 1);
SafeArrayPutElement(array, &idx, SysAllocString(buf));
*ppData = array;
return S_OK;
}
STDMETHODIMP GetMetadataNames(SAFEARRAY** ppMetadataNames) override
{
*ppMetadataNames = nullptr;
return S_FALSE;
}
STDMETHODIMP GetMetadata(BSTR /*pszName*/, SAFEARRAY** ppData) override
{
*ppData = nullptr;
return S_FALSE;
}
STDMETHODIMP GetName(BSTR* ppszRowName) override
{
*ppszRowName = nullptr;
return S_FALSE;
}
private:
size_t _index = 0;
};
struct ArrayIndexTaefAdapterSource : Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom | Microsoft::WRL::InhibitFtmBase>, IDataSource>
{
HRESULT RuntimeClassInitialize(const size_t count)
{
_count = count;
return S_OK;
}
STDMETHODIMP Advance(IDataRow** ppDataRow) override
{
if (_index < _count)
{
return Microsoft::WRL::MakeAndInitialize<ArrayIndexTaefAdapterRow>(ppDataRow, _index++);
}
else
{
*ppDataRow = nullptr;
return S_OK;
}
}
STDMETHODIMP Reset() override
{
_index = 0;
return S_OK;
}
STDMETHODIMP GetTestDataNames(SAFEARRAY** names) override
{
LONG idx = 0;
const auto array = SafeArrayCreateVector(VT_BSTR, 0, 1);
SafeArrayPutElement(array, &idx, SysAllocString(L"index"));
*names = array;
return S_OK;
}
STDMETHODIMP GetTestDataType(BSTR /*name*/, BSTR* type) override
{
*type = nullptr;
return S_OK;
}
private:
size_t _count = 0;
size_t _index = 0;
};
// Compare two floats using a ULP (unit last place) tolerance of up to 4.
// Allows you to compare two floats that are almost equal.
// Think of: 0.200000000000000 vs. 0.200000000000001.

View File

@@ -109,93 +109,6 @@ static constexpr til::point point_offset_by_line(const til::point start, const t
// IMPORTANT: reference this _after_ defining point_offset_by_XXX. We need it for some definitions
#include "GeneratedUiaTextRangeMovementTests.g.cpp"
namespace
{
#pragma region TAEF hookup for the test case array above
struct ArrayIndexTaefAdapterRow : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom | Microsoft::WRL::InhibitFtmBase>, IDataRow>
{
HRESULT RuntimeClassInitialize(const size_t index)
{
_index = index;
return S_OK;
}
STDMETHODIMP GetTestData(BSTR /*pszName*/, SAFEARRAY** ppData) override
{
const auto indexString{ wil::str_printf<std::wstring>(L"%zu", _index) };
auto safeArray{ SafeArrayCreateVector(VT_BSTR, 0, 1) };
LONG index{ 0 };
auto indexBstr{ wil::make_bstr(indexString.c_str()) };
(void)SafeArrayPutElement(safeArray, &index, indexBstr.release());
*ppData = safeArray;
return S_OK;
}
STDMETHODIMP GetMetadataNames(SAFEARRAY** ppMetadataNames) override
{
*ppMetadataNames = nullptr;
return S_FALSE;
}
STDMETHODIMP GetMetadata(BSTR /*pszName*/, SAFEARRAY** ppData) override
{
*ppData = nullptr;
return S_FALSE;
}
STDMETHODIMP GetName(BSTR* ppszRowName) override
{
*ppszRowName = wil::make_bstr(s_movementTests[_index].name.data()).release();
return S_OK;
}
private:
size_t _index;
};
struct ArrayIndexTaefAdapterSource : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom | Microsoft::WRL::InhibitFtmBase>, IDataSource>
{
STDMETHODIMP Advance(IDataRow** ppDataRow) override
{
if (_index < s_movementTests.size())
{
Microsoft::WRL::MakeAndInitialize<ArrayIndexTaefAdapterRow>(ppDataRow, _index++);
}
else
{
*ppDataRow = nullptr;
}
return S_OK;
}
STDMETHODIMP Reset() override
{
_index = 0;
return S_OK;
}
STDMETHODIMP GetTestDataNames(SAFEARRAY** names) override
{
auto safeArray{ SafeArrayCreateVector(VT_BSTR, 0, 1) };
LONG index{ 0 };
auto dataNameBstr{ wil::make_bstr(L"index") };
(void)SafeArrayPutElement(safeArray, &index, dataNameBstr.release());
*names = safeArray;
return S_OK;
}
STDMETHODIMP GetTestDataType(BSTR /*name*/, BSTR* type) override
{
*type = nullptr;
return S_OK;
}
private:
size_t _index{ 0 };
};
#pragma endregion
}
extern "C" HRESULT __declspec(dllexport) __cdecl GeneratedMovementTestDataSource(IDataSource** ppDataSource, void*)
{
auto source{ Microsoft::WRL::Make<ArrayIndexTaefAdapterSource>() };

View File

@@ -67,6 +67,10 @@ public:
virtual void DeleteColumn(const VTInt distance) = 0; // DECDC
virtual void SetKeypadMode(const bool applicationMode) = 0; // DECKPAM, DECKPNM
virtual void SetAnsiMode(const bool ansiMode) = 0; // DECANM
virtual void SetKittyKeyboardProtocol(const VTParameter flags, const VTParameter mode) noexcept = 0; // KKP
virtual void QueryKittyKeyboardProtocol() = 0; // KKP
virtual void PushKittyKeyboardProtocol(const VTParameter flags) = 0; // KKP
virtual void PopKittyKeyboardProtocol(const VTParameter count) = 0; // KKP
virtual void SetTopBottomScrollingMargins(const VTInt topMargin, const VTInt bottomMargin) = 0; // DECSTBM
virtual void SetLeftRightScrollingMargins(const VTInt leftMargin, const VTInt rightMargin) = 0; // DECSLRM
virtual void EnquireAnswerback() = 0; // ENQ

View File

@@ -2054,6 +2054,51 @@ void AdaptDispatch::SetKeypadMode(const bool fApplicationMode) noexcept
_terminalInput.SetInputMode(TerminalInput::Mode::Keypad, fApplicationMode);
}
// - DECANM - Sets the terminal emulation mode to either ANSI-compatible or VT52.
// Arguments:
// - ansiMode - set to true to enable the ANSI mode, false for VT52 mode.
void AdaptDispatch::SetAnsiMode(const bool ansiMode)
{
// When an attempt is made to update the mode, the designated character sets
// need to be reset to defaults, even if the mode doesn't actually change.
_termOutput.SoftReset();
_api.GetStateMachine().SetParserMode(StateMachine::Mode::Ansi, ansiMode);
_terminalInput.SetInputMode(TerminalInput::Mode::Ansi, ansiMode);
// While input mode changes are often forwarded over conpty, we never want
// to do that for the DECANM mode.
}
// CSI = flags ; mode u - Sets kitty keyboard protocol flags
void AdaptDispatch::SetKittyKeyboardProtocol(const VTParameter flags, const VTParameter mode) noexcept
{
const auto kittyFlags = gsl::narrow_cast<uint8_t>(flags.value_or(0));
const auto KittyKeyboardProtocol = static_cast<TerminalInput::KittyKeyboardProtocolMode>(mode.value_or(1));
_terminalInput.SetKittyKeyboardProtocol(kittyFlags, KittyKeyboardProtocol);
}
// CSI ? u - Queries current kitty keyboard protocol flags
void AdaptDispatch::QueryKittyKeyboardProtocol()
{
const auto flags = static_cast<VTInt>(_terminalInput.GetKittyFlags());
_ReturnCsiResponse(fmt::format(FMT_COMPILE(L"?{}u"), flags));
}
// CSI > flags u - Pushes current kitty keyboard flags onto the stack and sets new flags
void AdaptDispatch::PushKittyKeyboardProtocol(const VTParameter flags)
{
const auto kittyFlags = gsl::narrow_cast<uint8_t>(flags.value_or(0));
_terminalInput.PushKittyFlags(kittyFlags);
}
// CSI < count u - Pops one or more entries from the kitty keyboard stack
void AdaptDispatch::PopKittyKeyboardProtocol(const VTParameter count)
{
const auto popCount = static_cast<size_t>(count.value_or(1));
_terminalInput.PopKittyFlags(popCount);
}
// Routine Description:
// - Internal logic for adding or removing lines in the active screen buffer.
// This also moves the cursor to the left margin, which is expected behavior for IL and DL.
@@ -2148,22 +2193,6 @@ void AdaptDispatch::DeleteColumn(const VTInt distance)
_InsertDeleteColumnHelper(-distance);
}
// - DECANM - Sets the terminal emulation mode to either ANSI-compatible or VT52.
// Arguments:
// - ansiMode - set to true to enable the ANSI mode, false for VT52 mode.
void AdaptDispatch::SetAnsiMode(const bool ansiMode)
{
// When an attempt is made to update the mode, the designated character sets
// need to be reset to defaults, even if the mode doesn't actually change.
_termOutput.SoftReset();
_api.GetStateMachine().SetParserMode(StateMachine::Mode::Ansi, ansiMode);
_terminalInput.SetInputMode(TerminalInput::Mode::Ansi, ansiMode);
// While input mode changes are often forwarded over conpty, we never want
// to do that for the DECANM mode.
}
// Routine Description:
// - DECSTBM - Set Scrolling Region
// This control function sets the top and bottom margins for the current page.

View File

@@ -97,6 +97,10 @@ namespace Microsoft::Console::VirtualTerminal
void RequestMode(const DispatchTypes::ModeParams param) override; // DECRQM
void SetKeypadMode(const bool applicationMode) noexcept override; // DECKPAM, DECKPNM
void SetAnsiMode(const bool ansiMode) override; // DECANM
void SetKittyKeyboardProtocol(const VTParameter flags, const VTParameter mode) noexcept override; // KKP
void QueryKittyKeyboardProtocol() override; // KKP
void PushKittyKeyboardProtocol(const VTParameter flags) override; // KKP
void PopKittyKeyboardProtocol(const VTParameter count) override; // KKP
void SetTopBottomScrollingMargins(const VTInt topMargin,
const VTInt bottomMargin) override; // DECSTBM
void SetLeftRightScrollingMargins(const VTInt leftMargin,

View File

@@ -54,6 +54,10 @@ public:
void DeleteColumn(const VTInt /*distance*/) override {} // DECDC
void SetKeypadMode(const bool /*applicationMode*/) override {} // DECKPAM, DECKPNM
void SetAnsiMode(const bool /*ansiMode*/) override {} // DECANM
void SetKittyKeyboardProtocol(const VTParameter /*flags*/, const VTParameter /*mode*/) noexcept override {} // KKP
void QueryKittyKeyboardProtocol() override {} // KKP
void PushKittyKeyboardProtocol(const VTParameter /*flags*/) override {} // KKP
void PopKittyKeyboardProtocol(const VTParameter /*count*/) override {} // KKP
void SetTopBottomScrollingMargins(const VTInt /*topMargin*/, const VTInt /*bottomMargin*/) override {} // DECSTBM
void SetLeftRightScrollingMargins(const VTInt /*leftMargin*/, const VTInt /*rightMargin*/) override {} // DECSLRM
void EnquireAnswerback() override {} // ENQ

View File

@@ -13,6 +13,7 @@
<ItemGroup>
<ClCompile Include="adapterTest.cpp" />
<ClCompile Include="inputTest.cpp" />
<ClCompile Include="kittyKeyboardProtocol.cpp" />
<ClCompile Include="MouseInputTest.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
@@ -71,4 +72,4 @@
<Import Project="$(SolutionDir)src\common.build.post.props" />
<Import Project="$(SolutionDir)src\common.build.tests.props" />
<Import Project="$(SolutionDir)src\common.nugetversions.targets" />
</Project>
</Project>

View File

@@ -27,10 +27,18 @@
<ClCompile Include="MouseInputTest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="kittyKeyboardProtocol.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\precomp.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natstepfilter" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,301 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include <consoletaeftemplates.hpp>
#include <WexTestClass.h>
#include "../../input/terminalInput.hpp"
using namespace WEX::TestExecution;
using namespace WEX::Logging;
using namespace WEX::Common;
using namespace Microsoft::Console::VirtualTerminal;
// Kitty keyboard protocol enhancement flag abbreviations
using KF = TerminalInput::KittyKeyboardProtocolFlags;
constexpr auto D = KF::DisambiguateEscapeCodes;
constexpr auto E = KF::ReportEventTypes;
constexpr auto A = KF::ReportAlternateKeys;
constexpr auto K = KF::ReportAllKeysAsEscapeCodes;
constexpr auto T = KF::ReportAssociatedText;
namespace
{
TerminalInput::OutputType process(TerminalInput& input, bool keyDown, uint16_t vk, uint16_t sc, wchar_t ch, uint32_t state)
{
INPUT_RECORD record{};
record.EventType = KEY_EVENT;
record.Event.KeyEvent.bKeyDown = keyDown ? TRUE : FALSE;
record.Event.KeyEvent.wRepeatCount = 1;
record.Event.KeyEvent.wVirtualKeyCode = vk;
record.Event.KeyEvent.wVirtualScanCode = sc;
record.Event.KeyEvent.uChar.UnicodeChar = ch;
record.Event.KeyEvent.dwControlKeyState = state;
return input.HandleKey(record);
}
TerminalInput createInput(uint8_t flags)
{
TerminalInput input;
input.SetKittyKeyboardProtocol(flags, TerminalInput::KittyKeyboardProtocolMode::Replace);
return input;
}
// Kitty modifier bits: shift=1, alt=2, ctrl=4, super=8, hyper=16, meta=32, caps_lock=64, num_lock=128
// Event types: press=1, repeat=2, release=3
constexpr auto Alt = LEFT_ALT_PRESSED;
constexpr auto Ctrl = LEFT_CTRL_PRESSED;
constexpr auto Shift = SHIFT_PRESSED;
struct TestCase
{
std::wstring_view name;
std::wstring_view expected;
uint8_t flags;
bool keyDown;
uint16_t vk;
uint16_t sc;
wchar_t ch;
uint32_t state;
};
constexpr TestCase testCases[] = {
// Core behavior: DisambiguateEscapeCodes (D)
{ L"D Esc", L"\x1b[27u", D, true, VK_ESCAPE, 1, 0, 0 },
{ L"D Ctrl+a", L"\x1b[97;5u", D, true, 'A', 0x1E, L'\x01', Ctrl },
{ L"D Ctrl+Alt+a", L"\x1b[97;7u", D, true, 'A', 0x1E, L'\x01', Ctrl | Alt },
{ L"D Shift+Alt+a", L"\x1b[97;4u", D, true, 'A', 0x1E, L'A', Shift | Alt },
// Modifiers with AllKeys (K): all keys use CSI u
{ L"K a", L"\x1b[97u", K, true, 'A', 0x1E, L'a', 0 },
{ L"K Shift+a", L"\x1b[97;2u", K, true, 'A', 0x1E, L'A', Shift },
{ L"K Alt+a", L"\x1b[97;3u", K, true, 'A', 0x1E, L'a', Alt },
{ L"K Ctrl+a", L"\x1b[97;5u", K, true, 'A', 0x1E, L'\x01', Ctrl },
{ L"K Shift+Alt+a", L"\x1b[97;4u", K, true, 'A', 0x1E, L'A', Shift | Alt },
{ L"K Shift+Ctrl+a", L"\x1b[97;6u", K, true, 'A', 0x1E, L'\x01', Shift | Ctrl },
{ L"K Alt+Ctrl+a", L"\x1b[97;7u", K, true, 'A', 0x1E, L'\x01', Alt | Ctrl },
{ L"K Shift+Alt+Ctrl+a", L"\x1b[97;8u", K, true, 'A', 0x1E, L'\x01', Shift | Alt | Ctrl },
{ L"K CapsLock+a", L"\x1b[97;65u", K, true, 'A', 0x1E, L'A', CAPSLOCK_ON },
{ L"K NumLock+a", L"\x1b[97;129u", K, true, 'A', 0x1E, L'a', NUMLOCK_ON },
{ L"K CapsLock+NumLock+a", L"\x1b[97;193u", K, true, 'A', 0x1E, L'A', CAPSLOCK_ON | NUMLOCK_ON },
{ L"K all mods", L"\x1b[97;200u", K, true, 'A', 0x1E, L'\x01', Shift | Alt | Ctrl | CAPSLOCK_ON | NUMLOCK_ON },
// Enter/Tab/Backspace: CSI u with K
{ L"K Enter", L"\x1b[13u", K, true, VK_RETURN, 0x1C, L'\r', 0 },
{ L"K Tab", L"\x1b[9u", K, true, VK_TAB, 0x0F, L'\t', 0 },
{ L"K Backspace", L"\x1b[127u", K, true, VK_BACK, 0x0E, L'\b', 0 },
{ L"K Shift+Enter", L"\x1b[13;2u", K, true, VK_RETURN, 0x1C, L'\r', Shift },
{ L"K Ctrl+Tab", L"\x1b[9;5u", K, true, VK_TAB, 0x0F, L'\t', Ctrl },
{ L"K Alt+Backspace", L"\x1b[127;3u", K, true, VK_BACK, 0x0E, L'\b', Alt },
{ L"K Shift+Tab", L"\x1b[9;2u", K, true, VK_TAB, 0x0F, 0, Shift },
// Event types (D|E, E|K): release sends ;1:3
{ L"D|E Esc press", L"\x1b[27u", D | E, true, VK_ESCAPE, 1, 0, 0 },
{ L"D|E Esc release", L"\x1b[27;1:3u", D | E, false, VK_ESCAPE, 1, 0, 0 },
{ L"E|K a press", L"\x1b[97u", E | K, true, 'A', 0x1E, L'a', 0 },
{ L"E|K a release", L"\x1b[97;1:3u", E | K, false, 'A', 0x1E, L'a', 0 },
{ L"E|K Enter release", L"\x1b[13;1:3u", E | K, false, VK_RETURN, 0x1C, L'\r', 0 },
{ L"E|K Tab release", L"\x1b[9;1:3u", E | K, false, VK_TAB, 0x0F, L'\t', 0 },
{ L"E|K Backspace release", L"\x1b[127;1:3u", E | K, false, VK_BACK, 0x0E, L'\b', 0 },
{ L"D|E Shift+Esc press", L"\x1b[27;2u", D | E, true, VK_ESCAPE, 1, 0, Shift },
{ L"D|E Shift+Esc release", L"\x1b[27;2:3u", D | E, false, VK_ESCAPE, 1, 0, Shift },
// Keypad keys (D disambiguates, getting CSI u with PUA codes)
{ L"D Numpad0", L"\x1b[57399u", D, true, VK_NUMPAD0, 0x52, L'0', 0 },
{ L"D Numpad5", L"\x1b[57404u", D, true, VK_NUMPAD5, 0x4C, L'5', 0 },
{ L"D Numpad9", L"\x1b[57408u", D, true, VK_NUMPAD9, 0x49, L'9', 0 },
{ L"D Numpad Decimal", L"\x1b[57409u", D, true, VK_DECIMAL, 0x53, L'.', 0 },
{ L"D Numpad Divide", L"\x1b[57410u", D, true, VK_DIVIDE, 0x35, L'/', ENHANCED_KEY },
{ L"D Numpad Multiply", L"\x1b[57411u", D, true, VK_MULTIPLY, 0x37, L'*', 0 },
{ L"D Numpad Subtract", L"\x1b[57412u", D, true, VK_SUBTRACT, 0x4A, L'-', 0 },
{ L"D Numpad Add", L"\x1b[57413u", D, true, VK_ADD, 0x4E, L'+', 0 },
{ L"D Shift+Numpad5", L"\x1b[57404;2u", D, true, VK_NUMPAD5, 0x4C, L'5', Shift },
// Lock keys and modifier keys (K reports them)
{ L"K CapsLock key", L"\x1b[57358u", K, true, VK_CAPITAL, 0x3A, 0, 0 },
{ L"K NumLock key", L"\x1b[57360u", K, true, VK_NUMLOCK, 0x45, 0, ENHANCED_KEY },
{ L"K ScrollLock key", L"\x1b[57359u", K, true, VK_SCROLL, 0x46, 0, 0 },
{ L"K Left Shift", L"\x1b[57441;2u", K, true, VK_SHIFT, 0x2A, 0, Shift },
{ L"K Right Shift", L"\x1b[57447;2u", K, true, VK_SHIFT, 0x36, 0, Shift },
{ L"K Left Ctrl", L"\x1b[57442;5u", K, true, VK_CONTROL, 0x1D, 0, Ctrl },
{ L"K Right Ctrl", L"\x1b[57448;5u", K, true, VK_CONTROL, 0x1D, 0, Ctrl | ENHANCED_KEY },
{ L"K Left Alt", L"\x1b[57443;3u", K, true, VK_MENU, 0x38, 0, Alt },
{ L"K Right Alt", L"\x1b[57449;3u", K, true, VK_MENU, 0x38, 0, RIGHT_ALT_PRESSED | ENHANCED_KEY },
{ L"K Left Win", L"\x1b[57444u", K, true, VK_LWIN, 0x5B, 0, ENHANCED_KEY },
{ L"K Right Win", L"\x1b[57450u", K, true, VK_RWIN, 0x5C, 0, ENHANCED_KEY },
// Special keys
{ L"K Pause", L"\x1b[57362u", K, true, VK_PAUSE, 0x45, 0, 0 },
{ L"K PrintScreen", L"\x1b[57361u", K, true, VK_SNAPSHOT, 0x37, 0, ENHANCED_KEY },
{ L"K Menu", L"\x1b[57363u", K, true, VK_APPS, 0x5D, 0, ENHANCED_KEY },
// Navigation keys: enhanced=keypad keys with PUA codes
{ L"K Keypad Home", L"\x1b[57423u", K, true, VK_HOME, 0x47, 0, 0 },
{ L"K Keypad End", L"\x1b[57424u", K, true, VK_END, 0x4F, 0, 0 },
{ L"K Keypad Insert", L"\x1b[57425u", K, true, VK_INSERT, 0x52, 0, 0 },
{ L"K Keypad Delete", L"\x1b[57426u", K, true, VK_DELETE, 0x53, 0, 0 },
{ L"K Keypad PageUp", L"\x1b[57421u", K, true, VK_PRIOR, 0x49, 0, 0 },
{ L"K Keypad PageDown", L"\x1b[57422u", K, true, VK_NEXT, 0x51, 0, 0 },
{ L"K Keypad Up", L"\x1b[57419u", K, true, VK_UP, 0x48, 0, 0 },
{ L"K Keypad Down", L"\x1b[57420u", K, true, VK_DOWN, 0x50, 0, 0 },
{ L"K Keypad Left", L"\x1b[57417u", K, true, VK_LEFT, 0x4B, 0, 0 },
{ L"K Keypad Right", L"\x1b[57418u", K, true, VK_RIGHT, 0x4D, 0, 0 },
// Media keys
{ L"K Media Play/Pause", L"\x1b[57430u", K, true, VK_MEDIA_PLAY_PAUSE, 0, 0, 0 },
{ L"K Media Stop", L"\x1b[57432u", K, true, VK_MEDIA_STOP, 0, 0, 0 },
{ L"K Media Next", L"\x1b[57435u", K, true, VK_MEDIA_NEXT_TRACK, 0, 0, 0 },
{ L"K Media Prev", L"\x1b[57436u", K, true, VK_MEDIA_PREV_TRACK, 0, 0, 0 },
{ L"K Volume Down", L"\x1b[57438u", K, true, VK_VOLUME_DOWN, 0, 0, 0 },
{ L"K Volume Up", L"\x1b[57439u", K, true, VK_VOLUME_UP, 0, 0, 0 },
{ L"K Volume Mute", L"\x1b[57440u", K, true, VK_VOLUME_MUTE, 0, 0, 0 },
// Function keys F13-F24 (PUA codes)
{ L"K F13", L"\x1b[57376u", K, true, VK_F13, 0x64, 0, 0 },
{ L"K F20", L"\x1b[57383u", K, true, VK_F20, 0x6B, 0, 0 },
{ L"K F24", L"\x1b[57387u", K, true, VK_F24, 0x76, 0, 0 },
{ L"K Shift+F13", L"\x1b[57376;2u", K, true, VK_F13, 0x64, 0, Shift },
// Alternate keys (A|K): shifted key and base layout key
{ L"A|K Shift+a", L"\x1b[97:65;2u", A | K, true, 'A', 0x1E, L'A', Shift },
{ L"A|K Shift+1", L"\x1b[49:33;2u", A | K, true, '1', 0x02, L'!', Shift },
{ L"A|K a (no shift)", L"\x1b[97u", A | K, true, 'A', 0x1E, L'a', 0 },
// Associated text (K|T): text codepoint in 3rd param
{ L"K|T Shift+a", L"\x1b[97;2;65u", K | T, true, 'A', 0x1E, L'A', Shift },
{ L"K|T Shift+1", L"\x1b[49;2;33u", K | T, true, '1', 0x02, L'!', Shift },
{ L"K|T Ctrl+a", L"\x1b[97;5u", K | T, true, 'A', 0x1E, L'\x01', Ctrl }, // control char omitted
// Edge cases
{ L"K Keypad Enter", L"\x1b[57414u", K, true, VK_RETURN, 0x1C, L'\r', ENHANCED_KEY },
{ L"K Regular Enter", L"\x1b[13u", K, true, VK_RETURN, 0x1C, L'\r', 0 },
{ L"K Shift+Alt+Ctrl+Esc", L"\x1b[27;8u", K, true, VK_ESCAPE, 1, 0, Shift | Alt | Ctrl },
{ L"E|K CapsLock+a", L"\x1b[97;65u", E | K, true, 'A', 0x1E, L'A', CAPSLOCK_ON },
{ L"E|K all mods release", L"\x1b[97;200:3u", E | K, false, 'A', 0x1E, L'\x01', Shift | Alt | Ctrl | CAPSLOCK_ON | NUMLOCK_ON },
// F1-F4 with kitty flags (CSI instead of SS3, F3 special case)
{ L"D F1", L"\x1b[P", D, true, VK_F1, 0x3B, 0, 0 },
{ L"D F2", L"\x1b[Q", D, true, VK_F2, 0x3C, 0, 0 },
{ L"D F3", L"\x1b[13~", D, true, VK_F3, 0x3D, 0, 0 }, // F3 uses ~ per updated spec
{ L"D F4", L"\x1b[S", D, true, VK_F4, 0x3E, 0, 0 },
{ L"D Shift+F1", L"\x1b[1;2P", D, true, VK_F1, 0x3B, 0, Shift },
{ L"K F5", L"\x1b[15~", K, true, VK_F5, 0x3F, 0, 0 },
{ L"K F12", L"\x1b[24~", K, true, VK_F12, 0x58, 0, 0 },
{ L"K Shift+F5", L"\x1b[15;2~", K, true, VK_F5, 0x3F, 0, Shift },
// Navigation with ENHANCED_KEY (regular arrows, not keypad)
{ L"K Up", L"\x1b[A", K, true, VK_UP, 0x48, 0, ENHANCED_KEY },
{ L"K Down", L"\x1b[B", K, true, VK_DOWN, 0x50, 0, ENHANCED_KEY },
{ L"K Right", L"\x1b[C", K, true, VK_RIGHT, 0x4D, 0, ENHANCED_KEY },
{ L"K Left", L"\x1b[D", K, true, VK_LEFT, 0x4B, 0, ENHANCED_KEY },
{ L"K Home", L"\x1b[H", K, true, VK_HOME, 0x47, 0, ENHANCED_KEY },
{ L"K End", L"\x1b[F", K, true, VK_END, 0x4F, 0, ENHANCED_KEY },
{ L"K Insert", L"\x1b[2~", K, true, VK_INSERT, 0x52, 0, ENHANCED_KEY },
{ L"K Delete", L"\x1b[3~", K, true, VK_DELETE, 0x53, 0, ENHANCED_KEY },
{ L"K PageUp", L"\x1b[5~", K, true, VK_PRIOR, 0x49, 0, ENHANCED_KEY },
{ L"K PageDown", L"\x1b[6~", K, true, VK_NEXT, 0x51, 0, ENHANCED_KEY },
{ L"K Shift+Up", L"\x1b[1;2A", K, true, VK_UP, 0x48, 0, Shift | ENHANCED_KEY },
{ L"K Ctrl+Home", L"\x1b[1;5H", K, true, VK_HOME, 0x47, 0, Ctrl | ENHANCED_KEY },
{ L"K Clear", L"\x1b[E", K, true, VK_CLEAR, 0x4C, 0, ENHANCED_KEY },
// Additional edge cases
// F-key with event type
{ L"E|K F1 release", L"\x1b[1;1:3P", E | K, false, VK_F1, 0x3B, 0, 0 },
{ L"E|K F5 release", L"\x1b[15;1:3~", E | K, false, VK_F5, 0x3F, 0, 0 },
// Navigation release
{ L"E|K Up release", L"\x1b[1;1:3A", E | K, false, VK_UP, 0x48, 0, ENHANCED_KEY },
{ L"E|K Insert release", L"\x1b[2;1:3~", E | K, false, VK_INSERT, 0x52, 0, ENHANCED_KEY },
// Alternate keys with modifiers
{ L"A|K Shift+Ctrl+a", L"\x1b[97:65;6u", A | K, true, 'A', 0x1E, L'\x01', Shift | Ctrl },
// Associated text with plain key
{ L"K|T a", L"\x1b[97;;97u", K | T, true, 'A', 0x1E, L'a', 0 },
// Text not reported on release
{ L"E|K|T a release", L"\x1b[97;1:3u", E | K | T, false, 'A', 0x1E, L'a', 0 },
// Escape has no associated text
{ L"K|T Esc", L"\x1b[27u", K | T, true, VK_ESCAPE, 1, 0, 0 },
// Combined flags: alternate keys with locks
{ L"A|K CapsLock+Shift+a", L"\x1b[97:65;66u", A | K, true, 'A', 0x1E, L'a', CAPSLOCK_ON | Shift },
// All flags combined
{ L"A|K|T Shift+a", L"\x1b[97:65;2;65u", A | K | T, true, 'A', 0x1E, L'A', Shift },
// Release without EventTypes flag: no output
{ L"K a release (no EventTypes)", L"", K, false, 'A', 0x1E, L'a', 0 },
// Enter/Tab/Backspace release without AllKeys: no output
{ L"D|E Enter press", L"\r", D | E, true, VK_RETURN, 0x1C, L'\r', 0 },
{ L"D|E Enter release (no AllKeys)", L"", D | E, false, VK_RETURN, 0x1C, L'\r', 0 },
// Modifier key press/release
{ L"E|K Left Shift press", L"\x1b[57441;2u", E | K, true, VK_SHIFT, 0x2A, 0, Shift },
{ L"E|K Left Shift release", L"\x1b[57441;1:3u", E | K, false, VK_SHIFT, 0x2A, 0, 0 },
// Lock key toggle (CapsLock)
{ L"E|K CapsLock press", L"\x1b[57358u", E | K, true, VK_CAPITAL, 0x3A, 0, 0 },
{ L"E|K CapsLock release (now on)", L"\x1b[57358;65:3u", E | K, false, VK_CAPITAL, 0x3A, 0, CAPSLOCK_ON },
// Associated text filtering
{ L"K|T Shift+a (text)", L"\x1b[97;2;65u", K | T, true, 'A', 0x1E, L'A', Shift },
{ L"K|T Ctrl+a (control char filtered)", L"\x1b[97;5u", K | T, true, 'A', 0x1E, L'\x01', Ctrl },
{ L"K|T Esc (no text)", L"\x1b[27u", K | T, true, VK_ESCAPE, 1, 0, 0 },
};
}
extern "C" HRESULT __declspec(dllexport) __cdecl KittyKeyTestDataSource(IDataSource** ppDataSource, void*)
{
return Microsoft::WRL::MakeAndInitialize<ArrayIndexTaefAdapterSource>(ppDataSource, std::size(testCases));
}
class KittyKeyboardProtocolTests
{
TEST_CLASS(KittyKeyboardProtocolTests);
TEST_METHOD(KeyPressTests)
{
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"DataSource", L"Export:KittyKeyTestDataSource")
END_TEST_METHOD_PROPERTIES()
DisableVerifyExceptions disableVerifyExceptions{};
SetVerifyOutput verifyOutputScope{ VerifyOutputSettings::LogOnlyFailures };
size_t i{};
TestData::TryGetValue(L"index", i);
const auto& tc = testCases[i];
Log::Comment(NoThrowString().Format(L"[%zu] %.*s", i, static_cast<int>(tc.name.size()), tc.name.data()));
auto input = createInput(tc.flags);
const auto expected = TerminalInput::MakeOutput(tc.expected);
const auto actual = process(input, tc.keyDown, tc.vk, tc.sc, tc.ch, tc.state);
const auto msg = fmt::format(L"{} != {}", til::visualize_control_codes(expected.value_or({})), til::visualize_control_codes(actual.value_or({})));
VERIFY_ARE_EQUAL(expected, actual, msg.c_str());
}
TEST_METHOD(KeyRepeatEvents)
{
auto input = createInput(E | K);
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[97u"), process(input, true, 'A', 0x1E, L'a', 0));
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[97;1:2u"), process(input, true, 'A', 0x1E, L'a', 0)); // repeat
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[97;1:2u"), process(input, true, 'A', 0x1E, L'a', 0)); // repeat
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[97;1:3u"), process(input, false, 'A', 0x1E, L'a', 0)); // release
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[97u"), process(input, true, 'A', 0x1E, L'a', 0)); // new press
}
TEST_METHOD(KeyRepeatWithModifiers)
{
auto input = createInput(E | K);
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[97;2u"), process(input, true, 'A', 0x1E, L'A', SHIFT_PRESSED));
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[97;2:2u"), process(input, true, 'A', 0x1E, L'A', SHIFT_PRESSED));
}
TEST_METHOD(KeyRepeatResetOnDifferentKey)
{
auto input = createInput(E | K);
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[97u"), process(input, true, 'A', 0x1E, L'a', 0));
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[98u"), process(input, true, 'B', 0x30, L'b', 0)); // different key
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1b[97u"), process(input, true, 'A', 0x1E, L'a', 0)); // not repeat
}
};

View File

@@ -26,6 +26,7 @@ SOURCES = \
$(SOURCES) \
adapterTest.cpp \
inputTest.cpp \
kittyKeyboardProtocol.cpp \
MouseInputTest.cpp \
INCLUDES = \

View File

@@ -12,7 +12,6 @@
<Import Project="$(SolutionDir)src\common.nugetversions.props" />
<ItemGroup>
<ClCompile Include="..\mouseInput.cpp" />
<ClCompile Include="..\mouseInputState.cpp" />
<ClCompile Include="..\terminalInput.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>

View File

@@ -487,7 +487,7 @@ TerminalInput::OutputType TerminalInput::_GenerateSGRSequence(const til::point p
// True if the alternate buffer is active and alternate scroll mode is enabled and the event is a mouse wheel event.
bool TerminalInput::ShouldSendAlternateScroll(const unsigned int button, const short delta) const noexcept
{
const auto inAltBuffer{ _mouseInputState.inAlternateBuffer };
const auto inAltBuffer{ _inAlternateBuffer };
const auto inAltScroll{ _inputMode.test(Mode::AlternateScroll) };
const auto wasMouseWheel{ (button == WM_MOUSEWHEEL || button == WM_MOUSEHWHEEL) && delta != 0 };
return inAltBuffer && inAltScroll && wasMouseWheel;
@@ -499,30 +499,28 @@ bool TerminalInput::ShouldSendAlternateScroll(const unsigned int button, const s
// - delta: The scroll wheel delta of the input event
TerminalInput::OutputType TerminalInput::_makeAlternateScrollOutput(const unsigned int button, const short delta) const
{
uint16_t vkey = 0;
if (button == WM_MOUSEWHEEL)
{
if (delta > 0)
{
return MakeOutput(_keyMap.at(VK_UP));
}
else
{
return MakeOutput(_keyMap.at(VK_DOWN));
}
vkey = delta > 0 ? VK_UP : VK_DOWN;
}
else if (button == WM_MOUSEHWHEEL)
{
if (delta > 0)
{
return MakeOutput(_keyMap.at(VK_RIGHT));
}
else
{
return MakeOutput(_keyMap.at(VK_LEFT));
}
vkey = delta > 0 ? VK_RIGHT : VK_LEFT;
}
else
{
return {};
}
const SanitizedKeyEvent key{
.virtualKey = vkey,
};
EncodingHelper enc;
_encodeRegular(enc, key);
std::wstring str;
_formatEncodingHelper(enc, str);
return str;
}

View File

@@ -1,30 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include <windows.h>
#include "terminalInput.hpp"
using namespace Microsoft::Console::VirtualTerminal;
// Routine Description:
// - Notify the MouseInput handler that the screen buffer has been swapped to the alternate buffer
// Parameters:
// <none>
// Return value:
// <none>
void TerminalInput::UseAlternateScreenBuffer() noexcept
{
_mouseInputState.inAlternateBuffer = true;
}
// Routine Description:
// - Notify the MouseInput handler that the screen buffer has been swapped to the alternate buffer
// Parameters:
// <none>
// Return value:
// <none>
void TerminalInput::UseMainScreenBuffer() noexcept
{
_mouseInputState.inAlternateBuffer = false;
}

View File

@@ -30,7 +30,6 @@ PRECOMPILED_INCLUDE = ..\precomp.h
SOURCES= \
..\terminalInput.cpp \
..\mouseInput.cpp \
..\mouseInputState.cpp \
INCLUDES = \
$(INCLUDES); \

File diff suppressed because it is too large Load Diff

View File

@@ -47,26 +47,134 @@ namespace Microsoft::Console::VirtualTerminal
AlternateScroll
};
// Kitty keyboard protocol progressive enhancement flags
// https://sw.kovidgoyal.net/kitty/keyboard-protocol/
struct KittyKeyboardProtocolFlags
{
static constexpr uint8_t None = 0;
static constexpr uint8_t DisambiguateEscapeCodes = 1 << 0;
static constexpr uint8_t ReportEventTypes = 1 << 1;
static constexpr uint8_t ReportAlternateKeys = 1 << 2;
static constexpr uint8_t ReportAllKeysAsEscapeCodes = 1 << 3;
static constexpr uint8_t ReportAssociatedText = 1 << 4;
static constexpr uint8_t All = (1 << 5) - 1;
};
enum class KittyKeyboardProtocolMode : uint8_t
{
Replace = 1,
Set = 2,
Reset = 3,
};
TerminalInput() noexcept;
void SetInputMode(const Mode mode, const bool enabled) noexcept;
bool GetInputMode(const Mode mode) const noexcept;
void UseAlternateScreenBuffer() noexcept;
void UseMainScreenBuffer() noexcept;
void SetInputMode(Mode mode, bool enabled) noexcept;
bool GetInputMode(Mode mode) const noexcept;
void ResetInputModes() noexcept;
void ForceDisableWin32InputMode(const bool win32InputMode) noexcept;
void ForceDisableWin32InputMode(bool win32InputMode) noexcept;
void ForceDisableKittyKeyboardProtocol(bool disable) noexcept;
// Kitty keyboard protocol methods
void SetKittyKeyboardProtocol(uint8_t flags, KittyKeyboardProtocolMode mode) noexcept;
uint8_t GetKittyFlags() const noexcept;
void PushKittyFlags(uint8_t flags);
void PopKittyFlags(size_t count);
void ResetKittyKeyboardProtocols() noexcept;
#pragma region MouseInput
// These methods are defined in mouseInput.cpp
bool IsTrackingMouseInput() const noexcept;
bool ShouldSendAlternateScroll(const unsigned int button, const short delta) const noexcept;
#pragma endregion
#pragma region MouseInputState Management
// These methods are defined in mouseInputState.cpp
void UseAlternateScreenBuffer() noexcept;
void UseMainScreenBuffer() noexcept;
bool ShouldSendAlternateScroll(unsigned int button, short delta) const noexcept;
#pragma endregion
private:
struct CodepointBuffer
{
#pragma warning(suppress : 26495) // Variable '...' is uninitialized. Always initialize a member variable (type.6).
explicit CodepointBuffer() noexcept = default;
explicit CodepointBuffer(uint32_t cp) noexcept;
void convertLowercase() noexcept;
uint32_t asSingleCodepoint() const noexcept;
wchar_t buf[4];
int len = 0;
};
struct SanitizedKeyEvent
{
uint16_t virtualKey = 0;
uint16_t scanCode = 0;
uint32_t codepoint = 0;
uint32_t controlKeyState = 0;
bool leftCtrlIsReallyPressed = false;
bool keyDown = false;
bool keyRepeat = false;
bool anyAltPressed() const noexcept;
bool bothAltPressed() const noexcept;
bool rightAltPressed() const noexcept;
bool bothCtrlPressed() const noexcept;
bool altGrPressed() const noexcept;
};
struct KeyboardHelper
{
#pragma warning(suppress : 26495) // Variable '...' is uninitialized. Always initialize a member variable (type.6).
explicit KeyboardHelper() noexcept = default;
uint32_t getUnmodifiedKeyboardKey(const SanitizedKeyEvent& key) noexcept; // Without Ctrl/Alt
uint32_t getKittyBaseKey(const SanitizedKeyEvent& key) noexcept; // Without Ctrl/Alt/Shift
uint32_t getKittyShiftedKey(const SanitizedKeyEvent& key) noexcept; // Without Ctrl/Alt, with Shift
uint32_t getKittyUSBaseKey(const SanitizedKeyEvent& key) noexcept; // Without Ctrl/Alt/Shift in US layout
private:
uint32_t getKeyboardKey(UINT vkey, DWORD controlKeyState, HKL hkl) noexcept;
void init() noexcept;
void initSlow() noexcept;
bool _initialized = false;
// Intentionally uninitialized until first use.
HKL _keyboardLayout;
uint8_t _keyboardState[256];
};
struct EncodingHelper
{
explicit EncodingHelper() noexcept;
bool shiftPressed() const noexcept;
bool altPressed() const noexcept;
bool ctrlPressed() const noexcept;
// The KKP CSI u sequence is a superset of other CSI sequences:
// CSI unicode-key-code:alternate-key-code-shift:alternate-key-code-base ; modifiers:event-type ; text-as-codepoint u
uint32_t csiUnicodeKeyCode;
uint32_t csiAltKeyCodeShifted; // KKP-specific
uint32_t csiAltKeyCodeBase; // KKP-specific
uint32_t csiModifier; // NOTE: The final VT sequence expects this to be 1-based.
uint32_t csiEventType; // KKP-specific
uint32_t csiTextAsCodepoint; // KKP-specific
// A non-zero csiFinal value indicates that this key
// should be encoded as `CSI ... ; $csiFinal`.
wchar_t csiFinal;
// A non-zero ss3Final value indicates that this key
// should be encoded as `ESC O $ss3Final`.
wchar_t ss3Final;
// If true, and Alt is pressed, an ESC prefix should be added to
// the final sequence. This only applies to non-KKP encodings.
bool altPrefix;
// Any other encoding ends up as a non-zero plain value.
// For instance, the Tab key gets translated to a plain "\t".
std::wstring_view plain;
};
// storage location for the leading surrogate of a utf-16 surrogate pair
wchar_t _leadingSurrogate = 0;
@@ -74,30 +182,41 @@ namespace Microsoft::Console::VirtualTerminal
DWORD _lastControlKeyState = 0;
uint64_t _lastLeftCtrlTime = 0;
uint64_t _lastRightAltTime = 0;
std::unordered_map<int, std::wstring> _keyMap;
std::wstring _focusInSequence;
std::wstring _focusOutSequence;
til::enumset<Mode> _inputMode{ Mode::Ansi, Mode::AutoRepeat, Mode::AlternateScroll };
bool _forceDisableWin32InputMode{ false };
bool _inAlternateBuffer{ false };
const wchar_t* _csi = L"\x1B[";
const wchar_t* _ss3 = L"\x1BO";
// Kitty keyboard protocol state
static constexpr size_t KittyStackMaxSize = 8;
bool _forceDisableKittyKeyboardProtocol = false;
uint8_t _kittyFlags = 0;
std::vector<uint8_t> _kittyMainStack;
std::vector<uint8_t> _kittyAltStack;
std::wstring_view _csi;
std::wstring_view _ss3;
std::wstring_view _focusInSequence;
std::wstring_view _focusOutSequence;
void _initKeyboardMap() noexcept;
DWORD _trackControlKeyState(const KEY_EVENT_RECORD& key) noexcept;
std::array<byte, 256> _getKeyboardState(const WORD virtualKeyCode, const DWORD controlKeyState) const;
[[nodiscard]] static wchar_t _makeCtrlChar(const wchar_t ch);
[[nodiscard]] StringType _makeCharOutput(wchar_t ch);
[[nodiscard]] static uint32_t _makeCtrlChar(uint32_t ch) noexcept;
[[nodiscard]] static StringType _makeCharOutput(uint32_t ch);
[[nodiscard]] static StringType _makeNoOutput() noexcept;
[[nodiscard]] void _escapeOutput(StringType& charSequence, const bool altIsPressed) const;
[[nodiscard]] OutputType _makeWin32Output(const KEY_EVENT_RECORD& key) const;
[[nodiscard]] OutputType _makeWin32Output(const KEY_EVENT_RECORD& key);
bool _encodeKitty(KeyboardHelper& kbd, EncodingHelper& enc, const SanitizedKeyEvent& key) noexcept;
static uint32_t _getKittyFunctionalKeyCode(UINT vkey, WORD scanCode, bool enhanced) noexcept;
void _encodeRegular(EncodingHelper& enc, const SanitizedKeyEvent& key) const noexcept;
bool _formatEncodingHelper(EncodingHelper& enc, std::wstring& str) const;
void _formatFallback(KeyboardHelper& kbd, const EncodingHelper& enc, const SanitizedKeyEvent& key, std::wstring& seq) const;
static void _stringPushCodepoint(std::wstring& str, uint32_t cp);
static uint32_t _codepointToLower(uint32_t cp) noexcept;
#pragma region MouseInputState Management
// These methods are defined in mouseInputState.cpp
struct MouseInputState
{
bool inAlternateBuffer{ false };
til::point lastPos{ -1, -1 };
unsigned int lastButton{ 0 };
int accumulatedDelta{ 0 };
@@ -113,7 +232,7 @@ namespace Microsoft::Console::VirtualTerminal
[[nodiscard]] OutputType _makeAlternateScrollOutput(unsigned int button, short delta) const;
static constexpr unsigned int s_GetPressedButton(const MouseButtonState state) noexcept;
static constexpr unsigned int s_GetPressedButton(MouseButtonState state) noexcept;
#pragma endregion
};
}

View File

@@ -670,6 +670,18 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParamete
case CsiActionCodes::DECPS_PlaySound:
_dispatch->PlaySounds(parameters);
break;
case CsiActionCodes::KKP_KittyKeyboardSet:
_dispatch->SetKittyKeyboardProtocol(parameters.at(0), parameters.at(1));
break;
case CsiActionCodes::KKP_KittyKeyboardQuery:
_dispatch->QueryKittyKeyboardProtocol();
break;
case CsiActionCodes::KKP_KittyKeyboardPush:
_dispatch->PushKittyKeyboardProtocol(parameters.at(0));
break;
case CsiActionCodes::KKP_KittyKeyboardPop:
_dispatch->PopKittyKeyboardProtocol(parameters.at(0));
break;
default:
break;
}

View File

@@ -166,7 +166,11 @@ namespace Microsoft::Console::VirtualTerminal
DECRQCRA_RequestChecksumRectangularArea = VTID("*y"),
DECINVM_InvokeMacro = VTID("*z"),
DECAC_AssignColor = VTID(",|"),
DECPS_PlaySound = VTID(",~")
DECPS_PlaySound = VTID(",~"),
KKP_KittyKeyboardSet = VTID("=u"),
KKP_KittyKeyboardQuery = VTID("?u"),
KKP_KittyKeyboardPush = VTID(">u"),
KKP_KittyKeyboardPop = VTID("<u")
};
enum DcsActionCodes : uint64_t

View File

@@ -0,0 +1,558 @@
<#
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
.SYNOPSIS
Scans XAML files for local:SettingContainer entries and generates GeneratedSettingsIndex.g.h / .g.cpp.
.PARAMETER SourceDir
Directory to scan recursively for .xaml files.
.PARAMETER OutputDir
Directory to place generated C++ files.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$false)][string]$SourceDir,
[Parameter(Mandatory=$false)][string]$OutputDir
)
# Prohibited UIDs (exact match, case-insensitive by default)
$ProhibitedUids = @(
'Extensions_Scope',
'Profile_MissingFontFaces',
'Profile_ProportionalFontFaces',
'ColorScheme_InboxSchemeDuplicate',
'ColorScheme_ColorsHeader',
'ColorScheme_Rename'
)
# Prohibited XAML files
$ProhibitedXamlFiles = @(
'CommonResources.xaml',
'KeyChordListener.xaml',
'NullableColorPicker.xaml',
'SettingContainerStyle.xaml',
'AISettings.xaml',
'Profiles_Base_Orphaned.xaml'
)
if (-not (Test-Path $SourceDir)) { throw "SourceDir not found: $SourceDir" }
if (-not (Test-Path $OutputDir)) { New-Item -ItemType Directory -Path $OutputDir | Out-Null }
$resourceKeys = ([xml](Get-Content "$($SourceDir)\Resources\en-US\Resources.resw")).root.data.name
$entries = @()
Get-ChildItem -Path $SourceDir -Recurse -Filter *.xaml | ForEach-Object {
# Skip whole file if prohibited
$filename = $_.Name
if ($ProhibitedXamlFiles -contains $filename)
{
return
}
$text = Get-Content -Raw -LiteralPath $_.FullName
# Extract Page x:Class
$pageClass = $null
if ($text -match '<Page\b[^>]*\bx:Class="([^"]+)"')
{
$pageClass = $matches[1]
}
elseif ($filename -eq 'Appearances.xaml')
{
# Appearances.xaml is a UserControl that is hosted in Profiles_Appearance.xaml
$pageClass = 'Microsoft::Terminal::Settings::Editor::Profiles_Appearance'
}
else
{
return
}
# Convert XAML namespace dots to C++ scope operators
$pageClass = ($pageClass -replace '\.', '::')
# Deduce BreadcrumbSubPage
# Special cases:
# - NewTabMenu: defer to UID, see NavigationParam section below
# - Extensions: defer to UID, see NavigationParam section below
$subPage = 'BreadcrumbSubPage::'
if ($pageClass -match 'Editor::Profiles_Appearance')
{
$subPage += 'Profile_Appearance'
}
elseif ($pageClass -match 'Editor::Profiles_Terminal')
{
$subPage += 'Profile_Terminal'
}
elseif ($pageClass -match 'Editor::Profiles_Advanced')
{
$subPage += 'Profile_Advanced'
}
elseif ($pageClass -match 'Editor::EditColorScheme')
{
$subPage += 'ColorSchemes_Edit'
}
else
{
$subPage += 'None'
}
# Register top-level pages
if ($filename -eq 'Launch.xaml')
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"Nav_Launch/Content`""
DisplayTextLocalized = "RS_(L`"Nav_Launch/Content`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"Launch_Nav`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L""'
File = $filename
}
}
elseif ($filename -eq 'Interaction.xaml')
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"Nav_Interaction/Content`""
DisplayTextLocalized = "RS_(L`"Nav_Interaction/Content`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"Interaction_Nav`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L""'
File = $filename
}
}
elseif ($filename -eq 'GlobalAppearance.xaml')
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"Nav_Appearance/Content`""
DisplayTextLocalized = "RS_(L`"Nav_Appearance/Content`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"GlobalAppearance_Nav`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L""'
File = $filename
}
}
elseif ($filename -eq 'ColorSchemes.xaml')
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"Nav_ColorSchemes/Content`""
DisplayTextLocalized = "RS_(L`"Nav_ColorSchemes/Content`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"ColorSchemes_Nav`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L""'
File = $filename
}
# Manually register the "add new" button
$entries += [pscustomobject]@{
DisplayTextUid = "L`"ColorScheme_AddNewButton/Text`""
DisplayTextLocalized = 'RS_(L"ColorScheme_AddNewButton/Text")'
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"ColorSchemes_Nav`"})"
SubPage = 'BreadcrumbSubPage::None'
ElementName = 'L"AddNewButton"'
File = $filename
}
}
elseif ($filename -eq 'Rendering.xaml')
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"Nav_Rendering/Content`""
DisplayTextLocalized = "RS_(L`"Nav_Rendering/Content`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"Rendering_Nav`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L""'
File = $filename
}
}
elseif ($filename -eq 'Compatibility.xaml')
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"Nav_Compatibility/Content`""
DisplayTextLocalized = "RS_(L`"Nav_Compatibility/Content`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"Compatibility_Nav`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L""'
File = $filename
}
}
elseif ($filename -eq 'Actions.xaml')
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"Nav_Actions/Content`""
DisplayTextLocalized = "RS_(L`"Nav_Actions/Content`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"Actions_Nav`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L""'
File = $filename
}
}
elseif ($filename -eq 'NewTabMenu.xaml')
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"Nav_NewTabMenu/Content`""
DisplayTextLocalized = "RS_(L`"Nav_NewTabMenu/Content`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"NewTabMenu_Nav`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L""'
File = $filename
}
}
elseif ($filename -eq 'Extensions.xaml')
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"Nav_Extensions/Content`""
DisplayTextLocalized = "RS_(L`"Nav_Extensions/Content`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"Extensions_Nav`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L""'
File = $filename
}
}
elseif ($filename -eq 'Profiles_Base.xaml')
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"Nav_ProfileDefaults/Content`""
DisplayTextLocalized = "RS_(L`"Nav_ProfileDefaults/Content`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"GlobalProfile_Nav`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L""'
File = $filename
}
}
elseif ($filename -eq 'AddProfile.xaml')
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"Nav_AddNewProfile/Content`""
DisplayTextLocalized = "RS_(L`"Nav_AddNewProfile/Content`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"AddProfile`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L""'
File = $filename
}
$entries += [pscustomobject]@{
DisplayTextUid = "L`"AddProfile_AddNewTextBlock/Text`""
DisplayTextLocalized = "RS_(L`"AddProfile_AddNewTextBlock/Text`")"
ParentPage = $pageClass
NavigationParam = "winrt::box_value(hstring{L`"AddProfile`"})"
SubPage = "BreadcrumbSubPage::None"
ElementName = 'L"AddNewButton"'
File = $filename
}
}
# Find all local:SettingContainer start tags
$pattern = '<local:SettingContainer\b([^>/]*)(/?>)'
$matchesAll = [System.Text.RegularExpressions.Regex]::Matches($text, $pattern, 'IgnoreCase')
foreach ($m in $matchesAll)
{
$attrBlock = $m.Groups[1].Value
# Extract Uid
if ($attrBlock -match '\bx:Uid="([^"]+)"')
{
$uid = $matches[1]
# Skip entry if UID prohibited
if ($ProhibitedUids -contains $uid)
{
continue
}
}
else
{
continue
}
# Extract Name
if ($attrBlock -match '\bx:Name="([^"]+)"')
{
$name = $matches[1]
}
elseif ($attrBlock -match '\bName="([^"]+)"')
{
$name = $matches[1]
}
else
{
$name = ""
}
# Profile.Appearance settings need a special prefix for the ElementName.
# This allows us to bring the element into view at runtime.
if ($filename -eq 'Appearances.xaml')
{
$name = 'App.' + $name
}
# Deduce NavigationParam
# includeInBuildIndex: include the entry in the build-time index (no special param at runtime)
# includeInPartialIndex: include the entry in the partial index, where the NavigationParam is the view model at runtime (i.e. profile vs profile defaults)
$includeInBuildIndex = $true
$includeInPartialIndex = $false
$navigationParam = 'nullptr'
if ($pageClass -match 'Editor::Launch')
{
$navigationParam = 'Launch_Nav'
}
elseif ($pageClass -match 'Editor::Interaction')
{
$navigationParam = 'Interaction_Nav'
}
elseif ($pageClass -match 'Editor::Rendering')
{
$navigationParam = 'Rendering_Nav'
}
elseif ($pageClass -match 'Editor::Compatibility')
{
$navigationParam = 'Compatibility_Nav'
}
elseif ($pageClass -match 'Editor::Actions')
{
$navigationParam = 'Actions_Nav'
}
elseif ($pageClass -match 'Editor::NewTabMenu')
{
if ($uid -match 'NewTabMenu_CurrentFolder')
{
$navigationParam = 'nullptr'
$subPage = 'BreadcrumbSubPage::NewTabMenu_Folder'
}
else
{
$navigationParam = 'NewTabMenu_Nav'
$subPage = 'BreadcrumbSubPage::None'
$includeInPartialIndex = $true
}
}
elseif ($pageClass -match 'Editor::Extensions')
{
$navigationParam = 'Extensions_Nav'
$subPage = 'BreadcrumbSubPage::None'
}
elseif ($pageClass -match 'Editor::Profiles_Base' -or
$pageClass -match 'Editor::Profiles_Appearance' -or
$pageClass -match 'Editor::Profiles_Terminal' -or
$pageClass -match 'Editor::Profiles_Advanced')
{
$navigationParam = 'GlobalProfile_Nav'
$includeInBuildIndex = !($name -eq "Name" -or $name -eq "Commandline")
$includeInPartialIndex = $true
}
elseif ($pageClass -match 'Editor::EditColorScheme')
{
# populate with color scheme name at runtime
$navigationParam = 'nullptr'
}
elseif ($pageClass -match 'Editor::GlobalAppearance')
{
$navigationParam = 'GlobalAppearance_Nav'
}
elseif ($pageClass -match 'Editor::AddProfile')
{
$navigationParam = 'AddProfile'
}
if ($includeInBuildIndex)
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"$($uid)/Header`""
DisplayTextLocalized = "RS_(L`"$($uid)/Header`")"
ParentPage = $pageClass
NavigationParam = $navigationParam -eq "nullptr" ? $navigationParam : "winrt::box_value(hstring{L`"$($navigationParam)`"})"
SubPage = $subPage
ElementName = "L`"$($name)`""
File = $filename
}
}
if ($includeInPartialIndex)
{
$entries += [pscustomobject]@{
DisplayTextUid = "L`"$($uid)/Header`""
DisplayTextLocalized = "RS_(L`"$($uid)/Header`")"
ParentPage = $pageClass
NavigationParam = 'nullptr' # VM param at runtime
SubPage = $navigationParam -eq 'NewTabMenu_Nav' ? 'BreadcrumbSubPage::NewTabMenu_Folder' : $subPage
ElementName = "L`"$($name)`""
File = $filename
}
}
}
}
# Ensure there aren't any duplicate entries
$entries = $entries | Sort-Object DisplayTextLocalized, ParentPage, NavigationParam, SubPage, ElementName, File -Unique
$buildTimeEntries = @()
$profileEntries = @()
$schemeEntries = @()
$ntmEntries = @()
foreach ($e in $entries)
{
$formattedEntry = " IndexEntry{ $($e.DisplayTextUid), $($e.DisplayTextLocalized), $($e.NavigationParam), $($e.SubPage), $($e.ElementName) }, // $($e.File)"
if ($e.NavigationParam -eq 'nullptr' -and
($e.ParentPage -match 'Profiles_Base' -or
$e.ParentPage -match 'Profiles_Appearance' -or
$e.ParentPage -match 'Profiles_Terminal' -or
$e.ParentPage -match 'Profiles_Advanced'))
{
$profileEntries += $formattedEntry
}
elseif ($e.SubPage -eq 'BreadcrumbSubPage::ColorSchemes_Edit')
{
$schemeEntries += $formattedEntry
}
elseif ($e.SubPage -eq 'BreadcrumbSubPage::NewTabMenu_Folder')
{
$ntmEntries += $formattedEntry
}
else
{
$buildTimeEntries += $formattedEntry
}
}
$headerPath = Join-Path $OutputDir 'GeneratedSettingsIndex.g.h'
$cppPath = Join-Path $OutputDir 'GeneratedSettingsIndex.g.cpp'
$header = @"
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
--*/
#pragma once
#include <winrt/Windows.UI.Xaml.Interop.h>
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
struct IndexEntry
{
// x:Uid of the SettingContainer's Header (i.e. "Globals_DefaultProfile/Header")
hstring DisplayTextUid;
// Localized display text shown in the SettingContainer (i.e. RS_(L"Globals_DefaultProfile/Header"))
hstring DisplayTextLocalized;
// Navigation argument (i.e. winrt::box_value(hstring) or nullptr)
// Use nullptr as placeholder for runtime navigation with a view model object
winrt::Windows::Foundation::IInspectable NavigationArg;
BreadcrumbSubPage SubPage;
// x:Name of the SettingContainer to navigate to on the page (i.e. "DefaultProfile")
hstring ElementName;
};
const std::array<IndexEntry, $($buildTimeEntries.Count)>& LoadBuildTimeIndex();
const std::array<IndexEntry, $($profileEntries.Count)>& LoadProfileIndex();
const std::array<IndexEntry, $($ntmEntries.Count)>& LoadNTMFolderIndex();
const std::array<IndexEntry, $($schemeEntries.Count)>& LoadColorSchemeIndex();
const IndexEntry& PartialProfileIndexEntry();
const IndexEntry& PartialNTMFolderIndexEntry();
const IndexEntry& PartialColorSchemeIndexEntry();
const IndexEntry& PartialExtensionIndexEntry();
const IndexEntry& PartialActionIndexEntry();
}
"@
$cpp = @"
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include <winrt/Microsoft.Terminal.Settings.Editor.h>
#include "GeneratedSettingsIndex.g.h"
#include <LibraryResources.h>
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
const std::array<IndexEntry, $($buildTimeEntries.Count)>& LoadBuildTimeIndex()
{
static std::array<IndexEntry, $($buildTimeEntries.Count)> entries =
{
$( ($buildTimeEntries -join "`r`n") )
};
return entries;
}
const std::array<IndexEntry, $($profileEntries.Count)>& LoadProfileIndex()
{
static std::array<IndexEntry, $($profileEntries.Count)> entries =
{
$( ($profileEntries -join "`r`n") )
};
return entries;
}
const std::array<IndexEntry, $($ntmEntries.Count)>& LoadNTMFolderIndex()
{
static std::array<IndexEntry, $($ntmEntries.Count)> entries =
{
$( ($ntmEntries -join "`r`n") )
};
return entries;
}
const std::array<IndexEntry, $($schemeEntries.Count)>& LoadColorSchemeIndex()
{
static std::array<IndexEntry, $($schemeEntries.Count)> entries =
{
$( ($schemeEntries -join "`r`n") )
};
return entries;
}
const IndexEntry& PartialProfileIndexEntry()
{
static IndexEntry entry{ L"", L"", nullptr, BreadcrumbSubPage::None, L"" };
return entry;
}
const IndexEntry& PartialNTMFolderIndexEntry()
{
static IndexEntry entry{ L"", L"", nullptr, BreadcrumbSubPage::NewTabMenu_Folder, L"" };
return entry;
}
const IndexEntry& PartialColorSchemeIndexEntry()
{
static IndexEntry entry{ L"", L"", nullptr, BreadcrumbSubPage::ColorSchemes_Edit, L"" };
return entry;
}
const IndexEntry& PartialExtensionIndexEntry()
{
static IndexEntry entry{ L"", L"", nullptr, BreadcrumbSubPage::Extensions_Extension, L"" };
return entry;
}
const IndexEntry& PartialActionIndexEntry()
{
static IndexEntry entry{ L"", L"", nullptr, BreadcrumbSubPage::Actions_Edit, L"" };
return entry;
}
}
"@
Set-Content -LiteralPath $headerPath -Value $header -NoNewline
Set-Content -LiteralPath $cppPath -Value $cpp -NoNewline
Write-Host "Generated:"
Write-Host " $headerPath"
Write-Host " $cppPath"