mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-19 03:10:49 +00:00
Compare commits
59 Commits
dev/migrie
...
dev/cazamo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91dbd65712 | ||
|
|
5d0e8c238c | ||
|
|
07792774f6 | ||
|
|
248ffa139e | ||
|
|
a214c37542 | ||
|
|
09bcdc0566 | ||
|
|
cee7393e1e | ||
|
|
26644183f2 | ||
|
|
8f7a26d05c | ||
|
|
7a05a7c603 | ||
|
|
c370efbdad | ||
|
|
543c67771e | ||
|
|
5471c4c400 | ||
|
|
5d188ce593 | ||
|
|
469a6d73be | ||
|
|
82a986af94 | ||
|
|
62a6b5eb2f | ||
|
|
6cc6fe1714 | ||
|
|
14e380a9b2 | ||
|
|
9cbaa980d8 | ||
|
|
27d4a0b575 | ||
|
|
e14dfec7b7 | ||
|
|
672945c3bf | ||
|
|
d008e80d02 | ||
|
|
3252b7ddf0 | ||
|
|
fe4069dd13 | ||
|
|
3abee35102 | ||
|
|
acb19efea7 | ||
|
|
22ba243185 | ||
|
|
018fade640 | ||
|
|
1a73aa6367 | ||
|
|
e87cc1f346 | ||
|
|
0228206e02 | ||
|
|
072ab20a4d | ||
|
|
6d968b54f3 | ||
|
|
20bdc21c79 | ||
|
|
a4c69cfc6a | ||
|
|
e5ea64586d | ||
|
|
81f881a579 | ||
|
|
45d75e701f | ||
|
|
83aa9fd889 | ||
|
|
8c99200e96 | ||
|
|
14bab6cc1a | ||
|
|
be2b1d30cb | ||
|
|
6fbf953fb2 | ||
|
|
cff62cc60e | ||
|
|
82536fd756 | ||
|
|
bf2e4e19d7 | ||
|
|
2706d05491 | ||
|
|
1dafcef36f | ||
|
|
53ddd92e7f | ||
|
|
e84e8d408f | ||
|
|
532343f1ce | ||
|
|
2a41f8a57c | ||
|
|
e56eb74788 | ||
|
|
cac844b1e9 | ||
|
|
915f085b60 | ||
|
|
1b8c99dff8 | ||
|
|
e01ff4faf0 |
2
.github/actions/spelling/allow/allow.txt
vendored
2
.github/actions/spelling/allow/allow.txt
vendored
@@ -71,7 +71,9 @@ sustainability
|
||||
sxn
|
||||
Tencent
|
||||
toolset
|
||||
Uids
|
||||
UEFI
|
||||
UIDs
|
||||
uiatextrange
|
||||
und
|
||||
vsdevcmd
|
||||
|
||||
2
.github/actions/spelling/expect/expect.txt
vendored
2
.github/actions/spelling/expect/expect.txt
vendored
@@ -866,6 +866,7 @@ KILLACTIVE
|
||||
KILLFOCUS
|
||||
kinda
|
||||
KIYEOK
|
||||
KKP
|
||||
KLF
|
||||
KLMNO
|
||||
KOK
|
||||
@@ -885,6 +886,7 @@ LBUTTONDOWN
|
||||
LBUTTONUP
|
||||
lcb
|
||||
lci
|
||||
LCMAP
|
||||
LCONTROL
|
||||
LCTRL
|
||||
lcx
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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; };
|
||||
|
||||
@@ -98,6 +98,7 @@ void Terminal::UpdateSettings(ICoreSettings settings)
|
||||
}
|
||||
|
||||
_getTerminalInput().ForceDisableWin32InputMode(settings.ForceVTInput());
|
||||
_getTerminalInput().ForceDisableKittyKeyboardProtocol(!settings.AllowKittyKeyboardMode());
|
||||
|
||||
if (settings.TabColor() == nullptr)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -53,13 +53,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
public:
|
||||
NavigateToCommandArgs(CommandViewModel command, Editor::IHostedInWindow windowRoot) :
|
||||
_Command(command),
|
||||
_WindowRoot(windowRoot) {}
|
||||
_WeakWindowRoot(windowRoot) {}
|
||||
|
||||
Editor::IHostedInWindow WindowRoot() const noexcept { return _WindowRoot; }
|
||||
Editor::IHostedInWindow WindowRoot() const noexcept { return _WeakWindowRoot.get(); }
|
||||
Editor::CommandViewModel Command() const noexcept { return _Command; }
|
||||
|
||||
private:
|
||||
Editor::IHostedInWindow _WindowRoot;
|
||||
winrt::weak_ref<Editor::IHostedInWindow> _WeakWindowRoot;
|
||||
Editor::CommandViewModel _Command{ nullptr };
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
@@ -210,6 +211,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);
|
||||
|
||||
|
||||
@@ -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}">
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}">
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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; };
|
||||
|
||||
@@ -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}" />
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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; };
|
||||
|
||||
@@ -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>
|
||||
|
||||
330
src/cascadia/TerminalSettingsEditor/IconPicker.cpp
Normal file
330
src/cascadia/TerminalSettingsEditor/IconPicker.cpp
Normal file
@@ -0,0 +1,330 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "IconPicker.h"
|
||||
#include "IconPicker.g.cpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
#include "SegoeFluentIconList.h"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "../WinRTUtils/inc/Utils.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::UI;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Xaml::Media;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Microsoft::UI::Xaml::Controls;
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
namespace MUX = Microsoft::UI::Xaml;
|
||||
namespace WUX = Windows::UI::Xaml;
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
static constexpr std::wstring_view HideIconValue{ L"none" };
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> IconPicker::_BuiltInIcons{ nullptr };
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> IconPicker::_IconTypes{ nullptr };
|
||||
DependencyProperty IconPicker::_CurrentIconPathProperty{ nullptr };
|
||||
DependencyProperty IconPicker::_WindowRootProperty{ nullptr };
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> IconPicker::BuiltInIcons() noexcept
|
||||
{
|
||||
if (!_BuiltInIcons)
|
||||
{
|
||||
// lazy load the built-in icons
|
||||
std::vector<Editor::EnumEntry> builtInIcons;
|
||||
for (auto& [val, name] : s_SegoeFluentIcons)
|
||||
{
|
||||
builtInIcons.emplace_back(make<EnumEntry>(hstring{ name }, box_value(val)));
|
||||
}
|
||||
_BuiltInIcons = single_threaded_observable_vector<Editor::EnumEntry>(std::move(builtInIcons));
|
||||
}
|
||||
return _BuiltInIcons;
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> IconPicker::IconTypes() noexcept
|
||||
{
|
||||
if (!_IconTypes)
|
||||
{
|
||||
// lazy load the icon types
|
||||
std::vector<Editor::EnumEntry> iconTypes;
|
||||
iconTypes.reserve(4);
|
||||
iconTypes.emplace_back(make<EnumEntry>(RS_(L"IconPicker_IconTypeNone"), box_value(IconType::None)));
|
||||
iconTypes.emplace_back(make<EnumEntry>(RS_(L"IconPicker_IconTypeFontIcon"), box_value(IconType::FontIcon)));
|
||||
iconTypes.emplace_back(make<EnumEntry>(RS_(L"IconPicker_IconTypeEmoji"), box_value(IconType::Emoji)));
|
||||
iconTypes.emplace_back(make<EnumEntry>(RS_(L"IconPicker_IconTypeImage"), box_value(IconType::Image)));
|
||||
_IconTypes = winrt::single_threaded_observable_vector<Editor::EnumEntry>(std::move(iconTypes));
|
||||
}
|
||||
return _IconTypes;
|
||||
}
|
||||
|
||||
IconPicker::IconPicker()
|
||||
{
|
||||
_InitializeProperties();
|
||||
InitializeComponent();
|
||||
|
||||
_DeduceCurrentIconType();
|
||||
PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) {
|
||||
const auto propertyName{ args.PropertyName() };
|
||||
// "CurrentIconPath" changes are handled by _OnCurrentIconPathChanged()
|
||||
if (propertyName == L"CurrentIconType")
|
||||
{
|
||||
PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"UsingNoIcon" });
|
||||
PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"UsingBuiltInIcon" });
|
||||
PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"UsingEmojiIcon" });
|
||||
PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"UsingImageIcon" });
|
||||
}
|
||||
else if (propertyName == L"CurrentBuiltInIcon")
|
||||
{
|
||||
CurrentIconPath(unbox_value<hstring>(_CurrentBuiltInIcon.EnumValue()));
|
||||
}
|
||||
else if (propertyName == L"CurrentEmojiIcon")
|
||||
{
|
||||
CurrentIconPath(CurrentEmojiIcon());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IconPicker::_InitializeProperties()
|
||||
{
|
||||
// Initialize any dependency properties here.
|
||||
// This performs a lazy load on these properties, instead of
|
||||
// initializing them when the DLL loads.
|
||||
if (!_CurrentIconPathProperty)
|
||||
{
|
||||
_CurrentIconPathProperty =
|
||||
DependencyProperty::Register(
|
||||
L"CurrentIconPath",
|
||||
xaml_typename<hstring>(),
|
||||
xaml_typename<Editor::IconPicker>(),
|
||||
PropertyMetadata{ nullptr, PropertyChangedCallback{ &IconPicker::_OnCurrentIconPathChanged } });
|
||||
}
|
||||
if (!_WindowRootProperty)
|
||||
{
|
||||
_WindowRootProperty =
|
||||
DependencyProperty::Register(
|
||||
L"WindowRoot",
|
||||
xaml_typename<IHostedInWindow>(),
|
||||
xaml_typename<Editor::IconPicker>(),
|
||||
PropertyMetadata{ nullptr });
|
||||
}
|
||||
}
|
||||
|
||||
void IconPicker::_OnCurrentIconPathChanged(const DependencyObject& d, const DependencyPropertyChangedEventArgs& /*e*/)
|
||||
{
|
||||
d.as<IconPicker>()->_DeduceCurrentIconType();
|
||||
}
|
||||
|
||||
safe_void_coroutine IconPicker::Icon_Click(const IInspectable&, const RoutedEventArgs&)
|
||||
{
|
||||
auto lifetime = get_strong();
|
||||
|
||||
const auto parentHwnd{ reinterpret_cast<HWND>(WindowRoot().GetHostingWindow()) };
|
||||
auto file = co_await OpenImagePicker(parentHwnd);
|
||||
if (!file.empty())
|
||||
{
|
||||
CurrentIconPath(file);
|
||||
}
|
||||
}
|
||||
|
||||
void IconPicker::BuiltInIconPicker_GotFocus(const IInspectable& sender, const RoutedEventArgs& /*e*/)
|
||||
{
|
||||
_updateIconFilter({});
|
||||
sender.as<AutoSuggestBox>().IsSuggestionListOpen(true);
|
||||
}
|
||||
|
||||
void IconPicker::BuiltInIconPicker_QuerySubmitted(const AutoSuggestBox& /*sender*/, const AutoSuggestBoxQuerySubmittedEventArgs& e)
|
||||
{
|
||||
const auto iconEntry = unbox_value_or<Editor::EnumEntry>(e.ChosenSuggestion(), nullptr);
|
||||
if (!iconEntry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
CurrentBuiltInIcon(iconEntry);
|
||||
}
|
||||
|
||||
void IconPicker::BuiltInIconPicker_TextChanged(const AutoSuggestBox& sender, const AutoSuggestBoxTextChangedEventArgs& e)
|
||||
{
|
||||
if (e.Reason() != AutoSuggestionBoxTextChangeReason::UserInput)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::wstring_view filter{ sender.Text() };
|
||||
filter = til::trim(filter, L' ');
|
||||
_updateIconFilter(filter);
|
||||
}
|
||||
|
||||
void IconPicker::_updateIconFilter(std::wstring_view filter)
|
||||
{
|
||||
if (_iconFilter != filter)
|
||||
{
|
||||
_filteredBuiltInIcons = nullptr;
|
||||
_iconFilter = filter;
|
||||
_updateFilteredIconList();
|
||||
PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"FilteredBuiltInIconList" });
|
||||
}
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> IconPicker::FilteredBuiltInIconList()
|
||||
{
|
||||
if (!_filteredBuiltInIcons)
|
||||
{
|
||||
_updateFilteredIconList();
|
||||
}
|
||||
return _filteredBuiltInIcons;
|
||||
}
|
||||
|
||||
void IconPicker::_updateFilteredIconList()
|
||||
{
|
||||
_filteredBuiltInIcons = BuiltInIcons();
|
||||
if (_iconFilter.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Find matching icons and populate the filtered list
|
||||
std::vector<Editor::EnumEntry> filtered;
|
||||
filtered.reserve(_filteredBuiltInIcons.Size());
|
||||
for (const auto& icon : _filteredBuiltInIcons)
|
||||
{
|
||||
if (til::contains_linguistic_insensitive(icon.EnumName(), _iconFilter))
|
||||
{
|
||||
filtered.emplace_back(icon);
|
||||
}
|
||||
}
|
||||
_filteredBuiltInIcons = winrt::single_threaded_observable_vector(std::move(filtered));
|
||||
}
|
||||
|
||||
void IconPicker::CurrentIconType(const Windows::Foundation::IInspectable& value)
|
||||
{
|
||||
if (_currentIconType != value)
|
||||
{
|
||||
// Switching from...
|
||||
if (_currentIconType && unbox_value<IconType>(_currentIconType.as<Editor::EnumEntry>().EnumValue()) == IconType::Image)
|
||||
{
|
||||
// Stash the current value of Icon. If the user
|
||||
// switches out of then back to IconType::Image, we want
|
||||
// the path that we display in the text box to remain unchanged.
|
||||
_lastIconPath = CurrentIconPath();
|
||||
}
|
||||
|
||||
// Set the member here instead of after setting Icon() below!
|
||||
// We have an Icon property changed handler defined for when we discard changes.
|
||||
// Inadvertently, that means that we call this setter again.
|
||||
// Setting the member here means that we early exit at the beginning of the function
|
||||
// because _currentIconType == value.
|
||||
_currentIconType = value;
|
||||
|
||||
// Switched to...
|
||||
switch (unbox_value<IconType>(value.as<Editor::EnumEntry>().EnumValue()))
|
||||
{
|
||||
case IconType::None:
|
||||
{
|
||||
CurrentIconPath(winrt::hstring{ HideIconValue });
|
||||
break;
|
||||
}
|
||||
case IconType::Image:
|
||||
{
|
||||
if (!_lastIconPath.empty())
|
||||
{
|
||||
// Conversely, if we switch to Image,
|
||||
// retrieve that saved value and apply it
|
||||
CurrentIconPath(_lastIconPath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IconType::FontIcon:
|
||||
{
|
||||
if (_CurrentBuiltInIcon)
|
||||
{
|
||||
CurrentIconPath(unbox_value<hstring>(_CurrentBuiltInIcon.EnumValue()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IconType::Emoji:
|
||||
{
|
||||
// Don't set Icon here!
|
||||
// Clear out the text box so we direct the user to use the emoji picker.
|
||||
CurrentEmojiIcon({});
|
||||
}
|
||||
}
|
||||
// We're not using the VM's Icon() setter above,
|
||||
// so notify HasIcon changed manually
|
||||
PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"CurrentIconType" });
|
||||
PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"HasIcon" });
|
||||
}
|
||||
}
|
||||
|
||||
bool IconPicker::UsingNoIcon() const
|
||||
{
|
||||
return _currentIconType == IconTypes().GetAt(0);
|
||||
}
|
||||
|
||||
bool IconPicker::UsingBuiltInIcon() const
|
||||
{
|
||||
return _currentIconType == IconTypes().GetAt(1);
|
||||
}
|
||||
|
||||
bool IconPicker::UsingEmojiIcon() const
|
||||
{
|
||||
return _currentIconType == IconTypes().GetAt(2);
|
||||
}
|
||||
|
||||
bool IconPicker::UsingImageIcon() const
|
||||
{
|
||||
return _currentIconType == IconTypes().GetAt(3);
|
||||
}
|
||||
|
||||
void IconPicker::_DeduceCurrentIconType()
|
||||
{
|
||||
const auto icon = CurrentIconPath();
|
||||
if (icon.empty() || icon == HideIconValue)
|
||||
{
|
||||
_currentIconType = IconTypes().GetAt(0);
|
||||
}
|
||||
else if (icon.size() == 1 && (L'\uE700' <= til::at(icon, 0) && til::at(icon, 0) <= L'\uF8B3'))
|
||||
{
|
||||
_currentIconType = IconTypes().GetAt(1);
|
||||
_DeduceCurrentBuiltInIcon();
|
||||
}
|
||||
else if (::Microsoft::Console::Utils::IsLikelyToBeEmojiOrSymbolIcon(icon))
|
||||
{
|
||||
// We already did a range check for MDL2 Assets in the previous one,
|
||||
// so if we're out of that range but still short, assume we're an emoji
|
||||
_currentIconType = IconTypes().GetAt(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentIconType = IconTypes().GetAt(3);
|
||||
}
|
||||
PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"CurrentIconType" });
|
||||
}
|
||||
|
||||
void IconPicker::_DeduceCurrentBuiltInIcon()
|
||||
{
|
||||
const auto icon = CurrentIconPath();
|
||||
for (uint32_t i = 0; i < BuiltInIcons().Size(); i++)
|
||||
{
|
||||
const auto& builtIn = BuiltInIcons().GetAt(i);
|
||||
if (icon == unbox_value<hstring>(builtIn.EnumValue()))
|
||||
{
|
||||
CurrentBuiltInIcon(builtIn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
CurrentBuiltInIcon(BuiltInIcons().GetAt(0));
|
||||
}
|
||||
|
||||
WUX::Controls::IconSource IconPicker::BuiltInIconConverter(const IInspectable& iconVal)
|
||||
{
|
||||
return Microsoft::Terminal::UI::IconPathConverter::IconSourceWUX(unbox_value<hstring>(iconVal));
|
||||
}
|
||||
}
|
||||
65
src/cascadia/TerminalSettingsEditor/IconPicker.h
Normal file
65
src/cascadia/TerminalSettingsEditor/IconPicker.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IconPicker.g.h"
|
||||
#include "EnumEntry.h"
|
||||
#include "Utils.h"
|
||||
#include "cppwinrt_utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct IconPicker : public HasScrollViewer<IconPicker>, IconPickerT<IconPicker>
|
||||
{
|
||||
public:
|
||||
IconPicker();
|
||||
|
||||
static constexpr std::wstring_view HideIconValue{ L"none" };
|
||||
static Windows::UI::Xaml::Controls::IconSource BuiltInIconConverter(const Windows::Foundation::IInspectable& iconVal);
|
||||
static Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> BuiltInIcons() noexcept;
|
||||
static Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> IconTypes() noexcept;
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> FilteredBuiltInIconList();
|
||||
safe_void_coroutine Icon_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
void BuiltInIconPicker_GotFocus(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
void BuiltInIconPicker_TextChanged(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const Windows::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs& e);
|
||||
void BuiltInIconPicker_QuerySubmitted(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const Windows::UI::Xaml::Controls::AutoSuggestBoxQuerySubmittedEventArgs& e);
|
||||
|
||||
Windows::Foundation::IInspectable CurrentIconType() const noexcept { return _currentIconType; }
|
||||
void CurrentIconType(const Windows::Foundation::IInspectable& value);
|
||||
|
||||
bool UsingNoIcon() const;
|
||||
bool UsingBuiltInIcon() const;
|
||||
bool UsingEmojiIcon() const;
|
||||
bool UsingImageIcon() const;
|
||||
|
||||
til::property_changed_event PropertyChanged;
|
||||
WINRT_OBSERVABLE_PROPERTY(hstring, CurrentEmojiIcon, PropertyChanged.raise);
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::EnumEntry, CurrentBuiltInIcon, PropertyChanged.raise, nullptr);
|
||||
|
||||
DEPENDENCY_PROPERTY(hstring, CurrentIconPath);
|
||||
DEPENDENCY_PROPERTY(IHostedInWindow, WindowRoot);
|
||||
|
||||
private:
|
||||
static Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> _BuiltInIcons;
|
||||
static Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> _IconTypes;
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> _filteredBuiltInIcons;
|
||||
std::wstring _iconFilter;
|
||||
Windows::Foundation::IInspectable _currentIconType{};
|
||||
winrt::hstring _lastIconPath;
|
||||
|
||||
static void _InitializeProperties();
|
||||
static void _OnCurrentIconPathChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);
|
||||
|
||||
void _DeduceCurrentIconType();
|
||||
void _DeduceCurrentBuiltInIcon();
|
||||
void _updateIconFilter(std::wstring_view filter);
|
||||
void _updateFilteredIconList();
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(IconPicker);
|
||||
}
|
||||
42
src/cascadia/TerminalSettingsEditor/IconPicker.idl
Normal file
42
src/cascadia/TerminalSettingsEditor/IconPicker.idl
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "EnumEntry.idl";
|
||||
import "MainPage.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
enum IconType
|
||||
{
|
||||
None = 0,
|
||||
FontIcon,
|
||||
Image,
|
||||
Emoji
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass IconPicker : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
IconPicker();
|
||||
|
||||
IInspectable CurrentIconType;
|
||||
Windows.Foundation.Collections.IObservableVector<EnumEntry> IconTypes { get; };
|
||||
|
||||
Boolean UsingBuiltInIcon { get; };
|
||||
Boolean UsingEmojiIcon { get; };
|
||||
Boolean UsingImageIcon { get; };
|
||||
|
||||
String CurrentEmojiIcon;
|
||||
|
||||
EnumEntry CurrentBuiltInIcon;
|
||||
Windows.Foundation.Collections.IObservableVector<EnumEntry> BuiltInIcons { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<EnumEntry> FilteredBuiltInIconList { get; };
|
||||
|
||||
static Windows.UI.Xaml.Controls.IconSource BuiltInIconConverter(IInspectable iconVal);
|
||||
|
||||
String CurrentIconPath;
|
||||
static Windows.UI.Xaml.DependencyProperty CurrentIconPathProperty { get; };
|
||||
|
||||
IHostedInWindow WindowRoot;
|
||||
static Windows.UI.Xaml.DependencyProperty WindowRootProperty { get; };
|
||||
}
|
||||
}
|
||||
100
src/cascadia/TerminalSettingsEditor/IconPicker.xaml
Normal file
100
src/cascadia/TerminalSettingsEditor/IconPicker.xaml
Normal file
@@ -0,0 +1,100 @@
|
||||
<!--
|
||||
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information.
|
||||
-->
|
||||
<UserControl x:Class="Microsoft.Terminal.Settings.Editor.IconPicker"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:model="using:Microsoft.Terminal.Settings.Model"
|
||||
xmlns:mtu="using:Microsoft.Terminal.UI"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="CommonResources.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Icon Type -->
|
||||
<ComboBox x:Uid="IconPicker_IconType"
|
||||
Grid.Column="0"
|
||||
ItemsSource="{x:Bind IconTypes}"
|
||||
SelectedItem="{x:Bind CurrentIconType, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:EnumEntry">
|
||||
<TextBlock Text="{x:Bind EnumName}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<!-- Built-In Icon -->
|
||||
<AutoSuggestBox x:Uid="IconPicker_BuiltInIcon"
|
||||
Grid.Column="1"
|
||||
GotFocus="BuiltInIconPicker_GotFocus"
|
||||
ItemsSource="{x:Bind FilteredBuiltInIconList, Mode=OneWay}"
|
||||
QuerySubmitted="BuiltInIconPicker_QuerySubmitted"
|
||||
Text="{x:Bind CurrentBuiltInIcon.EnumName, Mode=OneWay}"
|
||||
TextBoxStyle="{StaticResource TextBoxSettingStyle}"
|
||||
TextChanged="BuiltInIconPicker_TextChanged"
|
||||
Visibility="{x:Bind UsingBuiltInIcon, Mode=OneWay}">
|
||||
<AutoSuggestBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:EnumEntry">
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<IconSourceElement Grid.Column="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
IconSource="{x:Bind local:IconPicker.BuiltInIconConverter(EnumValue), Mode=OneTime}" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{x:Bind EnumName}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</AutoSuggestBox.ItemTemplate>
|
||||
</AutoSuggestBox>
|
||||
|
||||
<!-- Image (File) Icon -->
|
||||
<TextBox x:Uid="IconPicker_ImagePathBox"
|
||||
Grid.Column="1"
|
||||
MaxWidth="Infinity"
|
||||
HorizontalAlignment="Stretch"
|
||||
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
|
||||
IsSpellCheckEnabled="False"
|
||||
Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind CurrentIconPath, Mode=TwoWay}"
|
||||
Visibility="{x:Bind UsingImageIcon, Mode=OneWay}" />
|
||||
<Button x:Uid="IconPicker_IconBrowse"
|
||||
Grid.Column="2"
|
||||
Margin="0"
|
||||
VerticalAlignment="Top"
|
||||
Click="Icon_Click"
|
||||
Style="{StaticResource BrowseButtonStyle}"
|
||||
Visibility="{x:Bind UsingImageIcon, Mode=OneWay}" />
|
||||
|
||||
<!-- Emoji Icon -->
|
||||
<TextBox x:Uid="IconPicker_EmojiBox"
|
||||
Grid.Column="1"
|
||||
MaxWidth="Infinity"
|
||||
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
|
||||
IsSpellCheckEnabled="False"
|
||||
Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind CurrentEmojiIcon, Mode=TwoWay}"
|
||||
Visibility="{x:Bind UsingEmojiIcon, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -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,
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
@@ -83,7 +103,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
const auto crumb = winrt::make<Breadcrumb>(box_value(newTabMenuTag), RS_(L"Nav_NewTabMenu/Content"), BreadcrumbSubPage::None);
|
||||
_breadcrumbs.Append(crumb);
|
||||
}
|
||||
contentFrame().Navigate(xaml_typename<Editor::NewTabMenu>(), _newTabMenuPageVM);
|
||||
contentFrame().Navigate(xaml_typename<Editor::NewTabMenu>(), winrt::make<NavigateToPageArgs>(_newTabMenuPageVM, *this));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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>(), _newTabMenuPageVM);
|
||||
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>(), _newTabMenuPageVM);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
|
||||
#include "MainPage.g.h"
|
||||
#include "Breadcrumb.g.h"
|
||||
#include "NavigateToPageArgs.g.h"
|
||||
#include "Utils.h"
|
||||
#include "SearchIndex.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
@@ -23,6 +25,24 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
WINRT_PROPERTY(BreadcrumbSubPage, SubPage);
|
||||
};
|
||||
|
||||
struct NavigateToPageArgs : NavigateToPageArgsT<NavigateToPageArgs>
|
||||
{
|
||||
public:
|
||||
NavigateToPageArgs(Windows::Foundation::IInspectable viewModel, Editor::IHostedInWindow windowRoot, const hstring& elementToFocus = {}) :
|
||||
_ViewModel(viewModel),
|
||||
_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;
|
||||
Windows::Foundation::IInspectable _ViewModel{ nullptr };
|
||||
hstring _ElementToFocus{};
|
||||
};
|
||||
|
||||
struct MainPage : MainPageT<MainPage>
|
||||
{
|
||||
MainPage() = delete;
|
||||
@@ -30,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);
|
||||
@@ -68,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;
|
||||
|
||||
@@ -16,6 +16,13 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
UInt64 GetHostingWindow();
|
||||
}
|
||||
|
||||
runtimeclass NavigateToPageArgs
|
||||
{
|
||||
IHostedInWindow WindowRoot { get; };
|
||||
IInspectable ViewModel { get; };
|
||||
String ElementToFocus { get; };
|
||||
}
|
||||
|
||||
enum BreadcrumbSubPage
|
||||
{
|
||||
None = 0,
|
||||
|
||||
@@ -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="" />
|
||||
</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="" />
|
||||
</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="" />
|
||||
</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="" />
|
||||
</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="" />
|
||||
</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="" />
|
||||
</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="" />
|
||||
</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="" />
|
||||
</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="" />
|
||||
</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="" />
|
||||
</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="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
Tag="OpenJson_Nav" />
|
||||
</muxc:NavigationView.FooterMenuItems>
|
||||
|
||||
<Grid>
|
||||
|
||||
@@ -69,6 +69,10 @@
|
||||
<DependentUpon>NullableColorPicker.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IconPicker.h">
|
||||
<DependentUpon>IconPicker.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EditColorScheme.h">
|
||||
<DependentUpon>EditColorScheme.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -89,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>
|
||||
@@ -169,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>
|
||||
@@ -193,6 +201,9 @@
|
||||
<Page Include="NullableColorPicker.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="IconPicker.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="EditColorScheme.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
@@ -269,6 +280,10 @@
|
||||
<DependentUpon>NullableColorPicker.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IconPicker.cpp">
|
||||
<DependentUpon>IconPicker.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EditColorScheme.cpp">
|
||||
<DependentUpon>EditColorScheme.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -291,6 +306,7 @@
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SearchIndex.cpp" />
|
||||
<ClCompile Include="MainPage.cpp">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
@@ -373,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>
|
||||
@@ -397,6 +415,10 @@
|
||||
<DependentUpon>NullableColorPicker.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="IconPicker.idl">
|
||||
<DependentUpon>IconPicker.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="EditColorScheme.idl">
|
||||
<DependentUpon>EditColorScheme.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -425,6 +447,7 @@
|
||||
<DependentUpon>Rendering.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="SearchIndex.idl" />
|
||||
<Midl Include="MainPage.idl">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Midl>
|
||||
@@ -541,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 "$(OpenConsoleDir)tools\GenerateSettingsIndex.ps1" -SourceDir "$(MSBuildThisFileDirectory)." -OutputDir "$(MSBuildThisFileDirectory)$(GeneratedFilesDir)."" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -55,6 +55,7 @@
|
||||
<Page Include="AddProfile.xaml" />
|
||||
<Page Include="KeyChordListener.xaml" />
|
||||
<Page Include="NullableColorPicker.xaml" />
|
||||
<Page Include="IconPicker.xaml" />
|
||||
<Page Include="NewTabMenu.xaml" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
40
src/cascadia/TerminalSettingsEditor/NavConstants.h
Normal file
40
src/cascadia/TerminalSettingsEditor/NavConstants.h
Normal 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 */
|
||||
};
|
||||
}
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "NewTabMenu.h"
|
||||
#include "NavigateToPageArgs.g.h"
|
||||
#include "NewTabMenu.g.cpp"
|
||||
#include "NavigateToPageArgs.g.h"
|
||||
#include "NewTabMenuEntryTemplateSelector.g.cpp"
|
||||
#include "EnumEntry.h"
|
||||
|
||||
@@ -41,7 +43,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
void NewTabMenu::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
_ViewModel = e.Parameter().as<Editor::NewTabMenuViewModel>();
|
||||
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
|
||||
_ViewModel = args.ViewModel().as<Editor::NewTabMenuViewModel>();
|
||||
|
||||
BringIntoViewWhenLoaded(args.ElementToFocus());
|
||||
_windowRoot = args.WindowRoot();
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalSettingsEditorProvider,
|
||||
|
||||
@@ -38,11 +38,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
void AddFolderNameTextBox_KeyDown(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
|
||||
void AddFolderNameTextBox_TextChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Controls::TextChangedEventArgs& e);
|
||||
|
||||
Editor::IHostedInWindow WindowRoot() const noexcept { return _windowRoot; }
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::NewTabMenuViewModel, ViewModel, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
private:
|
||||
Editor::NewTabMenuEntryViewModel _draggedEntry{ nullptr };
|
||||
Editor::IHostedInWindow _windowRoot;
|
||||
|
||||
void _ScrollToEntry(const Editor::NewTabMenuEntryViewModel& entry);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
NewTabMenu();
|
||||
NewTabMenuViewModel ViewModel { get; };
|
||||
IHostedInWindow WindowRoot { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass NewTabMenuEntryTemplateSelector : Windows.UI.Xaml.Controls.DataTemplateSelector
|
||||
|
||||
@@ -319,29 +319,51 @@
|
||||
Visibility="{x:Bind ViewModel.IsFolderView, Mode=OneWay}">
|
||||
<TextBlock x:Uid="NewTabMenu_CurrentFolderTextBlock"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
|
||||
<!-- TODO GH #18281: Icon -->
|
||||
<!-- Once PR #17965 merges, we can add that kind of control to set an icon -->
|
||||
|
||||
<!-- Name -->
|
||||
<local:SettingContainer x:Uid="NewTabMenu_CurrentFolderName"
|
||||
Grid.Row="0"
|
||||
<local:SettingContainer x:Name="CurrentFolderName"
|
||||
x:Uid="NewTabMenu_CurrentFolderName"
|
||||
CurrentValue="{x:Bind ViewModel.CurrentFolderName, Mode=OneWay}"
|
||||
Style="{StaticResource ExpanderSettingContainerStyle}">
|
||||
<TextBox Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind ViewModel.CurrentFolderName, Mode=TwoWay}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Icon -->
|
||||
<local:SettingContainer x:Uid="NewTabMenu_CurrentFolderIcon"
|
||||
CurrentValueAccessibleName="{x:Bind ViewModel.CurrentFolderLocalizedIcon, Mode=OneWay}"
|
||||
Style="{StaticResource ExpanderSettingContainerStyleWithComplexPreview}">
|
||||
<local:SettingContainer.CurrentValue>
|
||||
<Grid>
|
||||
<ContentControl Width="16"
|
||||
Height="16"
|
||||
Content="{x:Bind ViewModel.CurrentFolderIconPreview, Mode=OneWay}"
|
||||
IsTabStop="False"
|
||||
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(ViewModel.CurrentFolderUsingNoIcon), Mode=OneWay}" />
|
||||
<TextBlock Margin="0,0,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
|
||||
Style="{StaticResource SettingsPageItemDescriptionStyle}"
|
||||
Text="{x:Bind ViewModel.CurrentFolderLocalizedIcon, Mode=OneWay}"
|
||||
Visibility="{x:Bind ViewModel.CurrentFolderUsingNoIcon, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</local:SettingContainer.CurrentValue>
|
||||
<local:SettingContainer.Content>
|
||||
<local:IconPicker CurrentIconPath="{x:Bind ViewModel.CurrentFolderIconPath, Mode=TwoWay}"
|
||||
WindowRoot="{x:Bind WindowRoot, Mode=OneWay}" />
|
||||
</local:SettingContainer.Content>
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Inlining -->
|
||||
<local:SettingContainer x:Uid="NewTabMenu_CurrentFolderInlining"
|
||||
Grid.Row="1">
|
||||
<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"
|
||||
Grid.Row="2">
|
||||
<local:SettingContainer x:Name="CurrentFolderAllowEmpty"
|
||||
x:Uid="NewTabMenu_CurrentFolderAllowEmpty">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.CurrentFolderAllowEmpty, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</local:SettingContainer>
|
||||
@@ -353,7 +375,8 @@
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
|
||||
<!-- Add Profile -->
|
||||
<local:SettingContainer x:Uid="NewTabMenu_AddProfile"
|
||||
<local:SettingContainer x:Name="AddProfile"
|
||||
x:Uid="NewTabMenu_AddProfile"
|
||||
FontIconGlyph=""
|
||||
Style="{StaticResource SettingContainerWithIcon}">
|
||||
|
||||
@@ -402,7 +425,8 @@
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Add Separator -->
|
||||
<local:SettingContainer x:Uid="NewTabMenu_AddSeparator"
|
||||
<local:SettingContainer x:Name="AddSeparator"
|
||||
x:Uid="NewTabMenu_AddSeparator"
|
||||
FontIconGlyph=""
|
||||
Style="{StaticResource SettingContainerWithIcon}">
|
||||
<Button x:Name="AddSeparatorButton"
|
||||
@@ -418,7 +442,8 @@
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Add Folder -->
|
||||
<local:SettingContainer x:Uid="NewTabMenu_AddFolder"
|
||||
<local:SettingContainer x:Name="AddFolder"
|
||||
x:Uid="NewTabMenu_AddFolder"
|
||||
FontIconGlyph=""
|
||||
Style="{StaticResource SettingContainerWithIcon}">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
@@ -444,7 +469,8 @@
|
||||
</local:SettingContainer>
|
||||
|
||||
<!-- Add Match Profiles -->
|
||||
<local:SettingContainer x:Uid="NewTabMenu_AddMatchProfiles"
|
||||
<local:SettingContainer x:Name="AddMatchProfiles"
|
||||
x:Uid="NewTabMenu_AddMatchProfiles"
|
||||
FontIconGlyph=""
|
||||
Style="{StaticResource ExpanderSettingContainerStyleWithIcon}">
|
||||
<StackPanel Spacing="8">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "NewTabMenuViewModel.h"
|
||||
#include "IconPicker.h"
|
||||
|
||||
#include "NewTabMenuViewModel.g.cpp"
|
||||
#include "FolderTreeViewEntry.g.cpp"
|
||||
@@ -160,6 +161,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
// FolderTree needs to be updated when a folder is renamed
|
||||
_folderTreeCache = nullptr;
|
||||
}
|
||||
else if (viewModelProperty == L"Icon")
|
||||
{
|
||||
_NotifyChanges(L"CurrentFolderIconPreview", L"CurrentFolderLocalizedIcon", L"CurrentFolderIconPath", L"CurrentFolderUsingNoIcon");
|
||||
}
|
||||
}
|
||||
|
||||
hstring NewTabMenuViewModel::CurrentFolderName() const
|
||||
@@ -216,6 +221,60 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::Controls::IconElement NewTabMenuViewModel::CurrentFolderIconPreview() const
|
||||
{
|
||||
if (!_CurrentFolder)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
// IconWUX sets the icon width/height to 32 by default
|
||||
auto icon = Microsoft::Terminal::UI::IconPathConverter::IconWUX(_CurrentFolder.Icon());
|
||||
icon.Width(16);
|
||||
icon.Height(16);
|
||||
return icon;
|
||||
}
|
||||
|
||||
winrt::hstring NewTabMenuViewModel::CurrentFolderLocalizedIcon() const
|
||||
{
|
||||
if (!_CurrentFolder)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
if (CurrentFolderUsingNoIcon())
|
||||
{
|
||||
return RS_(L"IconPicker_IconTypeNone");
|
||||
}
|
||||
return _CurrentFolder.Icon(); // For display as a string
|
||||
}
|
||||
|
||||
winrt::hstring NewTabMenuViewModel::CurrentFolderIconPath() const
|
||||
{
|
||||
if (!_CurrentFolder)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return _CurrentFolder.Icon();
|
||||
}
|
||||
|
||||
void NewTabMenuViewModel::CurrentFolderIconPath(const winrt::hstring& path)
|
||||
{
|
||||
if (_CurrentFolder && _CurrentFolder.Icon() != path)
|
||||
{
|
||||
_CurrentFolder.Icon(path);
|
||||
_NotifyChanges(L"CurrentFolderIconPreview", L"CurrentFolderLocalizedIcon", L"CurrentFolderIconPath", L"CurrentFolderUsingNoIcon");
|
||||
}
|
||||
}
|
||||
|
||||
bool NewTabMenuViewModel::CurrentFolderUsingNoIcon() const noexcept
|
||||
{
|
||||
if (!_CurrentFolder)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto icon{ _CurrentFolder.Icon() };
|
||||
return icon.empty() || icon == IconPicker::HideIconValue;
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::NewTabMenuEntryViewModel> NewTabMenuViewModel::CurrentView() const
|
||||
{
|
||||
if (!_CurrentFolder)
|
||||
@@ -451,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 },
|
||||
|
||||
@@ -47,8 +47,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
bool CurrentFolderAllowEmpty() const;
|
||||
void CurrentFolderAllowEmpty(bool value);
|
||||
|
||||
Windows::UI::Xaml::Controls::IconElement CurrentFolderIconPreview() const;
|
||||
winrt::hstring CurrentFolderLocalizedIcon() const;
|
||||
winrt::hstring CurrentFolderIconPath() const;
|
||||
void CurrentFolderIconPath(const winrt::hstring& path);
|
||||
bool CurrentFolderUsingNoIcon() const noexcept;
|
||||
|
||||
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);
|
||||
@@ -67,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);
|
||||
};
|
||||
|
||||
@@ -134,6 +142,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
void Inlining(bool value);
|
||||
|
||||
hstring Icon() const { return _FolderEntry.Icon().Path(); }
|
||||
void Icon(const hstring& value)
|
||||
{
|
||||
_FolderEntry.Icon(Model::MediaResourceHelper::FromString(value));
|
||||
_NotifyChanges(L"Icon");
|
||||
}
|
||||
|
||||
GETSET_OBSERVABLE_PROJECTED_SETTING(_FolderEntry, Name);
|
||||
GETSET_OBSERVABLE_PROJECTED_SETTING(_FolderEntry, AllowEmpty);
|
||||
|
||||
@@ -41,6 +41,11 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
String ProfileMatcherCommandline;
|
||||
String AddFolderName;
|
||||
|
||||
Windows.UI.Xaml.Controls.IconElement CurrentFolderIconPreview { get; };
|
||||
String CurrentFolderLocalizedIcon { get; };
|
||||
String CurrentFolderIconPath;
|
||||
Boolean CurrentFolderUsingNoIcon { get; };
|
||||
|
||||
void RequestReorderEntry(NewTabMenuEntryViewModel vm, Boolean goingUp);
|
||||
void RequestDeleteEntry(NewTabMenuEntryViewModel vm);
|
||||
void RequestMoveEntriesToFolder(IVector<NewTabMenuEntryViewModel> entries, FolderEntryViewModel folderEntry);
|
||||
@@ -83,7 +88,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
FolderEntryViewModel(Microsoft.Terminal.Settings.Model.FolderEntry folderEntry, Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
||||
|
||||
String Name;
|
||||
String Icon { get; };
|
||||
String Icon;
|
||||
Boolean Inlining;
|
||||
Boolean AllowEmpty;
|
||||
IObservableVector<Microsoft.Terminal.Settings.Editor.NewTabMenuEntryViewModel> Entries;
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
#include "pch.h"
|
||||
#include "ProfileViewModel.h"
|
||||
#include "ProfileViewModel.g.cpp"
|
||||
#include "EnumEntry.h"
|
||||
#include "Appearances.h"
|
||||
#include "EnumEntry.h"
|
||||
#include "IconPicker.h"
|
||||
|
||||
#include "../WinRTUtils/inc/Utils.h"
|
||||
#include "../../renderer/base/FontCache.h"
|
||||
#include "../TerminalSettingsAppAdapterLib/TerminalSettings.h"
|
||||
#include "SegoeFluentIconList.h"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
using namespace winrt::Windows::UI::Text;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
@@ -28,9 +27,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_MonospaceFontList{ nullptr };
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_FontList{ nullptr };
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> ProfileViewModel::_BuiltInIcons{ nullptr };
|
||||
|
||||
static constexpr std::wstring_view HideIconValue{ L"none" };
|
||||
|
||||
ProfileViewModel::ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& appSettings, const Windows::UI::Core::CoreDispatcher& dispatcher) :
|
||||
_profile{ profile },
|
||||
@@ -47,17 +43,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
_InitializeCurrentBellSounds();
|
||||
|
||||
// set up IconTypes
|
||||
std::vector<IInspectable> iconTypes;
|
||||
iconTypes.reserve(4);
|
||||
iconTypes.emplace_back(make<EnumEntry>(RS_(L"Profile_IconTypeNone"), box_value(IconType::None)));
|
||||
iconTypes.emplace_back(make<EnumEntry>(RS_(L"Profile_IconTypeFontIcon"), box_value(IconType::FontIcon)));
|
||||
iconTypes.emplace_back(make<EnumEntry>(RS_(L"Profile_IconTypeEmoji"), box_value(IconType::Emoji)));
|
||||
iconTypes.emplace_back(make<EnumEntry>(RS_(L"Profile_IconTypeImage"), box_value(IconType::Image)));
|
||||
_IconTypes = winrt::single_threaded_vector<IInspectable>(std::move(iconTypes));
|
||||
_DeduceCurrentIconType();
|
||||
_DeduceCurrentBuiltInIcon();
|
||||
|
||||
// Add a property changed handler to our own property changed event.
|
||||
// This propagates changes from the settings model to anybody listening to our
|
||||
// unique view model members.
|
||||
@@ -92,32 +77,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
else if (viewModelProperty == L"Icon")
|
||||
{
|
||||
// _DeduceCurrentIconType() ends with a "CurrentIconType" notification
|
||||
// so we don't need to call _UpdateIconPreview() here
|
||||
_DeduceCurrentIconType();
|
||||
// The icon changed; let's re-evaluate it with its new context.
|
||||
_appSettings.ResolveMediaResources();
|
||||
}
|
||||
else if (viewModelProperty == L"CurrentIconType")
|
||||
{
|
||||
// "Using*" handles the visibility of the IconType-related UI.
|
||||
// The others propagate the rendered icon into a preview (i.e. nav view, container item)
|
||||
_NotifyChanges(L"UsingNoIcon",
|
||||
L"UsingBuiltInIcon",
|
||||
L"UsingEmojiIcon",
|
||||
L"UsingImageIcon",
|
||||
L"LocalizedIcon",
|
||||
|
||||
// Propagate the rendered icon into a preview (i.e. nav view, container item)
|
||||
_NotifyChanges(L"LocalizedIcon",
|
||||
L"IconPreview",
|
||||
L"IconPath",
|
||||
L"EvaluatedIcon");
|
||||
}
|
||||
else if (viewModelProperty == L"CurrentBuiltInIcon")
|
||||
{
|
||||
IconPath(unbox_value<hstring>(_CurrentBuiltInIcon.EnumValue()));
|
||||
}
|
||||
else if (viewModelProperty == L"CurrentEmojiIcon")
|
||||
{
|
||||
IconPath(CurrentEmojiIcon());
|
||||
L"EvaluatedIcon",
|
||||
L"UsingNoIcon");
|
||||
}
|
||||
else if (viewModelProperty == L"CurrentBellSounds")
|
||||
{
|
||||
@@ -190,61 +158,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_defaultAppearanceViewModel.IsDefault(true);
|
||||
}
|
||||
|
||||
void ProfileViewModel::_UpdateBuiltInIcons()
|
||||
{
|
||||
std::vector<Editor::EnumEntry> builtInIcons;
|
||||
for (auto& [val, name] : s_SegoeFluentIcons)
|
||||
{
|
||||
builtInIcons.emplace_back(make<EnumEntry>(hstring{ name }, box_value(val)));
|
||||
}
|
||||
_BuiltInIcons = single_threaded_observable_vector<Editor::EnumEntry>(std::move(builtInIcons));
|
||||
}
|
||||
|
||||
void ProfileViewModel::_DeduceCurrentIconType()
|
||||
{
|
||||
const auto profileIcon = IconPath();
|
||||
if (profileIcon == HideIconValue)
|
||||
{
|
||||
_currentIconType = _IconTypes.GetAt(0);
|
||||
}
|
||||
else if (profileIcon.size() == 1 && (L'\uE700' <= til::at(profileIcon, 0) && til::at(profileIcon, 0) <= L'\uF8B3'))
|
||||
{
|
||||
_currentIconType = _IconTypes.GetAt(1);
|
||||
_DeduceCurrentBuiltInIcon();
|
||||
}
|
||||
else if (::Microsoft::Console::Utils::IsLikelyToBeEmojiOrSymbolIcon(profileIcon))
|
||||
{
|
||||
// We already did a range check for MDL2 Assets in the previous one,
|
||||
// so if we're out of that range but still short, assume we're an emoji
|
||||
_currentIconType = _IconTypes.GetAt(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentIconType = _IconTypes.GetAt(3);
|
||||
}
|
||||
_NotifyChanges(L"CurrentIconType");
|
||||
}
|
||||
|
||||
void ProfileViewModel::_DeduceCurrentBuiltInIcon()
|
||||
{
|
||||
if (!_BuiltInIcons)
|
||||
{
|
||||
_UpdateBuiltInIcons();
|
||||
}
|
||||
const auto profileIcon = IconPath();
|
||||
for (uint32_t i = 0; i < _BuiltInIcons.Size(); i++)
|
||||
{
|
||||
const auto& builtIn = _BuiltInIcons.GetAt(i);
|
||||
if (profileIcon == unbox_value<hstring>(builtIn.EnumValue()))
|
||||
{
|
||||
_CurrentBuiltInIcon = builtIn;
|
||||
return;
|
||||
}
|
||||
}
|
||||
_CurrentBuiltInIcon = _BuiltInIcons.GetAt(0);
|
||||
_NotifyChanges(L"CurrentBuiltInIcon");
|
||||
}
|
||||
|
||||
void ProfileViewModel::LeftPadding(double value) noexcept
|
||||
{
|
||||
if (std::abs(_parsedPadding.Left - value) >= .0001)
|
||||
@@ -636,9 +549,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
winrt::hstring ProfileViewModel::LocalizedIcon() const
|
||||
{
|
||||
if (_currentIconType && unbox_value<IconType>(_currentIconType.as<Editor::EnumEntry>().EnumValue()) == IconType::None)
|
||||
if (UsingNoIcon())
|
||||
{
|
||||
return RS_(L"Profile_IconTypeNone");
|
||||
return RS_(L"IconPicker_IconTypeNone");
|
||||
}
|
||||
return IconPath(); // For display as a string
|
||||
}
|
||||
@@ -652,83 +565,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
return icon;
|
||||
}
|
||||
|
||||
void ProfileViewModel::CurrentIconType(const Windows::Foundation::IInspectable& value)
|
||||
bool ProfileViewModel::UsingNoIcon() const noexcept
|
||||
{
|
||||
if (_currentIconType != value)
|
||||
{
|
||||
// Switching from...
|
||||
if (_currentIconType && unbox_value<IconType>(_currentIconType.as<Editor::EnumEntry>().EnumValue()) == IconType::Image)
|
||||
{
|
||||
// Stash the current value of Icon. If the user
|
||||
// switches out of then back to IconType::Image, we want
|
||||
// the path that we display in the text box to remain unchanged.
|
||||
_lastIconPath = IconPath();
|
||||
}
|
||||
|
||||
// Set the member here instead of after setting Icon() below!
|
||||
// We have an Icon property changed handler defined for when we discard changes.
|
||||
// Inadvertently, that means that we call this setter again.
|
||||
// Setting the member here means that we early exit at the beginning of the function
|
||||
// because _currentIconType == value.
|
||||
_currentIconType = value;
|
||||
|
||||
// Switched to...
|
||||
switch (unbox_value<IconType>(value.as<Editor::EnumEntry>().EnumValue()))
|
||||
{
|
||||
case IconType::None:
|
||||
{
|
||||
IconPath(winrt::hstring{ HideIconValue });
|
||||
break;
|
||||
}
|
||||
case IconType::Image:
|
||||
{
|
||||
if (!_lastIconPath.empty())
|
||||
{
|
||||
// Conversely, if we switch to Image,
|
||||
// retrieve that saved value and apply it
|
||||
IconPath(_lastIconPath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IconType::FontIcon:
|
||||
{
|
||||
if (_CurrentBuiltInIcon)
|
||||
{
|
||||
IconPath(unbox_value<hstring>(_CurrentBuiltInIcon.EnumValue()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IconType::Emoji:
|
||||
{
|
||||
// Don't set Icon here!
|
||||
// Clear out the text box so we direct the user to use the emoji picker.
|
||||
CurrentEmojiIcon({});
|
||||
}
|
||||
}
|
||||
// We're not using the VM's Icon() setter above,
|
||||
// so notify HasIcon changed manually
|
||||
_NotifyChanges(L"CurrentIconType", L"HasIcon");
|
||||
}
|
||||
}
|
||||
|
||||
bool ProfileViewModel::UsingNoIcon() const
|
||||
{
|
||||
return _currentIconType == _IconTypes.GetAt(0);
|
||||
}
|
||||
|
||||
bool ProfileViewModel::UsingBuiltInIcon() const
|
||||
{
|
||||
return _currentIconType == _IconTypes.GetAt(1);
|
||||
}
|
||||
|
||||
bool ProfileViewModel::UsingEmojiIcon() const
|
||||
{
|
||||
return _currentIconType == _IconTypes.GetAt(2);
|
||||
}
|
||||
|
||||
bool ProfileViewModel::UsingImageIcon() const
|
||||
{
|
||||
return _currentIconType == _IconTypes.GetAt(3);
|
||||
const auto iconPath{ IconPath() };
|
||||
return iconPath.empty() || iconPath == IconPicker::HideIconValue;
|
||||
}
|
||||
|
||||
hstring ProfileViewModel::BellStylePreview() const
|
||||
|
||||
@@ -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),
|
||||
_WindowRoot(windowRoot) {}
|
||||
|
||||
Editor::IHostedInWindow WindowRoot() const noexcept { return _WindowRoot; }
|
||||
Editor::ProfileViewModel Profile() const noexcept { return _Profile; }
|
||||
|
||||
private:
|
||||
Editor::IHostedInWindow _WindowRoot;
|
||||
Editor::ProfileViewModel _Profile{ nullptr };
|
||||
};
|
||||
|
||||
struct BellSoundViewModel : BellSoundViewModelT<BellSoundViewModel>, ViewModelHelper<BellSoundViewModel>
|
||||
{
|
||||
public:
|
||||
@@ -49,13 +33,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
static void UpdateFontList() noexcept;
|
||||
static Windows::Foundation::Collections::IObservableVector<Editor::Font> CompleteFontList() noexcept { return _FontList; };
|
||||
static Windows::Foundation::Collections::IObservableVector<Editor::Font> MonospaceFontList() noexcept { return _MonospaceFontList; };
|
||||
static Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> BuiltInIcons() noexcept { return _BuiltInIcons; };
|
||||
|
||||
ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& settings, const Windows::UI::Core::CoreDispatcher& dispatcher);
|
||||
Control::IControlSettings TermSettings() const;
|
||||
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;
|
||||
@@ -86,23 +75,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
return _profile.Icon().Resolved();
|
||||
}
|
||||
Windows::Foundation::IInspectable CurrentIconType() const noexcept
|
||||
{
|
||||
return _currentIconType;
|
||||
}
|
||||
Windows::UI::Xaml::Controls::IconElement IconPreview() const;
|
||||
winrt::hstring LocalizedIcon() const;
|
||||
void CurrentIconType(const Windows::Foundation::IInspectable& value);
|
||||
bool UsingNoIcon() const;
|
||||
bool UsingBuiltInIcon() const;
|
||||
bool UsingEmojiIcon() const;
|
||||
bool UsingImageIcon() const;
|
||||
winrt::hstring IconPath() const { return _profile.Icon().Path(); }
|
||||
void IconPath(const winrt::hstring& path)
|
||||
{
|
||||
Icon(Model::MediaResourceHelper::FromString(path));
|
||||
_NotifyChanges(L"Icon", L"IconPath");
|
||||
}
|
||||
bool UsingNoIcon() const noexcept;
|
||||
|
||||
// starting directory
|
||||
hstring CurrentStartingDirectoryPreview() const;
|
||||
@@ -134,8 +115,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(ProfileSubPage, CurrentPage);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::BellSoundViewModel>, CurrentBellSounds);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Editor::EnumEntry, CurrentBuiltInIcon, nullptr);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, CurrentEmojiIcon);
|
||||
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, Guid);
|
||||
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, ConnectionType);
|
||||
@@ -166,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);
|
||||
@@ -174,7 +154,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
WINRT_PROPERTY(bool, IsBaseLayer, false);
|
||||
WINRT_PROPERTY(bool, FocusDeleteButton, false);
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable>, IconTypes);
|
||||
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);
|
||||
@@ -185,8 +165,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
winrt::guid _originalProfileGuid{};
|
||||
winrt::hstring _lastBgImagePath;
|
||||
winrt::hstring _lastStartingDirectoryPath;
|
||||
winrt::hstring _lastIconPath;
|
||||
Windows::Foundation::IInspectable _currentIconType{};
|
||||
Editor::AppearanceViewModel _defaultAppearanceViewModel;
|
||||
Windows::UI::Core::CoreDispatcher _dispatcher;
|
||||
|
||||
@@ -197,13 +175,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
void _MarkDuplicateBellSoundDirectories();
|
||||
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _MonospaceFontList;
|
||||
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _FontList;
|
||||
static Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> _BuiltInIcons;
|
||||
|
||||
Model::CascadiaSettings _appSettings;
|
||||
Editor::AppearanceViewModel _unfocusedAppearanceViewModel;
|
||||
void _UpdateBuiltInIcons();
|
||||
void _DeduceCurrentIconType();
|
||||
void _DeduceCurrentBuiltInIcon();
|
||||
};
|
||||
|
||||
struct DeleteProfileEventArgs :
|
||||
|
||||
@@ -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; };
|
||||
@@ -42,14 +36,6 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
Advanced = 3
|
||||
};
|
||||
|
||||
enum IconType
|
||||
{
|
||||
None = 0,
|
||||
FontIcon,
|
||||
Image,
|
||||
Emoji
|
||||
};
|
||||
|
||||
runtimeclass ProfileViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
event Windows.Foundation.TypedEventHandler<ProfileViewModel, DeleteProfileEventArgs> DeleteProfileRequested;
|
||||
@@ -107,17 +93,8 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
Windows.UI.Xaml.Controls.IconElement IconPreview { get; };
|
||||
String EvaluatedIcon { get; };
|
||||
String LocalizedIcon { get; };
|
||||
String CurrentEmojiIcon;
|
||||
IInspectable CurrentIconType;
|
||||
Windows.Foundation.Collections.IVector<IInspectable> IconTypes { get; };
|
||||
Boolean UsingNoIcon { get; };
|
||||
Boolean UsingBuiltInIcon { get; };
|
||||
Boolean UsingEmojiIcon { get; };
|
||||
Boolean UsingImageIcon { get; };
|
||||
String IconPath;
|
||||
|
||||
EnumEntry CurrentBuiltInIcon;
|
||||
Windows.Foundation.Collections.IObservableVector<EnumEntry> BuiltInIcons { get; };
|
||||
Boolean UsingNoIcon { get; };
|
||||
|
||||
String TabTitlePreview { get; };
|
||||
String AnswerbackMessagePreview { get; };
|
||||
@@ -157,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);
|
||||
|
||||
@@ -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>();
|
||||
_windowRoot = args.WindowRoot();
|
||||
BringIntoViewWhenLoaded(args.ElementToFocus());
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalSettingsEditorProvider,
|
||||
|
||||
@@ -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}">
|
||||
|
||||
@@ -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>();
|
||||
_windowRoot = 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();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}" />
|
||||
|
||||
@@ -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>();
|
||||
_windowRoot = args.WindowRoot();
|
||||
BringIntoViewWhenLoaded(args.ElementToFocus());
|
||||
|
||||
// Check the use parent directory box if the starting directory is empty
|
||||
if (_Profile.StartingDirectory().empty())
|
||||
@@ -133,18 +134,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
}
|
||||
|
||||
safe_void_coroutine Profiles_Base::Icon_Click(const IInspectable&, const RoutedEventArgs&)
|
||||
{
|
||||
auto lifetime = get_strong();
|
||||
|
||||
const auto parentHwnd{ reinterpret_cast<HWND>(_windowRoot.GetHostingWindow()) };
|
||||
auto file = co_await OpenImagePicker(parentHwnd);
|
||||
if (!file.empty())
|
||||
{
|
||||
_Profile.IconPath(file);
|
||||
}
|
||||
}
|
||||
|
||||
safe_void_coroutine Profiles_Base::StartingDirectory_Click(const IInspectable&, const RoutedEventArgs&)
|
||||
{
|
||||
auto lifetime = get_strong();
|
||||
@@ -169,77 +158,4 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_Profile.StartingDirectory(folder);
|
||||
}
|
||||
}
|
||||
|
||||
IconSource Profiles_Base::BuiltInIconConverter(const IInspectable& iconVal)
|
||||
{
|
||||
return Microsoft::Terminal::UI::IconPathConverter::IconSourceWUX(unbox_value<hstring>(iconVal));
|
||||
}
|
||||
|
||||
void Profiles_Base::BuiltInIconPicker_GotFocus(const IInspectable& sender, const RoutedEventArgs& /*e*/)
|
||||
{
|
||||
_updateIconFilter({});
|
||||
sender.as<AutoSuggestBox>().IsSuggestionListOpen(true);
|
||||
}
|
||||
|
||||
void Profiles_Base::BuiltInIconPicker_QuerySubmitted(const AutoSuggestBox& /*sender*/, const AutoSuggestBoxQuerySubmittedEventArgs& e)
|
||||
{
|
||||
const auto iconEntry = unbox_value_or<EnumEntry>(e.ChosenSuggestion(), nullptr);
|
||||
if (!iconEntry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_Profile.CurrentBuiltInIcon(iconEntry);
|
||||
}
|
||||
|
||||
void Profiles_Base::BuiltInIconPicker_TextChanged(const AutoSuggestBox& sender, const AutoSuggestBoxTextChangedEventArgs& e)
|
||||
{
|
||||
if (e.Reason() != AutoSuggestionBoxTextChangeReason::UserInput)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::wstring_view filter{ sender.Text() };
|
||||
filter = til::trim(filter, L' ');
|
||||
_updateIconFilter(filter);
|
||||
}
|
||||
|
||||
void Profiles_Base::_updateIconFilter(std::wstring_view filter)
|
||||
{
|
||||
if (_iconFilter != filter)
|
||||
{
|
||||
_filteredBuiltInIcons = nullptr;
|
||||
_iconFilter = filter;
|
||||
_updateFilteredIconList();
|
||||
PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"FilteredBuiltInIconList" });
|
||||
}
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> Profiles_Base::FilteredBuiltInIconList()
|
||||
{
|
||||
if (!_filteredBuiltInIcons)
|
||||
{
|
||||
_updateFilteredIconList();
|
||||
}
|
||||
return _filteredBuiltInIcons;
|
||||
}
|
||||
|
||||
void Profiles_Base::_updateFilteredIconList()
|
||||
{
|
||||
_filteredBuiltInIcons = ProfileViewModel::BuiltInIcons();
|
||||
if (_iconFilter.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Find matching icons and populate the filtered list
|
||||
std::vector<Editor::EnumEntry> filtered;
|
||||
filtered.reserve(_filteredBuiltInIcons.Size());
|
||||
for (const auto& icon : _filteredBuiltInIcons)
|
||||
{
|
||||
if (til::contains_linguistic_insensitive(icon.EnumName(), _iconFilter))
|
||||
{
|
||||
filtered.emplace_back(icon);
|
||||
}
|
||||
}
|
||||
_filteredBuiltInIcons = winrt::single_threaded_observable_vector(std::move(filtered));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,32 +18,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
void OnNavigatedFrom(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
|
||||
|
||||
safe_void_coroutine StartingDirectory_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
safe_void_coroutine Icon_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
safe_void_coroutine Commandline_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
void Appearance_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
void Terminal_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
void Advanced_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
void DeleteConfirmation_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> FilteredBuiltInIconList();
|
||||
void BuiltInIconPicker_GotFocus(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
void BuiltInIconPicker_TextChanged(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const Windows::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs& e);
|
||||
void BuiltInIconPicker_QuerySubmitted(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const Windows::UI::Xaml::Controls::AutoSuggestBoxQuerySubmittedEventArgs& e);
|
||||
|
||||
static Windows::UI::Xaml::Controls::IconSource BuiltInIconConverter(const Windows::Foundation::IInspectable& iconVal);
|
||||
|
||||
til::property_changed_event PropertyChanged;
|
||||
Editor::IHostedInWindow WindowRoot() const noexcept { return _windowRoot; }
|
||||
WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr);
|
||||
|
||||
private:
|
||||
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker;
|
||||
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
|
||||
Editor::IHostedInWindow _windowRoot;
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> _filteredBuiltInIcons;
|
||||
std::wstring _iconFilter;
|
||||
|
||||
void _updateIconFilter(std::wstring_view filter);
|
||||
void _updateFilteredIconList();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "ProfileViewModel.idl";
|
||||
import "MainPage.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
@@ -9,8 +10,6 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
Profiles_Base();
|
||||
ProfileViewModel Profile { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<EnumEntry> FilteredBuiltInIconList { get; };
|
||||
|
||||
static Windows.UI.Xaml.Controls.IconSource BuiltInIconConverter(IInspectable iconVal);
|
||||
IHostedInWindow WindowRoot { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}"
|
||||
@@ -122,87 +124,14 @@
|
||||
</Grid>
|
||||
</local:SettingContainer.CurrentValue>
|
||||
<local:SettingContainer.Content>
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Icon Type -->
|
||||
<ComboBox x:Uid="Profile_IconType"
|
||||
Grid.Column="0"
|
||||
ItemsSource="{x:Bind Profile.IconTypes}"
|
||||
SelectedItem="{x:Bind Profile.CurrentIconType, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:EnumEntry">
|
||||
<TextBlock Text="{x:Bind EnumName}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<!-- Built-In Icon -->
|
||||
<AutoSuggestBox x:Uid="Profile_BuiltInIcon"
|
||||
Grid.Column="1"
|
||||
GotFocus="BuiltInIconPicker_GotFocus"
|
||||
ItemsSource="{x:Bind FilteredBuiltInIconList, Mode=OneWay}"
|
||||
QuerySubmitted="BuiltInIconPicker_QuerySubmitted"
|
||||
Text="{x:Bind Profile.CurrentBuiltInIcon.EnumName, Mode=OneWay}"
|
||||
TextBoxStyle="{StaticResource TextBoxSettingStyle}"
|
||||
TextChanged="BuiltInIconPicker_TextChanged"
|
||||
Visibility="{x:Bind Profile.UsingBuiltInIcon, Mode=OneWay}">
|
||||
<AutoSuggestBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:EnumEntry">
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<IconSourceElement Grid.Column="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
IconSource="{x:Bind local:Profiles_Base.BuiltInIconConverter(EnumValue), Mode=OneTime}" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{x:Bind EnumName}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</AutoSuggestBox.ItemTemplate>
|
||||
</AutoSuggestBox>
|
||||
|
||||
<!-- Image (File) Icon -->
|
||||
<TextBox x:Uid="Profile_IconBox"
|
||||
Grid.Column="1"
|
||||
MaxWidth="Infinity"
|
||||
HorizontalAlignment="Stretch"
|
||||
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
|
||||
IsSpellCheckEnabled="False"
|
||||
Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind Profile.IconPath, Mode=TwoWay}"
|
||||
Visibility="{x:Bind Profile.UsingImageIcon, Mode=OneWay}" />
|
||||
<Button x:Uid="Profile_IconBrowse"
|
||||
Grid.Column="2"
|
||||
Margin="0"
|
||||
VerticalAlignment="Top"
|
||||
Click="Icon_Click"
|
||||
Style="{StaticResource BrowseButtonStyle}"
|
||||
Visibility="{x:Bind Profile.UsingImageIcon, Mode=OneWay}" />
|
||||
|
||||
<!-- Emoji Icon -->
|
||||
<TextBox x:Uid="Profile_IconEmojiBox"
|
||||
Grid.Column="1"
|
||||
MaxWidth="Infinity"
|
||||
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
|
||||
IsSpellCheckEnabled="False"
|
||||
Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind Profile.CurrentEmojiIcon, Mode=TwoWay}"
|
||||
Visibility="{x:Bind Profile.UsingEmojiIcon, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<local:IconPicker CurrentIconPath="{x:Bind Profile.IconPath, Mode=TwoWay}"
|
||||
WindowRoot="{x:Bind WindowRoot, Mode=OneWay}" />
|
||||
</local:SettingContainer.Content>
|
||||
</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}"
|
||||
@@ -229,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}">
|
||||
@@ -238,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}" />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -546,6 +546,14 @@
|
||||
<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>Kitty keyboard protocol mode</value>
|
||||
<comment>Header for a control to set the kitty keyboard protocol mode.</comment>
|
||||
</data>
|
||||
<data name="Profile_AllowKittyKeyboardMode.HelpText" xml:space="preserve">
|
||||
<value>Sets the baseline flags for the kitty keyboard protocol. Value is a sum of: 1=Disambiguate, 2=Report event types, 4=Report alternate keys, 8=Report all keys, 16=Report text.</value>
|
||||
<comment>Additional description for what the "kitty keyboard mode" setting does.</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>
|
||||
@@ -1110,11 +1118,11 @@
|
||||
<value>Icon</value>
|
||||
<comment>Header for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one.</comment>
|
||||
</data>
|
||||
<data name="Profile_IconBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<data name="IconPicker_ImagePathBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Icon</value>
|
||||
<comment>Name for a control to determine what icon can be used to represent this profile. This is not necessarily a file path, but can be one.</comment>
|
||||
<comment>Name for a control to determine what icon can be used. This is not necessarily a file path, but can be one. It's usually used for images.</comment>
|
||||
</data>
|
||||
<data name="Profile_IconEmojiBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<data name="IconPicker_EmojiBox.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Icon</value>
|
||||
<comment>Name for a control to determine what icon can be used to represent this profile.</comment>
|
||||
</data>
|
||||
@@ -1122,7 +1130,7 @@
|
||||
<value>Emoji or image file location of the icon used in the profile.</value>
|
||||
<comment>A description for what the "icon" setting does. Presented near "Profile_Icon".</comment>
|
||||
</data>
|
||||
<data name="Profile_IconBrowse.Content" xml:space="preserve">
|
||||
<data name="IconPicker_IconBrowse.Content" xml:space="preserve">
|
||||
<value>Browse...</value>
|
||||
<comment>Button label that opens a file picker in a new window. The "..." is standard to mean it will open a new window.</comment>
|
||||
</data>
|
||||
@@ -2318,31 +2326,31 @@
|
||||
<value>Use theme color</value>
|
||||
<comment>Label for a button directing the user to use the tab color defined in the terminal's current theme.</comment>
|
||||
</data>
|
||||
<data name="Profile_IconTypeNone" xml:space="preserve">
|
||||
<data name="IconPicker_IconTypeNone" xml:space="preserve">
|
||||
<value>None</value>
|
||||
<comment>An option to choose from for the "icon style" dropdown. When selected, there will be no icon for the profile.</comment>
|
||||
<comment>An option to choose from for the "icon style" dropdown. When selected, there will be no icon set.</comment>
|
||||
</data>
|
||||
<data name="Profile_IconTypeImage" xml:space="preserve">
|
||||
<data name="IconPicker_IconTypeImage" xml:space="preserve">
|
||||
<value>File</value>
|
||||
<comment>An option to choose from for the "icon style" dropdown. When selected, a custom image can set for the profile's icon.</comment>
|
||||
<comment>An option to choose from for the "icon style" dropdown. When selected, a custom image can set as the icon.</comment>
|
||||
</data>
|
||||
<data name="Profile_IconTypeEmoji" xml:space="preserve">
|
||||
<data name="IconPicker_IconTypeEmoji" xml:space="preserve">
|
||||
<value>Emoji</value>
|
||||
<comment>An option to choose from for the "icon style" dropdown. When selected, an emoji can be set for the profile's icon.</comment>
|
||||
<comment>An option to choose from for the "icon style" dropdown. When selected, an emoji can be set as the icon.</comment>
|
||||
</data>
|
||||
<data name="Profile_IconTypeFontIcon" xml:space="preserve">
|
||||
<data name="IconPicker_IconTypeFontIcon" xml:space="preserve">
|
||||
<value>Built-in Icon</value>
|
||||
<comment>An option to choose from for the "icon style" dropdown. When selected, the user can choose from several preselected options to set the profile's icon.</comment>
|
||||
<comment>An option to choose from for the "icon style" dropdown. When selected, the user can choose from several preselected options to set as the icon.</comment>
|
||||
</data>
|
||||
<data name="Profile_IconEmojiBox.PlaceholderText" xml:space="preserve">
|
||||
<data name="IconPicker_EmojiBox.PlaceholderText" xml:space="preserve">
|
||||
<value>Use "Win + period" to open the emoji picker</value>
|
||||
<comment>"Win + period" refers to the OS key binding to open the emoji picker.</comment>
|
||||
</data>
|
||||
<data name="Profile_IconType.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<data name="IconPicker_IconType.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Icon type</value>
|
||||
<comment>Accessible name for a control allowing the user to select the type of icon they would like to use.</comment>
|
||||
</data>
|
||||
<data name="Profile_BuiltInIcon.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<data name="IconPicker_BuiltInIcon.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Icon</value>
|
||||
<comment>Accessible name for a control allowing the user to select the icon from a list of built in icons.</comment>
|
||||
</data>
|
||||
@@ -2446,6 +2454,14 @@
|
||||
<value>Folder Name</value>
|
||||
<comment>Header for a control that allows the user to modify the name of the current folder entry.</comment>
|
||||
</data>
|
||||
<data name="NewTabMenu_CurrentFolderIcon.Header" xml:space="preserve">
|
||||
<value>Folder Icon</value>
|
||||
<comment>Header for a control that allows the user to modify the icon of the current folder entry.</comment>
|
||||
</data>
|
||||
<data name="NewTabMenu_CurrentFolderIcon.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Folder Icon</value>
|
||||
<comment>Name for a control to that allows the user to modify the icon of the current folder entry.</comment>
|
||||
</data>
|
||||
<data name="NewTabMenu_CurrentFolderInlining.Header" xml:space="preserve">
|
||||
<value>Allow inlining</value>
|
||||
<comment>Header for a control that allows the nested entries to be presented inline rather than with a folder.</comment>
|
||||
@@ -2575,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:\ -> /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:\ -> /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:\ -> /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:\ -> 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">
|
||||
@@ -2701,4 +2717,16 @@
|
||||
<data name="Settings_ResetApplicationStateConfirmationButton.Content" xml:space="preserve">
|
||||
<value>Yes, clear the cache</value>
|
||||
</data>
|
||||
</root>
|
||||
<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>
|
||||
</data>
|
||||
</root>
|
||||
476
src/cascadia/TerminalSettingsEditor/SearchIndex.cpp
Normal file
476
src/cascadia/TerminalSettingsEditor/SearchIndex.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
117
src/cascadia/TerminalSettingsEditor/SearchIndex.h
Normal file
117
src/cascadia/TerminalSettingsEditor/SearchIndex.h
Normal 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);
|
||||
}
|
||||
22
src/cascadia/TerminalSettingsEditor/SearchIndex.idl
Normal file
22
src/cascadia/TerminalSettingsEditor/SearchIndex.idl
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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__)
|
||||
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1151,6 +1151,8 @@ void AtlasEngine::_mapComplex(IDWriteFontFace2* mappedFontFace, u32 idx, u32 len
|
||||
const size_t col2 = _api.bufferLineColumn[a.textPosition + i];
|
||||
const auto fg = colors[col1 << shift];
|
||||
|
||||
// TODO: Instead of aligning each DWrite-cluster to the cell grid,
|
||||
// we should align each grapheme cluster to the cell grid.
|
||||
const auto expectedAdvance = (col2 - col1) * _p.s->font->cellSize.x;
|
||||
f32 actualAdvance = 0;
|
||||
for (auto j = prevCluster; j < nextCluster; ++j)
|
||||
|
||||
@@ -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) = 0; // CSI = flags ; mode u
|
||||
virtual void QueryKittyKeyboardProtocol() = 0; // CSI ? u
|
||||
virtual void PushKittyKeyboardProtocol(const VTParameter flags) = 0; // CSI > flags u
|
||||
virtual void PopKittyKeyboardProtocol(const VTParameter count) = 0; // CSI < count u
|
||||
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
|
||||
|
||||
@@ -2054,6 +2054,35 @@ void AdaptDispatch::SetKeypadMode(const bool fApplicationMode) noexcept
|
||||
_terminalInput.SetInputMode(TerminalInput::Mode::Keypad, fApplicationMode);
|
||||
}
|
||||
|
||||
// CSI = flags ; mode u - Sets kitty keyboard protocol flags
|
||||
void AdaptDispatch::SetKittyKeyboardProtocol(const VTParameter flags, const VTParameter mode)
|
||||
{
|
||||
const auto kittyFlags = static_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 = static_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.
|
||||
|
||||
@@ -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) override; // Kitty keyboard protocol CSI = flags ; mode u
|
||||
void QueryKittyKeyboardProtocol() override; // Kitty keyboard protocol CSI ? u
|
||||
void PushKittyKeyboardProtocol(const VTParameter flags) override; // Kitty keyboard protocol CSI > flags u
|
||||
void PopKittyKeyboardProtocol(const VTParameter count) override; // Kitty keyboard protocol CSI < count u
|
||||
void SetTopBottomScrollingMargins(const VTInt topMargin,
|
||||
const VTInt bottomMargin) override; // DECSTBM
|
||||
void SetLeftRightScrollingMargins(const VTInt leftMargin,
|
||||
|
||||
@@ -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*/) override {} // CSI = flags ; mode u
|
||||
void QueryKittyKeyboardProtocol() override {} // CSI ? u
|
||||
void PushKittyKeyboardProtocol(const VTParameter /*flags*/) override {} // CSI > flags u
|
||||
void PopKittyKeyboardProtocol(const VTParameter /*count*/) override {} // CSI < count u
|
||||
void SetTopBottomScrollingMargins(const VTInt /*topMargin*/, const VTInt /*bottomMargin*/) override {} // DECSTBM
|
||||
void SetLeftRightScrollingMargins(const VTInt /*leftMargin*/, const VTInt /*rightMargin*/) override {} // DECSLRM
|
||||
void EnquireAnswerback() override {} // ENQ
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -30,7 +30,6 @@ PRECOMPILED_INCLUDE = ..\precomp.h
|
||||
SOURCES= \
|
||||
..\terminalInput.cpp \
|
||||
..\mouseInput.cpp \
|
||||
..\mouseInputState.cpp \
|
||||
|
||||
INCLUDES = \
|
||||
$(INCLUDES); \
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#include <til/unicode.h>
|
||||
|
||||
#include "../../inc/unicode.hpp"
|
||||
#include "../../interactivity/inc/VtApiRedirection.hpp"
|
||||
#include "../types/inc/IInputEvent.hpp"
|
||||
|
||||
using namespace std::string_literals;
|
||||
@@ -31,6 +29,16 @@ TerminalInput::TerminalInput() noexcept
|
||||
_initKeyboardMap();
|
||||
}
|
||||
|
||||
void TerminalInput::UseAlternateScreenBuffer() noexcept
|
||||
{
|
||||
_inAlternateBuffer = true;
|
||||
}
|
||||
|
||||
void TerminalInput::UseMainScreenBuffer() noexcept
|
||||
{
|
||||
_inAlternateBuffer = false;
|
||||
}
|
||||
|
||||
void TerminalInput::SetInputMode(const Mode mode, const bool enabled) noexcept
|
||||
{
|
||||
// If we're changing a tracking mode, we always clear other tracking modes first.
|
||||
@@ -70,6 +78,7 @@ void TerminalInput::ResetInputModes() noexcept
|
||||
_inputMode = { Mode::Ansi, Mode::AutoRepeat, Mode::AlternateScroll };
|
||||
_mouseInputState.lastPos = { -1, -1 };
|
||||
_mouseInputState.lastButton = 0;
|
||||
ResetKittyKeyboardProtocols();
|
||||
_initKeyboardMap();
|
||||
}
|
||||
|
||||
@@ -78,6 +87,86 @@ void TerminalInput::ForceDisableWin32InputMode(const bool win32InputMode) noexce
|
||||
_forceDisableWin32InputMode = win32InputMode;
|
||||
}
|
||||
|
||||
void TerminalInput::ForceDisableKittyKeyboardProtocol(const bool disable) noexcept
|
||||
{
|
||||
_forceDisableKittyKeyboardProtocol = disable;
|
||||
if (disable)
|
||||
{
|
||||
_kittyFlags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Kitty keyboard protocol methods
|
||||
|
||||
void TerminalInput::SetKittyKeyboardProtocol(const uint8_t flags, const KittyKeyboardProtocolMode mode) noexcept
|
||||
{
|
||||
if (_forceDisableKittyKeyboardProtocol)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case KittyKeyboardProtocolMode::Replace:
|
||||
_kittyFlags = flags & KittyKeyboardProtocolFlags::All;
|
||||
break;
|
||||
case KittyKeyboardProtocolMode::Set:
|
||||
_kittyFlags |= (flags & KittyKeyboardProtocolFlags::All);
|
||||
break;
|
||||
case KittyKeyboardProtocolMode::Reset:
|
||||
_kittyFlags &= ~(flags & KittyKeyboardProtocolFlags::All);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t TerminalInput::GetKittyFlags() const noexcept
|
||||
{
|
||||
return _kittyFlags;
|
||||
}
|
||||
|
||||
void TerminalInput::PushKittyFlags(const uint8_t flags) noexcept
|
||||
{
|
||||
if (_forceDisableKittyKeyboardProtocol)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto& stack = _getKittyStack();
|
||||
// Evict oldest entry if stack is full (DoS prevention)
|
||||
if (stack.size() >= KittyStackMaxSize)
|
||||
{
|
||||
stack.erase(stack.begin());
|
||||
}
|
||||
stack.push_back(_kittyFlags);
|
||||
_kittyFlags = flags & KittyKeyboardProtocolFlags::All;
|
||||
}
|
||||
|
||||
void TerminalInput::PopKittyFlags(const size_t count) noexcept
|
||||
{
|
||||
auto& stack = _getKittyStack();
|
||||
// If pop request exceeds stack size, reset all flags per spec:
|
||||
// "If a pop request is received that empties the stack, all flags are reset."
|
||||
if (count > stack.size())
|
||||
{
|
||||
stack.clear();
|
||||
_kittyFlags = 0;
|
||||
return;
|
||||
}
|
||||
// Pop the requested number of entries, restoring flags from last popped
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
_kittyFlags = stack.back();
|
||||
stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalInput::ResetKittyKeyboardProtocols() noexcept
|
||||
{
|
||||
_kittyFlags = 0;
|
||||
_kittyMainStack.clear();
|
||||
_kittyAltStack.clear();
|
||||
}
|
||||
|
||||
TerminalInput::OutputType TerminalInput::MakeUnhandled() noexcept
|
||||
{
|
||||
return {};
|
||||
@@ -121,12 +210,20 @@ TerminalInput::OutputType TerminalInput::HandleKey(const INPUT_RECORD& event)
|
||||
}
|
||||
|
||||
const auto controlKeyState = _trackControlKeyState(keyEvent);
|
||||
|
||||
const auto virtualKeyCode = keyEvent.wVirtualKeyCode;
|
||||
auto unicodeChar = keyEvent.uChar.UnicodeChar;
|
||||
|
||||
// Check if this key matches the last recorded key code.
|
||||
const auto matchingLastKeyPress = _lastVirtualKeyCode == virtualKeyCode;
|
||||
|
||||
// If kitty keyboard mode is active, use kitty keyboard protocol.
|
||||
// This handles release events when ReportEventTypes flag is set.
|
||||
if (_kittyFlags != 0)
|
||||
{
|
||||
return _makeKittyOutput(keyEvent, controlKeyState);
|
||||
}
|
||||
|
||||
// Only need to handle key down. See raw key handler (see RawReadWaitRoutine in stream.cpp)
|
||||
if (!keyEvent.bKeyDown)
|
||||
{
|
||||
@@ -669,3 +766,617 @@ TerminalInput::OutputType TerminalInput::_makeWin32Output(const KEY_EVENT_RECORD
|
||||
// Rc: the value of wRepeatCount - any number. If omitted, defaults to '1'.
|
||||
return fmt::format(FMT_COMPILE(L"{}{};{};{};{};{};{}_"), _csi, vk, sc, uc, kd, cs, rc);
|
||||
}
|
||||
|
||||
// Generates kitty keyboard protocol output for a key event.
|
||||
// https://sw.kovidgoyal.net/kitty/keyboard-protocol/
|
||||
TerminalInput::OutputType TerminalInput::_makeKittyOutput(const KEY_EVENT_RECORD& key, const DWORD controlKeyState)
|
||||
{
|
||||
const auto virtualKeyCode = key.wVirtualKeyCode;
|
||||
const auto virtualScanCode = key.wVirtualScanCode;
|
||||
const auto unicodeChar = key.uChar.UnicodeChar;
|
||||
const auto isKeyDown = key.bKeyDown;
|
||||
|
||||
// Swallow lone leading surrogates...
|
||||
if (til::is_leading_surrogate(unicodeChar))
|
||||
{
|
||||
_leadingSurrogate = unicodeChar;
|
||||
return _makeNoOutput();
|
||||
}
|
||||
|
||||
// ...and combine them with trailing surrogates.
|
||||
uint32_t fullCodepoint = unicodeChar;
|
||||
if (_leadingSurrogate != 0 && til::is_trailing_surrogate(unicodeChar))
|
||||
{
|
||||
fullCodepoint = til::combine_surrogates(_leadingSurrogate, unicodeChar);
|
||||
_leadingSurrogate = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_leadingSurrogate = 0;
|
||||
}
|
||||
|
||||
// Check if this key matches the last recorded key code (for repeat detection)
|
||||
const auto isRepeat = _lastVirtualKeyCode == virtualKeyCode && isKeyDown;
|
||||
if (!isKeyDown)
|
||||
{
|
||||
if (_lastVirtualKeyCode == virtualKeyCode)
|
||||
{
|
||||
_lastVirtualKeyCode = std::nullopt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastVirtualKeyCode = virtualKeyCode;
|
||||
}
|
||||
|
||||
// Note: Disambiguate flag (0x01) is implicitly handled - if we're in this function
|
||||
// at all (_kittyFlags != 0), then Ctrl+key and Alt+key combos get CSI u encoding.
|
||||
const auto reportEventTypes = (_kittyFlags & KittyKeyboardProtocolFlags::ReportEventTypes) != 0;
|
||||
const auto reportAllKeys = (_kittyFlags & KittyKeyboardProtocolFlags::ReportAllKeys) != 0;
|
||||
const auto reportAlternateKeys = (_kittyFlags & KittyKeyboardProtocolFlags::ReportAlternateKeys) != 0;
|
||||
const auto reportText = (_kittyFlags & KittyKeyboardProtocolFlags::ReportText) != 0;
|
||||
|
||||
// Without ReportEventTypes, we only handle key down events
|
||||
if (!isKeyDown && !reportEventTypes)
|
||||
{
|
||||
return _makeNoOutput();
|
||||
}
|
||||
|
||||
// Get the functional key code, or 0 if this key should use legacy encoding.
|
||||
const auto functionalKeyCode = _getKittyFunctionalKeyCode(virtualKeyCode, virtualScanCode, controlKeyState);
|
||||
const auto ctrlIsPressed = WI_IsAnyFlagSet(controlKeyState, CTRL_PRESSED);
|
||||
const auto altIsPressed = WI_IsAnyFlagSet(controlKeyState, ALT_PRESSED);
|
||||
|
||||
if (!reportAllKeys)
|
||||
{
|
||||
// Per spec: "Additionally, with this mode [ReportAllKeys], events for pressing
|
||||
// modifier keys are reported." So we skip modifier key events without it.
|
||||
if ((functionalKeyCode >= 57358 && functionalKeyCode <= 57360) ||
|
||||
(functionalKeyCode >= 57441 && functionalKeyCode <= 57450))
|
||||
{
|
||||
return _makeNoOutput();
|
||||
}
|
||||
|
||||
// Legacy encoding for Enter, Tab, and Backspace (spec recovery guarantee).
|
||||
// These keys use mode-aware legacy sequences unless ReportAllKeys is set, ensuring
|
||||
// users can type "reset<Enter>" if an app crashes with the protocol enabled.
|
||||
// Unlike CSI u (which is mode-independent), legacy encoding must honor LineFeed
|
||||
// and BackarrowKey modes. Ctrl/Alt combos still use CSI u for disambiguation.
|
||||
if (virtualKeyCode == VK_RETURN || virtualKeyCode == VK_TAB || virtualKeyCode == VK_BACK)
|
||||
{
|
||||
if (!isKeyDown || ctrlIsPressed || altIsPressed)
|
||||
{
|
||||
return _makeNoOutput();
|
||||
}
|
||||
|
||||
std::wstring str;
|
||||
switch (virtualKeyCode)
|
||||
{
|
||||
case VK_RETURN:
|
||||
str = _inputMode.test(Mode::LineFeed) ? L"\r\n" : L"\r";
|
||||
break;
|
||||
case VK_TAB:
|
||||
if (WI_IsFlagSet(controlKeyState, SHIFT_PRESSED))
|
||||
{
|
||||
str = fmt::format(FMT_COMPILE(L"{}Z"), _csi);
|
||||
}
|
||||
else
|
||||
{
|
||||
str = L"\t";
|
||||
}
|
||||
break;
|
||||
case VK_BACK:
|
||||
str = _inputMode.test(Mode::BackarrowKey) ? L"\x08" : L"\x7f";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return MakeOutput(std::move(str));
|
||||
}
|
||||
|
||||
// Fast path: For simple text key presses (key down, not a functional key, has a codepoint),
|
||||
// without Ctrl/Alt modifiers that require disambiguation, and not in reportAllKeys mode,
|
||||
// we can bypass CSI u encoding and send the character directly.
|
||||
if (isKeyDown && functionalKeyCode == 0 && fullCodepoint != 0 && !ctrlIsPressed && !altIsPressed)
|
||||
{
|
||||
const auto cb = _codepointToBuffer(fullCodepoint);
|
||||
return MakeOutput({ cb.buf, cb.len });
|
||||
}
|
||||
}
|
||||
|
||||
const auto isEnhanced = WI_IsFlagSet(controlKeyState, ENHANCED_KEY);
|
||||
wchar_t legacyFinalChar = 0;
|
||||
uint32_t legacyParam = 1;
|
||||
|
||||
switch (virtualKeyCode)
|
||||
{
|
||||
case VK_UP:
|
||||
if (isEnhanced)
|
||||
{
|
||||
legacyFinalChar = L'A';
|
||||
}
|
||||
break;
|
||||
case VK_DOWN:
|
||||
if (isEnhanced)
|
||||
{
|
||||
legacyFinalChar = L'B';
|
||||
}
|
||||
break;
|
||||
case VK_RIGHT:
|
||||
if (isEnhanced)
|
||||
{
|
||||
legacyFinalChar = L'C';
|
||||
}
|
||||
break;
|
||||
case VK_LEFT:
|
||||
if (isEnhanced)
|
||||
{
|
||||
legacyFinalChar = L'D';
|
||||
}
|
||||
break;
|
||||
case VK_HOME:
|
||||
if (isEnhanced)
|
||||
{
|
||||
legacyFinalChar = L'H';
|
||||
}
|
||||
break;
|
||||
case VK_END:
|
||||
if (isEnhanced)
|
||||
{
|
||||
legacyFinalChar = L'F';
|
||||
}
|
||||
break;
|
||||
case VK_INSERT:
|
||||
case VK_DELETE:
|
||||
if (isEnhanced)
|
||||
{
|
||||
legacyFinalChar = L'~';
|
||||
legacyParam = 2 + (virtualKeyCode - VK_INSERT);
|
||||
}
|
||||
break;
|
||||
case VK_PRIOR:
|
||||
case VK_NEXT:
|
||||
if (isEnhanced)
|
||||
{
|
||||
legacyFinalChar = L'~';
|
||||
legacyParam = 5 + (virtualKeyCode - VK_PRIOR);
|
||||
}
|
||||
break;
|
||||
case VK_F1:
|
||||
case VK_F2:
|
||||
case VK_F4:
|
||||
legacyFinalChar = L'P' + (virtualKeyCode - VK_F1);
|
||||
break;
|
||||
case VK_F3:
|
||||
// Note: F3 cannot use CSI R as that conflicts with Cursor Position Report.
|
||||
// The kitty spec explicitly removed CSI R for F3.
|
||||
legacyFinalChar = L'~';
|
||||
legacyParam = 13;
|
||||
break;
|
||||
case VK_F5:
|
||||
legacyFinalChar = L'~';
|
||||
legacyParam = 15;
|
||||
break;
|
||||
case VK_F6:
|
||||
case VK_F7:
|
||||
case VK_F8:
|
||||
case VK_F9:
|
||||
case VK_F10:
|
||||
legacyFinalChar = L'~';
|
||||
legacyParam = 17 + (virtualKeyCode - VK_F6);
|
||||
break;
|
||||
case VK_F11:
|
||||
case VK_F12:
|
||||
legacyFinalChar = L'~';
|
||||
legacyParam = 23 + (virtualKeyCode - VK_F11);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate kitty modifiers early - needed for legacy sequences too
|
||||
// kitty: shift=1, alt=2, ctrl=4, super=8, hyper=16, meta=32, caps_lock=64, num_lock=128
|
||||
uint32_t modifiers = 0;
|
||||
if (WI_IsFlagSet(controlKeyState, SHIFT_PRESSED))
|
||||
{
|
||||
modifiers |= 1;
|
||||
}
|
||||
if (WI_IsAnyFlagSet(controlKeyState, ALT_PRESSED))
|
||||
{
|
||||
modifiers |= 2;
|
||||
}
|
||||
if (WI_IsAnyFlagSet(controlKeyState, CTRL_PRESSED))
|
||||
{
|
||||
modifiers |= 4;
|
||||
}
|
||||
// Per spec: "Lock modifiers are not reported for text producing keys, to keep them
|
||||
// usable in legacy programs. To get lock modifiers for all keys use the Report all
|
||||
// keys as escape codes enhancement." So we report them for functional keys always,
|
||||
// and for text-producing keys only when ReportAllKeys is set.
|
||||
if (functionalKeyCode != 0 || reportAllKeys)
|
||||
{
|
||||
if (WI_IsFlagSet(controlKeyState, CAPSLOCK_ON))
|
||||
{
|
||||
modifiers |= 64;
|
||||
}
|
||||
if (WI_IsFlagSet(controlKeyState, NUMLOCK_ON))
|
||||
{
|
||||
modifiers |= 128;
|
||||
}
|
||||
}
|
||||
const auto encodedModifiers = 1 + modifiers;
|
||||
|
||||
// Determine event type: 1=press, 2=repeat, 3=release
|
||||
uint32_t eventType = 1;
|
||||
if (!isKeyDown)
|
||||
{
|
||||
eventType = 3;
|
||||
}
|
||||
else if (isRepeat)
|
||||
{
|
||||
eventType = 2;
|
||||
}
|
||||
|
||||
// If this is a key that uses legacy CSI sequences, generate it
|
||||
if (legacyFinalChar != 0)
|
||||
{
|
||||
// Format: CSI param ; modifiers ~ or CSI param ; modifiers : event-type ~
|
||||
std::wstring seq;
|
||||
seq.append(_csi);
|
||||
if (legacyParam > 1)
|
||||
{
|
||||
fmt::format_to(std::back_inserter(seq), FMT_COMPILE(L"{}"), legacyParam);
|
||||
}
|
||||
if (encodedModifiers > 1 || (reportEventTypes && eventType > 1))
|
||||
{
|
||||
fmt::format_to(std::back_inserter(seq), FMT_COMPILE(L";{}"), encodedModifiers);
|
||||
if (reportEventTypes && eventType > 1)
|
||||
{
|
||||
fmt::format_to(std::back_inserter(seq), FMT_COMPILE(L":{}"), eventType);
|
||||
}
|
||||
}
|
||||
seq.push_back(legacyFinalChar);
|
||||
return seq;
|
||||
}
|
||||
|
||||
// According to kitty protocol:
|
||||
// > the codepoint used is always the lower-case (or more technically, un-shifted) version of the key
|
||||
uint32_t keyCode = functionalKeyCode;
|
||||
if (keyCode == 0)
|
||||
{
|
||||
// For alphabetic keys, use the virtual key code converted to lowercase.
|
||||
// We can't use unicodeChar because when Ctrl is pressed, unicodeChar
|
||||
// becomes the control character (e.g., Ctrl+C gives unicodeChar=0x03).
|
||||
if (virtualKeyCode >= 'A' && virtualKeyCode <= 'Z')
|
||||
{
|
||||
keyCode = virtualKeyCode + 32; // Convert to lowercase ('A'->'a')
|
||||
}
|
||||
// Space needs special handling because Ctrl+Space produces NUL (0).
|
||||
else if (virtualKeyCode == VK_SPACE)
|
||||
{
|
||||
keyCode = L' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
keyCode = fullCodepoint;
|
||||
|
||||
// For control characters (e.g., Ctrl+[ produces ESC), use ToUnicodeEx
|
||||
// to get the base character without modifiers.
|
||||
if (!_codepointIsNonControl(keyCode))
|
||||
{
|
||||
const auto hkl = GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), nullptr));
|
||||
auto keyState = _getKeyboardState(virtualKeyCode, 0);
|
||||
|
||||
// Disable Ctrl and Alt modifiers to obtain the base character mapping.
|
||||
keyState.at(VK_CONTROL) = keyState.at(VK_LCONTROL) = keyState.at(VK_RCONTROL) = 0;
|
||||
keyState.at(VK_MENU) = keyState.at(VK_LMENU) = keyState.at(VK_RMENU) = 0;
|
||||
|
||||
wchar_t buffer[4];
|
||||
const auto result = ToUnicodeEx(virtualKeyCode, 0, keyState.data(), buffer, 4, 4, hkl);
|
||||
|
||||
if (result > 0 && result < 4)
|
||||
{
|
||||
keyCode = _bufferToCodepoint(&buffer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
keyCode = _codepointToLower(keyCode);
|
||||
|
||||
if (!_codepointIsNonControl(keyCode))
|
||||
{
|
||||
return _makeNoOutput();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add alternate keys if requested (shifted key and base layout key)
|
||||
uint32_t shiftedKey = 0;
|
||||
uint32_t baseLayoutKey = 0;
|
||||
if (reportAlternateKeys && functionalKeyCode == 0)
|
||||
{
|
||||
// Shifted key: the uppercase/shifted version of the key
|
||||
if ((modifiers & 1) != 0 && fullCodepoint != 0 && fullCodepoint != keyCode)
|
||||
{
|
||||
shiftedKey = fullCodepoint;
|
||||
}
|
||||
|
||||
// Base layout key: the key in the standard US PC-101 layout.
|
||||
static const auto usLayout = LoadKeyboardLayoutW(L"00000409", 0);
|
||||
if (usLayout != nullptr && virtualKeyCode != 0)
|
||||
{
|
||||
auto keyState = _getKeyboardState(virtualKeyCode, 0); // No modifiers for base key
|
||||
wchar_t baseChar[4]{};
|
||||
const auto result = ToUnicodeEx(virtualKeyCode, 0, keyState.data(), baseChar, 4, 4, usLayout);
|
||||
if (result == 1 && baseChar[0] >= 0x20)
|
||||
{
|
||||
// Use lowercase version of the base layout key
|
||||
auto baseKey = static_cast<uint32_t>(baseChar[0]);
|
||||
if (baseKey >= L'A' && baseKey <= L'Z')
|
||||
{
|
||||
baseKey += 32;
|
||||
}
|
||||
// Only include if different from keyCode
|
||||
if (baseKey != keyCode)
|
||||
{
|
||||
baseLayoutKey = baseKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CSI unicode-key-code:shifted-key:base-layout-key ; modifiers:event-type ; text-as-codepoints u
|
||||
|
||||
std::wstring seq;
|
||||
fmt::format_to(std::back_inserter(seq), FMT_COMPILE(L"{}{}"), _csi, keyCode);
|
||||
|
||||
// Append alternate keys to sequence if present
|
||||
if (shiftedKey != 0 || baseLayoutKey != 0)
|
||||
{
|
||||
seq.push_back(L':');
|
||||
if (shiftedKey != 0)
|
||||
{
|
||||
fmt::format_to(std::back_inserter(seq), FMT_COMPILE(L"{}"), shiftedKey);
|
||||
}
|
||||
if (baseLayoutKey != 0)
|
||||
{
|
||||
fmt::format_to(std::back_inserter(seq), FMT_COMPILE(L":{}"), baseLayoutKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if we need to output text-as-codepoints (third field)
|
||||
// Exclude C0 (< 0x20) and C1 (0x80-0x9F) control codes per spec.
|
||||
const auto isValidText = fullCodepoint >= 0x20 && (fullCodepoint < 0x80 || fullCodepoint > 0x9F);
|
||||
const auto needsText = reportText && reportAllKeys && functionalKeyCode == 0 && isValidText && isKeyDown;
|
||||
|
||||
// We need to include modifiers field if:
|
||||
// - modifiers are non-default (encodedModifiers > 1), OR
|
||||
// - we need to report non-press event type, OR
|
||||
// - we need to output text (text is the 3rd field, so we must have 2nd field too)
|
||||
const auto needsEventType = reportEventTypes && eventType > 1;
|
||||
if (encodedModifiers > 1 || needsEventType || needsText)
|
||||
{
|
||||
// Per spec: "If no modifiers are present, the modifiers field must have the value 1"
|
||||
// when event type sub-field is needed.
|
||||
fmt::format_to(std::back_inserter(seq), FMT_COMPILE(L";{}"), encodedModifiers);
|
||||
if (needsEventType)
|
||||
{
|
||||
fmt::format_to(std::back_inserter(seq), FMT_COMPILE(L":{}"), eventType);
|
||||
}
|
||||
if (needsText)
|
||||
{
|
||||
fmt::format_to(std::back_inserter(seq), FMT_COMPILE(L";{}"), fullCodepoint);
|
||||
}
|
||||
}
|
||||
|
||||
seq.push_back(L'u');
|
||||
return seq;
|
||||
}
|
||||
|
||||
// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions
|
||||
// NOTE: The definition documents keys named as KP_*, which are keypad keys.
|
||||
uint32_t TerminalInput::_getKittyFunctionalKeyCode(const WORD virtualKeyCode, const WORD virtualScanCode, const DWORD controlKeyState) noexcept
|
||||
{
|
||||
const auto isEnhanced = WI_IsFlagSet(controlKeyState, ENHANCED_KEY);
|
||||
|
||||
switch (virtualKeyCode)
|
||||
{
|
||||
// Special keys with C0 control codes
|
||||
case VK_ESCAPE:
|
||||
return 27; // ESCAPE
|
||||
case VK_RETURN:
|
||||
return isEnhanced ? 57414 : 13; // KP_RETURN : ENTER
|
||||
case VK_TAB:
|
||||
return 9; // TAB
|
||||
case VK_BACK:
|
||||
return 127; // BACKSPACE
|
||||
|
||||
// Navigation keys - when ENHANCED_KEY is not set, these are keypad keys
|
||||
case VK_INSERT:
|
||||
return isEnhanced ? 0 : 57425; // legacy : KP_INSERT
|
||||
case VK_DELETE:
|
||||
return isEnhanced ? 0 : 57426; // legacy : KP_DELETE
|
||||
case VK_LEFT:
|
||||
return isEnhanced ? 0 : 57417; // legacy : KP_LEFT
|
||||
case VK_RIGHT:
|
||||
return isEnhanced ? 0 : 57418; // legacy : KP_RIGHT
|
||||
case VK_UP:
|
||||
return isEnhanced ? 0 : 57419; // legacy : KP_UP
|
||||
case VK_DOWN:
|
||||
return isEnhanced ? 0 : 57420; // legacy : KP_DOWN
|
||||
case VK_PRIOR:
|
||||
return isEnhanced ? 0 : 57421; // legacy : KP_PAGE_UP
|
||||
case VK_NEXT:
|
||||
return isEnhanced ? 0 : 57422; // legacy : KP_PAGE_DOWN
|
||||
case VK_HOME:
|
||||
return isEnhanced ? 0 : 57423; // legacy : KP_HOME
|
||||
case VK_END:
|
||||
return isEnhanced ? 0 : 57424; // legacy : KP_END
|
||||
|
||||
// Lock keys
|
||||
case VK_CAPITAL:
|
||||
return 57358; // CAPS_LOCK
|
||||
case VK_SCROLL:
|
||||
return 57359; // SCROLL_LOCK
|
||||
case VK_NUMLOCK:
|
||||
return 57360; // NUM_LOCK
|
||||
|
||||
// Other special keys
|
||||
case VK_SNAPSHOT:
|
||||
return 57361; // PRINT_SCREEN
|
||||
case VK_PAUSE:
|
||||
return 57362; // PAUSE
|
||||
case VK_APPS:
|
||||
return 57363; // MENU
|
||||
|
||||
// Function keys
|
||||
case VK_F1:
|
||||
case VK_F2:
|
||||
case VK_F3:
|
||||
case VK_F4:
|
||||
case VK_F5:
|
||||
case VK_F6:
|
||||
case VK_F7:
|
||||
case VK_F8:
|
||||
case VK_F9:
|
||||
case VK_F10:
|
||||
case VK_F11:
|
||||
case VK_F12:
|
||||
return 0; // Use legacy sequences
|
||||
case VK_F13:
|
||||
case VK_F14:
|
||||
case VK_F15:
|
||||
case VK_F16:
|
||||
case VK_F17:
|
||||
case VK_F18:
|
||||
case VK_F19:
|
||||
case VK_F20:
|
||||
case VK_F21:
|
||||
case VK_F22:
|
||||
case VK_F23:
|
||||
case VK_F24:
|
||||
return 57376 + (virtualKeyCode - VK_F13); // F13-F24
|
||||
|
||||
// Keypad keys
|
||||
case VK_NUMPAD0:
|
||||
case VK_NUMPAD1:
|
||||
case VK_NUMPAD2:
|
||||
case VK_NUMPAD3:
|
||||
case VK_NUMPAD4:
|
||||
case VK_NUMPAD5:
|
||||
case VK_NUMPAD6:
|
||||
case VK_NUMPAD7:
|
||||
case VK_NUMPAD8:
|
||||
case VK_NUMPAD9:
|
||||
return 57399 + (virtualKeyCode - VK_NUMPAD0); // KP_0-KP_9
|
||||
case VK_DECIMAL:
|
||||
return 57409; // KP_DECIMAL
|
||||
case VK_DIVIDE:
|
||||
return 57410; // KP_DIVIDE
|
||||
case VK_MULTIPLY:
|
||||
return 57411; // KP_MULTIPLY
|
||||
case VK_SUBTRACT:
|
||||
return 57412; // KP_SUBTRACT
|
||||
case VK_ADD:
|
||||
return 57413; // KP_ADD
|
||||
case VK_SEPARATOR:
|
||||
return 57416; // KP_SEPARATOR
|
||||
case VK_CLEAR:
|
||||
return 57427; // KP_BEGIN
|
||||
|
||||
// Media keys
|
||||
case VK_MEDIA_PLAY_PAUSE:
|
||||
return 57430; // MEDIA_PLAY_PAUSE
|
||||
case VK_MEDIA_STOP:
|
||||
return 57432; // MEDIA_STOP
|
||||
case VK_MEDIA_NEXT_TRACK:
|
||||
return 57435; // MEDIA_TRACK_NEXT
|
||||
case VK_MEDIA_PREV_TRACK:
|
||||
return 57436; // MEDIA_TRACK_PREVIOUS
|
||||
case VK_VOLUME_DOWN:
|
||||
return 57438; // LOWER_VOLUME
|
||||
case VK_VOLUME_UP:
|
||||
return 57439; // RAISE_VOLUME
|
||||
case VK_VOLUME_MUTE:
|
||||
return 57440; // MUTE_VOLUME
|
||||
|
||||
// Modifier keys
|
||||
case VK_SHIFT:
|
||||
return virtualScanCode == 0x2A ? 57441 : 57447; // LEFT_SHIFT : RIGHT_SHIFT
|
||||
case VK_LSHIFT:
|
||||
return 57441; // LEFT_SHIFT
|
||||
case VK_RSHIFT:
|
||||
return 57447; // RIGHT_SHIFT
|
||||
case VK_CONTROL:
|
||||
return isEnhanced ? 57448 : 57442; // RIGHT_CONTROL : LEFT_CONTROL
|
||||
case VK_LCONTROL:
|
||||
return 57442; // LEFT_CONTROL
|
||||
case VK_RCONTROL:
|
||||
return 57448; // RIGHT_CONTROL
|
||||
case VK_MENU:
|
||||
return isEnhanced ? 57449 : 57443; // RIGHT_ALT : LEFT_ALT
|
||||
case VK_LMENU:
|
||||
return 57443; // LEFT_ALT
|
||||
case VK_RMENU:
|
||||
return 57449; // RIGHT_ALT
|
||||
case VK_LWIN:
|
||||
return 57444; // LEFT_SUPER
|
||||
case VK_RWIN:
|
||||
return 57450; // RIGHT_SUPER
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t>& TerminalInput::_getKittyStack() noexcept
|
||||
{
|
||||
return _inAlternateBuffer ? _kittyAltStack : _kittyMainStack;
|
||||
}
|
||||
|
||||
bool TerminalInput::_codepointIsNonControl(uint32_t cp) noexcept
|
||||
{
|
||||
return cp > 0x1f && (cp < 0x7f || cp > 0x9f);
|
||||
}
|
||||
|
||||
TerminalInput::CodepointBuffer TerminalInput::_codepointToBuffer(uint32_t cp) noexcept
|
||||
{
|
||||
CodepointBuffer cb;
|
||||
if (cp <= 0xFFFF)
|
||||
{
|
||||
cb.buf[0] = static_cast<wchar_t>(cp);
|
||||
cb.buf[1] = 0;
|
||||
cb.len = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cp -= 0x10000;
|
||||
cb.buf[0] = static_cast<wchar_t>((cp >> 10) + 0xD800);
|
||||
cb.buf[1] = static_cast<wchar_t>((cp & 0x3FF) + 0xDC00);
|
||||
cb.buf[2] = 0;
|
||||
cb.len = 2;
|
||||
}
|
||||
return cb;
|
||||
}
|
||||
|
||||
uint32_t TerminalInput::_bufferToCodepoint(const wchar_t* str) noexcept
|
||||
{
|
||||
if (til::is_leading_surrogate(str[0]) && til::is_trailing_surrogate(str[1]))
|
||||
{
|
||||
return til::combine_surrogates(str[0], str[1]);
|
||||
}
|
||||
return str[0];
|
||||
}
|
||||
|
||||
uint32_t TerminalInput::_codepointToLower(uint32_t cp) noexcept
|
||||
{
|
||||
auto cb = _codepointToBuffer(cp);
|
||||
// NOTE: MSDN states that `lpSrcStr == lpDestStr` is valid for LCMAP_LOWERCASE.
|
||||
const auto len = LCMapStringW(LOCALE_INVARIANT, LCMAP_LOWERCASE, cb.buf, cb.len, cb.buf, gsl::narrow_cast<int>(std::size(cb.buf)));
|
||||
// NOTE: LCMapStringW returns the length including the null terminator. I'm not checking for it,
|
||||
// because after decades, LCMapStringW should be reliable enough to return len==0 for OOM.
|
||||
if (len > 1)
|
||||
{
|
||||
return _bufferToCodepoint(cb.buf);
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
@@ -47,26 +47,55 @@ 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 Disambiguate = 1 << 0; // Disambiguate escape codes
|
||||
static constexpr uint8_t ReportEventTypes = 1 << 1; // Report event types (press/repeat/release)
|
||||
static constexpr uint8_t ReportAlternateKeys = 1 << 2; // Report alternate keys
|
||||
static constexpr uint8_t ReportAllKeys = 1 << 3; // Report all keys as escape codes
|
||||
static constexpr uint8_t ReportText = 1 << 4; // Report associated text
|
||||
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) noexcept;
|
||||
void PopKittyFlags(size_t count) noexcept;
|
||||
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
|
||||
{
|
||||
wchar_t buf[3];
|
||||
uint16_t len;
|
||||
};
|
||||
|
||||
// storage location for the leading surrogate of a utf-16 surrogate pair
|
||||
wchar_t _leadingSurrogate = 0;
|
||||
|
||||
@@ -80,24 +109,38 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
|
||||
til::enumset<Mode> _inputMode{ Mode::Ansi, Mode::AutoRepeat, Mode::AlternateScroll };
|
||||
bool _forceDisableWin32InputMode{ false };
|
||||
bool _inAlternateBuffer{ false };
|
||||
|
||||
// Kitty keyboard protocol state - separate stacks for main and alternate screen buffers
|
||||
static constexpr size_t KittyStackMaxSize = 16;
|
||||
bool _forceDisableKittyKeyboardProtocol = false;
|
||||
uint8_t _kittyFlags = 0;
|
||||
std::vector<uint8_t> _kittyMainStack;
|
||||
std::vector<uint8_t> _kittyAltStack;
|
||||
|
||||
const wchar_t* _csi = L"\x1B[";
|
||||
const wchar_t* _ss3 = L"\x1BO";
|
||||
|
||||
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);
|
||||
std::array<byte, 256> _getKeyboardState(WORD virtualKeyCode, DWORD controlKeyState) const;
|
||||
[[nodiscard]] static wchar_t _makeCtrlChar(wchar_t ch);
|
||||
[[nodiscard]] StringType _makeCharOutput(wchar_t ch);
|
||||
[[nodiscard]] static StringType _makeNoOutput() noexcept;
|
||||
[[nodiscard]] void _escapeOutput(StringType& charSequence, const bool altIsPressed) const;
|
||||
void _escapeOutput(StringType& charSequence, bool altIsPressed) const;
|
||||
[[nodiscard]] OutputType _makeWin32Output(const KEY_EVENT_RECORD& key) const;
|
||||
[[nodiscard]] OutputType _makeKittyOutput(const KEY_EVENT_RECORD& key, DWORD controlKeyState);
|
||||
[[nodiscard]] static uint32_t _getKittyFunctionalKeyCode(WORD virtualKeyCode, WORD virtualScanCode, DWORD controlKeyState) noexcept;
|
||||
std::vector<uint8_t>& _getKittyStack() noexcept;
|
||||
static bool _codepointIsNonControl(uint32_t cp) noexcept;
|
||||
static CodepointBuffer _codepointToBuffer(uint32_t cp) noexcept;
|
||||
static uint32_t _bufferToCodepoint(const wchar_t* str) noexcept;
|
||||
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 +156,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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -546,6 +546,18 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParamete
|
||||
case CsiActionCodes::ANSISYSRC_CursorRestore:
|
||||
_dispatch->CursorRestoreState();
|
||||
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;
|
||||
case CsiActionCodes::IL_InsertLine:
|
||||
_dispatch->InsertLine(parameters.at(0));
|
||||
break;
|
||||
|
||||
@@ -136,6 +136,10 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
DECSLRM_SetLeftRightMargins = VTID("s"),
|
||||
DTTERM_WindowManipulation = VTID("t"), // NOTE: Overlaps with DECSLPP. Fix when/if implemented.
|
||||
ANSISYSRC_CursorRestore = VTID("u"),
|
||||
KKP_KittyKeyboardSet = VTID("=u"),
|
||||
KKP_KittyKeyboardQuery = VTID("?u"),
|
||||
KKP_KittyKeyboardPush = VTID(">u"),
|
||||
KKP_KittyKeyboardPop = VTID("<u"),
|
||||
DECREQTPARM_RequestTerminalParameters = VTID("x"),
|
||||
PPA_PagePositionAbsolute = VTID(" P"),
|
||||
PPR_PagePositionRelative = VTID(" Q"),
|
||||
|
||||
558
tools/GenerateSettingsIndex.ps1
Normal file
558
tools/GenerateSettingsIndex.ps1
Normal 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"
|
||||
Reference in New Issue
Block a user