mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-18 18:56:25 +00:00
Compare commits
97 Commits
v1.25.1322
...
dev/cazamo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32252145f2 | ||
|
|
1734d13cc0 | ||
|
|
c8f1050e21 | ||
|
|
8db1805e17 | ||
|
|
27e1aee881 | ||
|
|
5ae13b3e42 | ||
|
|
bfecd07bdb | ||
|
|
23e056a727 | ||
|
|
831313c959 | ||
|
|
2d077ca892 | ||
|
|
3353323880 | ||
|
|
d9927ca6ad | ||
|
|
3b5bed8331 | ||
|
|
410ef5ae8d | ||
|
|
378ed3b970 | ||
|
|
fef53512aa | ||
|
|
43f16efbab | ||
|
|
244550044a | ||
|
|
2c75721447 | ||
|
|
c8e2e08763 | ||
|
|
34c6115a59 | ||
|
|
fc0eb78f5f | ||
|
|
debaceee2f | ||
|
|
057128e661 | ||
|
|
36d28e2ba9 | ||
|
|
0fe444e07a | ||
|
|
cc1d632598 | ||
|
|
a7d6e27a48 | ||
|
|
2e7e37ae09 | ||
|
|
92bccdfa4a | ||
|
|
f25e6fe2d1 | ||
|
|
e9f83fc4eb | ||
|
|
c371c484a3 | ||
|
|
1072d69fb7 | ||
|
|
b472d9fed9 | ||
|
|
7fcdeaac40 | ||
|
|
2766f21d31 | ||
|
|
f1424a471b | ||
|
|
7b5e633d57 | ||
|
|
0af4eb0e21 | ||
|
|
b0a7ef1d39 | ||
|
|
65bf620d34 | ||
|
|
8092f4644b | ||
|
|
bc84287fcb | ||
|
|
055ffabcdc | ||
|
|
14c6f36285 | ||
|
|
7cdbb7c795 | ||
|
|
5afc9bc86a | ||
|
|
6b83fa705a | ||
|
|
e6c43c0d4c | ||
|
|
75d02c29bd | ||
|
|
f11515e692 | ||
|
|
7f0c9e5374 | ||
|
|
019bb766db | ||
|
|
4cebcc193e | ||
|
|
b5eafa6912 | ||
|
|
6d550b36ff | ||
|
|
289ecec719 | ||
|
|
4a780e189c | ||
|
|
c4ac493617 | ||
|
|
e2336d4d00 | ||
|
|
4623575b79 | ||
|
|
87c79b3b18 | ||
|
|
ed85ebc41a | ||
|
|
a2ec2f31cc | ||
|
|
d8360a3789 | ||
|
|
27fc46d895 | ||
|
|
2cea85d22e | ||
|
|
762c24f102 | ||
|
|
43ccd382ec | ||
|
|
9cafea210c | ||
|
|
54292253c8 | ||
|
|
14f12098fe | ||
|
|
94a463e3e7 | ||
|
|
059f8ebb7e | ||
|
|
6c76f05da8 | ||
|
|
6b93fb645e | ||
|
|
847d44c59b | ||
|
|
be99303d3c | ||
|
|
a953ed8ea3 | ||
|
|
26289f4206 | ||
|
|
eed9d1c6b4 | ||
|
|
e433f52224 | ||
|
|
e21edd6d63 | ||
|
|
96158194bd | ||
|
|
11bd5beb7e | ||
|
|
8d21f06ae1 | ||
|
|
a54d71a096 | ||
|
|
d9101991d4 | ||
|
|
5a188c220e | ||
|
|
44bc11206b | ||
|
|
be91a1dac6 | ||
|
|
db9d5be258 | ||
|
|
2407ef345d | ||
|
|
c72203ddd1 | ||
|
|
13eaf5af94 | ||
|
|
14fbb9980b |
Binary file not shown.
|
After Width: | Height: | Size: 943 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/cascadia/CascadiaPackage/ProfileGeneratorIcons/SSH.png
Normal file
BIN
src/cascadia/CascadiaPackage/ProfileGeneratorIcons/SSH.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 787 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/cascadia/CascadiaPackage/ProfileGeneratorIcons/WSL.png
Normal file
BIN
src/cascadia/CascadiaPackage/ProfileGeneratorIcons/WSL.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
@@ -21,6 +21,11 @@
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
<Link>ProfileIcons\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||
</Content>
|
||||
<!-- Profile Generator Icons -->
|
||||
<Content Include="$(OpenConsoleDir)src\cascadia\CascadiaPackage\ProfileGeneratorIcons\**\*">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
<Link>ProfileGeneratorIcons\%(RecursiveDir)%(FileName)%(Extension)</Link>
|
||||
</Content>
|
||||
<!-- Default Settings -->
|
||||
<Content Include="$(OpenConsoleDir)src\cascadia\TerminalSettingsModel\defaults.json">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
|
||||
@@ -77,6 +77,16 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
|
||||
|
||||
if (profile.Source() == L"Windows.Terminal.InstallPowerShell")
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider,
|
||||
"InstallPowerShellStubInvoked",
|
||||
TraceLoggingDescription("Event emitted when the 'Install Latest PowerShell' stub was invoked"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
|
||||
}
|
||||
|
||||
// Try to handle auto-elevation
|
||||
if (_maybeElevate(newTerminalArgs, settings, profile))
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,12 @@
|
||||
|
||||
#include "ActionsViewModel.g.h"
|
||||
#include "KeyBindingViewModel.g.h"
|
||||
#include "CommandViewModel.g.h"
|
||||
#include "ArgWrapper.g.h"
|
||||
#include "ActionArgsViewModel.g.h"
|
||||
#include "KeyChordViewModel.g.h"
|
||||
#include "ModifyKeyBindingEventArgs.g.h"
|
||||
#include "ModifyKeyChordEventArgs.g.h"
|
||||
#include "Utils.h"
|
||||
#include "ViewModelHelpers.h"
|
||||
|
||||
@@ -19,6 +24,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
};
|
||||
|
||||
struct CommandViewModelComparator
|
||||
{
|
||||
bool operator()(const Editor::CommandViewModel& lhs, const Editor::CommandViewModel& rhs) const
|
||||
{
|
||||
return lhs.DisplayName() < rhs.DisplayName();
|
||||
}
|
||||
};
|
||||
|
||||
struct ModifyKeyBindingEventArgs : ModifyKeyBindingEventArgsT<ModifyKeyBindingEventArgs>
|
||||
{
|
||||
public:
|
||||
@@ -34,6 +47,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
WINRT_PROPERTY(hstring, NewActionName);
|
||||
};
|
||||
|
||||
struct ModifyKeyChordEventArgs : ModifyKeyChordEventArgsT<ModifyKeyChordEventArgs>
|
||||
{
|
||||
public:
|
||||
ModifyKeyChordEventArgs(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys) :
|
||||
_OldKeys{ oldKeys },
|
||||
_NewKeys{ newKeys } {}
|
||||
|
||||
WINRT_PROPERTY(Control::KeyChord, OldKeys, nullptr);
|
||||
WINRT_PROPERTY(Control::KeyChord, NewKeys, nullptr);
|
||||
};
|
||||
|
||||
struct KeyBindingViewModel : KeyBindingViewModelT<KeyBindingViewModel>, ViewModelHelper<KeyBindingViewModel>
|
||||
{
|
||||
public:
|
||||
@@ -99,32 +123,200 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
hstring _KeyChordText{};
|
||||
};
|
||||
|
||||
struct CommandViewModel : CommandViewModelT<CommandViewModel>, ViewModelHelper<CommandViewModel>
|
||||
{
|
||||
public:
|
||||
CommandViewModel(winrt::Microsoft::Terminal::Settings::Model::Command cmd,
|
||||
std::vector<Control::KeyChord> keyChordList,
|
||||
const Editor::ActionsViewModel actionsPageVM,
|
||||
const Windows::Foundation::Collections::IMap<Model::ShortcutAction, winrt::hstring>& availableShortcutActionsAndNames);
|
||||
void Initialize();
|
||||
|
||||
winrt::hstring DisplayName();
|
||||
winrt::hstring Name();
|
||||
void Name(const winrt::hstring& newName);
|
||||
|
||||
winrt::hstring ID();
|
||||
void ID(const winrt::hstring& newID);
|
||||
|
||||
bool IsUserAction();
|
||||
|
||||
void Edit_Click();
|
||||
til::typed_event<Editor::CommandViewModel, IInspectable> EditRequested;
|
||||
|
||||
void Delete_Click();
|
||||
til::typed_event<Editor::CommandViewModel, IInspectable> DeleteRequested;
|
||||
|
||||
void AddKeybinding_Click();
|
||||
|
||||
til::typed_event<IInspectable, Editor::ArgWrapper> PropagateColorSchemeRequested;
|
||||
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(IInspectable, ProposedShortcutAction);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Editor::ActionArgsViewModel, ActionArgsVM, nullptr);
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<hstring>, AvailableShortcutActions, nullptr);
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::KeyChordViewModel>, KeyChordViewModelList, nullptr);
|
||||
WINRT_PROPERTY(bool, IsNewCommand, false);
|
||||
|
||||
private:
|
||||
winrt::Microsoft::Terminal::Settings::Model::Command _command;
|
||||
std::vector<Control::KeyChord> _keyChordList;
|
||||
weak_ref<Editor::ActionsViewModel> _actionsPageVM{ nullptr };
|
||||
void _RegisterKeyChordVMEvents(Editor::KeyChordViewModel kcVM);
|
||||
void _RegisterActionArgsVMEvents(Editor::ActionArgsViewModel actionArgsVM);
|
||||
void _ReplaceCommandWithUserCopy();
|
||||
void _CreateAndInitializeActionArgsVMHelper();
|
||||
Windows::Foundation::Collections::IMap<Model::ShortcutAction, winrt::hstring> _AvailableActionsAndNamesMap;
|
||||
std::unordered_map<winrt::hstring, Model::ShortcutAction> _NameToActionMap;
|
||||
};
|
||||
|
||||
struct ArgWrapper : ArgWrapperT<ArgWrapper>, ViewModelHelper<ArgWrapper>
|
||||
{
|
||||
public:
|
||||
ArgWrapper(const winrt::hstring& name, const winrt::hstring& type, const bool required, const Windows::Foundation::IInspectable& value);
|
||||
void Initialize();
|
||||
|
||||
winrt::hstring Name() const noexcept { return _name; };
|
||||
winrt::hstring Type() const noexcept { return _type; };
|
||||
bool Required() const noexcept { return _required; };
|
||||
|
||||
// We cannot use the macro here because we need to implement additional logic for the setter
|
||||
Windows::Foundation::IInspectable EnumValue() const noexcept { return _EnumValue; };
|
||||
void EnumValue(const Windows::Foundation::IInspectable& value);
|
||||
Windows::Foundation::Collections::IObservableVector<Microsoft::Terminal::Settings::Editor::EnumEntry> EnumList() const noexcept { return _EnumList; };
|
||||
Windows::Foundation::Collections::IObservableVector<Microsoft::Terminal::Settings::Editor::FlagEntry> FlagList() const noexcept { return _FlagList; };
|
||||
|
||||
// unboxing functions
|
||||
winrt::hstring UnboxString(const Windows::Foundation::IInspectable& value);
|
||||
winrt::hstring UnboxGuid(const Windows::Foundation::IInspectable& value);
|
||||
int32_t UnboxInt32(const Windows::Foundation::IInspectable& value);
|
||||
float UnboxInt32Optional(const Windows::Foundation::IInspectable& value);
|
||||
uint32_t UnboxUInt32(const Windows::Foundation::IInspectable& value);
|
||||
float UnboxUInt32Optional(const Windows::Foundation::IInspectable& value);
|
||||
float UnboxUInt64(const Windows::Foundation::IInspectable& value);
|
||||
float UnboxFloat(const Windows::Foundation::IInspectable& value);
|
||||
winrt::Windows::Foundation::IReference<bool> UnboxBoolOptional(const Windows::Foundation::IInspectable& value);
|
||||
winrt::Windows::Foundation::IReference<Microsoft::Terminal::Core::Color> UnboxTerminalCoreColorOptional(const Windows::Foundation::IInspectable& value);
|
||||
winrt::Windows::Foundation::IReference<Microsoft::Terminal::Core::Color> UnboxWindowsUIColorOptional(const Windows::Foundation::IInspectable& value);
|
||||
|
||||
// bind back functions
|
||||
void StringBindBack(const winrt::hstring& newValue);
|
||||
void GuidBindBack(const winrt::hstring& newValue);
|
||||
void Int32BindBack(const double newValue);
|
||||
void Int32OptionalBindBack(const double newValue);
|
||||
void UInt32BindBack(const double newValue);
|
||||
void UInt32OptionalBindBack(const double newValue);
|
||||
void UInt64BindBack(const double newValue);
|
||||
void FloatBindBack(const double newValue);
|
||||
void BoolOptionalBindBack(const Windows::Foundation::IReference<bool> newValue);
|
||||
void TerminalCoreColorBindBack(const winrt::Windows::Foundation::IReference<Microsoft::Terminal::Core::Color> newValue);
|
||||
void WindowsUIColorBindBack(const winrt::Windows::Foundation::IReference<Microsoft::Terminal::Core::Color> newValue);
|
||||
|
||||
til::typed_event<IInspectable, Editor::ArgWrapper> ColorSchemeRequested;
|
||||
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Editor::ColorSchemeViewModel, DefaultColorScheme, nullptr);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::IInspectable, Value, nullptr);
|
||||
|
||||
private:
|
||||
winrt::hstring _name;
|
||||
winrt::hstring _type;
|
||||
bool _required;
|
||||
Windows::Foundation::IInspectable _EnumValue{ nullptr };
|
||||
Windows::Foundation::Collections::IObservableVector<Microsoft::Terminal::Settings::Editor::EnumEntry> _EnumList;
|
||||
Windows::Foundation::Collections::IObservableVector<Microsoft::Terminal::Settings::Editor::FlagEntry> _FlagList;
|
||||
};
|
||||
|
||||
struct ActionArgsViewModel : ActionArgsViewModelT<ActionArgsViewModel>, ViewModelHelper<ActionArgsViewModel>
|
||||
{
|
||||
public:
|
||||
ActionArgsViewModel(const Microsoft::Terminal::Settings::Model::ActionAndArgs actionAndArgs);
|
||||
void Initialize();
|
||||
|
||||
bool HasArgs() const noexcept;
|
||||
|
||||
til::typed_event<IInspectable, IInspectable> WrapperValueChanged;
|
||||
til::typed_event<IInspectable, Editor::ArgWrapper> PropagateColorSchemeRequested;
|
||||
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::ArgWrapper>, ArgValues, nullptr);
|
||||
|
||||
private:
|
||||
Model::ActionAndArgs _actionAndArgs{ nullptr };
|
||||
};
|
||||
|
||||
struct KeyChordViewModel : KeyChordViewModelT<KeyChordViewModel>, ViewModelHelper<KeyChordViewModel>
|
||||
{
|
||||
public:
|
||||
KeyChordViewModel(Control::KeyChord CurrentKeys);
|
||||
|
||||
void CurrentKeys(const Control::KeyChord& newKeys);
|
||||
Control::KeyChord CurrentKeys() const noexcept;
|
||||
|
||||
void ToggleEditMode();
|
||||
void AttemptAcceptChanges();
|
||||
void CancelChanges();
|
||||
void DeleteKeyChord();
|
||||
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsInEditMode, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Control::KeyChord, ProposedKeys);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(winrt::hstring, KeyChordText);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::UI::Xaml::Controls::Flyout, AcceptChangesFlyout, nullptr);
|
||||
|
||||
public:
|
||||
til::typed_event<Editor::KeyChordViewModel, Terminal::Control::KeyChord> AddKeyChordRequested;
|
||||
til::typed_event<Editor::KeyChordViewModel, Editor::ModifyKeyChordEventArgs> ModifyKeyChordRequested;
|
||||
til::typed_event<Editor::KeyChordViewModel, Terminal::Control::KeyChord> DeleteKeyChordRequested;
|
||||
|
||||
private:
|
||||
Control::KeyChord _currentKeys;
|
||||
};
|
||||
|
||||
struct ActionsViewModel : ActionsViewModelT<ActionsViewModel>, ViewModelHelper<ActionsViewModel>
|
||||
{
|
||||
public:
|
||||
ActionsViewModel(Model::CascadiaSettings settings);
|
||||
void UpdateSettings(const Model::CascadiaSettings& settings);
|
||||
|
||||
void OnAutomationPeerAttached();
|
||||
void AddNewKeybinding();
|
||||
void AddNewCommand();
|
||||
|
||||
void CurrentCommand(const Editor::CommandViewModel& newCommand);
|
||||
Editor::CommandViewModel CurrentCommand();
|
||||
void CmdListItemClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::Controls::ItemClickEventArgs& e);
|
||||
|
||||
void AttemptDeleteKeyChord(const Control::KeyChord& keys);
|
||||
void AttemptAddOrModifyKeyChord(const Editor::KeyChordViewModel& senderVM, winrt::hstring commandID, const Control::KeyChord& newKeys, const Control::KeyChord& oldKeys);
|
||||
void AttemptAddCopiedCommand(const Model::Command& newCommand);
|
||||
|
||||
til::typed_event<IInspectable, IInspectable> FocusContainer;
|
||||
til::typed_event<IInspectable, IInspectable> UpdateBackground;
|
||||
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::KeyBindingViewModel>, KeyBindingList);
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::CommandViewModel>, CommandList);
|
||||
WINRT_OBSERVABLE_PROPERTY(ActionsSubPage, CurrentPage, _propertyChangedHandlers, ActionsSubPage::Base);
|
||||
|
||||
private:
|
||||
Editor::CommandViewModel _CurrentCommand{ nullptr };
|
||||
bool _AutomationPeerAttached{ false };
|
||||
Model::CascadiaSettings _Settings;
|
||||
Windows::Foundation::Collections::IObservableVector<hstring> _AvailableActionAndArgs;
|
||||
Windows::Foundation::Collections::IMap<hstring, Model::ActionAndArgs> _AvailableActionMap;
|
||||
Windows::Foundation::Collections::IMap<Model::ShortcutAction, winrt::hstring> _AvailableActionsAndNamesMap;
|
||||
|
||||
void _MakeCommandVMsHelper();
|
||||
|
||||
std::optional<uint32_t> _GetContainerIndexByKeyChord(const Control::KeyChord& keys);
|
||||
void _RegisterEvents(com_ptr<implementation::KeyBindingViewModel>& kbdVM);
|
||||
void _RegisterCmdVMEvents(com_ptr<implementation::CommandViewModel>& cmdVM);
|
||||
|
||||
void _KeyBindingViewModelPropertyChangedHandler(const Windows::Foundation::IInspectable& senderVM, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
|
||||
void _KeyBindingViewModelDeleteKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Control::KeyChord& args);
|
||||
void _KeyBindingViewModelModifyKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Editor::ModifyKeyBindingEventArgs& args);
|
||||
void _KeyBindingViewModelDeleteNewlyAddedKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const IInspectable& args);
|
||||
|
||||
void _CmdVMPropertyChangedHandler(const IInspectable& sender, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
|
||||
void _CmdVMEditRequestedHandler(const Editor::CommandViewModel& senderVM, const IInspectable& args);
|
||||
void _CmdVMDeleteRequestedHandler(const Editor::CommandViewModel& senderVM, const IInspectable& args);
|
||||
void _CmdVMPropagateColorSchemeRequestedHandler(const IInspectable& sender, const Editor::ArgWrapper& wrapper);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "EnumEntry.idl";
|
||||
import "ColorSchemeViewModel.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
runtimeclass ModifyKeyBindingEventArgs
|
||||
@@ -11,6 +14,12 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
String NewActionName { get; };
|
||||
}
|
||||
|
||||
runtimeclass ModifyKeyChordEventArgs
|
||||
{
|
||||
Microsoft.Terminal.Control.KeyChord OldKeys { get; };
|
||||
Microsoft.Terminal.Control.KeyChord NewKeys { get; };
|
||||
}
|
||||
|
||||
runtimeclass KeyBindingViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
// Settings Model side
|
||||
@@ -46,15 +55,125 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
event Windows.Foundation.TypedEventHandler<KeyBindingViewModel, Microsoft.Terminal.Control.KeyChord> DeleteKeyBindingRequested;
|
||||
}
|
||||
|
||||
runtimeclass CommandViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
// Settings Model side
|
||||
String Name;
|
||||
String ID;
|
||||
Boolean IsUserAction { get; };
|
||||
// keybindings
|
||||
IObservableVector<KeyChordViewModel> KeyChordViewModelList { get; };
|
||||
// action args
|
||||
ActionArgsViewModel ActionArgsVM { get; };
|
||||
|
||||
// View-model specific
|
||||
String DisplayName { get; };
|
||||
Boolean IsNewCommand;
|
||||
|
||||
// UI side (command list page)
|
||||
void Edit_Click();
|
||||
|
||||
// UI side (edit command page)
|
||||
IObservableVector<String> AvailableShortcutActions { get; };
|
||||
Object ProposedShortcutAction;
|
||||
void Delete_Click();
|
||||
void AddKeybinding_Click();
|
||||
event Windows.Foundation.TypedEventHandler<Object, ArgWrapper> PropagateColorSchemeRequested;
|
||||
}
|
||||
|
||||
runtimeclass ArgWrapper : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
String Name { get; };
|
||||
String Type { get; };
|
||||
Boolean Required { get; };
|
||||
IInspectable Value;
|
||||
IInspectable EnumValue;
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> EnumList { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.FlagEntry> FlagList { get; };
|
||||
ColorSchemeViewModel DefaultColorScheme;
|
||||
|
||||
// unboxing functions
|
||||
String UnboxString(Object value);
|
||||
String UnboxGuid(Object value);
|
||||
UInt32 UnboxInt32(Object value);
|
||||
Single UnboxInt32Optional(Object value);
|
||||
UInt32 UnboxUInt32(Object value);
|
||||
Single UnboxUInt32Optional(Object value);
|
||||
Single UnboxUInt64(Object value);
|
||||
Single UnboxFloat(Object value);
|
||||
Windows.Foundation.IReference<Boolean> UnboxBoolOptional(Object value);
|
||||
Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> UnboxTerminalCoreColorOptional(Object value);
|
||||
Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> UnboxWindowsUIColorOptional(Object value);
|
||||
|
||||
// bind back functions
|
||||
void StringBindBack(String newValue);
|
||||
void GuidBindBack(String newValue);
|
||||
void Int32BindBack(Double newValue);
|
||||
void Int32OptionalBindBack(Double newValue);
|
||||
void UInt32BindBack(Double newValue);
|
||||
void UInt32OptionalBindBack(Double newValue);
|
||||
void UInt64BindBack(Double newValue);
|
||||
void FloatBindBack(Double newValue);
|
||||
void BoolOptionalBindBack(Windows.Foundation.IReference<Boolean> newValue);
|
||||
void TerminalCoreColorBindBack(Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> newValue);
|
||||
void WindowsUIColorBindBack(Windows.Foundation.IReference<Microsoft.Terminal.Core.Color> newValue);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, ArgWrapper> ColorSchemeRequested;
|
||||
}
|
||||
|
||||
runtimeclass ActionArgsViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
Boolean HasArgs { get; };
|
||||
IObservableVector<ArgWrapper> ArgValues;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> WrapperValueChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, ArgWrapper> PropagateColorSchemeRequested;
|
||||
}
|
||||
|
||||
runtimeclass KeyChordViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
String KeyChordText { get; };
|
||||
|
||||
// UI side
|
||||
Microsoft.Terminal.Control.KeyChord ProposedKeys;
|
||||
Windows.UI.Xaml.Controls.Flyout AcceptChangesFlyout;
|
||||
Boolean IsInEditMode { get; };
|
||||
void ToggleEditMode();
|
||||
void AttemptAcceptChanges();
|
||||
void CancelChanges();
|
||||
void DeleteKeyChord();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<KeyChordViewModel, Microsoft.Terminal.Control.KeyChord> AddKeyChordRequested;
|
||||
event Windows.Foundation.TypedEventHandler<KeyChordViewModel, ModifyKeyChordEventArgs> ModifyKeyChordRequested;
|
||||
event Windows.Foundation.TypedEventHandler<KeyChordViewModel, Microsoft.Terminal.Control.KeyChord> DeleteKeyChordRequested;
|
||||
}
|
||||
|
||||
enum ActionsSubPage
|
||||
{
|
||||
Base = 0,
|
||||
Edit = 1
|
||||
};
|
||||
|
||||
runtimeclass ActionsViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
ActionsViewModel(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
||||
void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
||||
|
||||
void OnAutomationPeerAttached();
|
||||
void AddNewKeybinding();
|
||||
void AddNewCommand();
|
||||
|
||||
IObservableVector<KeyBindingViewModel> KeyBindingList { get; };
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FocusContainer;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> UpdateBackground;
|
||||
|
||||
ActionsSubPage CurrentPage;
|
||||
CommandViewModel CurrentCommand;
|
||||
|
||||
void AttemptAddOrModifyKeyChord(KeyChordViewModel senderVM, String commandID, Microsoft.Terminal.Control.KeyChord newKeys, Microsoft.Terminal.Control.KeyChord oldKeys);
|
||||
void AttemptDeleteKeyChord(Microsoft.Terminal.Control.KeyChord keys);
|
||||
void AttemptAddCopiedCommand(Microsoft.Terminal.Settings.Model.Command newCommand);
|
||||
|
||||
IObservableVector<CommandViewModel> CommandList { get; };
|
||||
void CmdListItemClicked(IInspectable sender, Windows.UI.Xaml.Controls.ItemClickEventArgs args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<IconSourceElement Grid.Column="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
IconSource="{x:Bind mtu:IconPathConverter.IconSourceWUX(Icon), Mode=OneTime}" />
|
||||
IconSource="{x:Bind mtu:IconPathConverter.IconSourceWUX(EvaluatedIcon), Mode=OneTime}" />
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{x:Bind Name}" />
|
||||
|
||||
102
src/cascadia/TerminalSettingsEditor/ArgsTemplateSelectors.cpp
Normal file
102
src/cascadia/TerminalSettingsEditor/ArgsTemplateSelectors.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ArgsTemplateSelectors.h"
|
||||
#include "ArgsTemplateSelectors.g.cpp"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
Windows::UI::Xaml::DataTemplate ArgsTemplateSelectors::SelectTemplateCore(const winrt::Windows::Foundation::IInspectable& item, const winrt::Windows::UI::Xaml::DependencyObject& /*container*/)
|
||||
{
|
||||
return SelectTemplateCore(item);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This method is called once command palette decides how to render a filtered command.
|
||||
// Currently we support two ways to render command, that depend on its palette item type:
|
||||
// - For TabPalette item we render an icon, a title, and some tab-related indicators like progress bar (as defined by TabItemTemplate)
|
||||
// - All other items are currently rendered with icon, title and optional key-chord (as defined by GeneralItemTemplate)
|
||||
// Arguments:
|
||||
// - item - an instance of filtered command to render
|
||||
// Return Value:
|
||||
// - data template to use for rendering
|
||||
Windows::UI::Xaml::DataTemplate ArgsTemplateSelectors::SelectTemplateCore(const winrt::Windows::Foundation::IInspectable& item)
|
||||
{
|
||||
if (const auto argWrapper{ item.try_as<Microsoft::Terminal::Settings::Editor::ArgWrapper>() })
|
||||
{
|
||||
const auto argType = argWrapper.Type();
|
||||
if (argType == L"winrt::hstring")
|
||||
{
|
||||
return StringTemplate();
|
||||
}
|
||||
else if (argType == L"winrt::guid")
|
||||
{
|
||||
return GuidTemplate();
|
||||
}
|
||||
else if (argType == L"int32_t")
|
||||
{
|
||||
return Int32Template();
|
||||
}
|
||||
else if (argType == L"uint32_t")
|
||||
{
|
||||
return UInt32Template();
|
||||
}
|
||||
else if (argType == L"uint64_t")
|
||||
{
|
||||
return UInt64Template();
|
||||
}
|
||||
else if (argType == L"float")
|
||||
{
|
||||
return FloatTemplate();
|
||||
}
|
||||
else if (argType == L"bool" ||
|
||||
argType == L"Windows::Foundation::IReference<bool>")
|
||||
{
|
||||
return BoolOptionalTemplate();
|
||||
}
|
||||
else if (argType == L"Windows::Foundation::IReference<int32_t>")
|
||||
{
|
||||
return Int32OptionalTemplate();
|
||||
}
|
||||
else if (argType == L"Windows::Foundation::IReference<uint32_t>")
|
||||
{
|
||||
return UInt32OptionalTemplate();
|
||||
}
|
||||
else if (argType == L"Model::ResizeDirection" ||
|
||||
argType == L"Model::FocusDirection" ||
|
||||
argType == L"SettingsTarget" ||
|
||||
argType == L"MoveTabDirection" ||
|
||||
argType == L"Microsoft::Terminal::Control::ScrollToMarkDirection" ||
|
||||
argType == L"CommandPaletteLaunchMode" ||
|
||||
argType == L"FindMatchDirection" ||
|
||||
argType == L"Model::DesktopBehavior" ||
|
||||
argType == L"Model::MonitorBehavior" ||
|
||||
argType == L"winrt::Microsoft::Terminal::Control::ClearBufferType" ||
|
||||
argType == L"SelectOutputDirection" ||
|
||||
argType == L"Windows::Foundation::IReference<TabSwitcherMode>" ||
|
||||
argType == L"Model::SplitDirection" ||
|
||||
argType == L"SplitType")
|
||||
{
|
||||
return EnumTemplate();
|
||||
}
|
||||
else if (argType == L"SuggestionsSource")
|
||||
{
|
||||
return FlagTemplate();
|
||||
}
|
||||
else if (argType == L"Windows::Foundation::IReference<Microsoft::Terminal::Core::Color>")
|
||||
{
|
||||
return TerminalCoreColorOptionalTemplate();
|
||||
}
|
||||
else if (argType == L"Windows::Foundation::IReference<Windows::UI::Color>")
|
||||
{
|
||||
return WindowsUIColorOptionalTemplate();
|
||||
}
|
||||
else if (argType == L"Windows::Foundation::IReference<Control::CopyFormat>")
|
||||
{
|
||||
return FlagTemplate();
|
||||
}
|
||||
}
|
||||
return NoArgTemplate();
|
||||
}
|
||||
}
|
||||
37
src/cascadia/TerminalSettingsEditor/ArgsTemplateSelectors.h
Normal file
37
src/cascadia/TerminalSettingsEditor/ArgsTemplateSelectors.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ArgsTemplateSelectors.g.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct ArgsTemplateSelectors : ArgsTemplateSelectorsT<ArgsTemplateSelectors>
|
||||
{
|
||||
ArgsTemplateSelectors() = default;
|
||||
|
||||
Windows::UI::Xaml::DataTemplate SelectTemplateCore(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::DependencyObject&);
|
||||
Windows::UI::Xaml::DataTemplate SelectTemplateCore(const winrt::Windows::Foundation::IInspectable&);
|
||||
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, NoArgTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, GuidTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, Int32Template);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, Int32OptionalTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, UInt32Template);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, UInt32OptionalTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, UInt64Template);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, FloatTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, StringTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, BoolOptionalTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, EnumTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, FlagTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, TerminalCoreColorOptionalTemplate);
|
||||
WINRT_PROPERTY(winrt::Windows::UI::Xaml::DataTemplate, WindowsUIColorOptionalTemplate);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ArgsTemplateSelectors);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
[default_interface] runtimeclass ArgsTemplateSelectors : Windows.UI.Xaml.Controls.DataTemplateSelector
|
||||
{
|
||||
ArgsTemplateSelectors();
|
||||
|
||||
Windows.UI.Xaml.DataTemplate NoArgTemplate;
|
||||
Windows.UI.Xaml.DataTemplate GuidTemplate;
|
||||
Windows.UI.Xaml.DataTemplate Int32Template;
|
||||
Windows.UI.Xaml.DataTemplate Int32OptionalTemplate;
|
||||
Windows.UI.Xaml.DataTemplate UInt32Template;
|
||||
Windows.UI.Xaml.DataTemplate UInt32OptionalTemplate;
|
||||
Windows.UI.Xaml.DataTemplate UInt64Template;
|
||||
Windows.UI.Xaml.DataTemplate FloatTemplate;
|
||||
Windows.UI.Xaml.DataTemplate StringTemplate;
|
||||
Windows.UI.Xaml.DataTemplate BoolOptionalTemplate;
|
||||
Windows.UI.Xaml.DataTemplate EnumTemplate;
|
||||
Windows.UI.Xaml.DataTemplate FlagTemplate;
|
||||
Windows.UI.Xaml.DataTemplate TerminalCoreColorOptionalTemplate;
|
||||
Windows.UI.Xaml.DataTemplate WindowsUIColorOptionalTemplate;
|
||||
}
|
||||
}
|
||||
@@ -1199,7 +1199,7 @@
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
@@ -1222,7 +1222,8 @@
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
||||
<FontIcon Margin="20,0,8,0"
|
||||
<FontIcon Grid.Column="1"
|
||||
Margin="20,0,8,0"
|
||||
HorizontalAlignment="Right"
|
||||
FontSize="10"
|
||||
FontWeight="Black"
|
||||
@@ -1266,4 +1267,24 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="NewInfoBadge"
|
||||
TargetType="muxc:InfoBadge">
|
||||
<Setter Property="Padding" Value="5,1,5,2" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="muxc:InfoBadge">
|
||||
<Border x:Name="RootGrid"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.InfoBadgeCornerRadius}">
|
||||
<TextBlock x:Uid="NewInfoBadgeTextBlock"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="10" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
||||
45
src/cascadia/TerminalSettingsEditor/EditAction.cpp
Normal file
45
src/cascadia/TerminalSettingsEditor/EditAction.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "EditAction.h"
|
||||
#include "EditAction.g.cpp"
|
||||
#include "LibraryResources.h"
|
||||
#include "../TerminalSettingsModel/AllShortcutActions.h"
|
||||
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
EditAction::EditAction()
|
||||
{
|
||||
InitializeComponent();
|
||||
_itemTemplateSelector = Resources().Lookup(winrt::box_value(L"ArgsTemplateSelector")).try_as<ArgsTemplateSelectors>();
|
||||
_listItemTemplate = Resources().Lookup(winrt::box_value(L"ListItemTemplate")).try_as<DataTemplate>();
|
||||
}
|
||||
|
||||
void EditAction::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
_ViewModel = e.Parameter().as<Editor::CommandViewModel>();
|
||||
}
|
||||
|
||||
void EditAction::_choosingItemContainer(
|
||||
const Windows::UI::Xaml::Controls::ListViewBase& /*sender*/,
|
||||
const Windows::UI::Xaml::Controls::ChoosingItemContainerEventArgs& args)
|
||||
{
|
||||
const auto dataTemplate = _itemTemplateSelector.SelectTemplate(args.Item());
|
||||
const auto itemContainer = args.ItemContainer();
|
||||
|
||||
if (!itemContainer || itemContainer.ContentTemplate() != dataTemplate)
|
||||
{
|
||||
ElementFactoryGetArgs factoryArgs{};
|
||||
const auto listViewItem = _listItemTemplate.GetElement(factoryArgs).try_as<Controls::ListViewItem>();
|
||||
listViewItem.ContentTemplate(dataTemplate);
|
||||
|
||||
args.ItemContainer(listViewItem);
|
||||
}
|
||||
|
||||
args.IsContainerPrepared(true);
|
||||
}
|
||||
}
|
||||
34
src/cascadia/TerminalSettingsEditor/EditAction.h
Normal file
34
src/cascadia/TerminalSettingsEditor/EditAction.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "EditAction.g.h"
|
||||
#include "ActionsViewModel.h"
|
||||
#include "Utils.h"
|
||||
#include "ViewModelHelpers.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct EditAction : public HasScrollViewer<EditAction>, EditActionT<EditAction>
|
||||
{
|
||||
public:
|
||||
EditAction();
|
||||
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
|
||||
|
||||
til::property_changed_event PropertyChanged;
|
||||
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::CommandViewModel, ViewModel, PropertyChanged.raise, nullptr);
|
||||
|
||||
private:
|
||||
friend struct EditActionT<EditAction>; // for Xaml to bind events
|
||||
Windows::UI::Xaml::DataTemplate _listItemTemplate;
|
||||
winrt::Microsoft::Terminal::Settings::Editor::ArgsTemplateSelectors _itemTemplateSelector{ nullptr };
|
||||
void _choosingItemContainer(const Windows::UI::Xaml::Controls::ListViewBase& sender, const Windows::UI::Xaml::Controls::ChoosingItemContainerEventArgs& args);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(EditAction);
|
||||
}
|
||||
13
src/cascadia/TerminalSettingsEditor/EditAction.idl
Normal file
13
src/cascadia/TerminalSettingsEditor/EditAction.idl
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "ActionsViewModel.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
[default_interface] runtimeclass EditAction : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
EditAction();
|
||||
CommandViewModel ViewModel { get; };
|
||||
}
|
||||
}
|
||||
636
src/cascadia/TerminalSettingsEditor/EditAction.xaml
Normal file
636
src/cascadia/TerminalSettingsEditor/EditAction.xaml
Normal file
@@ -0,0 +1,636 @@
|
||||
<!--
|
||||
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information.
|
||||
-->
|
||||
<Page x:Class="Microsoft.Terminal.Settings.Editor.EditAction"
|
||||
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:mtu="using:Microsoft.Terminal.UI"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="CommonResources.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<!-- Theme Dictionary -->
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<!-- KeyChordText styles -->
|
||||
<Style x:Key="KeyChordBorderStyle"
|
||||
TargetType="Button">
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="1" />
|
||||
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
<!-- Override visual states -->
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="border">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAccentRevealBackgroundBrush}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed" />
|
||||
<VisualState x:Name="Disabled" />
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<!-- Define the appearance of the button -->
|
||||
<Border x:Name="border"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="KeyChordTextBlockStyle"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<!-- KeyChordText styles -->
|
||||
<Style x:Key="KeyChordBorderStyle"
|
||||
TargetType="Button">
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="1" />
|
||||
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
<!-- Override visual states -->
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="border">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAccentRevealBackgroundBrush}"/>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed" />
|
||||
<VisualState x:Name="Disabled" />
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<!-- Define the appearance of the button -->
|
||||
<Border x:Name="border"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="KeyChordTextBlockStyle"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<!-- KeyChordText styles (use XAML defaults for High Contrast theme) -->
|
||||
<Style x:Key="KeyChordBorderStyle"
|
||||
TargetType="Button" />
|
||||
<Style x:Key="KeyChordTextBlockStyle"
|
||||
TargetType="TextBlock" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<GridLength x:Key="ArgumentNameWidth">148</GridLength>
|
||||
|
||||
<!-- Styles -->
|
||||
<Style x:Key="KeyBindingContainerStyle"
|
||||
BasedOn="{StaticResource DefaultListViewItemStyle}"
|
||||
TargetType="ListViewItem">
|
||||
<Setter Property="Padding" Value="4" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="XYFocusKeyboardNavigation" Value="Enabled" />
|
||||
</Style>
|
||||
<Style x:Key="KeyChordEditorStyle"
|
||||
TargetType="local:KeyChordListener">
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
</Style>
|
||||
<x:Int32 x:Key="EditButtonSize">32</x:Int32>
|
||||
<x:Double x:Key="EditButtonIconSize">15</x:Double>
|
||||
<Style x:Key="EditButtonStyle"
|
||||
BasedOn="{StaticResource DefaultButtonStyle}"
|
||||
TargetType="Button">
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="Height" Value="{StaticResource EditButtonSize}" />
|
||||
<Setter Property="Width" Value="{StaticResource EditButtonSize}" />
|
||||
</Style>
|
||||
<Style x:Key="AccentEditButtonStyle"
|
||||
BasedOn="{StaticResource AccentButtonStyle}"
|
||||
TargetType="Button">
|
||||
<Setter Property="Padding" Value="3" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="Height" Value="{StaticResource EditButtonSize}" />
|
||||
<Setter Property="Width" Value="{StaticResource EditButtonSize}" />
|
||||
</Style>
|
||||
|
||||
<!-- Templates -->
|
||||
<DataTemplate x:Key="KeyChordTemplate"
|
||||
x:DataType="local:KeyChordViewModel">
|
||||
<ListViewItem Style="{StaticResource KeyBindingContainerStyle}">
|
||||
<Grid Padding="2,0,2,0"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button Style="{ThemeResource KeyChordBorderStyle}"
|
||||
Click="{x:Bind ToggleEditMode}"
|
||||
Grid.Column="0"
|
||||
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(IsInEditMode), Mode=OneWay}">
|
||||
<TextBlock FontSize="14"
|
||||
Style="{ThemeResource KeyChordTextBlockStyle}"
|
||||
Text="{x:Bind KeyChordText, Mode=OneWay}"
|
||||
TextWrapping="WrapWholeWords"/>
|
||||
</Button>
|
||||
<Grid Grid.Column="0"
|
||||
ColumnSpacing="8"
|
||||
Visibility="{x:Bind IsInEditMode, Mode=OneWay}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Edit Mode: Key Chord Listener -->
|
||||
<local:KeyChordListener Keys="{x:Bind ProposedKeys, Mode=TwoWay}"
|
||||
Style="{StaticResource KeyChordEditorStyle}"
|
||||
Grid.Column="0"/>
|
||||
|
||||
<!-- Cancel editing the action -->
|
||||
<Button x:Uid="Actions_CancelButton"
|
||||
Style="{StaticResource EditButtonStyle}"
|
||||
Click="{x:Bind CancelChanges}"
|
||||
Grid.Column="1">
|
||||
<FontIcon FontSize="{StaticResource EditButtonIconSize}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
|
||||
<!-- Accept changes -->
|
||||
<Button x:Uid="Actions_AcceptButton"
|
||||
Style="{StaticResource AccentEditButtonStyle}"
|
||||
Click="{x:Bind AttemptAcceptChanges}"
|
||||
Flyout="{x:Bind AcceptChangesFlyout, Mode=OneWay}"
|
||||
Grid.Column="2">
|
||||
<FontIcon FontSize="{StaticResource EditButtonIconSize}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<Button Grid.Column="1"
|
||||
Margin="8,0,0,0"
|
||||
Style="{StaticResource DeleteSmallButtonStyle}">
|
||||
<Button.Content>
|
||||
<FontIcon FontSize="{StaticResource EditButtonIconSize}"
|
||||
Glyph="" />
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="Actions_DeleteConfirmationMessage"
|
||||
Style="{StaticResource CustomFlyoutTextStyle}" />
|
||||
<Button x:Uid="Actions_DeleteConfirmationButton"
|
||||
Click="{x:Bind DeleteKeyChord}" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="ListItemTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem HorizontalContentAlignment="Stretch" />
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="NoArgTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem />
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="Int32Template"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<muxc:NumberBox Value="{x:Bind UnboxInt32(Value), Mode=TwoWay, BindBack=Int32BindBack}"
|
||||
Style="{StaticResource NumberBoxSettingStyle}"
|
||||
Minimum="0"
|
||||
Maximum="999"
|
||||
LargeChange="1"
|
||||
SmallChange="1"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="UInt32Template"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<muxc:NumberBox Value="{x:Bind UnboxUInt32(Value), Mode=TwoWay, BindBack=UInt32BindBack}"
|
||||
Style="{StaticResource NumberBoxSettingStyle}"
|
||||
Minimum="0"
|
||||
Maximum="999"
|
||||
LargeChange="1"
|
||||
SmallChange="1"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="UInt32OptionalTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<muxc:NumberBox Value="{x:Bind UnboxUInt32Optional(Value), Mode=TwoWay, BindBack=UInt32OptionalBindBack}"
|
||||
Style="{StaticResource NumberBoxSettingStyle}"
|
||||
Minimum="0"
|
||||
Maximum="999"
|
||||
LargeChange="1"
|
||||
SmallChange="1"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="UInt64Template"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<muxc:NumberBox Value="{x:Bind UnboxUInt64(Value), Mode=TwoWay, BindBack=UInt64BindBack}"
|
||||
Style="{StaticResource NumberBoxSettingStyle}"
|
||||
Minimum="0"
|
||||
Maximum="999"
|
||||
LargeChange="1"
|
||||
SmallChange="1"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="Int32OptionalTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<muxc:NumberBox Value="{x:Bind UnboxInt32Optional(Value), Mode=TwoWay, BindBack=Int32OptionalBindBack}"
|
||||
Style="{StaticResource NumberBoxSettingStyle}"
|
||||
Minimum="0"
|
||||
Maximum="999"
|
||||
LargeChange="1"
|
||||
SmallChange="1"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="FloatTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<muxc:NumberBox Value="{x:Bind UnboxFloat(Value), Mode=TwoWay, BindBack=FloatBindBack}"
|
||||
Style="{StaticResource NumberBoxSettingStyle}"
|
||||
Minimum="0"
|
||||
Maximum="999"
|
||||
LargeChange="1"
|
||||
SmallChange="1"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="StringTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<TextBox Text="{x:Bind UnboxString(Value), Mode=TwoWay, BindBack=StringBindBack}"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="GuidTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<TextBox Text="{x:Bind UnboxGuid(Value), Mode=TwoWay, BindBack=GuidBindBack}"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="BoolOptionalTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<CheckBox IsChecked="{x:Bind UnboxBoolOptional(Value), Mode=TwoWay, BindBack=BoolOptionalBindBack}"
|
||||
IsThreeState="True"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="EnumComboBoxTemplate"
|
||||
x:DataType="local:EnumEntry">
|
||||
<TextBlock Text="{x:Bind EnumName, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="EnumTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<ComboBox ItemTemplate="{StaticResource EnumComboBoxTemplate}"
|
||||
ItemsSource="{x:Bind EnumList, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind EnumValue, Mode=TwoWay}"
|
||||
Style="{StaticResource ComboBoxSettingStyle}"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="FlagItemTemplate"
|
||||
x:DataType="local:FlagEntry">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="40" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox IsChecked="{x:Bind IsSet, Mode=TwoWay}"
|
||||
Grid.Column="0" />
|
||||
<TextBlock Text="{x:Bind FlagName, Mode=OneWay}"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Padding="0,0,0,4"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="FlagTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<ListView ItemTemplate="{StaticResource FlagItemTemplate}"
|
||||
SelectionMode="None"
|
||||
ItemsSource="{x:Bind FlagList, Mode=OneWay}"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
Padding="-8,0,0,0"
|
||||
Margin="0"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="TerminalCoreColorOptionalTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<local:NullableColorPicker x:Uid="Actions_NullableColorPicker"
|
||||
CurrentColor="{x:Bind UnboxTerminalCoreColorOptional(Value), Mode=TwoWay, BindBack=TerminalCoreColorBindBack}"
|
||||
ColorSchemeVM="{x:Bind DefaultColorScheme, Mode=OneWay}"
|
||||
NullColorPreview="{x:Bind DefaultColorScheme.ForegroundColor.Color, Mode=OneWay}"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="WindowsUIColorOptionalTemplate"
|
||||
x:DataType="local:ArgWrapper">
|
||||
<ListViewItem>
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{StaticResource ArgumentNameWidth}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{x:Bind Name}"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
<local:NullableColorPicker x:Uid="Actions_NullableColorPicker"
|
||||
CurrentColor="{x:Bind UnboxWindowsUIColorOptional(Value), Mode=TwoWay, BindBack=WindowsUIColorBindBack}"
|
||||
ColorSchemeVM="{x:Bind DefaultColorScheme, Mode=OneWay}"
|
||||
NullColorPreview="{x:Bind DefaultColorScheme.ForegroundColor.Color, Mode=OneWay}"
|
||||
Grid.Column="1"/>
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<local:ArgsTemplateSelectors x:Key="ArgsTemplateSelector"
|
||||
NoArgTemplate="{StaticResource NoArgTemplate}"
|
||||
Int32Template="{StaticResource Int32Template}"
|
||||
Int32OptionalTemplate="{StaticResource Int32OptionalTemplate}"
|
||||
UInt32Template="{StaticResource UInt32Template}"
|
||||
UInt32OptionalTemplate="{StaticResource UInt32OptionalTemplate}"
|
||||
UInt64Template="{StaticResource UInt64Template}"
|
||||
FloatTemplate="{StaticResource FloatTemplate}"
|
||||
StringTemplate="{StaticResource StringTemplate}"
|
||||
GuidTemplate="{StaticResource GuidTemplate}"
|
||||
BoolOptionalTemplate="{StaticResource BoolOptionalTemplate}"
|
||||
EnumTemplate="{StaticResource EnumTemplate}"
|
||||
FlagTemplate="{StaticResource FlagTemplate}"
|
||||
TerminalCoreColorOptionalTemplate="{StaticResource TerminalCoreColorOptionalTemplate}"
|
||||
WindowsUIColorOptionalTemplate="{StaticResource WindowsUIColorOptionalTemplate}" />
|
||||
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<Border MaxWidth="{StaticResource StandardControlMaxWidth}">
|
||||
<Grid MaxWidth="600"
|
||||
HorizontalAlignment="Left"
|
||||
RowSpacing="8"
|
||||
ColumnSpacing="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="Actions_Name"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"/>
|
||||
<TextBox Text="{x:Bind ViewModel.Name, Mode=TwoWay}"
|
||||
PlaceholderText="{x:Bind ViewModel.DisplayName, Mode=OneWay}"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"/>
|
||||
<TextBlock x:Uid="Actions_ShortcutAction"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"/>
|
||||
<ComboBox VerticalAlignment="Center"
|
||||
ItemsSource="{x:Bind ViewModel.AvailableShortcutActions, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.ProposedShortcutAction, Mode=TwoWay}"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"/>
|
||||
<TextBlock x:Uid="Actions_Arguments"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"
|
||||
Grid.Row="2"
|
||||
Visibility="{x:Bind ViewModel.ActionArgsVM.HasArgs, Mode=OneWay}"/>
|
||||
<ListView ItemsSource="{x:Bind ViewModel.ActionArgsVM.ArgValues, Mode=OneWay}"
|
||||
ChoosingItemContainer="_choosingItemContainer"
|
||||
SelectionMode="None"
|
||||
AllowDrop="False"
|
||||
CanDragItems="False"
|
||||
CanReorderItems="False"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="-28,0,0,0">
|
||||
</ListView>
|
||||
<TextBlock x:Uid="Actions_Keybindings"
|
||||
VerticalAlignment="Center"
|
||||
Grid.Column="0"
|
||||
Grid.Row="3"/>
|
||||
<ListView ItemTemplate="{StaticResource KeyChordTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.KeyChordViewModelList, Mode=OneWay}"
|
||||
SelectionMode="None"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1">
|
||||
<ListView.Header>
|
||||
<Button Click="{x:Bind ViewModel.AddKeybinding_Click}">
|
||||
<TextBlock x:Uid="Actions_AddKeyChord" />
|
||||
</Button>
|
||||
</ListView.Header>
|
||||
</ListView>
|
||||
<Button Style="{StaticResource DeleteButtonStyle}"
|
||||
IsEnabled="{x:Bind ViewModel.IsUserAction}"
|
||||
Grid.Row="4"
|
||||
Grid.Column="0">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon FontSize="{StaticResource StandardIconSize}"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="Actions_DeleteButton2"
|
||||
Style="{StaticResource IconButtonTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="Actions_CommandDeleteConfirmationMessage"
|
||||
Style="{StaticResource CustomFlyoutTextStyle}" />
|
||||
<Button x:Uid="Actions_CommandDeleteConfirmationButton"
|
||||
Click="{x:Bind ViewModel.Delete_Click}" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Page>
|
||||
@@ -17,6 +17,7 @@ Author(s):
|
||||
#pragma once
|
||||
|
||||
#include "EnumEntry.g.h"
|
||||
#include "FlagEntry.g.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
@@ -55,4 +56,41 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, EnumName, PropertyChanged.raise);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::Foundation::IInspectable, EnumValue, PropertyChanged.raise);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct FlagEntryComparator
|
||||
{
|
||||
bool operator()(const Editor::FlagEntry& lhs, const Editor::FlagEntry& rhs) const
|
||||
{
|
||||
return lhs.FlagValue().as<T>() < rhs.FlagValue().as<T>();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct FlagEntryReverseComparator
|
||||
{
|
||||
bool operator()(const Editor::FlagEntry& lhs, const Editor::FlagEntry& rhs) const
|
||||
{
|
||||
return lhs.FlagValue().as<T>() > rhs.FlagValue().as<T>();
|
||||
}
|
||||
};
|
||||
|
||||
struct FlagEntry : FlagEntryT<FlagEntry>
|
||||
{
|
||||
public:
|
||||
FlagEntry(const winrt::hstring flagName, const winrt::Windows::Foundation::IInspectable& flagValue, const bool isSet) :
|
||||
_FlagName{ flagName },
|
||||
_FlagValue{ flagValue },
|
||||
_IsSet{ isSet } {}
|
||||
|
||||
hstring ToString()
|
||||
{
|
||||
return FlagName();
|
||||
}
|
||||
|
||||
til::property_changed_event PropertyChanged;
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, FlagName, PropertyChanged.raise);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::Foundation::IInspectable, FlagValue, PropertyChanged.raise);
|
||||
WINRT_OBSERVABLE_PROPERTY(bool, IsSet, PropertyChanged.raise);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,4 +8,11 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
String EnumName { get; };
|
||||
IInspectable EnumValue { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass FlagEntry : Windows.UI.Xaml.Data.INotifyPropertyChanged, Windows.Foundation.IStringable
|
||||
{
|
||||
String FlagName { get; };
|
||||
IInspectable FlagValue { get; };
|
||||
Boolean IsSet;
|
||||
}
|
||||
}
|
||||
|
||||
509
src/cascadia/TerminalSettingsEditor/Extensions.cpp
Normal file
509
src/cascadia/TerminalSettingsEditor/Extensions.cpp
Normal file
@@ -0,0 +1,509 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "Extensions.h"
|
||||
#include "Extensions.g.cpp"
|
||||
#include "ExtensionPackageViewModel.g.cpp"
|
||||
#include "ExtensionsViewModel.g.cpp"
|
||||
#include "FragmentProfileViewModel.g.cpp"
|
||||
#include "ExtensionPackageTemplateSelector.g.cpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
#include "..\WinRTUtils\inc\Utils.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
static constexpr std::wstring_view ExtensionPageId{ L"page.extensions" };
|
||||
|
||||
Extensions::Extensions()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_extensionPackageIdentifierTemplateSelector = Resources().Lookup(box_value(L"ExtensionPackageIdentifierTemplateSelector")).as<Editor::ExtensionPackageTemplateSelector>();
|
||||
|
||||
Automation::AutomationProperties::SetName(ActiveExtensionsList(), RS_(L"Extensions_ActiveExtensionsHeader/Text"));
|
||||
Automation::AutomationProperties::SetName(ModifiedProfilesList(), RS_(L"Extensions_ModifiedProfilesHeader/Text"));
|
||||
Automation::AutomationProperties::SetName(AddedProfilesList(), RS_(L"Extensions_AddedProfilesHeader/Text"));
|
||||
Automation::AutomationProperties::SetName(AddedColorSchemesList(), RS_(L"Extensions_AddedColorSchemesHeader/Text"));
|
||||
}
|
||||
|
||||
void Extensions::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
_ViewModel = e.Parameter().as<Editor::ExtensionsViewModel>();
|
||||
auto vmImpl = get_self<ExtensionsViewModel>(_ViewModel);
|
||||
vmImpl->ExtensionPackageIdentifierTemplateSelector(_extensionPackageIdentifierTemplateSelector);
|
||||
vmImpl->LazyLoadExtensions();
|
||||
vmImpl->MarkAsVisited();
|
||||
}
|
||||
|
||||
void Extensions::ExtensionNavigator_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/)
|
||||
{
|
||||
const auto extPkgVM = sender.as<Controls::Button>().Tag().as<Editor::ExtensionPackageViewModel>();
|
||||
_ViewModel.CurrentExtensionPackage(extPkgVM);
|
||||
}
|
||||
|
||||
void Extensions::NavigateToProfile_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/)
|
||||
{
|
||||
const auto& profileGuid = sender.as<Controls::Button>().Tag().as<guid>();
|
||||
get_self<ExtensionsViewModel>(_ViewModel)->NavigateToProfile(profileGuid);
|
||||
}
|
||||
|
||||
void Extensions::NavigateToColorScheme_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/)
|
||||
{
|
||||
const auto& schemeVM = sender.as<Controls::Button>().Tag().as<Editor::ColorSchemeViewModel>();
|
||||
get_self<ExtensionsViewModel>(_ViewModel)->NavigateToColorScheme(schemeVM);
|
||||
}
|
||||
|
||||
ExtensionsViewModel::ExtensionsViewModel(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM) :
|
||||
_settings{ settings },
|
||||
_colorSchemesPageVM{ colorSchemesPageVM },
|
||||
_extensionsLoaded{ false }
|
||||
{
|
||||
UpdateSettings(settings, colorSchemesPageVM);
|
||||
|
||||
PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) {
|
||||
const auto viewModelProperty{ args.PropertyName() };
|
||||
|
||||
const bool extensionPackageChanged = viewModelProperty == L"CurrentExtensionPackage";
|
||||
const bool profilesModifiedChanged = viewModelProperty == L"ProfilesModified";
|
||||
const bool profilesAddedChanged = viewModelProperty == L"ProfilesAdded";
|
||||
const bool colorSchmesAddedChanged = viewModelProperty == L"ColorSchemesAdded";
|
||||
if (extensionPackageChanged || (!IsExtensionView() && (profilesModifiedChanged || profilesAddedChanged || colorSchmesAddedChanged)))
|
||||
{
|
||||
// Use these booleans to track which of our observable vectors need to be refreshed.
|
||||
// This prevents a full refresh of the UI when enabling/disabling extensions.
|
||||
// If the CurrentExtensionPackage changed, we want to update all components.
|
||||
// Otherwise, just update the ones that we were notified about.
|
||||
const bool updateProfilesModified = extensionPackageChanged || profilesModifiedChanged;
|
||||
const bool updateProfilesAdded = extensionPackageChanged || profilesAddedChanged;
|
||||
const bool updateColorSchemesAdded = extensionPackageChanged || colorSchmesAddedChanged;
|
||||
_UpdateListViews(updateProfilesModified, updateProfilesAdded, updateColorSchemesAdded);
|
||||
|
||||
if (extensionPackageChanged)
|
||||
{
|
||||
_NotifyChanges(L"IsExtensionView", L"CurrentExtensionPackageIdentifierTemplate");
|
||||
}
|
||||
else if (profilesModifiedChanged)
|
||||
{
|
||||
_NotifyChanges(L"NoProfilesModified");
|
||||
}
|
||||
else if (profilesAddedChanged)
|
||||
{
|
||||
_NotifyChanges(L"NoProfilesAdded");
|
||||
}
|
||||
else if (colorSchmesAddedChanged)
|
||||
{
|
||||
_NotifyChanges(L"NoSchemesAdded");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ExtensionsViewModel::_UpdateListViews(bool updateProfilesModified, bool updateProfilesAdded, bool updateColorSchemesAdded)
|
||||
{
|
||||
// STL vectors to track relevant components for extensions to display in UI
|
||||
std::vector<Editor::FragmentProfileViewModel> profilesModifiedTotal;
|
||||
std::vector<Editor::FragmentProfileViewModel> profilesAddedTotal;
|
||||
std::vector<Editor::FragmentColorSchemeViewModel> colorSchemesAddedTotal;
|
||||
|
||||
// Helper lambda to add the contents of an extension package to the current view.
|
||||
auto addPackageContentsToView = [&](const Editor::ExtensionPackageViewModel& extPkg) {
|
||||
auto extPkgVM = get_self<ExtensionPackageViewModel>(extPkg);
|
||||
for (const auto& ext : extPkgVM->FragmentExtensions())
|
||||
{
|
||||
if (updateProfilesModified)
|
||||
{
|
||||
for (const auto& profile : ext.ProfilesModified())
|
||||
{
|
||||
profilesModifiedTotal.push_back(profile);
|
||||
}
|
||||
}
|
||||
if (updateProfilesAdded)
|
||||
{
|
||||
for (const auto& profile : ext.ProfilesAdded())
|
||||
{
|
||||
profilesAddedTotal.push_back(profile);
|
||||
}
|
||||
}
|
||||
if (updateColorSchemesAdded)
|
||||
{
|
||||
for (const auto& scheme : ext.ColorSchemesAdded())
|
||||
{
|
||||
colorSchemesAddedTotal.push_back(scheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Populate the STL vectors that we want to update
|
||||
if (const auto currentExtensionPackage = CurrentExtensionPackage())
|
||||
{
|
||||
// Update all of the views to reflect the current extension package, if one is selected.
|
||||
addPackageContentsToView(currentExtensionPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only populate the views with components from enabled extensions
|
||||
for (const auto& extPkg : _extensionPackages)
|
||||
{
|
||||
if (extPkg.Enabled())
|
||||
{
|
||||
addPackageContentsToView(extPkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the lists linguistically for nicer presentation.
|
||||
// Update the WinRT lists bound to UI.
|
||||
if (updateProfilesModified)
|
||||
{
|
||||
std::sort(profilesModifiedTotal.begin(), profilesModifiedTotal.end(), FragmentProfileViewModel::SortAscending);
|
||||
_profilesModifiedView = winrt::single_threaded_observable_vector(std::move(profilesModifiedTotal));
|
||||
}
|
||||
if (updateProfilesAdded)
|
||||
{
|
||||
std::sort(profilesAddedTotal.begin(), profilesAddedTotal.end(), FragmentProfileViewModel::SortAscending);
|
||||
_profilesAddedView = winrt::single_threaded_observable_vector(std::move(profilesAddedTotal));
|
||||
}
|
||||
if (updateColorSchemesAdded)
|
||||
{
|
||||
std::sort(colorSchemesAddedTotal.begin(), colorSchemesAddedTotal.end(), FragmentColorSchemeViewModel::SortAscending);
|
||||
_colorSchemesAddedView = winrt::single_threaded_observable_vector(std::move(colorSchemesAddedTotal));
|
||||
}
|
||||
}
|
||||
|
||||
void ExtensionsViewModel::UpdateSettings(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM)
|
||||
{
|
||||
_settings = settings;
|
||||
_colorSchemesPageVM = colorSchemesPageVM;
|
||||
_CurrentExtensionPackage = nullptr;
|
||||
|
||||
// The extension packages may not be loaded yet because we want to wait until we actually navigate to the page to do so.
|
||||
// In that case, omit "updating" them. They'll get the proper references when we lazy load them.
|
||||
if (_extensionPackages)
|
||||
{
|
||||
for (const auto& extPkg : _extensionPackages)
|
||||
{
|
||||
get_self<ExtensionPackageViewModel>(extPkg)->UpdateSettings(_settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtensionsViewModel::LazyLoadExtensions()
|
||||
{
|
||||
if (_extensionsLoaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::vector<Model::ExtensionPackage> extensions = wil::to_vector(_settings.Extensions());
|
||||
|
||||
// these vectors track components all extensions successfully added
|
||||
std::vector<Editor::ExtensionPackageViewModel> extensionPackages;
|
||||
std::vector<Editor::FragmentProfileViewModel> profilesModifiedTotal;
|
||||
std::vector<Editor::FragmentProfileViewModel> profilesAddedTotal;
|
||||
std::vector<Editor::FragmentColorSchemeViewModel> colorSchemesAddedTotal;
|
||||
for (const auto& extPkg : extensions)
|
||||
{
|
||||
auto extPkgVM = winrt::make_self<ExtensionPackageViewModel>(extPkg, _settings);
|
||||
for (const auto& fragExt : extPkg.FragmentsView())
|
||||
{
|
||||
const auto extensionEnabled = GetExtensionState(fragExt.Source(), _settings);
|
||||
|
||||
// these vectors track everything the current extension attempted to bring in
|
||||
std::vector<Editor::FragmentProfileViewModel> currentProfilesModified;
|
||||
std::vector<Editor::FragmentProfileViewModel> currentProfilesAdded;
|
||||
std::vector<Editor::FragmentColorSchemeViewModel> currentColorSchemesAdded;
|
||||
|
||||
if (fragExt.ModifiedProfilesView())
|
||||
{
|
||||
for (const auto&& entry : fragExt.ModifiedProfilesView())
|
||||
{
|
||||
// Ensure entry successfully modifies a profile before creating and registering the object
|
||||
if (const auto& deducedProfile = _settings.FindProfile(entry.ProfileGuid()))
|
||||
{
|
||||
auto vm = winrt::make<FragmentProfileViewModel>(entry, fragExt, deducedProfile);
|
||||
currentProfilesModified.push_back(vm);
|
||||
if (extensionEnabled)
|
||||
{
|
||||
profilesModifiedTotal.push_back(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fragExt.NewProfilesView())
|
||||
{
|
||||
for (const auto&& entry : fragExt.NewProfilesView())
|
||||
{
|
||||
// Ensure entry successfully points to a profile before creating and registering the object.
|
||||
// The profile may have been removed by the user.
|
||||
if (const auto& deducedProfile = _settings.FindProfile(entry.ProfileGuid()))
|
||||
{
|
||||
auto vm = winrt::make<FragmentProfileViewModel>(entry, fragExt, deducedProfile);
|
||||
currentProfilesAdded.push_back(vm);
|
||||
if (extensionEnabled)
|
||||
{
|
||||
profilesAddedTotal.push_back(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fragExt.ColorSchemesView())
|
||||
{
|
||||
for (const auto&& entry : fragExt.ColorSchemesView())
|
||||
{
|
||||
for (const auto& schemeVM : _colorSchemesPageVM.AllColorSchemes())
|
||||
{
|
||||
if (schemeVM.Name() == entry.ColorSchemeName())
|
||||
{
|
||||
auto vm = winrt::make<FragmentColorSchemeViewModel>(entry, fragExt, schemeVM);
|
||||
currentColorSchemesAdded.push_back(vm);
|
||||
if (extensionEnabled)
|
||||
{
|
||||
colorSchemesAddedTotal.push_back(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort the lists linguistically for nicer presentation
|
||||
std::sort(currentProfilesModified.begin(), currentProfilesModified.end(), FragmentProfileViewModel::SortAscending);
|
||||
std::sort(currentProfilesAdded.begin(), currentProfilesAdded.end(), FragmentProfileViewModel::SortAscending);
|
||||
std::sort(currentColorSchemesAdded.begin(), currentColorSchemesAdded.end(), FragmentColorSchemeViewModel::SortAscending);
|
||||
|
||||
extPkgVM->FragmentExtensions().Append(winrt::make<FragmentExtensionViewModel>(fragExt, currentProfilesModified, currentProfilesAdded, currentColorSchemesAdded));
|
||||
extPkgVM->PropertyChanged([&](const IInspectable& sender, const PropertyChangedEventArgs& args) {
|
||||
const auto viewModelProperty{ args.PropertyName() };
|
||||
if (viewModelProperty == L"Enabled")
|
||||
{
|
||||
// If the extension was enabled/disabled,
|
||||
// check if any of its fragments modified profiles, added profiles, or added color schemes.
|
||||
// Only notify what was affected!
|
||||
bool hasModifiedProfiles = false;
|
||||
bool hasAddedProfiles = false;
|
||||
bool hasAddedColorSchemes = false;
|
||||
for (const auto& fragExtVM : sender.as<ExtensionPackageViewModel>()->FragmentExtensions())
|
||||
{
|
||||
const auto profilesModified = fragExtVM.ProfilesModified();
|
||||
const auto profilesAdded = fragExtVM.ProfilesAdded();
|
||||
const auto colorSchemesAdded = fragExtVM.ColorSchemesAdded();
|
||||
hasModifiedProfiles |= profilesModified && profilesModified.Size() > 0;
|
||||
hasAddedProfiles |= profilesAdded && profilesAdded.Size() > 0;
|
||||
hasAddedColorSchemes |= colorSchemesAdded && colorSchemesAdded.Size() > 0;
|
||||
}
|
||||
if (hasModifiedProfiles)
|
||||
{
|
||||
_NotifyChanges(L"ProfilesModified");
|
||||
}
|
||||
if (hasAddedProfiles)
|
||||
{
|
||||
_NotifyChanges(L"ProfilesAdded");
|
||||
}
|
||||
if (hasAddedColorSchemes)
|
||||
{
|
||||
_NotifyChanges(L"ColorSchemesAdded");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
extensionPackages.push_back(*extPkgVM);
|
||||
}
|
||||
|
||||
// sort the lists linguistically for nicer presentation
|
||||
std::sort(extensionPackages.begin(), extensionPackages.end(), ExtensionPackageViewModel::SortAscending);
|
||||
std::sort(profilesModifiedTotal.begin(), profilesModifiedTotal.end(), FragmentProfileViewModel::SortAscending);
|
||||
std::sort(profilesAddedTotal.begin(), profilesAddedTotal.end(), FragmentProfileViewModel::SortAscending);
|
||||
std::sort(colorSchemesAddedTotal.begin(), colorSchemesAddedTotal.end(), FragmentColorSchemeViewModel::SortAscending);
|
||||
|
||||
_extensionPackages = single_threaded_observable_vector<Editor::ExtensionPackageViewModel>(std::move(extensionPackages));
|
||||
_profilesModifiedView = single_threaded_observable_vector<Editor::FragmentProfileViewModel>(std::move(profilesModifiedTotal));
|
||||
_profilesAddedView = single_threaded_observable_vector<Editor::FragmentProfileViewModel>(std::move(profilesAddedTotal));
|
||||
_colorSchemesAddedView = single_threaded_observable_vector<Editor::FragmentColorSchemeViewModel>(std::move(colorSchemesAddedTotal));
|
||||
_extensionsLoaded = true;
|
||||
}
|
||||
|
||||
Windows::UI::Xaml::DataTemplate ExtensionsViewModel::CurrentExtensionPackageIdentifierTemplate() const
|
||||
{
|
||||
return _ExtensionPackageIdentifierTemplateSelector.SelectTemplate(CurrentExtensionPackage());
|
||||
}
|
||||
|
||||
bool ExtensionsViewModel::DisplayBadge() const noexcept
|
||||
{
|
||||
return !Model::ApplicationState::SharedInstance().BadgeDismissed(ExtensionPageId);
|
||||
}
|
||||
|
||||
// Returns true if the extension is enabled, false otherwise
|
||||
bool ExtensionsViewModel::GetExtensionState(hstring extensionSource, const Model::CascadiaSettings& settings)
|
||||
{
|
||||
if (const auto& disabledExtensions = settings.GlobalSettings().DisabledProfileSources())
|
||||
{
|
||||
uint32_t ignored;
|
||||
return !disabledExtensions.IndexOf(extensionSource, ignored);
|
||||
}
|
||||
// "disabledProfileSources" not defined --> all extensions are enabled
|
||||
return true;
|
||||
}
|
||||
|
||||
// Enable/Disable an extension
|
||||
void ExtensionsViewModel::SetExtensionState(hstring extensionSource, const Model::CascadiaSettings& settings, bool enableExt)
|
||||
{
|
||||
// get the current status of the extension
|
||||
uint32_t idx;
|
||||
bool currentlyEnabled = true;
|
||||
const auto& disabledExtensions = settings.GlobalSettings().DisabledProfileSources();
|
||||
if (disabledExtensions)
|
||||
{
|
||||
currentlyEnabled = !disabledExtensions.IndexOf(extensionSource, idx);
|
||||
}
|
||||
|
||||
// current status mismatches the desired status,
|
||||
// update the list of disabled extensions
|
||||
if (currentlyEnabled != enableExt)
|
||||
{
|
||||
// If we're disabling an extension and we don't have "disabledProfileSources" defined,
|
||||
// create it in the model directly
|
||||
if (!disabledExtensions && !enableExt)
|
||||
{
|
||||
std::vector<hstring> disabledProfileSources{ extensionSource };
|
||||
settings.GlobalSettings().DisabledProfileSources(single_threaded_vector<hstring>(std::move(disabledProfileSources)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the list of disabled extensions
|
||||
if (enableExt)
|
||||
{
|
||||
disabledExtensions.RemoveAt(idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
disabledExtensions.Append(extensionSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Thickness Extensions::CalculateMargin(bool hidden)
|
||||
{
|
||||
return ThicknessHelper::FromLengths(/*left*/ 0,
|
||||
/*top*/ hidden ? 0 : 20,
|
||||
/*right*/ 0,
|
||||
/*bottom*/ 0);
|
||||
}
|
||||
|
||||
void ExtensionsViewModel::NavigateToProfile(const guid profileGuid)
|
||||
{
|
||||
NavigateToProfileRequested.raise(*this, profileGuid);
|
||||
}
|
||||
|
||||
void ExtensionsViewModel::NavigateToColorScheme(const Editor::ColorSchemeViewModel& schemeVM)
|
||||
{
|
||||
_colorSchemesPageVM.CurrentScheme(schemeVM);
|
||||
NavigateToColorSchemeRequested.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
void ExtensionsViewModel::MarkAsVisited()
|
||||
{
|
||||
Model::ApplicationState::SharedInstance().DismissBadge(ExtensionPageId);
|
||||
_NotifyChanges(L"DisplayBadge");
|
||||
}
|
||||
|
||||
bool ExtensionPackageViewModel::SortAscending(const Editor::ExtensionPackageViewModel& lhs, const Editor::ExtensionPackageViewModel& rhs)
|
||||
{
|
||||
auto getKey = [&](const Editor::ExtensionPackageViewModel& pkgVM) {
|
||||
const auto pkg = pkgVM.Package();
|
||||
const auto displayName = pkg.DisplayName();
|
||||
return displayName.empty() ? pkg.Source() : displayName;
|
||||
};
|
||||
|
||||
return til::compare_linguistic_insensitive(getKey(lhs), getKey(rhs)) < 0;
|
||||
}
|
||||
|
||||
void ExtensionPackageViewModel::UpdateSettings(const Model::CascadiaSettings& settings)
|
||||
{
|
||||
const auto oldEnabled = Enabled();
|
||||
_settings = settings;
|
||||
if (oldEnabled != Enabled())
|
||||
{
|
||||
// The enabled state of the extension has changed, notify the UI
|
||||
_NotifyChanges(L"Enabled");
|
||||
}
|
||||
}
|
||||
|
||||
hstring ExtensionPackageViewModel::Scope() const noexcept
|
||||
{
|
||||
return _package.Scope() == Model::FragmentScope::User ? RS_(L"Extensions_ScopeUser") : RS_(L"Extensions_ScopeSystem");
|
||||
}
|
||||
|
||||
bool ExtensionPackageViewModel::Enabled() const
|
||||
{
|
||||
return ExtensionsViewModel::GetExtensionState(_package.Source(), _settings);
|
||||
}
|
||||
|
||||
void ExtensionPackageViewModel::Enabled(bool val)
|
||||
{
|
||||
if (Enabled() != val)
|
||||
{
|
||||
ExtensionsViewModel::SetExtensionState(_package.Source(), _settings, val);
|
||||
_NotifyChanges(L"Enabled");
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the accessible name for the extension package in the following format:
|
||||
// "<DisplayName?>, <Source>"
|
||||
hstring ExtensionPackageViewModel::AccessibleName() const noexcept
|
||||
{
|
||||
hstring name;
|
||||
const auto source = _package.Source();
|
||||
if (const auto displayName = _package.DisplayName(); !displayName.empty())
|
||||
{
|
||||
return hstring{ fmt::format(FMT_COMPILE(L"{}, {}"), displayName, source) };
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
bool FragmentProfileViewModel::SortAscending(const Editor::FragmentProfileViewModel& lhs, const Editor::FragmentProfileViewModel& rhs)
|
||||
{
|
||||
return til::compare_linguistic_insensitive(lhs.Profile().Name(), rhs.Profile().Name()) < 0;
|
||||
}
|
||||
|
||||
hstring FragmentProfileViewModel::AccessibleName() const noexcept
|
||||
{
|
||||
return hstring{ fmt::format(FMT_COMPILE(L"{}, {}"), Profile().Name(), SourceName()) };
|
||||
}
|
||||
|
||||
bool FragmentColorSchemeViewModel::SortAscending(const Editor::FragmentColorSchemeViewModel& lhs, const Editor::FragmentColorSchemeViewModel& rhs)
|
||||
{
|
||||
return til::compare_linguistic_insensitive(lhs.ColorSchemeVM().Name(), rhs.ColorSchemeVM().Name()) < 0;
|
||||
}
|
||||
|
||||
hstring FragmentColorSchemeViewModel::AccessibleName() const noexcept
|
||||
{
|
||||
return hstring{ fmt::format(FMT_COMPILE(L"{}, {}"), ColorSchemeVM().Name(), SourceName()) };
|
||||
}
|
||||
|
||||
DataTemplate ExtensionPackageTemplateSelector::SelectTemplateCore(const IInspectable& item, const DependencyObject& /*container*/)
|
||||
{
|
||||
return SelectTemplateCore(item);
|
||||
}
|
||||
|
||||
DataTemplate ExtensionPackageTemplateSelector::SelectTemplateCore(const IInspectable& item)
|
||||
{
|
||||
if (const auto extPkgVM = item.try_as<Editor::ExtensionPackageViewModel>())
|
||||
{
|
||||
if (!extPkgVM.Package().DisplayName().empty())
|
||||
{
|
||||
return ComplexTemplate();
|
||||
}
|
||||
return DefaultTemplate();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
193
src/cascadia/TerminalSettingsEditor/Extensions.h
Normal file
193
src/cascadia/TerminalSettingsEditor/Extensions.h
Normal file
@@ -0,0 +1,193 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Extensions.g.h"
|
||||
#include "ExtensionsViewModel.g.h"
|
||||
#include "ExtensionPackageViewModel.g.h"
|
||||
#include "FragmentExtensionViewModel.g.h"
|
||||
#include "FragmentProfileViewModel.g.h"
|
||||
#include "FragmentColorSchemeViewModel.g.h"
|
||||
#include "ExtensionPackageTemplateSelector.g.h"
|
||||
#include "ViewModelHelpers.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct Extensions : public HasScrollViewer<Extensions>, ExtensionsT<Extensions>
|
||||
{
|
||||
public:
|
||||
Windows::UI::Xaml::Thickness CalculateMargin(bool hidden);
|
||||
|
||||
Extensions();
|
||||
|
||||
void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
|
||||
|
||||
void ExtensionNavigator_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void NavigateToProfile_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void NavigateToColorScheme_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
|
||||
WINRT_PROPERTY(Editor::ExtensionsViewModel, ViewModel, nullptr);
|
||||
|
||||
private:
|
||||
Editor::ExtensionPackageTemplateSelector _extensionPackageIdentifierTemplateSelector;
|
||||
};
|
||||
|
||||
struct ExtensionsViewModel : ExtensionsViewModelT<ExtensionsViewModel>, ViewModelHelper<ExtensionsViewModel>
|
||||
{
|
||||
public:
|
||||
ExtensionsViewModel(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM);
|
||||
|
||||
// Properties
|
||||
Windows::UI::Xaml::DataTemplate CurrentExtensionPackageIdentifierTemplate() const;
|
||||
bool IsExtensionView() const noexcept { return _CurrentExtensionPackage != nullptr; }
|
||||
bool NoExtensionPackages() const noexcept { return _extensionPackages.Size() == 0; }
|
||||
bool NoProfilesModified() const noexcept { return _profilesModifiedView.Size() == 0; }
|
||||
bool NoProfilesAdded() const noexcept { return _profilesAddedView.Size() == 0; }
|
||||
bool NoSchemesAdded() const noexcept { return _colorSchemesAddedView.Size() == 0; }
|
||||
bool DisplayBadge() const noexcept;
|
||||
|
||||
// Views
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::ExtensionPackageViewModel> ExtensionPackages() const noexcept { return _extensionPackages; }
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::FragmentProfileViewModel> ProfilesModified() const noexcept { return _profilesModifiedView; }
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::FragmentProfileViewModel> ProfilesAdded() const noexcept { return _profilesAddedView; }
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::FragmentColorSchemeViewModel> ColorSchemesAdded() const noexcept { return _colorSchemesAddedView; }
|
||||
|
||||
// Methods
|
||||
void LazyLoadExtensions();
|
||||
void UpdateSettings(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM);
|
||||
void NavigateToProfile(const guid profileGuid);
|
||||
void NavigateToColorScheme(const Editor::ColorSchemeViewModel& schemeVM);
|
||||
void MarkAsVisited();
|
||||
|
||||
static bool GetExtensionState(hstring extensionSource, const Model::CascadiaSettings& settings);
|
||||
static void SetExtensionState(hstring extensionSource, const Model::CascadiaSettings& settings, bool enableExt);
|
||||
|
||||
til::typed_event<IInspectable, guid> NavigateToProfileRequested;
|
||||
til::typed_event<IInspectable, Editor::ColorSchemeViewModel> NavigateToColorSchemeRequested;
|
||||
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Editor::ExtensionPackageViewModel, CurrentExtensionPackage, nullptr);
|
||||
WINRT_PROPERTY(Editor::ExtensionPackageTemplateSelector, ExtensionPackageIdentifierTemplateSelector, nullptr);
|
||||
|
||||
private:
|
||||
Model::CascadiaSettings _settings;
|
||||
Editor::ColorSchemesPageViewModel _colorSchemesPageVM;
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::ExtensionPackageViewModel> _extensionPackages;
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::FragmentProfileViewModel> _profilesModifiedView;
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::FragmentProfileViewModel> _profilesAddedView;
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::FragmentColorSchemeViewModel> _colorSchemesAddedView;
|
||||
bool _extensionsLoaded;
|
||||
|
||||
void _UpdateListViews(bool updateProfilesModified, bool updateProfilesAdded, bool updateColorSchemesAdded);
|
||||
};
|
||||
|
||||
struct ExtensionPackageViewModel : ExtensionPackageViewModelT<ExtensionPackageViewModel>, ViewModelHelper<ExtensionPackageViewModel>
|
||||
{
|
||||
public:
|
||||
ExtensionPackageViewModel(const Model::ExtensionPackage& pkg, const Model::CascadiaSettings& settings) :
|
||||
_package{ pkg },
|
||||
_settings{ settings },
|
||||
_fragmentExtensions{ single_threaded_observable_vector<Editor::FragmentExtensionViewModel>() } {}
|
||||
|
||||
static bool SortAscending(const Editor::ExtensionPackageViewModel& lhs, const Editor::ExtensionPackageViewModel& rhs);
|
||||
|
||||
void UpdateSettings(const Model::CascadiaSettings& settings);
|
||||
|
||||
Model::ExtensionPackage Package() const noexcept { return _package; }
|
||||
hstring Scope() const noexcept;
|
||||
bool Enabled() const;
|
||||
void Enabled(bool val);
|
||||
hstring AccessibleName() const noexcept;
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::FragmentExtensionViewModel> FragmentExtensions() { return _fragmentExtensions; }
|
||||
|
||||
private:
|
||||
Model::ExtensionPackage _package;
|
||||
Model::CascadiaSettings _settings;
|
||||
Windows::Foundation::Collections::IObservableVector<Editor::FragmentExtensionViewModel> _fragmentExtensions;
|
||||
};
|
||||
|
||||
struct FragmentExtensionViewModel : FragmentExtensionViewModelT<FragmentExtensionViewModel>, ViewModelHelper<FragmentExtensionViewModel>
|
||||
{
|
||||
public:
|
||||
FragmentExtensionViewModel(const Model::FragmentSettings& fragment,
|
||||
std::vector<FragmentProfileViewModel>& profilesModified,
|
||||
std::vector<FragmentProfileViewModel>& profilesAdded,
|
||||
std::vector<FragmentColorSchemeViewModel>& colorSchemesAdded) :
|
||||
_fragment{ fragment },
|
||||
_profilesModified{ single_threaded_vector(std::move(profilesModified)) },
|
||||
_profilesAdded{ single_threaded_vector(std::move(profilesAdded)) },
|
||||
_colorSchemesAdded{ single_threaded_vector(std::move(colorSchemesAdded)) } {}
|
||||
|
||||
Model::FragmentSettings Fragment() const noexcept { return _fragment; }
|
||||
Windows::Foundation::Collections::IVectorView<FragmentProfileViewModel> ProfilesModified() const noexcept { return _profilesModified.GetView(); }
|
||||
Windows::Foundation::Collections::IVectorView<FragmentProfileViewModel> ProfilesAdded() const noexcept { return _profilesAdded.GetView(); }
|
||||
Windows::Foundation::Collections::IVectorView<FragmentColorSchemeViewModel> ColorSchemesAdded() const noexcept { return _colorSchemesAdded.GetView(); }
|
||||
|
||||
private:
|
||||
Model::FragmentSettings _fragment;
|
||||
Windows::Foundation::Collections::IVector<FragmentProfileViewModel> _profilesModified;
|
||||
Windows::Foundation::Collections::IVector<FragmentProfileViewModel> _profilesAdded;
|
||||
Windows::Foundation::Collections::IVector<FragmentColorSchemeViewModel> _colorSchemesAdded;
|
||||
};
|
||||
|
||||
struct FragmentProfileViewModel : FragmentProfileViewModelT<FragmentProfileViewModel>, ViewModelHelper<FragmentProfileViewModel>
|
||||
{
|
||||
public:
|
||||
FragmentProfileViewModel(const Model::FragmentProfileEntry& entry, const Model::FragmentSettings& fragment, const Model::Profile& deducedProfile) :
|
||||
_entry{ entry },
|
||||
_fragment{ fragment },
|
||||
_deducedProfile{ deducedProfile } {}
|
||||
|
||||
static bool SortAscending(const Editor::FragmentProfileViewModel& lhs, const Editor::FragmentProfileViewModel& rhs);
|
||||
|
||||
Model::Profile Profile() const { return _deducedProfile; };
|
||||
hstring SourceName() const { return _fragment.Source(); }
|
||||
hstring Json() const { return _entry.Json(); }
|
||||
hstring AccessibleName() const noexcept;
|
||||
|
||||
private:
|
||||
Model::FragmentProfileEntry _entry;
|
||||
Model::FragmentSettings _fragment;
|
||||
Model::Profile _deducedProfile;
|
||||
};
|
||||
|
||||
struct FragmentColorSchemeViewModel : FragmentColorSchemeViewModelT<FragmentColorSchemeViewModel>, ViewModelHelper<FragmentColorSchemeViewModel>
|
||||
{
|
||||
public:
|
||||
FragmentColorSchemeViewModel(const Model::FragmentColorSchemeEntry& entry, const Model::FragmentSettings& fragment, const Editor::ColorSchemeViewModel& deducedSchemeVM) :
|
||||
_entry{ entry },
|
||||
_fragment{ fragment },
|
||||
_deducedSchemeVM{ deducedSchemeVM } {}
|
||||
|
||||
static bool SortAscending(const Editor::FragmentColorSchemeViewModel& lhs, const Editor::FragmentColorSchemeViewModel& rhs);
|
||||
|
||||
Editor::ColorSchemeViewModel ColorSchemeVM() const { return _deducedSchemeVM; };
|
||||
hstring SourceName() const { return _fragment.Source(); }
|
||||
hstring Json() const { return _entry.Json(); }
|
||||
hstring AccessibleName() const noexcept;
|
||||
|
||||
private:
|
||||
Model::FragmentColorSchemeEntry _entry;
|
||||
Model::FragmentSettings _fragment;
|
||||
Editor::ColorSchemeViewModel _deducedSchemeVM;
|
||||
};
|
||||
|
||||
struct ExtensionPackageTemplateSelector : public ExtensionPackageTemplateSelectorT<ExtensionPackageTemplateSelector>
|
||||
{
|
||||
public:
|
||||
ExtensionPackageTemplateSelector() = 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);
|
||||
|
||||
WINRT_PROPERTY(Windows::UI::Xaml::DataTemplate, DefaultTemplate, nullptr);
|
||||
WINRT_PROPERTY(Windows::UI::Xaml::DataTemplate, ComplexTemplate, nullptr);
|
||||
};
|
||||
};
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(Extensions);
|
||||
BASIC_FACTORY(ExtensionPackageTemplateSelector);
|
||||
}
|
||||
81
src/cascadia/TerminalSettingsEditor/Extensions.idl
Normal file
81
src/cascadia/TerminalSettingsEditor/Extensions.idl
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "ColorSchemesPageViewModel.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
[default_interface] runtimeclass Extensions : Windows.UI.Xaml.Controls.Page
|
||||
{
|
||||
Extensions();
|
||||
ExtensionsViewModel ViewModel { get; };
|
||||
|
||||
Windows.UI.Xaml.Thickness CalculateMargin(Boolean hidden);
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass ExtensionsViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
// Properties
|
||||
ExtensionPackageViewModel CurrentExtensionPackage;
|
||||
Windows.UI.Xaml.DataTemplate CurrentExtensionPackageIdentifierTemplate { get; };
|
||||
Boolean IsExtensionView { get; };
|
||||
Boolean NoExtensionPackages { get; };
|
||||
Boolean NoProfilesModified { get; };
|
||||
Boolean NoProfilesAdded { get; };
|
||||
Boolean NoSchemesAdded { get; };
|
||||
Boolean DisplayBadge { get; };
|
||||
|
||||
// Views
|
||||
IVector<ExtensionPackageViewModel> ExtensionPackages { get; };
|
||||
IObservableVector<FragmentProfileViewModel> ProfilesModified { get; };
|
||||
IObservableVector<FragmentProfileViewModel> ProfilesAdded { get; };
|
||||
IObservableVector<FragmentColorSchemeViewModel> ColorSchemesAdded { get; };
|
||||
|
||||
// Methods
|
||||
void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings, ColorSchemesPageViewModel colorSchemesPageVM);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Guid> NavigateToProfileRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, ColorSchemeViewModel> NavigateToColorSchemeRequested;
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass ExtensionPackageViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
Microsoft.Terminal.Settings.Model.ExtensionPackage Package { get; };
|
||||
Boolean Enabled;
|
||||
String Scope { get; };
|
||||
String AccessibleName { get; };
|
||||
IVector<FragmentExtensionViewModel> FragmentExtensions { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass FragmentExtensionViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
Microsoft.Terminal.Settings.Model.FragmentSettings Fragment { get; };
|
||||
IVectorView<FragmentProfileViewModel> ProfilesModified { get; };
|
||||
IVectorView<FragmentProfileViewModel> ProfilesAdded { get; };
|
||||
IVectorView<FragmentColorSchemeViewModel> ColorSchemesAdded { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass FragmentProfileViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
Microsoft.Terminal.Settings.Model.Profile Profile { get; };
|
||||
String SourceName { get; };
|
||||
String Json { get; };
|
||||
String AccessibleName { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass FragmentColorSchemeViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
ColorSchemeViewModel ColorSchemeVM { get; };
|
||||
String SourceName { get; };
|
||||
String Json { get; };
|
||||
String AccessibleName { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass ExtensionPackageTemplateSelector : Windows.UI.Xaml.Controls.DataTemplateSelector
|
||||
{
|
||||
ExtensionPackageTemplateSelector();
|
||||
|
||||
Windows.UI.Xaml.DataTemplate DefaultTemplate;
|
||||
Windows.UI.Xaml.DataTemplate ComplexTemplate;
|
||||
}
|
||||
}
|
||||
497
src/cascadia/TerminalSettingsEditor/Extensions.xaml
Normal file
497
src/cascadia/TerminalSettingsEditor/Extensions.xaml
Normal file
@@ -0,0 +1,497 @@
|
||||
<!--
|
||||
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information.
|
||||
-->
|
||||
<Page x:Class="Microsoft.Terminal.Settings.Editor.Extensions"
|
||||
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">
|
||||
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="CommonResources.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style x:Key="ItalicDisclaimerStyle"
|
||||
BasedOn="{StaticResource DisclaimerStyle}"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="FontStyle" Value="Italic" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="CodeBlockStyle"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="Cascadia Mono, Consolas" />
|
||||
<Setter Property="IsTextSelectionEnabled" Value="True" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="CodeBlockScrollViewerStyle"
|
||||
TargetType="ScrollViewer">
|
||||
<Setter Property="AutomationProperties.AccessibilityView" Value="Raw" />
|
||||
<Setter Property="HorizontalScrollMode" Value="Auto" />
|
||||
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
|
||||
<Setter Property="VerticalScrollMode" Value="Disabled" />
|
||||
<Setter Property="VerticalScrollBarVisibility" Value="Disabled" />
|
||||
</Style>
|
||||
|
||||
<local:ExtensionPackageTemplateSelector x:Key="ExtensionPackageIdentifierTemplateSelector"
|
||||
ComplexTemplate="{StaticResource ComplexExtensionIdentifierTemplate}"
|
||||
DefaultTemplate="{StaticResource DefaultExtensionIdentifierTemplate}" />
|
||||
|
||||
<DataTemplate x:Key="DefaultExtensionIdentifierTemplate"
|
||||
x:DataType="local:ExtensionPackageViewModel">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<FontIcon Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
FontSize="32"
|
||||
Glyph="" />
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Package.Source}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="ComplexExtensionIdentifierTemplate"
|
||||
x:DataType="local:ExtensionPackageViewModel">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<IconSourceElement Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="0"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Margin="0,0,8,0"
|
||||
IconSource="{x:Bind mtu:IconPathConverter.IconSourceWUX(Package.Icon)}" />
|
||||
<TextBlock Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Text="{x:Bind Package.DisplayName}" />
|
||||
<TextBlock Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource SettingsPageItemDescriptionStyle}"
|
||||
Text="{x:Bind Package.Source}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<local:ExtensionPackageTemplateSelector x:Key="ExtensionPackageNavigatorTemplateSelector"
|
||||
ComplexTemplate="{StaticResource ComplexExtensionNavigatorTemplate}"
|
||||
DefaultTemplate="{StaticResource DefaultExtensionNavigatorTemplate}" />
|
||||
|
||||
<DataTemplate x:Key="DefaultExtensionNavigatorTemplate"
|
||||
x:DataType="local:ExtensionPackageViewModel">
|
||||
<Button AutomationProperties.Name="{x:Bind AccessibleName}"
|
||||
Click="ExtensionNavigator_Click"
|
||||
Style="{StaticResource NavigatorButtonStyle}"
|
||||
Tag="{x:Bind}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ContentPresenter Content="{x:Bind}"
|
||||
ContentTemplate="{StaticResource DefaultExtensionIdentifierTemplate}" />
|
||||
|
||||
<ToggleSwitch Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="{x:Bind AccessibleName}"
|
||||
IsOn="{x:Bind Enabled, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
|
||||
</Grid>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="ComplexExtensionNavigatorTemplate"
|
||||
x:DataType="local:ExtensionPackageViewModel">
|
||||
<Button AutomationProperties.Name="{x:Bind AccessibleName}"
|
||||
Click="ExtensionNavigator_Click"
|
||||
Style="{StaticResource NavigatorButtonStyle}"
|
||||
Tag="{x:Bind}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ContentPresenter Content="{x:Bind}"
|
||||
ContentTemplate="{StaticResource ComplexExtensionIdentifierTemplate}" />
|
||||
|
||||
<ToggleSwitch Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="{x:Bind AccessibleName}"
|
||||
IsOn="{x:Bind Enabled, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="FragmentProfileViewModelTemplate"
|
||||
x:DataType="local:FragmentProfileViewModel">
|
||||
<muxc:Expander AutomationProperties.Name="{x:Bind AccessibleName}"
|
||||
Style="{StaticResource ExpanderStyle}">
|
||||
<muxc:Expander.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0"
|
||||
Orientation="Horizontal">
|
||||
<IconSourceElement Width="16"
|
||||
Height="16"
|
||||
Margin="0,0,8,0"
|
||||
IconSource="{x:Bind mtu:IconPathConverter.IconSourceWUX(Profile.EvaluatedIcon), Mode=OneWay}" />
|
||||
|
||||
<TextBlock Text="{x:Bind Profile.Name, Mode=OneWay}" />
|
||||
|
||||
<Button x:Name="NavigateToProfileButton"
|
||||
x:Uid="Extensions_NavigateToProfileButton"
|
||||
Click="NavigateToProfile_Click"
|
||||
Style="{StaticResource SettingContainerResetButtonStyle}"
|
||||
Tag="{x:Bind Profile.Guid}"
|
||||
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(Profile.Deleted)}">
|
||||
<FontIcon Glyph=""
|
||||
Style="{StaticResource SettingContainerFontIconStyle}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Style="{StaticResource SettingContainerCurrentValueTextBlockStyle}"
|
||||
Text="{x:Bind SourceName}" />
|
||||
</Grid>
|
||||
</muxc:Expander.Header>
|
||||
<muxc:Expander.Content>
|
||||
<ScrollViewer Style="{StaticResource CodeBlockScrollViewerStyle}">
|
||||
<TextBlock Style="{StaticResource CodeBlockStyle}"
|
||||
Text="{x:Bind Json, Mode=OneWay}" />
|
||||
</ScrollViewer>
|
||||
</muxc:Expander.Content>
|
||||
</muxc:Expander>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- This styling matches that of ExpanderSettingContainerStyle for consistency -->
|
||||
<Style x:Key="ExpanderStyle"
|
||||
TargetType="muxc:Expander">
|
||||
<Setter Property="MaxWidth" Value="1000" />
|
||||
<Setter Property="MinHeight" Value="64" />
|
||||
<Setter Property="Margin" Value="0,4,0,0" />
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="JsonTemplate"
|
||||
x:DataType="local:FragmentExtensionViewModel">
|
||||
<muxc:Expander Header="{x:Bind Fragment.Filename}"
|
||||
Style="{StaticResource ExpanderStyle}">
|
||||
<ScrollViewer Style="{StaticResource CodeBlockScrollViewerStyle}">
|
||||
<TextBlock Style="{StaticResource CodeBlockStyle}"
|
||||
Text="{x:Bind Fragment.Json}" />
|
||||
</ScrollViewer>
|
||||
</muxc:Expander>
|
||||
</DataTemplate>
|
||||
|
||||
<!--
|
||||
Copied over from Appearances.xaml. We're unable to add the DataTemplate to CommonResources.xaml
|
||||
because it needs a code-behind class to use {x:Bind}
|
||||
-->
|
||||
<DataTemplate x:Key="ColorChipTemplate"
|
||||
x:DataType="local:ColorTableEntry">
|
||||
<Border Width="8"
|
||||
Height="8"
|
||||
Background="{x:Bind mtu:Converters.ColorToBrush(Color)}"
|
||||
CornerRadius="1" />
|
||||
</DataTemplate>
|
||||
|
||||
<!--
|
||||
Copied over from Appearances.xaml. We're unable to add the DataTemplate to CommonResources.xaml
|
||||
because it needs a code-behind class to use {x:Bind}
|
||||
-->
|
||||
<DataTemplate x:Key="ColorSchemeVMTemplate"
|
||||
x:DataType="local:ColorSchemeViewModel">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Grid Grid.Column="0"
|
||||
Padding="8"
|
||||
VerticalAlignment="Center"
|
||||
Background="{x:Bind mtu:Converters.ColorToBrush(BackgroundColor.Color), Mode=OneWay}"
|
||||
ColumnSpacing="1"
|
||||
CornerRadius="2"
|
||||
RowSpacing="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ContentControl Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Content="{x:Bind ColorEntryAt(0), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Content="{x:Bind ColorEntryAt(1), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Content="{x:Bind ColorEntryAt(2), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="0"
|
||||
Grid.Column="3"
|
||||
Content="{x:Bind ColorEntryAt(3), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="0"
|
||||
Grid.Column="4"
|
||||
Content="{x:Bind ColorEntryAt(4), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="0"
|
||||
Grid.Column="5"
|
||||
Content="{x:Bind ColorEntryAt(5), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="0"
|
||||
Grid.Column="6"
|
||||
Content="{x:Bind ColorEntryAt(6), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="0"
|
||||
Grid.Column="7"
|
||||
Content="{x:Bind ColorEntryAt(7), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Content="{x:Bind ColorEntryAt(8), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Content="{x:Bind ColorEntryAt(9), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Content="{x:Bind ColorEntryAt(10), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="1"
|
||||
Grid.Column="3"
|
||||
Content="{x:Bind ColorEntryAt(11), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="1"
|
||||
Grid.Column="4"
|
||||
Content="{x:Bind ColorEntryAt(12), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="1"
|
||||
Grid.Column="5"
|
||||
Content="{x:Bind ColorEntryAt(13), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="1"
|
||||
Grid.Column="6"
|
||||
Content="{x:Bind ColorEntryAt(14), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<ContentControl Grid.Row="1"
|
||||
Grid.Column="7"
|
||||
Content="{x:Bind ColorEntryAt(15), Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorChipTemplate}"
|
||||
IsTabStop="False" />
|
||||
<TextBlock Grid.RowSpan="2"
|
||||
Grid.Column="8"
|
||||
MaxWidth="192"
|
||||
Margin="4,0,4,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontFamily="Cascadia Code"
|
||||
Foreground="{x:Bind mtu:Converters.ColorToBrush(ForegroundColor.Color), Mode=OneWay}"
|
||||
Text="{x:Bind Name, Mode=OneWay}"
|
||||
TextTrimming="WordEllipsis" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="FragmentColorSchemeViewModelTemplate"
|
||||
x:DataType="local:FragmentColorSchemeViewModel">
|
||||
<muxc:Expander AutomationProperties.Name="{x:Bind AccessibleName}"
|
||||
Style="{StaticResource ExpanderStyle}">
|
||||
<muxc:Expander.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0"
|
||||
Orientation="Horizontal">
|
||||
<ContentPresenter Content="{x:Bind ColorSchemeVM, Mode=OneWay}"
|
||||
ContentTemplate="{StaticResource ColorSchemeVMTemplate}" />
|
||||
<Button x:Name="NavigateToColorSchemeButton"
|
||||
x:Uid="Extensions_NavigateToColorSchemeButton"
|
||||
Click="NavigateToColorScheme_Click"
|
||||
Style="{StaticResource SettingContainerResetButtonStyle}"
|
||||
Tag="{x:Bind ColorSchemeVM}">
|
||||
<FontIcon Glyph=""
|
||||
Style="{StaticResource SettingContainerFontIconStyle}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Style="{StaticResource SettingContainerCurrentValueTextBlockStyle}"
|
||||
Text="{x:Bind SourceName}" />
|
||||
</Grid>
|
||||
</muxc:Expander.Header>
|
||||
<muxc:Expander.Content>
|
||||
<ScrollViewer Style="{StaticResource CodeBlockScrollViewerStyle}">
|
||||
<TextBlock Style="{StaticResource CodeBlockStyle}"
|
||||
Text="{x:Bind Json, Mode=OneWay}" />
|
||||
</ScrollViewer>
|
||||
</muxc:Expander.Content>
|
||||
</muxc:Expander>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
|
||||
<!-- [Root View Only] -->
|
||||
<StackPanel MaxWidth="{StaticResource StandardControlMaxWidth}"
|
||||
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(ViewModel.IsExtensionView), Mode=OneWay}">
|
||||
|
||||
<!-- Learn more about fragment extensions -->
|
||||
<HyperlinkButton x:Uid="Extensions_DisclaimerHyperlink"
|
||||
NavigateUri="https://learn.microsoft.com/en-us/windows/terminal/json-fragment-extensions" />
|
||||
|
||||
<!-- Grouping: Active Extensions -->
|
||||
<TextBlock x:Uid="Extensions_ActiveExtensionsHeader"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
<ItemsControl x:Name="ActiveExtensionsList"
|
||||
IsTabStop="False"
|
||||
ItemTemplateSelector="{StaticResource ExtensionPackageNavigatorTemplateSelector}"
|
||||
ItemsSource="{x:Bind ViewModel.ExtensionPackages}"
|
||||
XYFocusKeyboardNavigation="Enabled" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- [Extension View Only] -->
|
||||
<StackPanel MaxWidth="{StaticResource StandardControlMaxWidth}"
|
||||
Visibility="{x:Bind ViewModel.IsExtensionView, Mode=OneWay}">
|
||||
<!-- Extension Status -->
|
||||
<muxc:Expander AutomationProperties.Name="{x:Bind ViewModel.CurrentExtensionPackage.AccessibleName, Mode=OneWay}"
|
||||
IsExpanded="True"
|
||||
Style="{StaticResource ExpanderStyle}">
|
||||
<muxc:Expander.Header>
|
||||
<Grid MinHeight="64">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!--
|
||||
BODGY
|
||||
Theoretically, you could use a ContentTemplateSelector directly. However, that doesn't work.
|
||||
For some reason, we just get the object type's ToString called and the selector gets nullptr as a parameter.
|
||||
Adding the template as a view model property is a workaround.
|
||||
-->
|
||||
<ContentPresenter Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Bind ViewModel.CurrentExtensionPackage, Mode=OneWay}"
|
||||
ContentTemplate="{x:Bind ViewModel.CurrentExtensionPackageIdentifierTemplate, Mode=OneWay}" />
|
||||
|
||||
<ToggleSwitch Grid.Column="1"
|
||||
Margin="0"
|
||||
AutomationProperties.Name="{x:Bind ViewModel.CurrentExtensionPackage.AccessibleName, Mode=OneWay}"
|
||||
IsOn="{x:Bind ViewModel.CurrentExtensionPackage.Enabled, Mode=TwoWay}"
|
||||
Style="{StaticResource ToggleSwitchInExpanderStyle}"
|
||||
Tag="{x:Bind ViewModel.CurrentExtensionPackage.Package.Source, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</muxc:Expander.Header>
|
||||
<muxc:Expander.Content>
|
||||
<StackPanel>
|
||||
<!-- Scope -->
|
||||
<local:SettingContainer x:Uid="Extensions_Scope"
|
||||
Content="{x:Bind ViewModel.CurrentExtensionPackage.Scope, Mode=OneWay}"
|
||||
IsTabStop="False"
|
||||
Style="{StaticResource SettingContainerWithTextContent}" />
|
||||
<!-- JSON -->
|
||||
<ItemsControl IsTabStop="False"
|
||||
ItemTemplate="{StaticResource JsonTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.CurrentExtensionPackage.FragmentExtensions, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</muxc:Expander.Content>
|
||||
</muxc:Expander>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Grouping: Modified Profiles -->
|
||||
<StackPanel Margin="{x:Bind CalculateMargin(ViewModel.NoProfilesModified), Mode=OneWay}"
|
||||
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(ViewModel.NoProfilesModified), Mode=OneWay}">
|
||||
<TextBlock x:Uid="Extensions_ModifiedProfilesHeader"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
<ItemsControl x:Name="ModifiedProfilesList"
|
||||
IsTabStop="False"
|
||||
ItemTemplate="{StaticResource FragmentProfileViewModelTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.ProfilesModified, Mode=OneWay}"
|
||||
XYFocusKeyboardNavigation="Enabled" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Grouping: Added Profiles -->
|
||||
<StackPanel Margin="{x:Bind CalculateMargin(ViewModel.NoProfilesAdded), Mode=OneWay}"
|
||||
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(ViewModel.NoProfilesAdded), Mode=OneWay}">
|
||||
<TextBlock x:Uid="Extensions_AddedProfilesHeader"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
<ItemsControl x:Name="AddedProfilesList"
|
||||
IsTabStop="False"
|
||||
ItemTemplate="{StaticResource FragmentProfileViewModelTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.ProfilesAdded, Mode=OneWay}"
|
||||
XYFocusKeyboardNavigation="Enabled" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Grouping: Added Color Schemes -->
|
||||
<StackPanel Margin="{x:Bind CalculateMargin(ViewModel.NoProfilesAdded), Mode=OneWay}"
|
||||
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(ViewModel.NoSchemesAdded), Mode=OneWay}">
|
||||
<TextBlock x:Uid="Extensions_AddedColorSchemesHeader"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
<ItemsControl x:Name="AddedColorSchemesList"
|
||||
IsTabStop="False"
|
||||
ItemTemplate="{StaticResource FragmentColorSchemeViewModelTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.ColorSchemesAdded, Mode=OneWay}"
|
||||
XYFocusKeyboardNavigation="Enabled" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Page>
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "Compatibility.h"
|
||||
#include "Rendering.h"
|
||||
#include "RenderingViewModel.h"
|
||||
#include "Extensions.h"
|
||||
#include "Actions.h"
|
||||
#include "NewActions.h"
|
||||
#include "ProfileViewModel.h"
|
||||
#include "GlobalAppearance.h"
|
||||
#include "GlobalAppearanceViewModel.h"
|
||||
@@ -44,7 +46,9 @@ 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 newActionsTag{ L"NewActions_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" };
|
||||
@@ -112,6 +116,50 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
});
|
||||
|
||||
_actionsVM = winrt::make<ActionsViewModel>(_settingsClone);
|
||||
_actionsViewModelChangedRevoker = _actionsVM.PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& args) {
|
||||
const auto settingName{ args.PropertyName() };
|
||||
if (settingName == L"CurrentPage")
|
||||
{
|
||||
if (_actionsVM.CurrentPage() == ActionsSubPage::Edit)
|
||||
{
|
||||
contentFrame().Navigate(xaml_typename<Editor::EditAction>(), _actionsVM.CurrentCommand());
|
||||
const auto crumb = winrt::make<Breadcrumb>(box_value(newActionsTag), L"Edit Action...", BreadcrumbSubPage::Actions_Edit);
|
||||
_breadcrumbs.Append(crumb);
|
||||
}
|
||||
else if (_actionsVM.CurrentPage() == ActionsSubPage::Base)
|
||||
{
|
||||
_Navigate(winrt::hstring{ newActionsTag }, BreadcrumbSubPage::None);
|
||||
}
|
||||
}
|
||||
});
|
||||
auto extensionsVMImpl = winrt::make_self<ExtensionsViewModel>(_settingsClone, _colorSchemesPageVM);
|
||||
extensionsVMImpl->NavigateToProfileRequested({ this, &MainPage::_NavigateToProfileHandler });
|
||||
extensionsVMImpl->NavigateToColorSchemeRequested({ this, &MainPage::_NavigateToColorSchemeHandler });
|
||||
_extensionsVM = *extensionsVMImpl;
|
||||
_extensionsViewModelChangedRevoker = _extensionsVM.PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& args) {
|
||||
const auto settingName{ args.PropertyName() };
|
||||
if (settingName == L"CurrentExtensionPackage")
|
||||
{
|
||||
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);
|
||||
_breadcrumbs.Append(crumb);
|
||||
SettingsMainPage_ScrollViewer().ScrollToVerticalOffset(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we don't have a current extension package, we're at the root of the Extensions page
|
||||
_breadcrumbs.Clear();
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure to initialize the profiles _after_ we have initialized the color schemes page VM, because we pass
|
||||
// that VM into the appearance VMs within the profiles
|
||||
_InitializeProfilesList();
|
||||
@@ -161,7 +209,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_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);
|
||||
|
||||
// We'll update the profile in the _profilesNavState whenever we actually navigate to one
|
||||
|
||||
@@ -183,7 +233,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
// found the one that was selected before the refresh
|
||||
SettingsNav().SelectedItem(item);
|
||||
_Navigate(*stringTag, crumb->SubPage());
|
||||
_Navigate(*breadcrumbStringTag, crumb->SubPage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -198,6 +248,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (const auto& breadcrumbExtensionPackage{ crumb->Tag().try_as<Editor::ExtensionPackageViewModel>() })
|
||||
{
|
||||
if (stringTag == extensionsTag)
|
||||
{
|
||||
// navigate to the NewTabMenu page,
|
||||
// _Navigate() will handle trying to find the right subpage
|
||||
SettingsNav().SelectedItem(item);
|
||||
_Navigate(breadcrumbExtensionPackage, BreadcrumbSubPage::NewTabMenu_Folder);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (const auto& profileTag{ tag.try_as<ProfileViewModel>() })
|
||||
{
|
||||
@@ -441,6 +502,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Actions/Content"), BreadcrumbSubPage::None);
|
||||
_breadcrumbs.Append(crumb);
|
||||
}
|
||||
else if (clickedItemTag == newActionsTag)
|
||||
{
|
||||
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Actions/Content"), BreadcrumbSubPage::None);
|
||||
_breadcrumbs.Append(crumb);
|
||||
contentFrame().Navigate(xaml_typename<Editor::NewActions>(), _actionsVM);
|
||||
|
||||
if (subPage == BreadcrumbSubPage::Actions_Edit && _actionsVM.CurrentCommand() != nullptr)
|
||||
{
|
||||
_actionsVM.CurrentPage(ActionsSubPage::Edit);
|
||||
}
|
||||
}
|
||||
else if (clickedItemTag == newTabMenuTag)
|
||||
{
|
||||
if (_newTabMenuPageVM.CurrentFolder())
|
||||
@@ -457,6 +529,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_breadcrumbs.Append(crumb);
|
||||
}
|
||||
}
|
||||
else if (clickedItemTag == extensionsTag)
|
||||
{
|
||||
if (_extensionsVM.CurrentExtensionPackage())
|
||||
{
|
||||
// Setting CurrentExtensionPackage triggers the PropertyChanged event,
|
||||
// which will navigate to the correct page and update the breadcrumbs appropriately
|
||||
_extensionsVM.CurrentExtensionPackage(nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
contentFrame().Navigate(xaml_typename<Editor::Extensions>(), _extensionsVM);
|
||||
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Extensions/Content"), BreadcrumbSubPage::None);
|
||||
_breadcrumbs.Append(crumb);
|
||||
}
|
||||
}
|
||||
else if (clickedItemTag == globalProfileTag)
|
||||
{
|
||||
auto profileVM{ _viewModelForProfile(_settingsClone.ProfileDefaults(), _settingsClone, Dispatcher()) };
|
||||
@@ -587,6 +674,40 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void MainPage::_Navigate(const Editor::ExtensionPackageViewModel& extPkgVM, BreadcrumbSubPage subPage)
|
||||
{
|
||||
_PreNavigateHelper();
|
||||
|
||||
contentFrame().Navigate(xaml_typename<Editor::Extensions>(), _extensionsVM);
|
||||
const auto crumb = winrt::make<Breadcrumb>(box_value(extensionsTag), RS_(L"Nav_Extensions/Content"), BreadcrumbSubPage::None);
|
||||
_breadcrumbs.Append(crumb);
|
||||
|
||||
if (subPage == BreadcrumbSubPage::None)
|
||||
{
|
||||
_extensionsVM.CurrentExtensionPackage(nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool found = false;
|
||||
for (const auto& pkgVM : _extensionsVM.ExtensionPackages())
|
||||
{
|
||||
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
|
||||
_extensionsVM.CurrentExtensionPackage(pkgVM);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
// If we couldn't find a reasonable match, just go back to the root
|
||||
_extensionsVM.CurrentExtensionPackage(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainPage::SaveButton_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
|
||||
{
|
||||
_settingsClone.LogSettingChanges(false);
|
||||
@@ -612,6 +733,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
_Navigate(*ntmEntryViewModel, subPage);
|
||||
}
|
||||
else if (const auto extPkgViewModel = tag.try_as<ExtensionPackageViewModel>())
|
||||
{
|
||||
_Navigate(*extPkgViewModel, subPage);
|
||||
}
|
||||
else
|
||||
{
|
||||
_Navigate(tag.as<hstring>(), subPage);
|
||||
@@ -809,6 +934,35 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
return _breadcrumbs;
|
||||
}
|
||||
|
||||
void MainPage::_NavigateToProfileHandler(const IInspectable& /*sender*/, winrt::guid profileGuid)
|
||||
{
|
||||
for (auto&& menuItem : _menuItemSource)
|
||||
{
|
||||
if (const auto& navViewItem{ menuItem.try_as<MUX::Controls::NavigationViewItem>() })
|
||||
{
|
||||
if (const auto& tag{ navViewItem.Tag() })
|
||||
{
|
||||
if (const auto& profileTag{ tag.try_as<ProfileViewModel>() })
|
||||
{
|
||||
if (profileTag->OriginalProfileGuid() == profileGuid)
|
||||
{
|
||||
SettingsNav().SelectedItem(menuItem);
|
||||
_Navigate(*profileTag, BreadcrumbSubPage::None);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Silently fail if the profile wasn't found
|
||||
}
|
||||
|
||||
void MainPage::_NavigateToColorSchemeHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/)
|
||||
{
|
||||
SettingsNav().SelectedItem(ColorSchemesNavItem());
|
||||
_Navigate(hstring{ colorSchemesTag }, BreadcrumbSubPage::ColorSchemes_Edit);
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Media::Brush MainPage::BackgroundBrush()
|
||||
{
|
||||
return SettingsNav().Background();
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<IInspectable> Breadcrumbs() noexcept;
|
||||
Editor::ExtensionsViewModel ExtensionsVM() const noexcept { return _extensionsVM; }
|
||||
|
||||
til::typed_event<Windows::Foundation::IInspectable, Model::SettingsTarget> OpenJson;
|
||||
|
||||
@@ -68,16 +69,23 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
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 _NavigateToProfileHandler(const IInspectable& sender, winrt::guid profileGuid);
|
||||
void _NavigateToColorSchemeHandler(const IInspectable& sender, const IInspectable& args);
|
||||
|
||||
void _UpdateBackgroundForMica();
|
||||
void _MoveXamlParsedNavItemsIntoItemSource();
|
||||
|
||||
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::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _profileViewModelChangedRevoker;
|
||||
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _colorSchemesPageViewModelChangedRevoker;
|
||||
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _actionsViewModelChangedRevoker;
|
||||
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ntmViewModelChangedRevoker;
|
||||
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _extensionsViewModelChangedRevoker;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "Extensions.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
// Due to a XAML Compiler bug, it is hard for us to propagate an HWND into a XAML-using runtimeclass.
|
||||
@@ -20,7 +22,9 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
Profile_Terminal,
|
||||
Profile_Advanced,
|
||||
ColorSchemes_Edit,
|
||||
NewTabMenu_Folder
|
||||
Actions_Edit,
|
||||
NewTabMenu_Folder,
|
||||
Extensions_Extension
|
||||
};
|
||||
|
||||
runtimeclass Breadcrumb : Windows.Foundation.IStringable
|
||||
@@ -42,6 +46,7 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
void SetHostingWindow(UInt64 window);
|
||||
|
||||
Windows.Foundation.Collections.IObservableVector<IInspectable> Breadcrumbs { get; };
|
||||
ExtensionsViewModel ExtensionsVM { get; };
|
||||
|
||||
Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
|
||||
}
|
||||
|
||||
@@ -120,7 +120,8 @@
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
|
||||
<muxc:NavigationViewItem x:Uid="Nav_ColorSchemes"
|
||||
<muxc:NavigationViewItem x:Name="ColorSchemesNavItem"
|
||||
x:Uid="Nav_ColorSchemes"
|
||||
Tag="ColorSchemes_Nav">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
@@ -148,11 +149,29 @@
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
|
||||
<muxc:NavigationViewItem x:Uid="Nav_NewActions"
|
||||
Tag="NewActions_Nav">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
|
||||
<muxc:NavigationViewItem x:Uid="Nav_NewTabMenu"
|
||||
Tag="NewTabMenu_Nav">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
</muxc:NavigationViewItem>
|
||||
|
||||
<muxc:NavigationViewItem x:Uid="Nav_Extensions"
|
||||
Tag="Extensions_Nav">
|
||||
<muxc:NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</muxc:NavigationViewItem.Icon>
|
||||
<muxc:NavigationViewItem.InfoBadge>
|
||||
<muxc:InfoBadge Style="{StaticResource NewInfoBadge}"
|
||||
Visibility="{x:Bind ExtensionsVM.DisplayBadge, Mode=OneWay}" />
|
||||
</muxc:NavigationViewItem.InfoBadge>
|
||||
</muxc:NavigationViewItem>
|
||||
|
||||
<muxc:NavigationViewItemHeader x:Uid="Nav_Profiles" />
|
||||
|
||||
@@ -44,6 +44,16 @@
|
||||
<ClInclude Include="Actions.h">
|
||||
<DependentUpon>Actions.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NewActions.h">
|
||||
<DependentUpon>NewActions.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ArgsTemplateSelectors.h">
|
||||
<DependentUpon>ArgsTemplateSelectors.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EditAction.h">
|
||||
<DependentUpon>EditAction.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AddProfile.h">
|
||||
<DependentUpon>AddProfile.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
@@ -125,6 +135,10 @@
|
||||
<DependentUpon>NewTabMenuViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Extensions.h">
|
||||
<DependentUpon>Extensions.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Profiles_Base.h">
|
||||
<DependentUpon>Profiles_Base.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -163,6 +177,12 @@
|
||||
<Page Include="Actions.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="NewActions.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="EditAction.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="AddProfile.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
@@ -199,6 +219,9 @@
|
||||
<Page Include="MainPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Extensions.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Profiles_Base.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
@@ -229,6 +252,16 @@
|
||||
<ClCompile Include="Actions.cpp">
|
||||
<DependentUpon>Actions.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="NewActions.cpp">
|
||||
<DependentUpon>NewActions.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ArgsTemplateSelectors.cpp">
|
||||
<DependentUpon>ArgsTemplateSelectors.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EditAction.cpp">
|
||||
<DependentUpon>EditAction.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AddProfile.cpp">
|
||||
<DependentUpon>AddProfile.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
@@ -309,6 +342,10 @@
|
||||
<DependentUpon>NewTabMenuViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Extensions.cpp">
|
||||
<DependentUpon>Extensions.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Profiles_Base.cpp">
|
||||
<DependentUpon>Profiles_Base.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -350,6 +387,17 @@
|
||||
<DependentUpon>Actions.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="NewActions.idl">
|
||||
<DependentUpon>NewActions.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="ArgsTemplateSelectors.idl">
|
||||
<SubType>Designer</SubType>
|
||||
</Midl>
|
||||
<Midl Include="EditAction.idl">
|
||||
<DependentUpon>EditAction.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="AddProfile.idl">
|
||||
<DependentUpon>AddProfile.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -408,6 +456,10 @@
|
||||
<Midl Include="GlobalAppearanceViewModel.idl" />
|
||||
<Midl Include="LaunchViewModel.idl" />
|
||||
<Midl Include="NewTabMenuViewModel.idl" />
|
||||
<Midl Include="Extensions.idl">
|
||||
<DependentUpon>Extensions.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="Profiles_Base.idl">
|
||||
<DependentUpon>Profiles_Base.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<Midl Include="LaunchViewModel.idl" />
|
||||
<Midl Include="EnumEntry.idl" />
|
||||
<Midl Include="SettingContainer.idl" />
|
||||
<Midl Include="ArgsTemplateSelectors.idl" />
|
||||
<Midl Include="TerminalColorConverters.idl" />
|
||||
<Midl Include="NewTabMenuViewModel.idl" />
|
||||
</ItemGroup>
|
||||
@@ -49,6 +50,8 @@
|
||||
<Page Include="Appearances.xaml" />
|
||||
<Page Include="Rendering.xaml" />
|
||||
<Page Include="Actions.xaml" />
|
||||
<Page Include="NewActions.xaml" />
|
||||
<Page Include="EditAction.xaml" />
|
||||
<Page Include="SettingContainerStyle.xaml" />
|
||||
<Page Include="AddProfile.xaml" />
|
||||
<Page Include="KeyChordListener.xaml" />
|
||||
|
||||
38
src/cascadia/TerminalSettingsEditor/NewActions.cpp
Normal file
38
src/cascadia/TerminalSettingsEditor/NewActions.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NewActions.h"
|
||||
#include "NewActions.g.cpp"
|
||||
#include "LibraryResources.h"
|
||||
#include "../TerminalSettingsModel/AllShortcutActions.h"
|
||||
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
NewActions::NewActions()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Automation::AutomationProperties::SetName(AddNewButton(), RS_(L"Actions_AddNewTextBlock/Text"));
|
||||
}
|
||||
|
||||
Automation::Peers::AutomationPeer NewActions::OnCreateAutomationPeer()
|
||||
{
|
||||
_ViewModel.OnAutomationPeerAttached();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NewActions::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
_ViewModel = e.Parameter().as<Editor::ActionsViewModel>();
|
||||
_ViewModel.CurrentPage(ActionsSubPage::Base);
|
||||
}
|
||||
|
||||
void NewActions::AddNew_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
_ViewModel.AddNewCommand();
|
||||
}
|
||||
}
|
||||
31
src/cascadia/TerminalSettingsEditor/NewActions.h
Normal file
31
src/cascadia/TerminalSettingsEditor/NewActions.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "NewActions.g.h"
|
||||
#include "ActionsViewModel.h"
|
||||
#include "Utils.h"
|
||||
#include "ViewModelHelpers.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct NewActions : public HasScrollViewer<NewActions>, NewActionsT<NewActions>
|
||||
{
|
||||
public:
|
||||
NewActions();
|
||||
|
||||
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
|
||||
Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer();
|
||||
|
||||
void AddNew_Click(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
|
||||
til::property_changed_event PropertyChanged;
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::ActionsViewModel, ViewModel, PropertyChanged.raise, nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(NewActions);
|
||||
}
|
||||
13
src/cascadia/TerminalSettingsEditor/NewActions.idl
Normal file
13
src/cascadia/TerminalSettingsEditor/NewActions.idl
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "ActionsViewModel.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
[default_interface] runtimeclass NewActions : Windows.UI.Xaml.Controls.Page
|
||||
{
|
||||
NewActions();
|
||||
ActionsViewModel ViewModel { get; };
|
||||
}
|
||||
}
|
||||
204
src/cascadia/TerminalSettingsEditor/NewActions.xaml
Normal file
204
src/cascadia/TerminalSettingsEditor/NewActions.xaml
Normal file
@@ -0,0 +1,204 @@
|
||||
<!--
|
||||
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information.
|
||||
-->
|
||||
<Page x:Class="Microsoft.Terminal.Settings.Editor.NewActions"
|
||||
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:mtu="using:Microsoft.Terminal.UI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="CommonResources.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<!-- Theme Dictionary -->
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<!-- TextBox colors ! -->
|
||||
<SolidColorBrush x:Key="TextControlBackground"
|
||||
Color="#333333" />
|
||||
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush"
|
||||
Color="#B5B5B5" />
|
||||
<SolidColorBrush x:Key="TextControlForeground"
|
||||
Color="#B5B5B5" />
|
||||
<SolidColorBrush x:Key="TextControlBorderBrush"
|
||||
Color="#404040" />
|
||||
<SolidColorBrush x:Key="TextControlButtonForeground"
|
||||
Color="#B5B5B5" />
|
||||
|
||||
<SolidColorBrush x:Key="TextControlBackgroundPointerOver"
|
||||
Color="#404040" />
|
||||
<SolidColorBrush x:Key="TextControlForegroundPointerOver"
|
||||
Color="#FFFFFF" />
|
||||
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver"
|
||||
Color="#404040" />
|
||||
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver"
|
||||
Color="#FF4343" />
|
||||
|
||||
<SolidColorBrush x:Key="TextControlBackgroundFocused"
|
||||
Color="#333333" />
|
||||
<SolidColorBrush x:Key="TextControlForegroundFocused"
|
||||
Color="#FFFFFF" />
|
||||
<SolidColorBrush x:Key="TextControlBorderBrushFocused"
|
||||
Color="#404040" />
|
||||
<SolidColorBrush x:Key="TextControlButtonForegroundPressed"
|
||||
Color="#FFFFFF" />
|
||||
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed"
|
||||
Color="#FF4343" />
|
||||
|
||||
<!-- KeyChordText styles -->
|
||||
<Style x:Key="KeyChordBorderStyle"
|
||||
TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="1" />
|
||||
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
<Style x:Key="KeyChordTextBlockStyle"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<!-- TextBox colors ! -->
|
||||
<SolidColorBrush x:Key="TextControlBackground"
|
||||
Color="#CCCCCC" />
|
||||
<SolidColorBrush x:Key="TextBoxPlaceholderTextThemeBrush"
|
||||
Color="#636363" />
|
||||
<SolidColorBrush x:Key="TextControlBorderBrush"
|
||||
Color="#636363" />
|
||||
<SolidColorBrush x:Key="TextControlButtonForeground"
|
||||
Color="#636363" />
|
||||
|
||||
<SolidColorBrush x:Key="TextControlBackgroundPointerOver"
|
||||
Color="#DADADA" />
|
||||
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver"
|
||||
Color="#636363" />
|
||||
<SolidColorBrush x:Key="TextControlButtonForegroundPointerOver"
|
||||
Color="#FF4343" />
|
||||
|
||||
<SolidColorBrush x:Key="TextControlBackgroundFocused"
|
||||
Color="#CCCCCC" />
|
||||
<SolidColorBrush x:Key="TextControlBorderBrushFocused"
|
||||
Color="#636363" />
|
||||
<SolidColorBrush x:Key="TextControlButtonForegroundPressed"
|
||||
Color="#FFFFFF" />
|
||||
<SolidColorBrush x:Key="TextControlButtonBackgroundPressed"
|
||||
Color="#FF4343" />
|
||||
|
||||
<!-- KeyChordText styles -->
|
||||
<Style x:Key="KeyChordBorderStyle"
|
||||
TargetType="Border">
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="1" />
|
||||
<Setter Property="Background" Value="{ThemeResource SystemAltMediumLowColor}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
<Style x:Key="KeyChordTextBlockStyle"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
|
||||
<!-- KeyChordText styles (use XAML defaults for High Contrast theme) -->
|
||||
<Style x:Key="KeyChordBorderStyle"
|
||||
TargetType="Border" />
|
||||
<Style x:Key="KeyChordTextBlockStyle"
|
||||
TargetType="TextBlock" />
|
||||
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<!-- Styles -->
|
||||
<Style x:Key="KeyBindingContainerStyle"
|
||||
BasedOn="{StaticResource DefaultListViewItemStyle}"
|
||||
TargetType="ListViewItem">
|
||||
<Setter Property="Padding" Value="4" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="XYFocusKeyboardNavigation" Value="Enabled" />
|
||||
</Style>
|
||||
<Style x:Key="KeyBindingNameTextBlockStyle"
|
||||
BasedOn="{StaticResource BaseTextBlockStyle}"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="TextWrapping" Value="WrapWholeWords" />
|
||||
</Style>
|
||||
<Style x:Key="KeyChordEditorStyle"
|
||||
TargetType="local:KeyChordListener">
|
||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<!-- Converters & Misc. -->
|
||||
<SolidColorBrush x:Key="ActionContainerBackgroundEditing"
|
||||
Color="{ThemeResource SystemListMediumColor}" />
|
||||
<SolidColorBrush x:Key="ActionContainerBackground"
|
||||
Color="Transparent" />
|
||||
|
||||
<!-- Templates -->
|
||||
<DataTemplate x:Key="CommandTemplate"
|
||||
x:DataType="local:CommandViewModel">
|
||||
<ListViewItem AutomationProperties.Name="{x:Bind DisplayName, Mode=OneWay}"
|
||||
Style="{StaticResource KeyBindingContainerStyle}">
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<!-- command name -->
|
||||
<ColumnDefinition Width="*" />
|
||||
<!-- key chord -->
|
||||
<ColumnDefinition Width="150" />
|
||||
<!-- edit buttons -->
|
||||
<!--
|
||||
This needs to be 112 because that is the width of the row of buttons in edit mode + padding.
|
||||
3 buttons: 32+32+32
|
||||
Padding: 8+ 8
|
||||
This allows the "edit" button to align with the "cancel" button seamlessly
|
||||
-->
|
||||
<ColumnDefinition Width="112" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Command Name -->
|
||||
<TextBlock Grid.Column="0"
|
||||
Style="{StaticResource KeyBindingNameTextBlockStyle}"
|
||||
Text="{x:Bind DisplayName, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<Border MaxWidth="{StaticResource StandardControlMaxWidth}">
|
||||
<StackPanel MaxWidth="600"
|
||||
HorizontalAlignment="Left"
|
||||
Spacing="8"
|
||||
Style="{StaticResource SettingsStackStyle}">
|
||||
<!-- Add New Button -->
|
||||
<Button x:Name="AddNewButton"
|
||||
Click="AddNew_Click">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon FontSize="{StaticResource StandardIconSize}"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="Actions_AddNewTextBlock"
|
||||
Style="{StaticResource IconButtonTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<!-- Keybindings -->
|
||||
<ListView x:Name="CommandsListView"
|
||||
IsItemClickEnabled="True"
|
||||
ItemTemplate="{StaticResource CommandTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.CommandList, Mode=OneWay}"
|
||||
ItemClick="{x:Bind ViewModel.CmdListItemClicked}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Page>
|
||||
@@ -22,8 +22,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_entryTemplateSelector = Resources().Lookup(box_value(L"NewTabMenuEntryTemplateSelector")).as<Editor::NewTabMenuEntryTemplateSelector>();
|
||||
|
||||
// Ideally, we'd bind IsEnabled to something like mtu:Converters.isEmpty(NewTabMenuListView.SelectedItems.Size) in the XAML,
|
||||
// but the XAML compiler can't find NewTabMenuListView when we try that. Rather than copying the list of selected items over
|
||||
// to the view model, we'll just do this instead (much simpler).
|
||||
|
||||
@@ -42,7 +42,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::NewTabMenuViewModel, ViewModel, _PropertyChangedHandlers, nullptr);
|
||||
|
||||
private:
|
||||
Editor::NewTabMenuEntryTemplateSelector _entryTemplateSelector{ nullptr };
|
||||
Editor::NewTabMenuEntryViewModel _draggedEntry{ nullptr };
|
||||
|
||||
void _ScrollToEntry(const Editor::NewTabMenuEntryViewModel& entry);
|
||||
|
||||
@@ -321,7 +321,7 @@
|
||||
<TextBlock x:Uid="NewTabMenu_CurrentFolderTextBlock"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
|
||||
<!-- TODO CARLOS: Icon -->
|
||||
<!-- TODO GH #18281: Icon -->
|
||||
<!-- Once PR #17965 merges, we can add that kind of control to set an icon -->
|
||||
|
||||
<!-- Name -->
|
||||
|
||||
@@ -684,6 +684,14 @@
|
||||
<value>Actions</value>
|
||||
<comment>Header for the "actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app.</comment>
|
||||
</data>
|
||||
<data name="Nav_NewActions.Content" xml:space="preserve">
|
||||
<value>New Actions</value>
|
||||
<comment>Header for the "new actions" menu item. This navigates to a page that lets you see and modify commands, key bindings, and actions that can be done in the app.</comment>
|
||||
</data>
|
||||
<data name="Nav_Extensions.Content" xml:space="preserve">
|
||||
<value>Extensions</value>
|
||||
<comment>Header for the "extensions" menu item. This navigates to a page that lets you see and modify extensions for the app.</comment>
|
||||
</data>
|
||||
<data name="Profile_OpacitySlider.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Background opacity</value>
|
||||
<comment>Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque.</comment>
|
||||
@@ -1784,6 +1792,34 @@
|
||||
<value>Delete the unfocused appearance for this profile.</value>
|
||||
<comment>A description for what the delete unfocused appearance button does.</comment>
|
||||
</data>
|
||||
<data name="Actions_DeleteButton2.Text" xml:space="preserve">
|
||||
<value>Delete action</value>
|
||||
<comment>Button label that deletes the selected action.</comment>
|
||||
</data>
|
||||
<data name="Actions_Name.Text" xml:space="preserve">
|
||||
<value>Action name</value>
|
||||
<comment>Label for the text box that edits the action name.</comment>
|
||||
</data>
|
||||
<data name="Actions_NameEntryBox.PlaceholderText" xml:space="preserve">
|
||||
<value>Action name</value>
|
||||
<comment>Placeholder text for the text box where the user can edit the action name.</comment>
|
||||
</data>
|
||||
<data name="Actions_ShortcutAction.Text" xml:space="preserve">
|
||||
<value>Action type</value>
|
||||
<comment>Label for the combo box that edits the action type.</comment>
|
||||
</data>
|
||||
<data name="Actions_Arguments.Text" xml:space="preserve">
|
||||
<value>Additional arguments</value>
|
||||
<comment>Label for the list of editable arguments for the currently selected action.</comment>
|
||||
</data>
|
||||
<data name="Actions_Keybindings.Text" xml:space="preserve">
|
||||
<value>Keybindings</value>
|
||||
<comment>Label for the list of editable keybindings for the current command.</comment>
|
||||
</data>
|
||||
<data name="Actions_AddKeyChord.Text" xml:space="preserve">
|
||||
<value>Add keybinding</value>
|
||||
<comment>Button label that adds a keybinding to the current action.</comment>
|
||||
</data>
|
||||
<data name="Actions_DeleteConfirmationButton.Content" xml:space="preserve">
|
||||
<value>Yes, delete key binding</value>
|
||||
<comment>Button label that confirms deletion of a key binding entry.</comment>
|
||||
@@ -1792,6 +1828,14 @@
|
||||
<value>Are you sure you want to delete this key binding?</value>
|
||||
<comment>Confirmation message displayed when the user attempts to delete a key binding entry.</comment>
|
||||
</data>
|
||||
<data name="Actions_CommandDeleteConfirmationButton.Content" xml:space="preserve">
|
||||
<value>Yes, delete action</value>
|
||||
<comment>Button label that confirms deletion of an action.</comment>
|
||||
</data>
|
||||
<data name="Actions_CommandDeleteConfirmationMessage.Text" xml:space="preserve">
|
||||
<value>Are you sure you want to delete this action?</value>
|
||||
<comment>Confirmation message displayed when the user attempts to delete an action.</comment>
|
||||
</data>
|
||||
<data name="Actions_InvalidKeyChordMessage" xml:space="preserve">
|
||||
<value>Invalid key chord. Please enter a valid key chord.</value>
|
||||
<comment>Error message displayed when an invalid key chord is input by the user.</comment>
|
||||
@@ -1836,6 +1880,274 @@
|
||||
<value>Action</value>
|
||||
<comment>Label for a control that sets the action of a key binding.</comment>
|
||||
</data>
|
||||
<data name="Actions_NullEnumValue" xml:space="preserve">
|
||||
<value>Null (use global setting)</value>
|
||||
<comment>An option to choose from for nullable enums. Clears the enum value.</comment>
|
||||
</data>
|
||||
<data name="Actions_CopyFormatHtml.Content" xml:space="preserve">
|
||||
<value>HTML</value>
|
||||
<comment>An option to choose from for the "copy format". Copies content in HTML format.</comment>
|
||||
</data>
|
||||
<data name="Actions_CopyFormatRtf.Content" xml:space="preserve">
|
||||
<value>RTF</value>
|
||||
<comment>An option to choose from for the "copy format". Copies content in Rich Text Format (RTF).</comment>
|
||||
</data>
|
||||
<data name="Actions_SplitDirectionAuto.Content" xml:space="preserve">
|
||||
<value>Automatic</value>
|
||||
<comment>An option to choose from for the "split direction". Automatically determines the split direction.</comment>
|
||||
</data>
|
||||
<data name="Actions_SplitDirectionUp.Content" xml:space="preserve">
|
||||
<value>Up</value>
|
||||
<comment>An option to choose from for the "split direction". Splits upward.</comment>
|
||||
</data>
|
||||
<data name="Actions_SplitDirectionRight.Content" xml:space="preserve">
|
||||
<value>Right</value>
|
||||
<comment>An option to choose from for the "split direction". Splits to the right.</comment>
|
||||
</data>
|
||||
<data name="Actions_SplitDirectionDown.Content" xml:space="preserve">
|
||||
<value>Down</value>
|
||||
<comment>An option to choose from for the "split direction". Splits downward.</comment>
|
||||
</data>
|
||||
<data name="Actions_SplitDirectionLeft.Content" xml:space="preserve">
|
||||
<value>Left</value>
|
||||
<comment>An option to choose from for the "split direction". Splits to the left.</comment>
|
||||
</data>
|
||||
<data name="Actions_SplitDirectionVertical.Content" xml:space="preserve">
|
||||
<value>Vertical</value>
|
||||
<comment>An option to choose from for the "split direction". Splits to the left.</comment>
|
||||
</data>
|
||||
<data name="Actions_SplitDirectionHorizontal.Content" xml:space="preserve">
|
||||
<value>Horizontal</value>
|
||||
<comment>An option to choose from for the "split direction". Splits to the left.</comment>
|
||||
</data>
|
||||
<data name="Actions_SplitTypeManual.Content" xml:space="preserve">
|
||||
<value>Manual</value>
|
||||
<comment>An option to choose from for the "split type". Creates a manual split.</comment>
|
||||
</data>
|
||||
<data name="Actions_SplitTypeDuplicate.Content" xml:space="preserve">
|
||||
<value>Duplicate</value>
|
||||
<comment>An option to choose from for the "split type". Creates a split by duplicating the current session.</comment>
|
||||
</data>
|
||||
<data name="Actions_ResizeDirectionNone.Content" xml:space="preserve">
|
||||
<value>None</value>
|
||||
<comment>An option to choose from for the "resize direction". None option.</comment>
|
||||
</data>
|
||||
<data name="Actions_ResizeDirectionLeft.Content" xml:space="preserve">
|
||||
<value>Left</value>
|
||||
<comment>An option to choose from for the "resize direction". Left option.</comment>
|
||||
</data>
|
||||
<data name="Actions_ResizeDirectionRight.Content" xml:space="preserve">
|
||||
<value>Right</value>
|
||||
<comment>An option to choose from for the "resize direction". Right option.</comment>
|
||||
</data>
|
||||
<data name="Actions_ResizeDirectionUp.Content" xml:space="preserve">
|
||||
<value>Up</value>
|
||||
<comment>An option to choose from for the "resize direction". Up option.</comment>
|
||||
</data>
|
||||
<data name="Actions_ResizeDirectionDown.Content" xml:space="preserve">
|
||||
<value>Down</value>
|
||||
<comment>An option to choose from for the "resize direction". Down option.</comment>
|
||||
</data>
|
||||
<data name="Actions_FocusDirectionNone.Content" xml:space="preserve">
|
||||
<value>None</value>
|
||||
<comment>An option to choose from for the "focus direction". None option.</comment>
|
||||
</data>
|
||||
<data name="Actions_FocusDirectionLeft.Content" xml:space="preserve">
|
||||
<value>Left</value>
|
||||
<comment>An option to choose from for the "focus direction". Left option.</comment>
|
||||
</data>
|
||||
<data name="Actions_FocusDirectionRight.Content" xml:space="preserve">
|
||||
<value>Right</value>
|
||||
<comment>An option to choose from for the "focus direction". Right option.</comment>
|
||||
</data>
|
||||
<data name="Actions_FocusDirectionUp.Content" xml:space="preserve">
|
||||
<value>Up</value>
|
||||
<comment>An option to choose from for the "focus direction". Up option.</comment>
|
||||
</data>
|
||||
<data name="Actions_FocusDirectionDown.Content" xml:space="preserve">
|
||||
<value>Down</value>
|
||||
<comment>An option to choose from for the "focus direction". Down option.</comment>
|
||||
</data>
|
||||
<data name="Actions_FocusDirectionPrevious.Content" xml:space="preserve">
|
||||
<value>Previous</value>
|
||||
<comment>An option to choose from for the "focus direction". Previous option.</comment>
|
||||
</data>
|
||||
<data name="Actions_FocusDirectionPreviousInOrder.Content" xml:space="preserve">
|
||||
<value>Previous In Order</value>
|
||||
<comment>An option to choose from for the "focus direction". Previous in order option.</comment>
|
||||
</data>
|
||||
<data name="Actions_FocusDirectionNextInOrder.Content" xml:space="preserve">
|
||||
<value>Next In Order</value>
|
||||
<comment>An option to choose from for the "focus direction". Next in order option.</comment>
|
||||
</data>
|
||||
<data name="Actions_FocusDirectionFirst.Content" xml:space="preserve">
|
||||
<value>First</value>
|
||||
<comment>An option to choose from for the "focus direction". First option.</comment>
|
||||
</data>
|
||||
<data name="Actions_FocusDirectionParent.Content" xml:space="preserve">
|
||||
<value>Parent</value>
|
||||
<comment>An option to choose from for the "focus direction". Parent option.</comment>
|
||||
</data>
|
||||
<data name="Actions_FocusDirectionChild.Content" xml:space="preserve">
|
||||
<value>Child</value>
|
||||
<comment>An option to choose from for the "focus direction". Child option.</comment>
|
||||
</data>
|
||||
<data name="Actions_SettingsTargetSettingsFile.Content" xml:space="preserve">
|
||||
<value>Settings File</value>
|
||||
<comment>An option to choose from for the "settings target". Targets the settings file.</comment>
|
||||
</data>
|
||||
<data name="Actions_SettingsTargetDefaultsFile.Content" xml:space="preserve">
|
||||
<value>Defaults File</value>
|
||||
<comment>An option to choose from for the "settings target". Targets the defaults file.</comment>
|
||||
</data>
|
||||
<data name="Actions_SettingsTargetAllFiles.Content" xml:space="preserve">
|
||||
<value>All Files</value>
|
||||
<comment>An option to choose from for the "settings target". Targets all files.</comment>
|
||||
</data>
|
||||
<data name="Actions_SettingsTargetSettingsUI.Content" xml:space="preserve">
|
||||
<value>Settings UI</value>
|
||||
<comment>An option to choose from for the "settings target". Targets the settings UI.</comment>
|
||||
</data>
|
||||
<data name="Actions_SettingsTargetDirectory.Content" xml:space="preserve">
|
||||
<value>Directory</value>
|
||||
<comment>An option to choose from for the "settings target". Targets the directory.</comment>
|
||||
</data>
|
||||
<data name="Actions_MoveTabDirectionNone.Content" xml:space="preserve">
|
||||
<value>None</value>
|
||||
<comment>An option to choose from for the "move tab direction". No movement.</comment>
|
||||
</data>
|
||||
<data name="Actions_MoveTabDirectionForward.Content" xml:space="preserve">
|
||||
<value>Forward</value>
|
||||
<comment>An option to choose from for the "move tab direction". Moves the tab forward.</comment>
|
||||
</data>
|
||||
<data name="Actions_MoveTabDirectionBackward.Content" xml:space="preserve">
|
||||
<value>Backward</value>
|
||||
<comment>An option to choose from for the "move tab direction". Moves the tab backward.</comment>
|
||||
</data>
|
||||
<data name="Actions_ScrollToMarkDirectionPrevious.Content" xml:space="preserve">
|
||||
<value>Previous</value>
|
||||
<comment>An option to choose from for the "scroll to mark direction". Scrolls to the previous mark.</comment>
|
||||
</data>
|
||||
<data name="Actions_ScrollToMarkDirectionNext.Content" xml:space="preserve">
|
||||
<value>Next</value>
|
||||
<comment>An option to choose from for the "scroll to mark direction". Scrolls to the next mark.</comment>
|
||||
</data>
|
||||
<data name="Actions_ScrollToMarkDirectionFirst.Content" xml:space="preserve">
|
||||
<value>First</value>
|
||||
<comment>An option to choose from for the "scroll to mark direction". Scrolls to the first mark.</comment>
|
||||
</data>
|
||||
<data name="Actions_ScrollToMarkDirectionLast.Content" xml:space="preserve">
|
||||
<value>Last</value>
|
||||
<comment>An option to choose from for the "scroll to mark direction". Scrolls to the last mark.</comment>
|
||||
</data>
|
||||
<data name="Actions_CommandPaletteLaunchModeAction.Content" xml:space="preserve">
|
||||
<value>Action</value>
|
||||
<comment>An option to choose from for the "command palette launch mode". Launches in action mode.</comment>
|
||||
</data>
|
||||
<data name="Actions_CommandPaletteLaunchModeCommandLine.Content" xml:space="preserve">
|
||||
<value>Command Line</value>
|
||||
<comment>An option to choose from for the "command palette launch mode". Launches in command line mode.</comment>
|
||||
</data>
|
||||
<data name="Actions_SuggestionsSourceNone.Content" xml:space="preserve">
|
||||
<value>None</value>
|
||||
<comment>An option to choose from for the "suggestions source". No suggestions source.</comment>
|
||||
</data>
|
||||
<data name="Actions_SuggestionsSourceTasks.Content" xml:space="preserve">
|
||||
<value>Tasks</value>
|
||||
<comment>An option to choose from for the "suggestions source". Suggestions come from tasks.</comment>
|
||||
</data>
|
||||
<data name="Actions_SuggestionsSourceSnippets.Content" xml:space="preserve">
|
||||
<value>Snippets</value>
|
||||
<comment>An option to choose from for the "suggestions source". Suggestions come from snippets.</comment>
|
||||
</data>
|
||||
<data name="Actions_SuggestionsSourceCommandHistory.Content" xml:space="preserve">
|
||||
<value>Command History</value>
|
||||
<comment>An option to choose from for the "suggestions source". Suggestions come from command history.</comment>
|
||||
</data>
|
||||
<data name="Actions_SuggestionsSourceDirectoryHistory.Content" xml:space="preserve">
|
||||
<value>Directory History</value>
|
||||
<comment>An option to choose from for the "suggestions source". Suggestions come from directory history.</comment>
|
||||
</data>
|
||||
<data name="Actions_SuggestionsSourceQuickFix.Content" xml:space="preserve">
|
||||
<value>Quick Fixes</value>
|
||||
<comment>An option to choose from for the "suggestions source". Suggestions come from quick fixes.</comment>
|
||||
</data>
|
||||
<data name="Actions_SuggestionsSourceAll.Content" xml:space="preserve">
|
||||
<value>All</value>
|
||||
<comment>An option to choose from for the "suggestions source". Includes all suggestion sources.</comment>
|
||||
</data>
|
||||
<data name="Actions_FindMatchDirectionNone.Content" xml:space="preserve">
|
||||
<value>None</value>
|
||||
<comment>An option to choose from for the "find match direction". No direction selected.</comment>
|
||||
</data>
|
||||
<data name="Actions_FindMatchDirectionNext.Content" xml:space="preserve">
|
||||
<value>Next</value>
|
||||
<comment>An option to choose from for the "find match direction". Finds the next match.</comment>
|
||||
</data>
|
||||
<data name="Actions_FindMatchDirectionPrev.Content" xml:space="preserve">
|
||||
<value>Previous</value>
|
||||
<comment>An option to choose from for the "find match direction". Finds the previous match.</comment>
|
||||
</data>
|
||||
<data name="Actions_DesktopBehaviorAny.Content" xml:space="preserve">
|
||||
<value>Any</value>
|
||||
<comment>An option to choose from for the "desktop behavior". Applies to any desktop.</comment>
|
||||
</data>
|
||||
<data name="Actions_DesktopBehaviorToCurrent.Content" xml:space="preserve">
|
||||
<value>To Current</value>
|
||||
<comment>An option to choose from for the "desktop behavior". Moves to the current desktop.</comment>
|
||||
</data>
|
||||
<data name="Actions_DesktopBehaviorOnCurrent.Content" xml:space="preserve">
|
||||
<value>On Current</value>
|
||||
<comment>An option to choose from for the "desktop behavior". Stays on the current desktop.</comment>
|
||||
</data>
|
||||
<data name="Actions_MonitorBehaviorAny.Content" xml:space="preserve">
|
||||
<value>Any</value>
|
||||
<comment>An option to choose from for the "monitor behavior". Applies to any monitor.</comment>
|
||||
</data>
|
||||
<data name="Actions_MonitorBehaviorToCurrent.Content" xml:space="preserve">
|
||||
<value>To Current</value>
|
||||
<comment>An option to choose from for the "monitor behavior". Moves to the current monitor.</comment>
|
||||
</data>
|
||||
<data name="Actions_MonitorBehaviorToMouse.Content" xml:space="preserve">
|
||||
<value>To Mouse</value>
|
||||
<comment>An option to choose from for the "monitor behavior". Moves to the monitor where the mouse is located.</comment>
|
||||
</data>
|
||||
<data name="Actions_ClearBufferTypeScreen.Content" xml:space="preserve">
|
||||
<value>Screen</value>
|
||||
<comment>An option to choose from for the "clear buffer type". Clears only the screen.</comment>
|
||||
</data>
|
||||
<data name="Actions_ClearBufferTypeScrollback.Content" xml:space="preserve">
|
||||
<value>Scrollback</value>
|
||||
<comment>An option to choose from for the "clear buffer type". Clears only the scrollback buffer.</comment>
|
||||
</data>
|
||||
<data name="Actions_ClearBufferTypeAll.Content" xml:space="preserve">
|
||||
<value>All</value>
|
||||
<comment>An option to choose from for the "clear buffer type". Clears both the screen and the scrollback buffer.</comment>
|
||||
</data>
|
||||
<data name="Actions_SelectOutputDirectionPrev.Content" xml:space="preserve">
|
||||
<value>Previous</value>
|
||||
<comment>An option to choose from for the "select output direction". Selects the previous output.</comment>
|
||||
</data>
|
||||
<data name="Actions_SelectOutputDirectionNext.Content" xml:space="preserve">
|
||||
<value>Next</value>
|
||||
<comment>An option to choose from for the "select output direction". Selects the next output.</comment>
|
||||
</data>
|
||||
<data name="Actions_TabSwitcherModeMru.Content" xml:space="preserve">
|
||||
<value>Most Recently Used</value>
|
||||
<comment>An option to choose from for the "tab switcher mode". Switches tabs based on most recently used order.</comment>
|
||||
</data>
|
||||
<data name="Actions_TabSwitcherModeInOrder.Content" xml:space="preserve">
|
||||
<value>In Order</value>
|
||||
<comment>An option to choose from for the "tab switcher mode". Switches tabs in sequential order.</comment>
|
||||
</data>
|
||||
<data name="Actions_TabSwitcherModeDisabled.Content" xml:space="preserve">
|
||||
<value>Disabled</value>
|
||||
<comment>An option to choose from for the "tab switcher mode". Disables tab switching.</comment>
|
||||
</data>
|
||||
<data name="Actions_NullableColorPicker.NullColorButtonLabel" xml:space="preserve">
|
||||
<value>No color</value>
|
||||
<comment>Label for a button directing the user to opt out of choosing a color.</comment>
|
||||
</data>
|
||||
<data name="KeyChordListener.[using:Windows.UI.Xaml.Automation]AutomationProperties.HelpText" xml:space="preserve">
|
||||
<value>Input your desired keyboard shortcut.</value>
|
||||
<comment>Help text directing users how to use the "KeyChordListener" control. Pressing a keyboard shortcut will be recorded by this control.</comment>
|
||||
@@ -2344,4 +2656,47 @@
|
||||
<value>This option is managed by enterprise policy and cannot be changed here.</value>
|
||||
<comment>This is displayed in concordance with Globals_StartOnUserLogin if the enterprise administrator has taken control of this setting.</comment>
|
||||
</data>
|
||||
<data name="Extensions_ActiveExtensionsHeader.Text" xml:space="preserve">
|
||||
<value>Active Extensions</value>
|
||||
</data>
|
||||
<data name="Extensions_ModifiedProfilesHeader.Text" xml:space="preserve">
|
||||
<value>Modified Profiles</value>
|
||||
</data>
|
||||
<data name="Extensions_AddedProfilesHeader.Text" xml:space="preserve">
|
||||
<value>Added Profiles</value>
|
||||
</data>
|
||||
<data name="Extensions_AddedColorSchemesHeader.Text" xml:space="preserve">
|
||||
<value>Added Color Schemes</value>
|
||||
</data>
|
||||
<data name="Extensions_DisclaimerHyperlink.Content" xml:space="preserve">
|
||||
<value>Learn more about extensions</value>
|
||||
</data>
|
||||
<data name="Extensions_NavigateToProfileButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Navigate to profile</value>
|
||||
</data>
|
||||
<data name="Extensions_NavigateToProfileButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Navigate to profile</value>
|
||||
</data>
|
||||
<data name="Extensions_NavigateToColorSchemeButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Navigate to color scheme</value>
|
||||
</data>
|
||||
<data name="Extensions_NavigateToColorSchemeButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Navigate to color scheme</value>
|
||||
</data>
|
||||
<data name="Extensions_ScopeUser" xml:space="preserve">
|
||||
<value>Current User</value>
|
||||
<comment>Label for the installation scope of an extension.</comment>
|
||||
</data>
|
||||
<data name="Extensions_ScopeSystem" xml:space="preserve">
|
||||
<value>All Users</value>
|
||||
<comment>Label for the installation scope of an extension</comment>
|
||||
</data>
|
||||
<data name="Extensions_Scope.Header" xml:space="preserve">
|
||||
<value>Scope</value>
|
||||
<comment>Header for the installation scope of the extension</comment>
|
||||
</data>
|
||||
<data name="NewInfoBadgeTextBlock.Text" xml:space="preserve">
|
||||
<value>NEW</value>
|
||||
<comment>Text is used on an info badge for new navigation items. Must be all caps.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -134,6 +134,7 @@
|
||||
</Style>
|
||||
|
||||
<Style x:Key="SettingContainerResetButtonStyle"
|
||||
BasedOn="{StaticResource DefaultButtonStyle}"
|
||||
TargetType="Button">
|
||||
<Setter Property="Margin" Value="5,0,0,0" />
|
||||
<Setter Property="Height" Value="19" />
|
||||
@@ -179,13 +180,18 @@
|
||||
<Setter Property="FontFamily" Value="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="SettingContainerCurrentValueTextBlockStyle"
|
||||
BasedOn="{StaticResource SettingsPageItemDescriptionStyle}"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="MaxWidth" Value="250" />
|
||||
<Setter Property="Margin" Value="0,0,-16,0" />
|
||||
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets" />
|
||||
</Style>
|
||||
|
||||
<DataTemplate x:Key="ExpanderSettingContainerStringPreviewTemplate">
|
||||
<TextBlock MaxWidth="250"
|
||||
Margin="0,0,-16,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="Segoe UI, Segoe Fluent Icons, Segoe MDL2 Assets"
|
||||
Style="{StaticResource SettingsPageItemDescriptionStyle}"
|
||||
<TextBlock Style="{StaticResource SettingContainerCurrentValueTextBlockStyle}"
|
||||
Text="{Binding}" />
|
||||
</DataTemplate>
|
||||
|
||||
@@ -228,6 +234,45 @@
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- A basic setting container displaying immutable text as content -->
|
||||
<Style x:Key="SettingContainerWithTextContent"
|
||||
TargetType="local:SettingContainer">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:SettingContainer">
|
||||
<Grid AutomationProperties.Name="{TemplateBinding Header}"
|
||||
Style="{StaticResource NonExpanderGrid}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0"
|
||||
Style="{StaticResource StackPanelInExpanderStyle}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
|
||||
Text="{TemplateBinding Header}" />
|
||||
<Button x:Name="ResetButton"
|
||||
Style="{StaticResource SettingContainerResetButtonStyle}">
|
||||
<FontIcon Glyph=""
|
||||
Style="{StaticResource SettingContainerFontIconStyle}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<TextBlock x:Name="HelpTextBlock"
|
||||
Style="{StaticResource SettingsPageItemDescriptionStyle}"
|
||||
Text="{TemplateBinding HelpText}" />
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="1"
|
||||
Margin="0,0,8,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Style="{ThemeResource SecondaryTextBlockStyle}"
|
||||
Text="{TemplateBinding Content}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!--
|
||||
A setting container for a setting that has no additional options.
|
||||
Includes space for an icon on the left side of the header.
|
||||
@@ -302,8 +347,7 @@
|
||||
<StackPanel Grid.Column="0"
|
||||
Style="{StaticResource StackPanelInExpanderStyle}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource SettingsPageItemHeaderStyle}"
|
||||
Text="{TemplateBinding Header}" />
|
||||
<ContentPresenter Content="{TemplateBinding Header}" />
|
||||
<Button x:Name="ResetButton"
|
||||
Style="{StaticResource SettingContainerResetButtonStyle}">
|
||||
<FontIcon Glyph=""
|
||||
|
||||
@@ -286,6 +286,25 @@ protected: \
|
||||
X(winrt::Microsoft::Terminal::Control::SelectionColor, Background, "background", false, nullptr) \
|
||||
X(winrt::Microsoft::Terminal::Core::MatchMode, MatchMode, "matchMode", false, winrt::Microsoft::Terminal::Core::MatchMode::None)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define NEW_TERMINAL_ARGS(X) \
|
||||
X(winrt::hstring, Commandline, "commandline", false, L"") \
|
||||
X(winrt::hstring, StartingDirectory, "startingDirectory", false, L"") \
|
||||
X(winrt::hstring, TabTitle, "tabTitle", false, L"") \
|
||||
X(Windows::Foundation::IReference<Windows::UI::Color>, TabColor, "tabColor", false, nullptr) \
|
||||
X(Windows::Foundation::IReference<int32_t>, ProfileIndex, "index", false, nullptr) \
|
||||
X(winrt::hstring, Profile, "profile", false, L"") \
|
||||
X(Windows::Foundation::IReference<bool>, SuppressApplicationTitle, "suppressApplicationTitle", false, nullptr) \
|
||||
X(winrt::hstring, ColorScheme, "colorScheme", args->SchemeName().empty(), L"") \
|
||||
X(Windows::Foundation::IReference<bool>, Elevate, "elevate", false, nullptr) \
|
||||
X(Windows::Foundation::IReference<bool>, ReloadEnvironmentVariables, "reloadEnvironmentVariables", false, nullptr)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define SPLIT_PANE_ARGS(X) \
|
||||
X(Model::SplitDirection, SplitDirection, "split", false, SplitDirection::Automatic) \
|
||||
X(SplitType, SplitMode, "splitMode", false, SplitType::Manual) \
|
||||
X(float, SplitSize, "size", false, 0.5f)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
@@ -358,41 +377,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// assumptions made in the macro.
|
||||
struct NewTerminalArgs : public NewTerminalArgsT<NewTerminalArgs>
|
||||
{
|
||||
NewTerminalArgs() = default;
|
||||
NewTerminalArgs(int32_t& profileIndex) :
|
||||
_ProfileIndex{ profileIndex } {};
|
||||
|
||||
PARTIAL_ACTION_ARG_BODY(NewTerminalArgs, NEW_TERMINAL_ARGS);
|
||||
ACTION_ARG(winrt::hstring, Type, L"");
|
||||
|
||||
ACTION_ARG(winrt::hstring, Commandline, L"");
|
||||
ACTION_ARG(winrt::hstring, StartingDirectory, L"");
|
||||
ACTION_ARG(winrt::hstring, TabTitle, L"");
|
||||
ACTION_ARG(Windows::Foundation::IReference<Windows::UI::Color>, TabColor, nullptr);
|
||||
ACTION_ARG(Windows::Foundation::IReference<int32_t>, ProfileIndex, nullptr);
|
||||
ACTION_ARG(winrt::hstring, Profile, L"");
|
||||
ACTION_ARG(winrt::guid, SessionId, winrt::guid{});
|
||||
ACTION_ARG(bool, AppendCommandLine, false);
|
||||
ACTION_ARG(Windows::Foundation::IReference<bool>, SuppressApplicationTitle, nullptr);
|
||||
ACTION_ARG(winrt::hstring, ColorScheme);
|
||||
ACTION_ARG(Windows::Foundation::IReference<bool>, Elevate, nullptr);
|
||||
ACTION_ARG(Windows::Foundation::IReference<bool>, ReloadEnvironmentVariables, nullptr);
|
||||
ACTION_ARG(uint64_t, ContentId);
|
||||
|
||||
static constexpr std::string_view CommandlineKey{ "commandline" };
|
||||
static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" };
|
||||
static constexpr std::string_view TabTitleKey{ "tabTitle" };
|
||||
static constexpr std::string_view TabColorKey{ "tabColor" };
|
||||
static constexpr std::string_view ProfileIndexKey{ "index" };
|
||||
static constexpr std::string_view ProfileKey{ "profile" };
|
||||
static constexpr std::string_view SessionIdKey{ "sessionId" };
|
||||
static constexpr std::string_view AppendCommandLineKey{ "appendCommandLine" };
|
||||
static constexpr std::string_view SuppressApplicationTitleKey{ "suppressApplicationTitle" };
|
||||
static constexpr std::string_view ColorSchemeKey{ "colorScheme" };
|
||||
static constexpr std::string_view ElevateKey{ "elevate" };
|
||||
static constexpr std::string_view ReloadEnvironmentVariablesKey{ "reloadEnvironmentVariables" };
|
||||
static constexpr std::string_view ContentKey{ "__content" };
|
||||
|
||||
public:
|
||||
NewTerminalArgs(int32_t& profileIndex) :
|
||||
_ProfileIndex{ profileIndex } {
|
||||
NEW_TERMINAL_ARGS(APPEND_ARG_DESCRIPTION);
|
||||
};
|
||||
hstring GenerateName() const;
|
||||
hstring ToCommandline() const;
|
||||
|
||||
@@ -471,6 +470,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
copy->_Elevate = _Elevate;
|
||||
copy->_ReloadEnvironmentVariables = _ReloadEnvironmentVariables;
|
||||
copy->_ContentId = _ContentId;
|
||||
copy->_argDescriptions = _argDescriptions;
|
||||
return *copy;
|
||||
}
|
||||
size_t Hash() const
|
||||
@@ -589,7 +589,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
NewTabArgs() = default;
|
||||
NewTabArgs(const Model::INewContentArgs& terminalArgs) :
|
||||
_ContentArgs{ terminalArgs } {};
|
||||
WINRT_PROPERTY(Model::INewContentArgs, ContentArgs, nullptr);
|
||||
WINRT_PROPERTY(Model::INewContentArgs, ContentArgs, Model::NewTerminalArgs{});
|
||||
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
@@ -632,34 +632,54 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
h.write(ContentArgs());
|
||||
return h.finalize();
|
||||
}
|
||||
uint32_t GetArgCount() const
|
||||
{
|
||||
return _ContentArgs.as<NewTerminalArgs>()->GetArgCount();
|
||||
}
|
||||
Model::ArgDescription GetArgDescriptionAt(uint32_t index) const
|
||||
{
|
||||
return _ContentArgs.as<NewTerminalArgs>()->GetArgDescriptionAt(index);
|
||||
}
|
||||
IInspectable GetArgAt(uint32_t index) const
|
||||
{
|
||||
return _ContentArgs.as<NewTerminalArgs>()->GetArgAt(index);
|
||||
}
|
||||
void SetArgAt(uint32_t index, IInspectable value)
|
||||
{
|
||||
_ContentArgs.as<NewTerminalArgs>()->SetArgAt(index, value);
|
||||
}
|
||||
};
|
||||
|
||||
struct SplitPaneArgs : public SplitPaneArgsT<SplitPaneArgs>
|
||||
{
|
||||
SplitPaneArgs() = default;
|
||||
SplitPaneArgs() {
|
||||
SPLIT_PANE_ARGS(APPEND_ARG_DESCRIPTION)
|
||||
};
|
||||
SplitPaneArgs(SplitType splitMode, SplitDirection direction, float size, const Model::INewContentArgs& terminalArgs) :
|
||||
_SplitMode{ splitMode },
|
||||
_SplitDirection{ direction },
|
||||
_SplitSize{ size },
|
||||
_ContentArgs{ terminalArgs } {};
|
||||
_ContentArgs{ terminalArgs } {
|
||||
SPLIT_PANE_ARGS(APPEND_ARG_DESCRIPTION)
|
||||
};
|
||||
SplitPaneArgs(SplitDirection direction, float size, const Model::INewContentArgs& terminalArgs) :
|
||||
_SplitDirection{ direction },
|
||||
_SplitSize{ size },
|
||||
_ContentArgs{ terminalArgs } {};
|
||||
_ContentArgs{ terminalArgs } {
|
||||
SPLIT_PANE_ARGS(APPEND_ARG_DESCRIPTION)
|
||||
};
|
||||
SplitPaneArgs(SplitDirection direction, const Model::INewContentArgs& terminalArgs) :
|
||||
_SplitDirection{ direction },
|
||||
_ContentArgs{ terminalArgs } {};
|
||||
_ContentArgs{ terminalArgs } {
|
||||
SPLIT_PANE_ARGS(APPEND_ARG_DESCRIPTION)
|
||||
};
|
||||
SplitPaneArgs(SplitType splitMode) :
|
||||
_SplitMode{ splitMode } {};
|
||||
_SplitMode{ splitMode } {
|
||||
SPLIT_PANE_ARGS(APPEND_ARG_DESCRIPTION)
|
||||
};
|
||||
|
||||
ACTION_ARG(Model::SplitDirection, SplitDirection, SplitDirection::Automatic);
|
||||
WINRT_PROPERTY(Model::INewContentArgs, ContentArgs, nullptr);
|
||||
ACTION_ARG(SplitType, SplitMode, SplitType::Manual);
|
||||
ACTION_ARG(float, SplitSize, 0.5f);
|
||||
|
||||
static constexpr std::string_view SplitKey{ "split" };
|
||||
static constexpr std::string_view SplitModeKey{ "splitMode" };
|
||||
static constexpr std::string_view SplitSizeKey{ "size" };
|
||||
SPLIT_PANE_ARGS(DECLARE_ARGS);
|
||||
WINRT_PROPERTY(Model::INewContentArgs, ContentArgs, Model::NewTerminalArgs{});
|
||||
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
@@ -681,7 +701,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<SplitPaneArgs>();
|
||||
JsonUtils::GetValueForKey(json, SplitKey, args->_SplitDirection);
|
||||
JsonUtils::GetValueForKey(json, SplitDirectionKey, args->_SplitDirection);
|
||||
JsonUtils::GetValueForKey(json, SplitModeKey, args->_SplitMode);
|
||||
JsonUtils::GetValueForKey(json, SplitSizeKey, args->_SplitSize);
|
||||
if (args->SplitSize() >= 1 || args->SplitSize() <= 0)
|
||||
@@ -701,7 +721,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
}
|
||||
const auto args{ get_self<SplitPaneArgs>(val) };
|
||||
auto json{ ContentArgsToJson(args->_ContentArgs) };
|
||||
JsonUtils::SetValueForKey(json, SplitKey, args->_SplitDirection);
|
||||
JsonUtils::SetValueForKey(json, SplitDirectionKey, args->_SplitDirection);
|
||||
JsonUtils::SetValueForKey(json, SplitModeKey, args->_SplitMode);
|
||||
JsonUtils::SetValueForKey(json, SplitSizeKey, args->_SplitSize);
|
||||
return json;
|
||||
@@ -713,6 +733,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
copy->_ContentArgs = _ContentArgs.Copy();
|
||||
copy->_SplitMode = _SplitMode;
|
||||
copy->_SplitSize = _SplitSize;
|
||||
copy->_argDescriptions = _argDescriptions;
|
||||
return *copy;
|
||||
}
|
||||
size_t Hash() const
|
||||
@@ -724,6 +745,59 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
h.write(SplitSize());
|
||||
return h.finalize();
|
||||
}
|
||||
uint32_t GetArgCount() const
|
||||
{
|
||||
if (const auto newTermArgs = _ContentArgs.try_as<NewTerminalArgs>())
|
||||
{
|
||||
return newTermArgs->GetArgCount() + gsl::narrow<uint32_t>(_argDescriptions.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
return gsl::narrow<uint32_t>(_argDescriptions.size());
|
||||
}
|
||||
}
|
||||
Model::ArgDescription GetArgDescriptionAt(uint32_t index) const
|
||||
{
|
||||
const auto additionalArgCount = gsl::narrow<uint32_t>(_argDescriptions.size());
|
||||
if (index < additionalArgCount)
|
||||
{
|
||||
return _argDescriptions.at(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _ContentArgs.as<NewTerminalArgs>()->GetArgDescriptionAt(index - additionalArgCount);
|
||||
}
|
||||
}
|
||||
IInspectable GetArgAt(uint32_t index) const
|
||||
{
|
||||
const auto additionalArgCount = gsl::narrow<uint32_t>(_argDescriptions.size());
|
||||
if (index < additionalArgCount)
|
||||
{
|
||||
uint32_t curIndex{ 0 };
|
||||
SPLIT_PANE_ARGS(GET_ARG_BY_INDEX);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _ContentArgs.as<NewTerminalArgs>()->GetArgAt(index - additionalArgCount);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void SetArgAt(uint32_t index, IInspectable value)
|
||||
{
|
||||
const auto additionalArgCount = gsl::narrow<uint32_t>(_argDescriptions.size());
|
||||
if (index < additionalArgCount)
|
||||
{
|
||||
uint32_t curIndex{ 0 };
|
||||
SPLIT_PANE_ARGS(SET_ARG_BY_INDEX);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ContentArgs.as<NewTerminalArgs>()->SetArgAt(index - additionalArgCount, value);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<ArgDescription> _argDescriptions;
|
||||
};
|
||||
|
||||
struct NewWindowArgs : public NewWindowArgsT<NewWindowArgs>
|
||||
@@ -731,7 +805,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
NewWindowArgs() = default;
|
||||
NewWindowArgs(const Model::INewContentArgs& terminalArgs) :
|
||||
_ContentArgs{ terminalArgs } {};
|
||||
WINRT_PROPERTY(Model::INewContentArgs, ContentArgs, nullptr);
|
||||
WINRT_PROPERTY(Model::INewContentArgs, ContentArgs, Model::NewTerminalArgs{});
|
||||
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
@@ -774,6 +848,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
h.write(ContentArgs());
|
||||
return h.finalize();
|
||||
}
|
||||
uint32_t GetArgCount() const
|
||||
{
|
||||
return _ContentArgs.as<NewTerminalArgs>()->GetArgCount();
|
||||
}
|
||||
Model::ArgDescription GetArgDescriptionAt(uint32_t index) const
|
||||
{
|
||||
return _ContentArgs.as<NewTerminalArgs>()->GetArgDescriptionAt(index);
|
||||
}
|
||||
IInspectable GetArgAt(uint32_t index) const
|
||||
{
|
||||
return _ContentArgs.as<NewTerminalArgs>()->GetArgAt(index);
|
||||
}
|
||||
void SetArgAt(uint32_t index, IInspectable value)
|
||||
{
|
||||
_ContentArgs.as<NewTerminalArgs>()->SetArgAt(index, value);
|
||||
}
|
||||
};
|
||||
|
||||
ACTION_ARGS_STRUCT(CopyTextArgs, COPY_TEXT_ARGS);
|
||||
@@ -913,6 +1003,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
h.write(winrt::get_abi(_Actions));
|
||||
return h.finalize();
|
||||
}
|
||||
uint32_t GetArgCount() const
|
||||
{
|
||||
return _Actions.Size();
|
||||
}
|
||||
Model::ArgDescription GetArgDescriptionAt(uint32_t /*index*/) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
IInspectable GetArgAt(uint32_t /*index*/) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
void SetArgAt(uint32_t /*index*/, IInspectable /*value*/)
|
||||
{
|
||||
throw winrt::hresult_not_implemented();
|
||||
}
|
||||
};
|
||||
|
||||
ACTION_ARGS_STRUCT(AdjustOpacityArgs, ADJUST_OPACITY_ARGS);
|
||||
|
||||
@@ -5,12 +5,24 @@ import "Command.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Model
|
||||
{
|
||||
struct ArgDescription
|
||||
{
|
||||
String Name;
|
||||
String Type;
|
||||
Boolean Required;
|
||||
};
|
||||
|
||||
interface IActionArgs
|
||||
{
|
||||
Boolean Equals(IActionArgs other);
|
||||
String GenerateName();
|
||||
IActionArgs Copy();
|
||||
UInt64 Hash();
|
||||
|
||||
UInt32 GetArgCount();
|
||||
ArgDescription GetArgDescriptionAt(UInt32 index);
|
||||
IInspectable GetArgAt(UInt32 index);
|
||||
void SetArgAt(UInt32 index, Object value);
|
||||
};
|
||||
|
||||
interface IActionEventArgs
|
||||
@@ -168,6 +180,11 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
UInt64 ContentId{ get; set; };
|
||||
|
||||
String ToCommandline();
|
||||
|
||||
UInt32 GetArgCount();
|
||||
ArgDescription GetArgDescriptionAt(UInt32 index);
|
||||
IInspectable GetArgAt(UInt32 index);
|
||||
void SetArgAt(UInt32 index, Object value);
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass ActionEventArgs : IActionEventArgs
|
||||
@@ -230,7 +247,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
{
|
||||
SendInputArgs(String input);
|
||||
|
||||
String Input { get; };
|
||||
String Input;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SplitPaneArgs : IActionArgs
|
||||
@@ -309,7 +326,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
[default_interface] runtimeclass CloseTabArgs : IActionArgs
|
||||
{
|
||||
CloseTabArgs(UInt32 tabIndex);
|
||||
Windows.Foundation.IReference<UInt32> Index { get; };
|
||||
Windows.Foundation.IReference<UInt32> Index;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass MoveTabArgs : IActionArgs
|
||||
|
||||
@@ -66,12 +66,43 @@ struct InitListPlaceholder
|
||||
#define CTOR_INIT(type, name, jsonKey, required, ...) \
|
||||
_##name{ name##Param },
|
||||
|
||||
// append this argument's description to the internal vector
|
||||
#define APPEND_ARG_DESCRIPTION(type, name, jsonKey, required, ...) \
|
||||
_argDescriptions.push_back({ L## #name, L## #type, std::wstring_view(L## #required) != L"false" });
|
||||
|
||||
// check each property in the Equals() method. You'll note there's a stray
|
||||
// `true` in the definition of Equals() below, that's to deal with trailing
|
||||
// commas
|
||||
#define EQUALS_ARGS(type, name, jsonKey, required, ...) \
|
||||
&&(otherAsUs->_##name == _##name)
|
||||
|
||||
// getter and setter for each property by index
|
||||
#define GET_ARG_BY_INDEX(type, name, jsonKey, required, ...) \
|
||||
if (index == curIndex++) \
|
||||
{ \
|
||||
if (_##name.has_value()) \
|
||||
{ \
|
||||
return winrt::box_value(_##name.value()); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
return winrt::box_value(static_cast<type>(__VA_ARGS__)); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SET_ARG_BY_INDEX(type, name, jsonKey, required, ...) \
|
||||
if (index == curIndex++) \
|
||||
{ \
|
||||
if (value) \
|
||||
{ \
|
||||
_##name = winrt::unbox_value<type>(value); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
_##name = std::nullopt; \
|
||||
} \
|
||||
}
|
||||
|
||||
// JSON deserialization. If the parameter is required to pass any validation,
|
||||
// add that as the `required` parameter here, as the body of a conditional
|
||||
// EX: For the RESIZE_PANE_ARGS
|
||||
@@ -79,11 +110,11 @@ struct InitListPlaceholder
|
||||
// the bit
|
||||
// args->ResizeDirection() == ResizeDirection::None
|
||||
// is used as the conditional for the validation here.
|
||||
#define FROM_JSON_ARGS(type, name, jsonKey, required, ...) \
|
||||
JsonUtils::GetValueForKey(json, jsonKey, args->_##name); \
|
||||
if (required) \
|
||||
{ \
|
||||
return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } }; \
|
||||
#define FROM_JSON_ARGS(type, name, jsonKey, required, ...) \
|
||||
JsonUtils::GetValueForKey(json, jsonKey, args->_##name); \
|
||||
if (required) \
|
||||
{ \
|
||||
return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } }; \
|
||||
}
|
||||
|
||||
// JSON serialization
|
||||
@@ -111,53 +142,110 @@ struct InitListPlaceholder
|
||||
// * NewTerminalArgs has a ToCommandline method it needs to additionally declare.
|
||||
// * GlobalSummonArgs has the QuakeModeFromJson helper
|
||||
|
||||
#define ACTION_ARG_BODY(className, argsMacro) \
|
||||
className() = default; \
|
||||
className( \
|
||||
argsMacro(CTOR_PARAMS) InitListPlaceholder = {}) : \
|
||||
argsMacro(CTOR_INIT) _placeholder{} {}; \
|
||||
argsMacro(DECLARE_ARGS); \
|
||||
\
|
||||
private: \
|
||||
InitListPlaceholder _placeholder; \
|
||||
\
|
||||
public: \
|
||||
hstring GenerateName() const; \
|
||||
bool Equals(const IActionArgs& other) \
|
||||
{ \
|
||||
auto otherAsUs = other.try_as<className>(); \
|
||||
if (otherAsUs) \
|
||||
{ \
|
||||
return true argsMacro(EQUALS_ARGS); \
|
||||
} \
|
||||
return false; \
|
||||
}; \
|
||||
static FromJsonResult FromJson(const Json::Value& json) \
|
||||
{ \
|
||||
auto args = winrt::make_self<className>(); \
|
||||
argsMacro(FROM_JSON_ARGS); \
|
||||
return { *args, {} }; \
|
||||
} \
|
||||
static Json::Value ToJson(const IActionArgs& val) \
|
||||
{ \
|
||||
if (!val) \
|
||||
{ \
|
||||
return {}; \
|
||||
} \
|
||||
Json::Value json{ Json::ValueType::objectValue }; \
|
||||
const auto args{ get_self<className>(val) }; \
|
||||
argsMacro(TO_JSON_ARGS); \
|
||||
return json; \
|
||||
} \
|
||||
IActionArgs Copy() const \
|
||||
{ \
|
||||
auto copy{ winrt::make_self<className>() }; \
|
||||
argsMacro(COPY_ARGS); \
|
||||
return *copy; \
|
||||
} \
|
||||
size_t Hash() const \
|
||||
{ \
|
||||
til::hasher h; \
|
||||
argsMacro(HASH_ARGS); \
|
||||
return h.finalize(); \
|
||||
#define ACTION_ARG_BODY(className, argsMacro) \
|
||||
className() { argsMacro(APPEND_ARG_DESCRIPTION) }; \
|
||||
className( \
|
||||
argsMacro(CTOR_PARAMS) InitListPlaceholder = {}) : \
|
||||
argsMacro(CTOR_INIT) _placeholder{} { \
|
||||
argsMacro(APPEND_ARG_DESCRIPTION) \
|
||||
}; \
|
||||
argsMacro(DECLARE_ARGS); \
|
||||
\
|
||||
private: \
|
||||
InitListPlaceholder _placeholder; \
|
||||
std::vector<ArgDescription> _argDescriptions; \
|
||||
\
|
||||
public: \
|
||||
hstring GenerateName() const; \
|
||||
bool Equals(const IActionArgs& other) \
|
||||
{ \
|
||||
auto otherAsUs = other.try_as<className>(); \
|
||||
if (otherAsUs) \
|
||||
{ \
|
||||
return true argsMacro(EQUALS_ARGS); \
|
||||
} \
|
||||
return false; \
|
||||
}; \
|
||||
static FromJsonResult FromJson(const Json::Value& json) \
|
||||
{ \
|
||||
auto args = winrt::make_self<className>(); \
|
||||
argsMacro(FROM_JSON_ARGS); \
|
||||
return { *args, {} }; \
|
||||
} \
|
||||
static Json::Value ToJson(const IActionArgs& val) \
|
||||
{ \
|
||||
if (!val) \
|
||||
{ \
|
||||
return {}; \
|
||||
} \
|
||||
Json::Value json{ Json::ValueType::objectValue }; \
|
||||
const auto args{ get_self<className>(val) }; \
|
||||
argsMacro(TO_JSON_ARGS); \
|
||||
return json; \
|
||||
} \
|
||||
IActionArgs Copy() const \
|
||||
{ \
|
||||
auto copy{ winrt::make_self<className>() }; \
|
||||
argsMacro(COPY_ARGS); \
|
||||
copy->_argDescriptions = _argDescriptions; \
|
||||
return *copy; \
|
||||
} \
|
||||
size_t Hash() const \
|
||||
{ \
|
||||
til::hasher h; \
|
||||
argsMacro(HASH_ARGS); \
|
||||
return h.finalize(); \
|
||||
} \
|
||||
uint32_t GetArgCount() const \
|
||||
{ \
|
||||
return gsl::narrow<uint32_t>(_argDescriptions.size()); \
|
||||
} \
|
||||
ArgDescription GetArgDescriptionAt(uint32_t index) const \
|
||||
{ \
|
||||
return _argDescriptions.at(index); \
|
||||
} \
|
||||
IInspectable GetArgAt(uint32_t index) const \
|
||||
{ \
|
||||
uint32_t curIndex{ 0 }; \
|
||||
argsMacro(GET_ARG_BY_INDEX) \
|
||||
return nullptr; \
|
||||
} \
|
||||
void SetArgAt(uint32_t index, IInspectable value) \
|
||||
{ \
|
||||
uint32_t curIndex{ 0 }; \
|
||||
argsMacro(SET_ARG_BY_INDEX) \
|
||||
}
|
||||
|
||||
#define PARTIAL_ACTION_ARG_BODY(className, argsMacro) \
|
||||
className(){ argsMacro(APPEND_ARG_DESCRIPTION) }; \
|
||||
className( \
|
||||
argsMacro(CTOR_PARAMS) InitListPlaceholder = {}) : \
|
||||
argsMacro(CTOR_INIT) _placeholder{} { \
|
||||
argsMacro(APPEND_ARG_DESCRIPTION) \
|
||||
}; \
|
||||
argsMacro(DECLARE_ARGS); \
|
||||
\
|
||||
private: \
|
||||
InitListPlaceholder _placeholder; \
|
||||
std::vector<ArgDescription> _argDescriptions; \
|
||||
\
|
||||
public: \
|
||||
uint32_t GetArgCount() const \
|
||||
{ \
|
||||
return gsl::narrow<uint32_t>(_argDescriptions.size()); \
|
||||
} \
|
||||
ArgDescription GetArgDescriptionAt(uint32_t index) const \
|
||||
{ \
|
||||
return _argDescriptions.at(index); \
|
||||
} \
|
||||
IInspectable GetArgAt(uint32_t index) const \
|
||||
{ \
|
||||
uint32_t curIndex{ 0 }; \
|
||||
argsMacro(GET_ARG_BY_INDEX) \
|
||||
return nullptr; \
|
||||
} \
|
||||
void SetArgAt(uint32_t index, IInspectable value) \
|
||||
{ \
|
||||
uint32_t curIndex{ 0 }; \
|
||||
argsMacro(SET_ARG_BY_INDEX) \
|
||||
}
|
||||
|
||||
@@ -380,6 +380,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
return _ResolvedKeyToActionMapCache.GetView();
|
||||
}
|
||||
|
||||
IVectorView<Model::Command> ActionMap::AllCommands()
|
||||
{
|
||||
if (!_ResolvedKeyToActionMapCache)
|
||||
{
|
||||
_RefreshKeyBindingCaches();
|
||||
}
|
||||
return _AllCommandsCache.GetView();
|
||||
}
|
||||
|
||||
void ActionMap::_RefreshKeyBindingCaches()
|
||||
{
|
||||
_CumulativeKeyToActionMapCache.clear();
|
||||
@@ -387,6 +396,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
_CumulativeActionToKeyMapCache.clear();
|
||||
std::unordered_map<KeyChord, Model::Command, KeyChordHash, KeyChordEquality> globalHotkeys;
|
||||
std::unordered_map<KeyChord, Model::Command, KeyChordHash, KeyChordEquality> resolvedKeyToActionMap;
|
||||
std::vector<Model::Command> allCommandsVector;
|
||||
|
||||
_PopulateCumulativeKeyMaps(_CumulativeKeyToActionMapCache, _CumulativeActionToKeyMapCache);
|
||||
_PopulateCumulativeActionMap(_CumulativeIDToActionMapCache);
|
||||
@@ -406,8 +416,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [_, cmd] : _CumulativeIDToActionMapCache)
|
||||
{
|
||||
allCommandsVector.emplace_back(cmd);
|
||||
}
|
||||
|
||||
_ResolvedKeyToActionMapCache = single_threaded_map(std::move(resolvedKeyToActionMap));
|
||||
_GlobalHotkeysCache = single_threaded_map(std::move(globalHotkeys));
|
||||
_AllCommandsCache = single_threaded_vector(std::move(allCommandsVector));
|
||||
}
|
||||
|
||||
com_ptr<ActionMap> ActionMap::Copy() const
|
||||
@@ -421,7 +437,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
actionMap->_ActionMap.reserve(_ActionMap.size());
|
||||
for (const auto& [actionID, cmd] : _ActionMap)
|
||||
{
|
||||
actionMap->_ActionMap.emplace(actionID, *winrt::get_self<Command>(cmd)->Copy());
|
||||
const auto copiedCmd = winrt::get_self<Command>(cmd)->Copy();
|
||||
actionMap->_ActionMap.emplace(actionID, *copiedCmd);
|
||||
copiedCmd->IDChanged({ actionMap.get(), &ActionMap::_CommandIDChangedHandler });
|
||||
}
|
||||
|
||||
// Name --> Command
|
||||
@@ -541,6 +559,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
}
|
||||
}
|
||||
}
|
||||
cmd.IDChanged({ this, &ActionMap::_CommandIDChangedHandler });
|
||||
_ActionMap.insert_or_assign(cmdID, cmd);
|
||||
}
|
||||
}
|
||||
@@ -573,6 +592,44 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
_changeLog.emplace(KeysKey);
|
||||
}
|
||||
|
||||
void ActionMap::_CommandIDChangedHandler(const Model::Command& senderCmd, const winrt::hstring& oldID)
|
||||
{
|
||||
const auto newID = senderCmd.ID();
|
||||
if (newID != oldID)
|
||||
{
|
||||
if (const auto foundCmd{ _GetActionByID(newID) })
|
||||
{
|
||||
if (foundCmd.ActionAndArgs() != senderCmd.ActionAndArgs())
|
||||
{
|
||||
// we found a command that has the same ID as this one, but that command has different ActionAndArgs
|
||||
// this means that foundCommand's action and/or args have been changed since its ID was generated,
|
||||
// generate a new one for it
|
||||
// Note: this is recursive! Found command's ID being changed lands us back in here to resolve any cascading collisions
|
||||
foundCmd.GenerateID();
|
||||
}
|
||||
}
|
||||
// update _ActionMap with the ID change
|
||||
_ActionMap.erase(oldID);
|
||||
_ActionMap.emplace(newID, senderCmd);
|
||||
|
||||
// update _KeyMap so that all keys that pointed to the old ID now point to the new ID
|
||||
std::unordered_set<KeyChord, KeyChordHash, KeyChordEquality> keysToRemap{};
|
||||
for (const auto& [keys, cmdID] : _KeyMap)
|
||||
{
|
||||
if (cmdID == oldID)
|
||||
{
|
||||
keysToRemap.insert(keys);
|
||||
}
|
||||
}
|
||||
for (const auto& keys : keysToRemap)
|
||||
{
|
||||
_KeyMap.erase(keys);
|
||||
_KeyMap.emplace(keys, newID);
|
||||
}
|
||||
}
|
||||
_RefreshKeyBindingCaches();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines whether the given key chord is explicitly unbound
|
||||
// Arguments:
|
||||
@@ -686,6 +743,24 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IVector<Control::KeyChord> ActionMap::AllKeyBindingsForAction(const winrt::hstring& cmdID)
|
||||
{
|
||||
if (!_ResolvedKeyToActionMapCache)
|
||||
{
|
||||
_RefreshKeyBindingCaches();
|
||||
}
|
||||
|
||||
std::vector<Control::KeyChord> keybindingsList;
|
||||
for (const auto& [key, ID] : _CumulativeKeyToActionMapCache)
|
||||
{
|
||||
if (ID == cmdID)
|
||||
{
|
||||
keybindingsList.emplace_back(key);
|
||||
}
|
||||
}
|
||||
return single_threaded_vector(std::move(keybindingsList));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Rebinds a key binding to a new key chord
|
||||
// Arguments:
|
||||
@@ -741,6 +816,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void ActionMap::AddKeyBinding(Control::KeyChord keys, const winrt::hstring& cmdID)
|
||||
{
|
||||
_KeyMap.insert_or_assign(keys, cmdID);
|
||||
_RefreshKeyBindingCaches();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Add a new key binding
|
||||
// - If the key chord is already in use, the conflicting command is overwritten.
|
||||
@@ -757,6 +838,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
AddAction(*cmd, keys);
|
||||
}
|
||||
|
||||
void ActionMap::DeleteUserCommand(const winrt::hstring& cmdID)
|
||||
{
|
||||
_ActionMap.erase(cmdID);
|
||||
_RefreshKeyBindingCaches();
|
||||
}
|
||||
|
||||
// This is a helper to aid in sorting commands by their `Name`s, alphabetically.
|
||||
static bool _compareSchemeNames(const ColorScheme& lhs, const ColorScheme& rhs)
|
||||
{
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
Windows::Foundation::Collections::IMapView<hstring, Model::Command> NameMap();
|
||||
Windows::Foundation::Collections::IMapView<Control::KeyChord, Model::Command> GlobalHotkeys();
|
||||
Windows::Foundation::Collections::IMapView<Control::KeyChord, Model::Command> KeyBindings();
|
||||
Windows::Foundation::Collections::IVectorView<Model::Command> AllCommands();
|
||||
com_ptr<ActionMap> Copy() const;
|
||||
|
||||
// queries
|
||||
@@ -63,6 +64,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
Model::Command GetActionByID(const winrt::hstring& cmdID) const;
|
||||
bool IsKeyChordExplicitlyUnbound(const Control::KeyChord& keys) const;
|
||||
Control::KeyChord GetKeyBindingForAction(const winrt::hstring& cmdID);
|
||||
Windows::Foundation::Collections::IVector<Control::KeyChord> AllKeyBindingsForAction(const winrt::hstring& cmdID);
|
||||
|
||||
// population
|
||||
void AddAction(const Model::Command& cmd, const Control::KeyChord& keys);
|
||||
@@ -78,7 +80,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// modification
|
||||
bool RebindKeys(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys);
|
||||
void DeleteKeyBinding(const Control::KeyChord& keys);
|
||||
void AddKeyBinding(Control::KeyChord keys, const winrt::hstring& cmdID);
|
||||
void RegisterKeyBinding(Control::KeyChord keys, Model::ActionAndArgs action);
|
||||
void DeleteUserCommand(const winrt::hstring& cmdID);
|
||||
void AddSendInputAction(winrt::hstring name, winrt::hstring input, const Control::KeyChord keys);
|
||||
|
||||
Windows::Foundation::Collections::IVector<Model::Command> ExpandedCommands();
|
||||
@@ -105,6 +109,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
std::vector<Model::Command> _updateLocalSnippetCache(winrt::hstring currentWorkingDirectory);
|
||||
|
||||
void _CommandIDChangedHandler(const Model::Command& senderCmd, const winrt::hstring& oldID);
|
||||
|
||||
Windows::Foundation::Collections::IMap<hstring, Model::ActionAndArgs> _AvailableActionsCache{ nullptr };
|
||||
Windows::Foundation::Collections::IMap<hstring, Model::Command> _NameMapCache{ nullptr };
|
||||
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _GlobalHotkeysCache{ nullptr };
|
||||
@@ -136,6 +142,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// This is effectively a combination of _CumulativeKeyMapCache and _CumulativeActionMapCache and its purpose is so that
|
||||
// we can give the SUI a view of the key chords and the commands they map to
|
||||
Windows::Foundation::Collections::IMap<Control::KeyChord, Model::Command> _ResolvedKeyToActionMapCache{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<Model::Command> _AllCommandsCache{ nullptr };
|
||||
|
||||
til::shared_mutex<std::unordered_map<hstring, std::vector<Model::Command>>> _cwdLocalSnippetsCache{};
|
||||
|
||||
|
||||
@@ -13,12 +13,14 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Command GetActionByKeyChord(Microsoft.Terminal.Control.KeyChord keys);
|
||||
Command GetActionByID(String cmdID);
|
||||
Microsoft.Terminal.Control.KeyChord GetKeyBindingForAction(String cmdID);
|
||||
IVector<Microsoft.Terminal.Control.KeyChord> AllKeyBindingsForAction(String cmdID);
|
||||
|
||||
Windows.Foundation.Collections.IMapView<String, ActionAndArgs> AvailableActions { get; };
|
||||
|
||||
Windows.Foundation.Collections.IMapView<String, Command> NameMap { get; };
|
||||
Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Command> KeyBindings { get; };
|
||||
Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Command> GlobalHotkeys { get; };
|
||||
Windows.Foundation.Collections.IVectorView<Command> AllCommands { get; };
|
||||
|
||||
IVector<Command> ExpandedCommands { get; };
|
||||
|
||||
@@ -27,9 +29,11 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
|
||||
[default_interface] runtimeclass ActionMap : IActionMapView
|
||||
{
|
||||
void AddAction(Command cmd, Microsoft.Terminal.Control.KeyChord keys);
|
||||
void RebindKeys(Microsoft.Terminal.Control.KeyChord oldKeys, Microsoft.Terminal.Control.KeyChord newKeys);
|
||||
void DeleteKeyBinding(Microsoft.Terminal.Control.KeyChord keys);
|
||||
|
||||
void DeleteUserCommand(String cmdID);
|
||||
void AddKeyBinding(Microsoft.Terminal.Control.KeyChord keys, String cmdID);
|
||||
void RegisterKeyBinding(Microsoft.Terminal.Control.KeyChord keys, ActionAndArgs action);
|
||||
void AddSendInputAction(String name, String input, Microsoft.Terminal.Control.KeyChord keys);
|
||||
}
|
||||
|
||||
@@ -309,6 +309,31 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
_throttler();
|
||||
}
|
||||
|
||||
bool ApplicationState::DismissBadge(const hstring& badgeId)
|
||||
{
|
||||
bool inserted{ false };
|
||||
{
|
||||
const auto state = _state.lock();
|
||||
if (!state->DismissedBadges)
|
||||
{
|
||||
state->DismissedBadges = std::unordered_set<hstring>{};
|
||||
}
|
||||
inserted = state->DismissedBadges->insert(badgeId).second;
|
||||
}
|
||||
_throttler();
|
||||
return inserted;
|
||||
}
|
||||
|
||||
bool ApplicationState::BadgeDismissed(const hstring& badgeId) const
|
||||
{
|
||||
const auto state = _state.lock_shared();
|
||||
if (state->DismissedBadges)
|
||||
{
|
||||
return state->DismissedBadges->contains(badgeId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate all getter/setters
|
||||
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
|
||||
type ApplicationState::name() const noexcept \
|
||||
|
||||
@@ -40,7 +40,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
X(FileSource::Local, Windows::Foundation::Collections::IVector<Model::WindowLayout>, PersistedWindowLayouts, "persistedWindowLayouts") \
|
||||
X(FileSource::Shared, Windows::Foundation::Collections::IVector<hstring>, RecentCommands, "recentCommands") \
|
||||
X(FileSource::Shared, Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage>, DismissedMessages, "dismissedMessages") \
|
||||
X(FileSource::Local, Windows::Foundation::Collections::IVector<hstring>, AllowedCommandlines, "allowedCommandlines")
|
||||
X(FileSource::Local, Windows::Foundation::Collections::IVector<hstring>, AllowedCommandlines, "allowedCommandlines") \
|
||||
X(FileSource::Local, std::unordered_set<hstring>, DismissedBadges, "dismissedBadges")
|
||||
|
||||
struct WindowLayout : WindowLayoutT<WindowLayout>
|
||||
{
|
||||
@@ -70,6 +71,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
Json::Value ToJson(FileSource parseSource) const noexcept;
|
||||
|
||||
void AppendPersistedWindowLayout(Model::WindowLayout layout);
|
||||
bool DismissBadge(const hstring& badgeId);
|
||||
bool BadgeDismissed(const hstring& badgeId) const;
|
||||
|
||||
// State getters/setters
|
||||
#define MTSM_APPLICATION_STATE_GEN(source, type, name, key, ...) \
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
void Reset();
|
||||
|
||||
void AppendPersistedWindowLayout(WindowLayout layout);
|
||||
Boolean DismissBadge(String badgeId);
|
||||
Boolean BadgeDismissed(String badgeId);
|
||||
|
||||
String SettingsHash;
|
||||
Windows.Foundation.Collections.IVector<WindowLayout> PersistedWindowLayouts;
|
||||
|
||||
@@ -8,16 +8,29 @@
|
||||
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
#include "DynamicProfileUtils.h"
|
||||
#include <LibraryResources.h>
|
||||
|
||||
using namespace ::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
|
||||
std::wstring_view GENERATOR_ICON_PATH{ L"ms-appx:///ProfileGeneratorIcons/AzureCloudShell.png" };
|
||||
|
||||
std::wstring_view AzureCloudShellGenerator::GetNamespace() const noexcept
|
||||
{
|
||||
return AzureGeneratorNamespace;
|
||||
}
|
||||
|
||||
std::wstring_view AzureCloudShellGenerator::GetDisplayName() const noexcept
|
||||
{
|
||||
return RS_(L"AzureCloudShellGeneratorDisplayName");
|
||||
}
|
||||
|
||||
std::wstring_view AzureCloudShellGenerator::GetIcon() const noexcept
|
||||
{
|
||||
return GENERATOR_ICON_PATH;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Checks if the Azure Cloud shell is available on this platform, and if it
|
||||
// is, creates a profile to be able to launch it.
|
||||
@@ -25,7 +38,7 @@ std::wstring_view AzureCloudShellGenerator::GetNamespace() const noexcept
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a vector with the Azure Cloud Shell connection profile, if available.
|
||||
void AzureCloudShellGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
|
||||
void AzureCloudShellGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles)
|
||||
{
|
||||
if (AzureConnection::IsAzureConnectionAvailable())
|
||||
{
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
{
|
||||
public:
|
||||
std::wstring_view GetNamespace() const noexcept override;
|
||||
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const override;
|
||||
std::wstring_view GetDisplayName() const noexcept override;
|
||||
std::wstring_view GetIcon() const noexcept override;
|
||||
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) override;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "DefaultTerminal.h"
|
||||
#include "FileUtils.h"
|
||||
|
||||
#include "AllShortcutActions.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
#include <VersionHelpers.h>
|
||||
#include <WtExeUtils.h>
|
||||
@@ -113,6 +115,10 @@ Model::CascadiaSettings CascadiaSettings::Copy() const
|
||||
settings->_globals = _globals->Copy();
|
||||
settings->_allProfiles = winrt::single_threaded_observable_vector(std::move(allProfiles));
|
||||
settings->_activeProfiles = winrt::single_threaded_observable_vector(std::move(activeProfiles));
|
||||
|
||||
// extension packages don't need a deep clone
|
||||
// because they're fully immutable. We can just copy the reference over instead.
|
||||
settings->_extensionPackages = _extensionPackages;
|
||||
}
|
||||
|
||||
// load errors
|
||||
@@ -173,6 +179,16 @@ IObservableVector<Model::Profile> CascadiaSettings::ActiveProfiles() const noexc
|
||||
return _activeProfiles;
|
||||
}
|
||||
|
||||
IVectorView<Model::ExtensionPackage> CascadiaSettings::Extensions()
|
||||
{
|
||||
if (!_extensionPackages)
|
||||
{
|
||||
// Lazy load the ExtensionPackage objects
|
||||
_extensionPackages = winrt::single_threaded_vector<Model::ExtensionPackage>(std::move(SettingsLoader::LoadExtensionPackages()));
|
||||
}
|
||||
return _extensionPackages.GetView();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns the globally configured keybindings
|
||||
// Arguments:
|
||||
@@ -980,6 +996,109 @@ winrt::hstring CascadiaSettings::ApplicationVersion()
|
||||
return RS_(L"ApplicationVersionUnknown");
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::Collections::IMap<Model::ShortcutAction, winrt::hstring> CascadiaSettings::AvailableShortcutActionsAndNames()
|
||||
{
|
||||
std::map<ShortcutAction, winrt::hstring> availableShortcutActionsAndNames;
|
||||
|
||||
#define ON_ALL_ACTIONS(action) availableShortcutActionsAndNames.emplace(ShortcutAction::action, RS_(L## #action));
|
||||
ALL_SHORTCUT_ACTIONS
|
||||
// Don't include internal actions here
|
||||
#undef ON_ALL_ACTIONS
|
||||
|
||||
return single_threaded_map(std::move(availableShortcutActionsAndNames));
|
||||
}
|
||||
|
||||
Model::IActionArgs CascadiaSettings::GetEmptyArgsForAction(Model::ShortcutAction shortcutAction)
|
||||
{
|
||||
switch (shortcutAction)
|
||||
{
|
||||
case Model::ShortcutAction::CopyText:
|
||||
return winrt::make<CopyTextArgs>();
|
||||
case Model::ShortcutAction::MovePane:
|
||||
return winrt::make<MovePaneArgs>();
|
||||
case Model::ShortcutAction::SwitchToTab:
|
||||
return winrt::make<SwitchToTabArgs>();
|
||||
case Model::ShortcutAction::ResizePane:
|
||||
return winrt::make<ResizePaneArgs>();
|
||||
case Model::ShortcutAction::MoveFocus:
|
||||
return winrt::make<MoveFocusArgs>();
|
||||
case Model::ShortcutAction::SwapPane:
|
||||
return winrt::make<SwapPaneArgs>();
|
||||
case Model::ShortcutAction::AdjustFontSize:
|
||||
return winrt::make<AdjustFontSizeArgs>();
|
||||
case Model::ShortcutAction::SendInput:
|
||||
return winrt::make<SendInputArgs>();
|
||||
case Model::ShortcutAction::OpenSettings:
|
||||
return winrt::make<OpenSettingsArgs>();
|
||||
case Model::ShortcutAction::SetFocusMode:
|
||||
return winrt::make<SetFocusModeArgs>();
|
||||
case Model::ShortcutAction::SetFullScreen:
|
||||
return winrt::make<SetFullScreenArgs>();
|
||||
case Model::ShortcutAction::SetMaximized:
|
||||
return winrt::make<SetMaximizedArgs>();
|
||||
case Model::ShortcutAction::SetColorScheme:
|
||||
return winrt::make<SetColorSchemeArgs>();
|
||||
case Model::ShortcutAction::RenameTab:
|
||||
return winrt::make<RenameTabArgs>();
|
||||
case Model::ShortcutAction::ExecuteCommandline:
|
||||
return winrt::make<ExecuteCommandlineArgs>();
|
||||
case Model::ShortcutAction::CloseOtherTabs:
|
||||
return winrt::make<CloseOtherTabsArgs>();
|
||||
case Model::ShortcutAction::CloseTabsAfter:
|
||||
return winrt::make<CloseTabsAfterArgs>();
|
||||
case Model::ShortcutAction::CloseTab:
|
||||
return winrt::make<CloseTabArgs>();
|
||||
case Model::ShortcutAction::MoveTab:
|
||||
return winrt::make<MoveTabArgs>();
|
||||
case Model::ShortcutAction::ScrollUp:
|
||||
return winrt::make<ScrollUpArgs>();
|
||||
case Model::ShortcutAction::ScrollDown:
|
||||
return winrt::make<ScrollDownArgs>();
|
||||
case Model::ShortcutAction::ScrollToMark:
|
||||
return winrt::make<ScrollToMarkArgs>();
|
||||
case Model::ShortcutAction::ToggleCommandPalette:
|
||||
return winrt::make<ToggleCommandPaletteArgs>();
|
||||
case Model::ShortcutAction::Suggestions:
|
||||
return winrt::make<SuggestionsArgs>();
|
||||
case Model::ShortcutAction::FindMatch:
|
||||
return winrt::make<FindMatchArgs>();
|
||||
case Model::ShortcutAction::RenameWindow:
|
||||
return winrt::make<RenameWindowArgs>();
|
||||
case Model::ShortcutAction::SearchForText:
|
||||
return winrt::make<SearchForTextArgs>();
|
||||
case Model::ShortcutAction::GlobalSummon:
|
||||
return winrt::make<GlobalSummonArgs>();
|
||||
case Model::ShortcutAction::FocusPane:
|
||||
return winrt::make<FocusPaneArgs>();
|
||||
case Model::ShortcutAction::ExportBuffer:
|
||||
return winrt::make<ExportBufferArgs>();
|
||||
case Model::ShortcutAction::ClearBuffer:
|
||||
return winrt::make<ClearBufferArgs>();
|
||||
case Model::ShortcutAction::AdjustOpacity:
|
||||
return winrt::make<AdjustOpacityArgs>();
|
||||
case Model::ShortcutAction::SelectCommand:
|
||||
return winrt::make<SelectCommandArgs>();
|
||||
case Model::ShortcutAction::SelectOutput:
|
||||
return winrt::make<SelectOutputArgs>();
|
||||
case Model::ShortcutAction::AddMark:
|
||||
return winrt::make<AddMarkArgs>();
|
||||
case Model::ShortcutAction::SetTabColor:
|
||||
return winrt::make<SetTabColorArgs>();
|
||||
case Model::ShortcutAction::PrevTab:
|
||||
return winrt::make<PrevTabArgs>();
|
||||
case Model::ShortcutAction::NextTab:
|
||||
return winrt::make<NextTabArgs>();
|
||||
case Model::ShortcutAction::NewTab:
|
||||
return winrt::make<NewTabArgs>();
|
||||
case Model::ShortcutAction::NewWindow:
|
||||
return winrt::make<NewWindowArgs>();
|
||||
case Model::ShortcutAction::SplitPane:
|
||||
return winrt::make<SplitPaneArgs>();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Determines if we're on an OS platform that supports
|
||||
// the default terminal handoff functionality.
|
||||
|
||||
@@ -18,6 +18,10 @@ Author(s):
|
||||
#pragma once
|
||||
|
||||
#include "CascadiaSettings.g.h"
|
||||
#include "FragmentSettings.g.h"
|
||||
#include "FragmentProfileEntry.g.h"
|
||||
#include "FragmentColorSchemeEntry.g.h"
|
||||
#include "ExtensionPackage.g.h"
|
||||
|
||||
#include "GlobalAppSettings.h"
|
||||
#include "Profile.h"
|
||||
@@ -39,6 +43,28 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
std::runtime_error(message) {}
|
||||
};
|
||||
|
||||
struct ExtensionPackage : ExtensionPackageT<ExtensionPackage>
|
||||
{
|
||||
public:
|
||||
ExtensionPackage(hstring source, FragmentScope scope) :
|
||||
_source{ source },
|
||||
_scope{ scope },
|
||||
_fragments{ winrt::single_threaded_vector<Model::FragmentSettings>() } {}
|
||||
|
||||
hstring Source() const noexcept { return _source; }
|
||||
FragmentScope Scope() const noexcept { return _scope; }
|
||||
Windows::Foundation::Collections::IVectorView<Model::FragmentSettings> FragmentsView() const noexcept { return _fragments.GetView(); }
|
||||
Windows::Foundation::Collections::IVector<Model::FragmentSettings> Fragments() const noexcept { return _fragments; }
|
||||
|
||||
WINRT_PROPERTY(hstring, Icon);
|
||||
WINRT_PROPERTY(hstring, DisplayName);
|
||||
|
||||
private:
|
||||
hstring _source;
|
||||
FragmentScope _scope;
|
||||
Windows::Foundation::Collections::IVector<Model::FragmentSettings> _fragments;
|
||||
};
|
||||
|
||||
struct ParsedSettings
|
||||
{
|
||||
winrt::com_ptr<implementation::GlobalAppSettings> globals;
|
||||
@@ -56,12 +82,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct SettingsLoader
|
||||
{
|
||||
static SettingsLoader Default(const std::string_view& userJSON, const std::string_view& inboxJSON);
|
||||
static std::vector<Model::ExtensionPackage> LoadExtensionPackages();
|
||||
SettingsLoader(const std::string_view& userJSON, const std::string_view& inboxJSON);
|
||||
|
||||
void GenerateProfiles();
|
||||
void GenerateExtensionPackagesFromProfileGenerators();
|
||||
void ApplyRuntimeInitialSettings();
|
||||
void MergeInboxIntoUserSettings();
|
||||
void FindFragmentsAndMergeIntoUserSettings();
|
||||
void FindFragmentsAndMergeIntoUserSettings(bool generateExtensionPackages);
|
||||
void MergeFragmentIntoUserSettings(const winrt::hstring& source, const std::string_view& content);
|
||||
void FinalizeLayering();
|
||||
bool DisableDeletedProfiles();
|
||||
@@ -70,6 +98,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
ParsedSettings inboxSettings;
|
||||
ParsedSettings userSettings;
|
||||
std::unordered_map<hstring, winrt::com_ptr<implementation::ExtensionPackage>> extensionPackageMap;
|
||||
bool duplicateProfile = false;
|
||||
|
||||
private:
|
||||
@@ -81,6 +110,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
const Json::Value& profilesList;
|
||||
const Json::Value& themes;
|
||||
};
|
||||
struct ParseFragmentMetadata
|
||||
{
|
||||
std::wstring_view jsonFilename;
|
||||
FragmentScope scope;
|
||||
};
|
||||
SettingsLoader() = default;
|
||||
|
||||
static std::pair<size_t, size_t> _lineAndColumnFromPosition(const std::string_view& string, const size_t position);
|
||||
static void _rethrowSerializationExceptionWithLocationInfo(const JsonUtils::DeserializationError& e, const std::string_view& settingsString);
|
||||
@@ -88,13 +123,16 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
static const Json::Value& _getJSONValue(const Json::Value& json, const std::string_view& key) noexcept;
|
||||
std::span<const winrt::com_ptr<implementation::Profile>> _getNonUserOriginProfiles() const;
|
||||
void _parse(const OriginTag origin, const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings);
|
||||
void _parseFragment(const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings);
|
||||
void _parseFragment(const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings, const std::optional<ParseFragmentMetadata>& fragmentMeta);
|
||||
static JsonSettings _parseJson(const std::string_view& content);
|
||||
static winrt::com_ptr<implementation::Profile> _parseProfile(const OriginTag origin, const winrt::hstring& source, const Json::Value& profileJson);
|
||||
void _appendProfile(winrt::com_ptr<Profile>&& profile, const winrt::guid& guid, ParsedSettings& settings);
|
||||
void _addUserProfileParent(const winrt::com_ptr<implementation::Profile>& profile);
|
||||
void _addOrMergeUserColorScheme(const winrt::com_ptr<implementation::ColorScheme>& colorScheme);
|
||||
void _executeGenerator(const IDynamicProfileGenerator& generator);
|
||||
bool _addOrMergeUserColorScheme(const winrt::com_ptr<implementation::ColorScheme>& colorScheme);
|
||||
static void _executeGenerator(IDynamicProfileGenerator& generator, std::vector<winrt::com_ptr<implementation::Profile>>& profilesList);
|
||||
void _patchInstallPowerShellProfile();
|
||||
winrt::com_ptr<implementation::ExtensionPackage> _registerFragment(const winrt::Microsoft::Terminal::Settings::Model::FragmentSettings& fragment, FragmentScope scope);
|
||||
Json::StreamWriterBuilder _getJsonStyledWriter();
|
||||
|
||||
std::unordered_set<winrt::hstring, til::transparent_hstring_hash, til::transparent_hstring_equal_to> _ignoredNamespaces;
|
||||
std::set<std::string> themesChangeLog;
|
||||
@@ -115,6 +153,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
static winrt::hstring ApplicationVersion();
|
||||
static bool IsPortableMode();
|
||||
|
||||
static Windows::Foundation::Collections::IMap<Model::ShortcutAction, winrt::hstring> AvailableShortcutActionsAndNames();
|
||||
static Model::IActionArgs GetEmptyArgsForAction(Model::ShortcutAction shortcutAction);
|
||||
|
||||
CascadiaSettings() noexcept = default;
|
||||
CascadiaSettings(const winrt::hstring& userJSON, const winrt::hstring& inboxJSON);
|
||||
CascadiaSettings(const std::string_view& userJSON, const std::string_view& inboxJSON = {});
|
||||
@@ -127,6 +168,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> AllProfiles() const noexcept;
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> ActiveProfiles() const noexcept;
|
||||
Model::ActionMap ActionMap() const noexcept;
|
||||
winrt::Windows::Foundation::Collections::IVectorView<Model::ExtensionPackage> Extensions();
|
||||
void WriteSettingsToDisk();
|
||||
Json::Value ToJson() const;
|
||||
Model::Profile ProfileDefaults() const;
|
||||
@@ -184,6 +226,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
winrt::com_ptr<implementation::Profile> _baseLayerProfile = winrt::make_self<implementation::Profile>();
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _allProfiles = winrt::single_threaded_observable_vector<Model::Profile>();
|
||||
winrt::Windows::Foundation::Collections::IObservableVector<Model::Profile> _activeProfiles = winrt::single_threaded_observable_vector<Model::Profile>();
|
||||
winrt::Windows::Foundation::Collections::IVector<Model::ExtensionPackage> _extensionPackages = nullptr;
|
||||
std::set<std::string> _themesChangeLog{};
|
||||
|
||||
// load errors
|
||||
@@ -199,6 +242,67 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
mutable std::once_flag _commandLinesCacheOnce;
|
||||
mutable std::vector<std::pair<std::wstring, Model::Profile>> _commandLinesCache;
|
||||
};
|
||||
|
||||
struct FragmentProfileEntry : FragmentProfileEntryT<FragmentProfileEntry>
|
||||
{
|
||||
public:
|
||||
FragmentProfileEntry(winrt::guid profileGuid, hstring json) :
|
||||
_profileGuid{ profileGuid },
|
||||
Json{ json } {}
|
||||
|
||||
winrt::guid ProfileGuid() const noexcept { return _profileGuid; }
|
||||
til::property<hstring> Json;
|
||||
|
||||
private:
|
||||
winrt::guid _profileGuid;
|
||||
};
|
||||
|
||||
struct FragmentColorSchemeEntry : FragmentColorSchemeEntryT<FragmentColorSchemeEntry>
|
||||
{
|
||||
public:
|
||||
FragmentColorSchemeEntry(hstring schemeName, hstring json) :
|
||||
_schemeName{ schemeName },
|
||||
_json{ json } {}
|
||||
|
||||
hstring ColorSchemeName() const noexcept { return _schemeName; }
|
||||
hstring Json() const noexcept { return _json; }
|
||||
|
||||
private:
|
||||
hstring _schemeName;
|
||||
hstring _json;
|
||||
};
|
||||
|
||||
struct FragmentSettings : FragmentSettingsT<FragmentSettings>
|
||||
{
|
||||
public:
|
||||
FragmentSettings(hstring source, hstring json, hstring filename) :
|
||||
_source{ source },
|
||||
_Json{ json },
|
||||
_filename{ filename } {}
|
||||
|
||||
hstring Source() const noexcept { return _source; }
|
||||
hstring Filename() const noexcept { return _filename; }
|
||||
Windows::Foundation::Collections::IVector<Model::FragmentProfileEntry> ModifiedProfiles() const noexcept { return _modifiedProfiles; }
|
||||
void ModifiedProfiles(const Windows::Foundation::Collections::IVector<Model::FragmentProfileEntry>& modifiedProfiles) noexcept { _modifiedProfiles = modifiedProfiles; }
|
||||
Windows::Foundation::Collections::IVector<Model::FragmentProfileEntry> NewProfiles() const noexcept { return _newProfiles; }
|
||||
void NewProfiles(const Windows::Foundation::Collections::IVector<Model::FragmentProfileEntry>& newProfiles) noexcept { _newProfiles = newProfiles; }
|
||||
Windows::Foundation::Collections::IVector<Model::FragmentColorSchemeEntry> ColorSchemes() const noexcept { return _colorSchemes; }
|
||||
void ColorSchemes(const Windows::Foundation::Collections::IVector<Model::FragmentColorSchemeEntry>& colorSchemes) noexcept { _colorSchemes = colorSchemes; }
|
||||
WINRT_PROPERTY(hstring, Json);
|
||||
|
||||
public:
|
||||
// views
|
||||
Windows::Foundation::Collections::IVectorView<Model::FragmentProfileEntry> ModifiedProfilesView() const noexcept { return _modifiedProfiles ? _modifiedProfiles.GetView() : nullptr; }
|
||||
Windows::Foundation::Collections::IVectorView<Model::FragmentProfileEntry> NewProfilesView() const noexcept { return _newProfiles ? _newProfiles.GetView() : nullptr; }
|
||||
Windows::Foundation::Collections::IVectorView<Model::FragmentColorSchemeEntry> ColorSchemesView() const noexcept { return _colorSchemes ? _colorSchemes.GetView() : nullptr; }
|
||||
|
||||
private:
|
||||
hstring _source;
|
||||
hstring _filename;
|
||||
Windows::Foundation::Collections::IVector<Model::FragmentProfileEntry> _modifiedProfiles;
|
||||
Windows::Foundation::Collections::IVector<Model::FragmentProfileEntry> _newProfiles;
|
||||
Windows::Foundation::Collections::IVector<Model::FragmentColorSchemeEntry> _colorSchemes;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
||||
|
||||
@@ -5,9 +5,16 @@ import "GlobalAppSettings.idl";
|
||||
import "Profile.idl";
|
||||
import "TerminalWarnings.idl";
|
||||
import "DefaultTerminal.idl";
|
||||
import "ActionArgs.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Model
|
||||
{
|
||||
enum FragmentScope
|
||||
{
|
||||
User,
|
||||
Machine
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass CascadiaSettings {
|
||||
static CascadiaSettings LoadDefaults();
|
||||
static CascadiaSettings LoadAll();
|
||||
@@ -20,6 +27,9 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
static String ApplicationDisplayName { get; };
|
||||
static String ApplicationVersion { get; };
|
||||
|
||||
static Windows.Foundation.Collections.IMap<Microsoft.Terminal.Settings.Model.ShortcutAction, String> AvailableShortcutActionsAndNames { get; };
|
||||
static IActionArgs GetEmptyArgsForAction(Microsoft.Terminal.Settings.Model.ShortcutAction shortcutAction);
|
||||
|
||||
CascadiaSettings(String userJSON, String inboxJSON);
|
||||
|
||||
CascadiaSettings Copy();
|
||||
@@ -38,6 +48,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Profile DuplicateProfile(Profile sourceProfile);
|
||||
|
||||
ActionMap ActionMap { get; };
|
||||
Windows.Foundation.Collections.IVectorView<ExtensionPackage> Extensions { get; };
|
||||
|
||||
IVectorView<SettingsLoadWarnings> Warnings { get; };
|
||||
Windows.Foundation.IReference<SettingsLoadErrors> GetLoadingError { get; };
|
||||
@@ -56,4 +67,35 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
|
||||
void ExpandCommands();
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass FragmentProfileEntry
|
||||
{
|
||||
Guid ProfileGuid { get; };
|
||||
String Json { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass FragmentColorSchemeEntry
|
||||
{
|
||||
String ColorSchemeName { get; };
|
||||
String Json { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass FragmentSettings
|
||||
{
|
||||
String Source { get; };
|
||||
String Json { get; };
|
||||
String Filename { get; };
|
||||
IVectorView<FragmentProfileEntry> ModifiedProfilesView { get; };
|
||||
IVectorView<FragmentProfileEntry> NewProfilesView { get; };
|
||||
IVectorView<FragmentColorSchemeEntry> ColorSchemesView { get; };
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass ExtensionPackage
|
||||
{
|
||||
String Source { get; };
|
||||
String DisplayName { get; };
|
||||
String Icon { get; };
|
||||
FragmentScope Scope { get; };
|
||||
IVectorView<FragmentSettings> FragmentsView { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED
|
||||
#include "SshHostGenerator.h"
|
||||
#endif
|
||||
#include "PowershellInstallationProfileGenerator.h"
|
||||
|
||||
#include "ApplicationState.h"
|
||||
#include "DefaultTerminal.h"
|
||||
@@ -124,6 +125,20 @@ SettingsLoader SettingsLoader::Default(const std::string_view& userJSON, const s
|
||||
return loader;
|
||||
}
|
||||
|
||||
std::vector<Model::ExtensionPackage> SettingsLoader::LoadExtensionPackages()
|
||||
{
|
||||
SettingsLoader loader{};
|
||||
loader.GenerateExtensionPackagesFromProfileGenerators();
|
||||
loader.FindFragmentsAndMergeIntoUserSettings(true);
|
||||
|
||||
std::vector<Model::ExtensionPackage> extensionPackages;
|
||||
for (auto [_, extPkg] : loader.extensionPackageMap)
|
||||
{
|
||||
extensionPackages.emplace_back(std::move(*extPkg));
|
||||
}
|
||||
return extensionPackages;
|
||||
}
|
||||
|
||||
// The SettingsLoader class is an internal implementation detail of CascadiaSettings.
|
||||
// Member methods aren't safe against misuse and you need to ensure to call them in a specific order.
|
||||
// See CascadiaSettings::LoadAll() for a specific usage example.
|
||||
@@ -174,19 +189,159 @@ SettingsLoader::SettingsLoader(const std::string_view& userJSON, const std::stri
|
||||
_userProfileCount = userSettings.profiles.size();
|
||||
}
|
||||
|
||||
// This method is used to generate the JSON writer used for writing json in a styled format.
|
||||
// We use it a few times throughout the loader, so we lazy load it and cache it here.
|
||||
Json::StreamWriterBuilder SettingsLoader::_getJsonStyledWriter()
|
||||
{
|
||||
static bool jsonWriterInitialized = false;
|
||||
static Json::StreamWriterBuilder styledWriter;
|
||||
if (!jsonWriterInitialized)
|
||||
{
|
||||
styledWriter["indentation"] = " ";
|
||||
styledWriter["commentStyle"] = "All";
|
||||
styledWriter.settings_["enableYAMLCompatibility"] = true; // suppress spaces around colons
|
||||
styledWriter.settings_["precision"] = 6; // prevent values like 1.1000000000000001
|
||||
jsonWriterInitialized = true;
|
||||
}
|
||||
return styledWriter;
|
||||
}
|
||||
|
||||
// Generate dynamic profiles and add them to the list of "inbox" profiles
|
||||
// (meaning profiles specified by the application rather by the user).
|
||||
void SettingsLoader::GenerateProfiles()
|
||||
{
|
||||
_executeGenerator(PowershellCoreProfileGenerator{});
|
||||
_executeGenerator(WslDistroGenerator{});
|
||||
_executeGenerator(AzureCloudShellGenerator{});
|
||||
_executeGenerator(VisualStudioGenerator{});
|
||||
auto generateProfiles = [&](IDynamicProfileGenerator& generator) {
|
||||
if (!_ignoredNamespaces.contains(generator.GetNamespace()))
|
||||
{
|
||||
_executeGenerator(generator, inboxSettings.profiles);
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
PowershellCoreProfileGenerator powerShellGenerator{};
|
||||
generateProfiles(powerShellGenerator);
|
||||
|
||||
if (Feature_PowerShellInstallerProfileGenerator::IsEnabled())
|
||||
{
|
||||
if (!powerShellGenerator.GetPowerShellInstances().empty())
|
||||
{
|
||||
// If PowerShell is installed, mark the installer profile for deletion
|
||||
const winrt::guid profileGuid{ L"{965a10f2-b0f2-55dc-a3c2-2ddbf639bf89}" };
|
||||
for (const auto& profile : userSettings.profiles)
|
||||
{
|
||||
if (profile->Guid() == profileGuid)
|
||||
{
|
||||
profile->Deleted(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only generate the installer stub profile if PowerShell isn't installed.
|
||||
PowershellInstallationProfileGenerator pwshInstallationGenerator{};
|
||||
generateProfiles(pwshInstallationGenerator);
|
||||
}
|
||||
}
|
||||
}
|
||||
WslDistroGenerator wslGenerator{};
|
||||
generateProfiles(wslGenerator);
|
||||
|
||||
AzureCloudShellGenerator acsGenerator{};
|
||||
generateProfiles(acsGenerator);
|
||||
|
||||
VisualStudioGenerator vsGenerator{};
|
||||
generateProfiles(vsGenerator);
|
||||
#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED
|
||||
_executeGenerator(SshHostGenerator{});
|
||||
SshHostGenerator sshGenerator{};
|
||||
generateProfiles(sshGenerator);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Generate ExtensionPackage objects from the profile generators.
|
||||
void SettingsLoader::GenerateExtensionPackagesFromProfileGenerators()
|
||||
{
|
||||
auto generateExtensionPackages = [&](IDynamicProfileGenerator& generator) {
|
||||
std::vector<winrt::com_ptr<implementation::Profile>> profilesList;
|
||||
_executeGenerator(generator, profilesList);
|
||||
|
||||
// These are needed for the FragmentSettings object
|
||||
std::vector<Model::FragmentProfileEntry> profileEntries;
|
||||
Json::Value profilesListJson{ Json::ValueType::arrayValue };
|
||||
|
||||
for (const auto& profile : profilesList)
|
||||
{
|
||||
const auto profileJson = profile->ToJson();
|
||||
profilesListJson.append(profileJson);
|
||||
profileEntries.push_back(winrt::make<FragmentProfileEntry>(profile->Guid(), hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), profileJson)) }));
|
||||
}
|
||||
|
||||
// Manually construct the JSON for the FragmentSettings object
|
||||
Json::Value json{ Json::ValueType::objectValue };
|
||||
json[JsonKey(ProfilesKey)] = profilesListJson;
|
||||
|
||||
auto generatorExtension = winrt::make_self<FragmentSettings>(hstring{ generator.GetNamespace() }, hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), json)) }, hstring{ L"settings.json" });
|
||||
generatorExtension->NewProfiles(winrt::single_threaded_vector<Model::FragmentProfileEntry>(std::move(profileEntries)));
|
||||
|
||||
auto extPkg = _registerFragment(std::move(*generatorExtension), FragmentScope::Machine);
|
||||
extPkg->DisplayName(hstring{ generator.GetDisplayName() });
|
||||
extPkg->Icon(hstring{ generator.GetIcon() });
|
||||
};
|
||||
|
||||
PowershellCoreProfileGenerator powerShellGenerator{};
|
||||
generateExtensionPackages(powerShellGenerator);
|
||||
|
||||
if (Feature_PowerShellInstallerProfileGenerator::IsEnabled())
|
||||
{
|
||||
PowershellInstallationProfileGenerator pwshInstallationGenerator{};
|
||||
generateExtensionPackages(pwshInstallationGenerator);
|
||||
_patchInstallPowerShellProfile();
|
||||
}
|
||||
|
||||
WslDistroGenerator wslGenerator{};
|
||||
generateExtensionPackages(wslGenerator);
|
||||
|
||||
AzureCloudShellGenerator acsGenerator{};
|
||||
generateExtensionPackages(acsGenerator);
|
||||
|
||||
VisualStudioGenerator vsGenerator{};
|
||||
generateExtensionPackages(vsGenerator);
|
||||
#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED
|
||||
SshHostGenerator sshGenerator{};
|
||||
generateExtensionPackages(sshGenerator);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Retrieve the "Install Latest PowerShell" profile and add a comment to the JSON to indicate it's conditionally applied
|
||||
void SettingsLoader::_patchInstallPowerShellProfile()
|
||||
{
|
||||
const hstring pwshInstallerNamespace{ PowershellInstallationProfileGenerator::Namespace };
|
||||
if (extensionPackageMap.contains(pwshInstallerNamespace))
|
||||
{
|
||||
if (const auto& fragExtList = extensionPackageMap[pwshInstallerNamespace]->Fragments(); fragExtList.Size() > 0)
|
||||
{
|
||||
auto fragExt = get_self<FragmentSettings>(fragExtList.GetAt(0));
|
||||
|
||||
// We want the comment to be the first thing in the object,
|
||||
// "closeOnExit" is the first property, so target that.
|
||||
auto fragExtJson = _parseJSON(til::u16u8(fragExt->Json()));
|
||||
fragExtJson[JsonKey(ProfilesKey)][0]["closeOnExit"].setComment(til::u16u8(fmt::format(FMT_COMPILE(L"// {}"), RS_(L"PowerShellInstallationProfileJsonComment"))), Json::CommentPlacement::commentBefore);
|
||||
fragExt->Json(hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), fragExtJson)) });
|
||||
|
||||
if (const auto& profileEntryList = fragExt->NewProfilesView(); profileEntryList.Size() > 0)
|
||||
{
|
||||
auto profileEntry = get_self<FragmentProfileEntry>(profileEntryList.GetAt(0));
|
||||
|
||||
// We want the comment to be the first thing in the object,
|
||||
// "closeOnExit" is the first property, so target that.
|
||||
auto profileJson = _parseJSON(til::u16u8(profileEntry->Json()));
|
||||
profileJson["closeOnExit"].setComment(til::u16u8(fmt::format(FMT_COMPILE(L"// {}"), RS_(L"PowerShellInstallationProfileJsonComment"))), Json::CommentPlacement::commentBefore);
|
||||
profileEntry->Json(hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), profileJson)) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A new settings.json gets a special treatment:
|
||||
// 1. The default profile is a PowerShell 7+ one, if one was generated,
|
||||
// and falls back to the standard PowerShell 5 profile otherwise.
|
||||
@@ -242,21 +397,27 @@ void SettingsLoader::MergeInboxIntoUserSettings()
|
||||
// merge them. Unfortunately however the "updates" key in fragment profiles make this impossible:
|
||||
// The targeted profile might be one that got created as part of SettingsLoader::MergeInboxIntoUserSettings.
|
||||
// Additionally the GUID in "updates" will conflict with existing GUIDs in .inboxSettings.
|
||||
void SettingsLoader::FindFragmentsAndMergeIntoUserSettings()
|
||||
void SettingsLoader::FindFragmentsAndMergeIntoUserSettings(bool generateExtensionPackages)
|
||||
{
|
||||
ParsedSettings fragmentSettings;
|
||||
|
||||
const auto parseAndLayerFragmentFiles = [&](const std::filesystem::path& path, const winrt::hstring& source) {
|
||||
const auto parseAndLayerFragmentFiles = [&](const std::filesystem::path& path, const winrt::hstring& source, FragmentScope scope) {
|
||||
for (const auto& fragmentExt : std::filesystem::directory_iterator{ path })
|
||||
{
|
||||
if (fragmentExt.path().extension() == jsonExtension)
|
||||
const auto fragExtPath = fragmentExt.path();
|
||||
if (fragExtPath.extension() == jsonExtension)
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto content = til::io::read_file_as_utf8_string_if_exists(fragmentExt.path());
|
||||
const auto content = til::io::read_file_as_utf8_string_if_exists(fragExtPath);
|
||||
if (!content.empty())
|
||||
{
|
||||
_parseFragment(source, content, fragmentSettings);
|
||||
_parseFragment(source,
|
||||
content,
|
||||
fragmentSettings,
|
||||
generateExtensionPackages ?
|
||||
static_cast<std::optional<ParseFragmentMetadata>>(ParseFragmentMetadata{ fragExtPath.filename().wstring(), scope }) :
|
||||
std::nullopt);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
@@ -278,9 +439,11 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings()
|
||||
const auto filename = fragmentExtFolder.path().filename();
|
||||
const auto& source = filename.native();
|
||||
|
||||
if (!_ignoredNamespaces.contains(std::wstring_view{ source }) && fragmentExtFolder.is_directory())
|
||||
if (fragmentExtFolder.is_directory())
|
||||
{
|
||||
parseAndLayerFragmentFiles(fragmentExtFolder.path(), winrt::hstring{ source });
|
||||
parseAndLayerFragmentFiles(fragmentExtFolder.path(),
|
||||
winrt::hstring{ source },
|
||||
rfid == FOLDERID_LocalAppData ? FragmentScope::User : FragmentScope::Machine); // scope
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,11 +475,8 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings()
|
||||
|
||||
for (const auto& ext : extensions)
|
||||
{
|
||||
const auto packageName = ext.Package().Id().FamilyName();
|
||||
if (_ignoredNamespaces.contains(std::wstring_view{ packageName }))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const auto& package = ext.Package();
|
||||
const auto packageName = package.Id().FamilyName();
|
||||
|
||||
// Likewise, getting the public folder from an extension is an async operation.
|
||||
auto foundFolder = extractValueFromTaskWithoutMainThreadAwait(ext.GetPublicFolderAsync());
|
||||
@@ -334,7 +494,18 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings()
|
||||
|
||||
if (std::filesystem::is_directory(path))
|
||||
{
|
||||
parseAndLayerFragmentFiles(path, packageName);
|
||||
// MSIX does not support machine-wide scope
|
||||
// See https://github.com/microsoft/winget-cli/discussions/1983
|
||||
parseAndLayerFragmentFiles(path,
|
||||
packageName,
|
||||
FragmentScope::User);
|
||||
|
||||
if (generateExtensionPackages)
|
||||
{
|
||||
auto extPkg = extensionPackageMap[packageName];
|
||||
extPkg->Icon(package.Logo().AbsoluteUri());
|
||||
extPkg->DisplayName(package.DisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,7 +516,7 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings()
|
||||
void SettingsLoader::MergeFragmentIntoUserSettings(const winrt::hstring& source, const std::string_view& content)
|
||||
{
|
||||
ParsedSettings fragmentSettings;
|
||||
_parseFragment(source, content, fragmentSettings);
|
||||
_parseFragment(source, content, fragmentSettings, std::nullopt);
|
||||
}
|
||||
|
||||
// Call this method before passing SettingsLoader to the CascadiaSettings constructor.
|
||||
@@ -724,15 +895,23 @@ void SettingsLoader::_parse(const OriginTag origin, const winrt::hstring& source
|
||||
|
||||
// Just like _parse, but is to be used for fragment files, which don't support anything but color
|
||||
// schemes and profiles. Additionally this function supports profiles which specify an "updates" key.
|
||||
void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings)
|
||||
// - fragmentMeta: If set, construct and register FragmentSettings objects. Provides metadata necessary for doing so.
|
||||
// Otherwise, completely skip over that extra work and apply parsed settings to the user settings, if allowed by disabledProfileSources ("_ignoredNamespaces").
|
||||
void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings, const std::optional<ParseFragmentMetadata>& fragmentMeta)
|
||||
{
|
||||
auto json = _parseJson(content);
|
||||
|
||||
const bool buildFragmentSettings = fragmentMeta.has_value();
|
||||
const bool applyToUserSettings = !buildFragmentSettings && !_ignoredNamespaces.contains(std::wstring_view{ source });
|
||||
winrt::com_ptr<implementation::FragmentSettings> fragmentSettings = buildFragmentSettings ?
|
||||
winrt::make_self<FragmentSettings>(source, hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), json.root)) }, hstring{ fragmentMeta->jsonFilename }) :
|
||||
nullptr;
|
||||
|
||||
settings.clear();
|
||||
|
||||
// Load GlobalAppSettings and ColorSchemes
|
||||
{
|
||||
settings.globals = winrt::make_self<GlobalAppSettings>();
|
||||
|
||||
std::vector<Model::FragmentColorSchemeEntry> fragmentColorSchemes;
|
||||
for (const auto& schemeJson : json.colorSchemes)
|
||||
{
|
||||
try
|
||||
@@ -740,72 +919,111 @@ void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::str
|
||||
if (const auto scheme = ColorScheme::FromJson(schemeJson))
|
||||
{
|
||||
scheme->Origin(OriginTag::Fragment);
|
||||
// Don't add the color scheme to the Fragment's GlobalSettings; that will
|
||||
// cause layering issues later. Add them to a staging area for later processing.
|
||||
// (search for STAGED COLORS to find the next step)
|
||||
settings.colorSchemes.emplace(scheme->Name(), std::move(scheme));
|
||||
if (buildFragmentSettings)
|
||||
{
|
||||
fragmentColorSchemes.emplace_back(winrt::make<FragmentColorSchemeEntry>(scheme->Name(), hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), schemeJson)) }));
|
||||
}
|
||||
else if (applyToUserSettings)
|
||||
{
|
||||
// Don't add the color scheme to the Fragment's GlobalSettings; that will
|
||||
// cause layering issues later. Add them to a staging area for later processing.
|
||||
// (search for STAGED COLORS to find the next step)
|
||||
settings.colorSchemes.emplace(scheme->Name(), std::move(scheme));
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
|
||||
// Parse out actions from the fragment. Manually opt-out of keybinding
|
||||
// parsing - fragments shouldn't be allowed to bind actions to keys
|
||||
// directly. We may want to revisit circa GH#2205
|
||||
settings.globals->LayerActionsFrom(json.root, OriginTag::Fragment, false);
|
||||
if (buildFragmentSettings)
|
||||
{
|
||||
fragmentSettings->ColorSchemes(fragmentColorSchemes.empty() ? nullptr : single_threaded_vector<Model::FragmentColorSchemeEntry>(std::move(fragmentColorSchemes)));
|
||||
}
|
||||
else if (applyToUserSettings)
|
||||
{
|
||||
// Parse out actions from the fragment. Manually opt-out of keybinding
|
||||
// parsing - fragments shouldn't be allowed to bind actions to keys
|
||||
// directly. We may want to revisit circa GH#2205
|
||||
settings.globals = winrt::make_self<GlobalAppSettings>();
|
||||
settings.globals->LayerActionsFrom(json.root, OriginTag::Fragment, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Load new and modified profiles
|
||||
{
|
||||
const auto size = json.profilesList.size();
|
||||
settings.profiles.reserve(size);
|
||||
settings.profilesByGuid.reserve(size);
|
||||
if (applyToUserSettings)
|
||||
{
|
||||
const auto size = json.profilesList.size();
|
||||
settings.profiles.reserve(size);
|
||||
settings.profilesByGuid.reserve(size);
|
||||
}
|
||||
|
||||
std::vector<Model::FragmentProfileEntry> newProfiles;
|
||||
std::vector<Model::FragmentProfileEntry> modifiedProfiles;
|
||||
for (const auto& profileJson : json.profilesList)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto profile = _parseProfile(OriginTag::Fragment, source, profileJson);
|
||||
// GH#9962: Discard Guid-less, Name-less profiles, but...
|
||||
// allow ones with an Updates field, as those are special for fragments.
|
||||
// We need to make sure to only call Guid() if HasGuid() is true,
|
||||
// as Guid() will dynamically generate a return value otherwise.
|
||||
auto profile = _parseProfile(OriginTag::Fragment, source, profileJson);
|
||||
const auto guid = profile->HasGuid() ? profile->Guid() : profile->Updates();
|
||||
auto destinationSet = profile->HasGuid() ? &newProfiles : &modifiedProfiles;
|
||||
if (guid != winrt::guid{})
|
||||
{
|
||||
_appendProfile(std::move(profile), guid, settings);
|
||||
if (buildFragmentSettings)
|
||||
{
|
||||
destinationSet->emplace_back(winrt::make<FragmentProfileEntry>(guid, hstring{ til::u8u16(Json::writeString(_getJsonStyledWriter(), profileJson)) }));
|
||||
}
|
||||
else if (applyToUserSettings)
|
||||
{
|
||||
_appendProfile(std::move(profile), guid, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
if (buildFragmentSettings)
|
||||
{
|
||||
fragmentSettings->NewProfiles(newProfiles.empty() ? nullptr : single_threaded_vector<Model::FragmentProfileEntry>(std::move(newProfiles)));
|
||||
fragmentSettings->ModifiedProfiles(modifiedProfiles.empty() ? nullptr : single_threaded_vector<Model::FragmentProfileEntry>(std::move(modifiedProfiles)));
|
||||
_registerFragment(std::move(*fragmentSettings), fragmentMeta->scope);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& fragmentProfile : settings.profiles)
|
||||
// Merge profiles, color schemes, and globals into the user settings (aka inheritance)
|
||||
if (applyToUserSettings)
|
||||
{
|
||||
if (const auto updates = fragmentProfile->Updates(); updates != winrt::guid{})
|
||||
for (const auto& fragmentProfile : settings.profiles)
|
||||
{
|
||||
if (const auto it = userSettings.profilesByGuid.find(updates); it != userSettings.profilesByGuid.end())
|
||||
if (const auto updates = fragmentProfile->Updates(); updates != winrt::guid{})
|
||||
{
|
||||
it->second->AddMostImportantParent(fragmentProfile);
|
||||
if (const auto it = userSettings.profilesByGuid.find(updates); it != userSettings.profilesByGuid.end())
|
||||
{
|
||||
it->second->AddMostImportantParent(fragmentProfile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_addUserProfileParent(fragmentProfile);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// STAGED COLORS are processed here: we merge them into the partially-loaded
|
||||
// settings directly so that we can resolve conflicts between user-generated
|
||||
// color schemes and fragment-originated ones.
|
||||
for (const auto& [_, fragmentColorScheme] : settings.colorSchemes)
|
||||
{
|
||||
_addUserProfileParent(fragmentProfile);
|
||||
_addOrMergeUserColorScheme(fragmentColorScheme);
|
||||
}
|
||||
}
|
||||
|
||||
// STAGED COLORS are processed here: we merge them into the partially-loaded
|
||||
// settings directly so that we can resolve conflicts between user-generated
|
||||
// color schemes and fragment-originated ones.
|
||||
for (const auto& fragmentColorScheme : settings.colorSchemes)
|
||||
{
|
||||
_addOrMergeUserColorScheme(fragmentColorScheme.second);
|
||||
// Add the parsed fragment globals as a parent of the user's settings.
|
||||
// Later, in FinalizeInheritance, this will result in the action map from
|
||||
// the fragments being applied before the user's own settings.
|
||||
userSettings.globals->AddLeastImportantParent(settings.globals);
|
||||
}
|
||||
|
||||
// Add the parsed fragment globals as a parent of the user's settings.
|
||||
// Later, in FinalizeInheritance, this will result in the action map from
|
||||
// the fragments being applied before the user's own settings.
|
||||
userSettings.globals->AddLeastImportantParent(settings.globals);
|
||||
}
|
||||
|
||||
SettingsLoader::JsonSettings SettingsLoader::_parseJson(const std::string_view& content)
|
||||
@@ -905,7 +1123,8 @@ void SettingsLoader::_addUserProfileParent(const winrt::com_ptr<implementation::
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsLoader::_addOrMergeUserColorScheme(const winrt::com_ptr<implementation::ColorScheme>& newScheme)
|
||||
// returns true if the scheme was successfully added, otherwise false
|
||||
bool SettingsLoader::_addOrMergeUserColorScheme(const winrt::com_ptr<implementation::ColorScheme>& newScheme)
|
||||
{
|
||||
// On entry, all the user color schemes have been loaded. Therefore, any insertions of inbox or fragment schemes
|
||||
// will fail; we can leverage this to detect when they are equivalent and delete the user's duplicate copies.
|
||||
@@ -931,36 +1150,33 @@ void SettingsLoader::_addOrMergeUserColorScheme(const winrt::com_ptr<implementat
|
||||
userSettings.colorSchemeRemappings.emplace(newScheme->Name(), newName);
|
||||
// And re-add it to the end.
|
||||
userSettings.colorSchemes.emplace(newName, std::move(existingScheme));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// As the name implies it executes a generator.
|
||||
// Generated profiles are added to .inboxSettings. Used by GenerateProfiles().
|
||||
void SettingsLoader::_executeGenerator(const IDynamicProfileGenerator& generator)
|
||||
void SettingsLoader::_executeGenerator(IDynamicProfileGenerator& generator, std::vector<winrt::com_ptr<implementation::Profile>>& profilesList)
|
||||
{
|
||||
const auto generatorNamespace = generator.GetNamespace();
|
||||
if (_ignoredNamespaces.contains(generatorNamespace))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto previousSize = inboxSettings.profiles.size();
|
||||
|
||||
const auto previousSize = profilesList.size();
|
||||
try
|
||||
{
|
||||
generator.GenerateProfiles(inboxSettings.profiles);
|
||||
generator.GenerateProfiles(profilesList);
|
||||
}
|
||||
CATCH_LOG_MSG("Dynamic Profile Namespace: \"%.*s\"", gsl::narrow<int>(generatorNamespace.size()), generatorNamespace.data())
|
||||
|
||||
// If the generator produced some profiles we're going to give them default attributes.
|
||||
// By setting the Origin/Source/etc. here, we deduplicate some code and ensure they aren't missing accidentally.
|
||||
if (inboxSettings.profiles.size() > previousSize)
|
||||
if (profilesList.size() > previousSize)
|
||||
{
|
||||
const winrt::hstring source{ generatorNamespace };
|
||||
|
||||
for (const auto& profile : std::span(inboxSettings.profiles).subspan(previousSize))
|
||||
for (const auto& profile : std::span(profilesList).subspan(previousSize))
|
||||
{
|
||||
profile->Origin(OriginTag::Generated);
|
||||
profile->Source(source);
|
||||
@@ -968,6 +1184,23 @@ void SettingsLoader::_executeGenerator(const IDynamicProfileGenerator& generator
|
||||
}
|
||||
}
|
||||
|
||||
winrt::com_ptr<ExtensionPackage> SettingsLoader::_registerFragment(const winrt::Microsoft::Terminal::Settings::Model::FragmentSettings& fragment, FragmentScope scope)
|
||||
{
|
||||
const auto src = fragment.Source();
|
||||
if (auto extPkg = extensionPackageMap[src])
|
||||
{
|
||||
extPkg->Fragments().Append(fragment);
|
||||
return extPkg;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto newExtPkg = winrt::make_self<ExtensionPackage>(src, scope);
|
||||
newExtPkg->Fragments().Append(fragment);
|
||||
extensionPackageMap[src] = newExtPkg;
|
||||
return newExtPkg;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a CascadiaSettings from whatever's saved on disk, or instantiates
|
||||
// a new one with the default values. If we're running as a packaged app,
|
||||
@@ -1040,7 +1273,7 @@ try
|
||||
loader.MergeInboxIntoUserSettings();
|
||||
// Fragments might reference user profiles created by a generator.
|
||||
// --> FindFragmentsAndMergeIntoUserSettings must be called after MergeInboxIntoUserSettings.
|
||||
loader.FindFragmentsAndMergeIntoUserSettings();
|
||||
loader.FindFragmentsAndMergeIntoUserSettings(false);
|
||||
loader.FinalizeLayering();
|
||||
|
||||
// DisableDeletedProfiles returns true whenever we encountered any new generated/dynamic profiles.
|
||||
@@ -1491,7 +1724,11 @@ void CascadiaSettings::_resolveNewTabMenuProfiles() const
|
||||
auto activeProfileCount = gsl::narrow_cast<int>(_activeProfiles.Size());
|
||||
for (auto profileIndex = 0; profileIndex < activeProfileCount; profileIndex++)
|
||||
{
|
||||
remainingProfilesMap.emplace(profileIndex, _activeProfiles.GetAt(profileIndex));
|
||||
const auto& profile = _activeProfiles.GetAt(profileIndex);
|
||||
if (!profile.Deleted())
|
||||
{
|
||||
remainingProfilesMap.emplace(profileIndex, _activeProfiles.GetAt(profileIndex));
|
||||
}
|
||||
}
|
||||
|
||||
// We keep track of the "remaining profiles" - those that have not yet been resolved
|
||||
|
||||
@@ -28,6 +28,21 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
Command::Command() = default;
|
||||
|
||||
Model::Command Command::NewUserCommand()
|
||||
{
|
||||
auto newCmd{ winrt::make_self<Command>() };
|
||||
newCmd->_Origin = OriginTag::User;
|
||||
return *newCmd;
|
||||
}
|
||||
|
||||
Model::Command Command::CopyAsUserCommand(Model::Command originalCmd)
|
||||
{
|
||||
auto command{ winrt::get_self<Command>(originalCmd) };
|
||||
auto copy{ command->Copy() };
|
||||
copy->_Origin = OriginTag::User;
|
||||
return *copy;
|
||||
}
|
||||
|
||||
com_ptr<Command> Command::Copy() const
|
||||
{
|
||||
auto command{ winrt::make_self<Command>() };
|
||||
@@ -113,6 +128,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
return hstring{ _ID };
|
||||
}
|
||||
|
||||
void Command::ID(const hstring& ID) noexcept
|
||||
{
|
||||
const auto oldID = _ID;
|
||||
_ID = ID;
|
||||
IDChanged.raise(*this, oldID);
|
||||
}
|
||||
|
||||
void Command::GenerateID()
|
||||
{
|
||||
if (_ActionAndArgs)
|
||||
@@ -120,8 +142,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
auto actionAndArgsImpl{ winrt::get_self<implementation::ActionAndArgs>(_ActionAndArgs) };
|
||||
if (const auto generatedID = actionAndArgsImpl->GenerateID(); !generatedID.empty())
|
||||
{
|
||||
_ID = generatedID;
|
||||
_IDWasGenerated = true;
|
||||
ID(generatedID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,6 +153,16 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
return _IDWasGenerated;
|
||||
}
|
||||
|
||||
Model::ActionAndArgs Command::ActionAndArgs() const noexcept
|
||||
{
|
||||
return _ActionAndArgs;
|
||||
}
|
||||
|
||||
void Command::ActionAndArgs(const Model::ActionAndArgs& value) noexcept
|
||||
{
|
||||
_ActionAndArgs = value;
|
||||
}
|
||||
|
||||
void Command::Name(const hstring& value)
|
||||
{
|
||||
if (!_name.has_value() || _name.value() != value)
|
||||
|
||||
@@ -45,6 +45,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
struct Command : CommandT<Command>
|
||||
{
|
||||
Command();
|
||||
static Model::Command NewUserCommand();
|
||||
static Model::Command CopyAsUserCommand(Model::Command originalCmd);
|
||||
com_ptr<Command> Copy() const;
|
||||
|
||||
static winrt::com_ptr<Command> FromJson(const Json::Value& json,
|
||||
@@ -73,9 +75,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
void Name(const hstring& name);
|
||||
|
||||
hstring ID() const noexcept;
|
||||
void ID(const hstring& ID) noexcept;
|
||||
void GenerateID();
|
||||
bool IDWasGenerated();
|
||||
|
||||
// we cannot use the WINRT_PROPERTY macro for ActionAndArgs because the setter has some additional logic regarding the ID
|
||||
Model::ActionAndArgs ActionAndArgs() const noexcept;
|
||||
void ActionAndArgs(const Model::ActionAndArgs& value) noexcept;
|
||||
|
||||
hstring IconPath() const noexcept;
|
||||
void IconPath(const hstring& val);
|
||||
|
||||
@@ -86,12 +93,15 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
hstring iconPath);
|
||||
|
||||
WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None);
|
||||
WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs);
|
||||
WINRT_PROPERTY(OriginTag, Origin);
|
||||
WINRT_PROPERTY(winrt::hstring, Description, L"");
|
||||
|
||||
public:
|
||||
til::typed_event<Model::Command, winrt::hstring> IDChanged;
|
||||
|
||||
private:
|
||||
Json::Value _originalJson;
|
||||
Model::ActionAndArgs _ActionAndArgs{};
|
||||
Windows::Foundation::Collections::IMap<winrt::hstring, Model::Command> _subcommands{ nullptr };
|
||||
std::optional<std::wstring> _name;
|
||||
std::wstring _ID;
|
||||
|
||||
@@ -35,13 +35,17 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
[default_interface] runtimeclass Command : ISettingsModelObject
|
||||
{
|
||||
Command();
|
||||
static Command NewUserCommand();
|
||||
static Command CopyAsUserCommand(Command originalCmd);
|
||||
|
||||
String Name { get; };
|
||||
String ID { get; };
|
||||
String Name;
|
||||
Boolean HasName();
|
||||
String ID;
|
||||
void GenerateID();
|
||||
|
||||
String Description { get; };
|
||||
|
||||
ActionAndArgs ActionAndArgs { get; };
|
||||
ActionAndArgs ActionAndArgs;
|
||||
|
||||
String IconPath;
|
||||
|
||||
@@ -51,5 +55,6 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
static IVector<Command> ParsePowerShellMenuComplete(String json, Int32 replaceLength);
|
||||
static IVector<Command> HistoryToCommands(IVector<String> commandHistory, String commandline, Boolean directories, String iconPath);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Command, String> IDChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Core::AdjustTextMode, AdjustIndistinguishableColors);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle);
|
||||
|
||||
// Actions
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::ResizeDirection, ResizeDirection);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::FocusDirection, FocusDirection);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::SplitDirection, SplitDirection);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::SplitType, SplitType);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::SettingsTarget, SettingsTarget);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::MoveTabDirection, MoveTabDirection);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::ScrollToMarkDirection, ScrollToMarkDirection);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::CommandPaletteLaunchMode, CommandPaletteLaunchMode);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::SuggestionsSource, SuggestionsSource);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::FindMatchDirection, FindMatchDirection);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::DesktopBehavior, DesktopBehavior);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::MonitorBehavior, MonitorBehavior);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::ClearBufferType, ClearBufferType);
|
||||
DEFINE_ENUM_MAP(Microsoft::Terminal::Settings::Model::SelectOutputDirection, SelectOutputDirection);
|
||||
|
||||
// FontWeight is special because the JsonUtils::ConversionTrait for it
|
||||
// creates a FontWeight object, but we need to use the uint16_t value.
|
||||
winrt::Windows::Foundation::Collections::IMap<winrt::hstring, uint16_t> EnumMappings::FontWeight()
|
||||
|
||||
@@ -49,6 +49,22 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::IntenseStyle> IntenseTextStyle();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Core::AdjustTextMode> AdjustIndistinguishableColors();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::PathTranslationStyle> PathTranslationStyle();
|
||||
|
||||
// Actions
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::ResizeDirection> ResizeDirection();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::FocusDirection> FocusDirection();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::SplitDirection> SplitDirection();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::SplitType> SplitType();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::SettingsTarget> SettingsTarget();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::MoveTabDirection> MoveTabDirection();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::ScrollToMarkDirection> ScrollToMarkDirection();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::CommandPaletteLaunchMode> CommandPaletteLaunchMode();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::SuggestionsSource> SuggestionsSource();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::FindMatchDirection> FindMatchDirection();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::DesktopBehavior> DesktopBehavior();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::MonitorBehavior> MonitorBehavior();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Control::ClearBufferType> ClearBufferType();
|
||||
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::Settings::Model::SelectOutputDirection> SelectOutputDirection();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,5 +31,21 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
static Windows.Foundation.Collections.IMap<String, UInt16> FontWeight { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.IntenseStyle> IntenseTextStyle { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.PathTranslationStyle> PathTranslationStyle { get; };
|
||||
|
||||
// Actions
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.ResizeDirection> ResizeDirection { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.FocusDirection> FocusDirection { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.SplitDirection> SplitDirection { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.SplitType> SplitType { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.SettingsTarget> SettingsTarget { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.MoveTabDirection> MoveTabDirection { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.ScrollToMarkDirection> ScrollToMarkDirection { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.CommandPaletteLaunchMode> CommandPaletteLaunchMode { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.SuggestionsSource> SuggestionsSource { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.FindMatchDirection> FindMatchDirection { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.DesktopBehavior> DesktopBehavior { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.MonitorBehavior> MonitorBehavior { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Control.ClearBufferType> ClearBufferType { get; };
|
||||
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.SelectOutputDirection> SelectOutputDirection { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,14 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
|
||||
globals->_NewTabMenu->Append(get_self<NewTabMenuEntry>(entry)->Copy());
|
||||
}
|
||||
}
|
||||
if (_DisabledProfileSources)
|
||||
{
|
||||
globals->_DisabledProfileSources = winrt::single_threaded_vector<hstring>();
|
||||
for (const auto& src : *_DisabledProfileSources)
|
||||
{
|
||||
globals->_DisabledProfileSources->Append(src);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& parent : _parents)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
public:
|
||||
virtual ~IDynamicProfileGenerator() = default;
|
||||
virtual std::wstring_view GetNamespace() const noexcept = 0;
|
||||
virtual void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const = 0;
|
||||
virtual std::wstring_view GetDisplayName() const noexcept = 0;
|
||||
virtual std::wstring_view GetIcon() const noexcept = 0;
|
||||
virtual void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) = 0;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
<DependentUpon>KeyChordSerialization.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PowershellCoreProfileGenerator.h" />
|
||||
<ClInclude Include="PowershellInstallationProfileGenerator.h" />
|
||||
<ClInclude Include="Profile.h">
|
||||
<DependentUpon>Profile.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
@@ -167,6 +168,7 @@
|
||||
<DependentUpon>KeyChordSerialization.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PowershellCoreProfileGenerator.cpp" />
|
||||
<ClCompile Include="PowershellInstallationProfileGenerator.cpp" />
|
||||
<ClCompile Include="Profile.cpp">
|
||||
<DependentUpon>Profile.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
<ClCompile Include="PowershellCoreProfileGenerator.cpp">
|
||||
<Filter>profileGeneration</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PowershellInstallationProfileGenerator.cpp">
|
||||
<Filter>profileGeneration</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="WslDistroGenerator.cpp">
|
||||
<Filter>profileGeneration</Filter>
|
||||
</ClCompile>
|
||||
@@ -57,6 +60,9 @@
|
||||
<ClInclude Include="PowershellCoreProfileGenerator.h">
|
||||
<Filter>profileGeneration</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PowershellInstallationProfileGenerator.h">
|
||||
<Filter>profileGeneration</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WslDistroGenerator.h">
|
||||
<Filter>profileGeneration</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -15,334 +15,311 @@
|
||||
#include <winrt/Windows.Management.Deployment.h>
|
||||
#include <appmodel.h>
|
||||
#include <shlobj.h>
|
||||
#include <LibraryResources.h>
|
||||
|
||||
static constexpr std::wstring_view POWERSHELL_PFN{ L"Microsoft.PowerShell_8wekyb3d8bbwe" };
|
||||
static constexpr std::wstring_view POWERSHELL_PREVIEW_PFN{ L"Microsoft.PowerShellPreview_8wekyb3d8bbwe" };
|
||||
static constexpr std::wstring_view PWSH_EXE{ L"pwsh.exe" };
|
||||
static constexpr std::wstring_view POWERSHELL_ICON{ L"ms-appx:///ProfileIcons/pwsh.png" };
|
||||
static constexpr std::wstring_view POWERSHELL_PREVIEW_ICON{ L"ms-appx:///ProfileIcons/pwsh-preview.png" };
|
||||
static constexpr std::wstring_view GENERATOR_POWERSHELL_ICON{ L"ms-appx:///ProfileGeneratorIcons/PowerShell.png" };
|
||||
static constexpr std::wstring_view POWERSHELL_PREFERRED_PROFILE_NAME{ L"PowerShell" };
|
||||
|
||||
namespace
|
||||
{
|
||||
enum PowerShellFlags
|
||||
{
|
||||
None = 0,
|
||||
|
||||
// These flags are used as a sort key, so they encode some native ordering.
|
||||
// They are ordered such that the "most important" flags have the largest
|
||||
// impact on the sort space. For example, since we want Preview to be very polar
|
||||
// we give it the highest flag value.
|
||||
// The "ideal" powershell instance has 0 flags (stable, native, Program Files location)
|
||||
//
|
||||
// With this ordering, the sort space ends up being (for PowerShell 6)
|
||||
// (numerically greater values are on the left; this is flipped in the final sort)
|
||||
//
|
||||
// <-- Less Valued .................................... More Valued -->
|
||||
// | All instances of PS 6 | All PS7 |
|
||||
// | Preview | Stable | ~~~ |
|
||||
// | Non-Native | Native | Non-Native | Native | ~~~ |
|
||||
// | Trd | Pack | Trd | Pack | Trd | Pack | Trd | Pack | ~~~ |
|
||||
// (where Pack is a stand-in for store, scoop, dotnet, though they have their own orders,
|
||||
// and Trd is a stand-in for "Traditional" (Program Files))
|
||||
//
|
||||
// In short, flags with larger magnitudes are pushed further down (therefore valued less)
|
||||
|
||||
// distribution method (choose one)
|
||||
Store = 1 << 0, // distributed via the store
|
||||
Scoop = 1 << 1, // installed via Scoop
|
||||
Dotnet = 1 << 2, // installed as a dotnet global tool
|
||||
Traditional = 1 << 3, // installed in traditional Program Files locations
|
||||
|
||||
// native architecture (choose one)
|
||||
WOWARM = 1 << 4, // non-native (Windows-on-Windows, ARM variety)
|
||||
WOWx86 = 1 << 5, // non-native (Windows-on-Windows, x86 variety)
|
||||
|
||||
// build type (choose one)
|
||||
Preview = 1 << 6, // preview version
|
||||
};
|
||||
DEFINE_ENUM_FLAG_OPERATORS(PowerShellFlags);
|
||||
|
||||
struct PowerShellInstance
|
||||
{
|
||||
int majorVersion; // 0 = we don't know, sort last.
|
||||
PowerShellFlags flags;
|
||||
std::filesystem::path executablePath;
|
||||
|
||||
constexpr bool operator<(const PowerShellInstance& second) const
|
||||
{
|
||||
if (majorVersion != second.majorVersion)
|
||||
{
|
||||
return majorVersion < second.majorVersion;
|
||||
}
|
||||
if (flags != second.flags)
|
||||
{
|
||||
return flags > second.flags; // flags are inverted because "0" is ideal; see above
|
||||
}
|
||||
return executablePath < second.executablePath; // fall back to path sorting
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Generates a name, based on flags, for a powershell instance.
|
||||
// Return value:
|
||||
// - the name
|
||||
std::wstring Name() const
|
||||
{
|
||||
std::wstringstream namestream;
|
||||
namestream << L"PowerShell";
|
||||
|
||||
if (WI_IsFlagSet(flags, PowerShellFlags::Store))
|
||||
{
|
||||
if (WI_IsFlagSet(flags, PowerShellFlags::Preview))
|
||||
{
|
||||
namestream << L" Preview";
|
||||
}
|
||||
namestream << L" (msix)";
|
||||
}
|
||||
else if (WI_IsFlagSet(flags, PowerShellFlags::Dotnet))
|
||||
{
|
||||
namestream << L" (dotnet global)";
|
||||
}
|
||||
else if (WI_IsFlagSet(flags, PowerShellFlags::Scoop))
|
||||
{
|
||||
namestream << L" (scoop)";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (majorVersion < 7)
|
||||
{
|
||||
namestream << L" Core";
|
||||
}
|
||||
if (majorVersion != 0)
|
||||
{
|
||||
namestream << L" " << majorVersion;
|
||||
}
|
||||
if (WI_IsFlagSet(flags, PowerShellFlags::Preview))
|
||||
{
|
||||
namestream << L" Preview";
|
||||
}
|
||||
if (WI_IsFlagSet(flags, PowerShellFlags::WOWx86))
|
||||
{
|
||||
namestream << L" (x86)";
|
||||
}
|
||||
if (WI_IsFlagSet(flags, PowerShellFlags::WOWARM))
|
||||
{
|
||||
namestream << L" (ARM)";
|
||||
}
|
||||
}
|
||||
return namestream.str();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
using namespace ::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
// Function Description:
|
||||
// - Finds all powershell instances with the traditional layout under a directory.
|
||||
// - The "traditional" directory layout requires that pwsh.exe exist in a versioned directory, as in
|
||||
// ROOT\6\pwsh.exe
|
||||
// Arguments:
|
||||
// - directory: the directory under which to search
|
||||
// - flags: flags to apply to all found instances
|
||||
// - out: the list into which to accumulate these instances.
|
||||
static void _accumulateTraditionalLayoutPowerShellInstancesInDirectory(std::wstring_view directory, PowerShellFlags flags, std::vector<PowerShellInstance>& out)
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
{
|
||||
const std::filesystem::path root{ wil::ExpandEnvironmentStringsW<std::wstring>(directory.data()) };
|
||||
if (std::filesystem::exists(root))
|
||||
DEFINE_ENUM_FLAG_OPERATORS(PowershellCoreProfileGenerator::PowerShellFlags);
|
||||
|
||||
constexpr bool PowershellCoreProfileGenerator::PowerShellInstance::operator<(const PowerShellInstance& second) const
|
||||
{
|
||||
for (const auto& versionedDir : std::filesystem::directory_iterator(root))
|
||||
if (majorVersion != second.majorVersion)
|
||||
{
|
||||
const auto versionedPath = versionedDir.path();
|
||||
const auto executable = versionedPath / PWSH_EXE;
|
||||
if (std::filesystem::exists(executable))
|
||||
return majorVersion < second.majorVersion;
|
||||
}
|
||||
if (flags != second.flags)
|
||||
{
|
||||
return flags > second.flags; // flags are inverted because "0" is ideal; see above
|
||||
}
|
||||
return executablePath < second.executablePath; // fall back to path sorting
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Generates a name, based on flags, for a powershell instance.
|
||||
// Return value:
|
||||
// - the name
|
||||
std::wstring PowershellCoreProfileGenerator::PowerShellInstance::Name() const
|
||||
{
|
||||
std::wstringstream namestream;
|
||||
namestream << L"PowerShell";
|
||||
|
||||
if (WI_IsFlagSet(flags, PowerShellFlags::Store))
|
||||
{
|
||||
if (WI_IsFlagSet(flags, PowerShellFlags::Preview))
|
||||
{
|
||||
const auto preview = versionedPath.filename().native().find(L"-preview") != std::wstring::npos;
|
||||
const auto previewFlag = preview ? PowerShellFlags::Preview : PowerShellFlags::None;
|
||||
out.emplace_back(PowerShellInstance{ std::stoi(versionedPath.filename()),
|
||||
PowerShellFlags::Traditional | flags | previewFlag,
|
||||
executable });
|
||||
namestream << L" Preview";
|
||||
}
|
||||
namestream << L" (msix)";
|
||||
}
|
||||
else if (WI_IsFlagSet(flags, PowerShellFlags::Dotnet))
|
||||
{
|
||||
namestream << L" (dotnet global)";
|
||||
}
|
||||
else if (WI_IsFlagSet(flags, PowerShellFlags::Scoop))
|
||||
{
|
||||
namestream << L" (scoop)";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (majorVersion < 7)
|
||||
{
|
||||
namestream << L" Core";
|
||||
}
|
||||
if (majorVersion != 0)
|
||||
{
|
||||
namestream << L" " << majorVersion;
|
||||
}
|
||||
if (WI_IsFlagSet(flags, PowerShellFlags::Preview))
|
||||
{
|
||||
namestream << L" Preview";
|
||||
}
|
||||
if (WI_IsFlagSet(flags, PowerShellFlags::WOWx86))
|
||||
{
|
||||
namestream << L" (x86)";
|
||||
}
|
||||
if (WI_IsFlagSet(flags, PowerShellFlags::WOWARM))
|
||||
{
|
||||
namestream << L" (ARM)";
|
||||
}
|
||||
}
|
||||
return namestream.str();
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Finds all powershell instances with the traditional layout under a directory.
|
||||
// - The "traditional" directory layout requires that pwsh.exe exist in a versioned directory, as in
|
||||
// ROOT\6\pwsh.exe
|
||||
// Arguments:
|
||||
// - directory: the directory under which to search
|
||||
// - flags: flags to apply to all found instances
|
||||
// - out: the list into which to accumulate these instances.
|
||||
static void _accumulateTraditionalLayoutPowerShellInstancesInDirectory(std::wstring_view directory, PowershellCoreProfileGenerator::PowerShellFlags flags, std::vector<PowershellCoreProfileGenerator::PowerShellInstance>& out)
|
||||
{
|
||||
const std::filesystem::path root{ wil::ExpandEnvironmentStringsW<std::wstring>(directory.data()) };
|
||||
if (std::filesystem::exists(root))
|
||||
{
|
||||
for (const auto& versionedDir : std::filesystem::directory_iterator(root))
|
||||
{
|
||||
const auto versionedPath = versionedDir.path();
|
||||
const auto executable = versionedPath / PWSH_EXE;
|
||||
if (std::filesystem::exists(executable))
|
||||
{
|
||||
const auto preview = versionedPath.filename().native().find(L"-preview") != std::wstring::npos;
|
||||
const auto previewFlag = preview ? PowershellCoreProfileGenerator::PowerShellFlags::Preview : PowershellCoreProfileGenerator::PowerShellFlags::None;
|
||||
out.emplace_back(PowershellCoreProfileGenerator::PowerShellInstance{ std::stoi(versionedPath.filename()),
|
||||
PowershellCoreProfileGenerator::PowerShellFlags::Traditional | flags | previewFlag,
|
||||
executable });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Finds the store package, if one exists, for a given package family name
|
||||
// Arguments:
|
||||
// - packageFamilyName: the package family name
|
||||
// Return Value:
|
||||
// - a package, or nullptr.
|
||||
static winrt::Windows::ApplicationModel::Package _getStorePackage(const std::wstring_view packageFamilyName) noexcept
|
||||
try
|
||||
{
|
||||
winrt::Windows::Management::Deployment::PackageManager packageManager;
|
||||
auto foundPackages = packageManager.FindPackagesForUser(L"", packageFamilyName);
|
||||
auto iterator = foundPackages.First();
|
||||
if (!iterator.HasCurrent())
|
||||
// Function Description:
|
||||
// - Finds the store package, if one exists, for a given package family name
|
||||
// Arguments:
|
||||
// - packageFamilyName: the package family name
|
||||
// Return Value:
|
||||
// - a package, or nullptr.
|
||||
static winrt::Windows::ApplicationModel::Package _getStorePackage(const std::wstring_view packageFamilyName) noexcept
|
||||
try
|
||||
{
|
||||
winrt::Windows::Management::Deployment::PackageManager packageManager;
|
||||
auto foundPackages = packageManager.FindPackagesForUser(L"", packageFamilyName);
|
||||
auto iterator = foundPackages.First();
|
||||
if (!iterator.HasCurrent())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return iterator.Current();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
return nullptr;
|
||||
}
|
||||
return iterator.Current();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Finds all powershell instances that have App Execution Aliases in the standard location
|
||||
// Arguments:
|
||||
// - out: the list into which to accumulate these instances.
|
||||
static void _accumulateStorePowerShellInstances(std::vector<PowerShellInstance>& out)
|
||||
{
|
||||
wil::unique_cotaskmem_string localAppDataFolder;
|
||||
if (FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &localAppDataFolder)))
|
||||
// Function Description:
|
||||
// - Finds all powershell instances that have App Execution Aliases in the standard location
|
||||
// Arguments:
|
||||
// - out: the list into which to accumulate these instances.
|
||||
static void _accumulateStorePowerShellInstances(std::vector<PowershellCoreProfileGenerator::PowerShellInstance>& out)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::filesystem::path appExecAliasPath{ localAppDataFolder.get() };
|
||||
appExecAliasPath /= L"Microsoft";
|
||||
appExecAliasPath /= L"WindowsApps";
|
||||
|
||||
if (std::filesystem::exists(appExecAliasPath))
|
||||
{
|
||||
// App execution aliases for preview powershell
|
||||
const auto previewPath = appExecAliasPath / POWERSHELL_PREVIEW_PFN;
|
||||
if (std::filesystem::exists(previewPath))
|
||||
wil::unique_cotaskmem_string localAppDataFolder;
|
||||
if (FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &localAppDataFolder)))
|
||||
{
|
||||
const auto previewPackage = _getStorePackage(POWERSHELL_PREVIEW_PFN);
|
||||
if (previewPackage)
|
||||
{
|
||||
out.emplace_back(PowerShellInstance{
|
||||
gsl::narrow_cast<int>(previewPackage.Id().Version().Major),
|
||||
PowerShellFlags::Store | PowerShellFlags::Preview,
|
||||
previewPath / PWSH_EXE });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// App execution aliases for stable powershell
|
||||
const auto gaPath = appExecAliasPath / POWERSHELL_PFN;
|
||||
if (std::filesystem::exists(gaPath))
|
||||
std::filesystem::path appExecAliasPath{ localAppDataFolder.get() };
|
||||
appExecAliasPath /= L"Microsoft";
|
||||
appExecAliasPath /= L"WindowsApps";
|
||||
|
||||
if (std::filesystem::exists(appExecAliasPath))
|
||||
{
|
||||
const auto gaPackage = _getStorePackage(POWERSHELL_PFN);
|
||||
if (gaPackage)
|
||||
// App execution aliases for preview powershell
|
||||
const auto previewPath = appExecAliasPath / POWERSHELL_PREVIEW_PFN;
|
||||
if (std::filesystem::exists(previewPath))
|
||||
{
|
||||
out.emplace_back(PowerShellInstance{
|
||||
gaPackage.Id().Version().Major,
|
||||
PowerShellFlags::Store,
|
||||
gaPath / PWSH_EXE,
|
||||
});
|
||||
const auto previewPackage = _getStorePackage(POWERSHELL_PREVIEW_PFN);
|
||||
if (previewPackage)
|
||||
{
|
||||
out.emplace_back(PowershellCoreProfileGenerator::PowerShellInstance{
|
||||
gsl::narrow_cast<int>(previewPackage.Id().Version().Major),
|
||||
PowershellCoreProfileGenerator::PowerShellFlags::Store | PowershellCoreProfileGenerator::PowerShellFlags::Preview,
|
||||
previewPath / PWSH_EXE });
|
||||
}
|
||||
}
|
||||
|
||||
// App execution aliases for stable powershell
|
||||
const auto gaPath = appExecAliasPath / POWERSHELL_PFN;
|
||||
if (std::filesystem::exists(gaPath))
|
||||
{
|
||||
const auto gaPackage = _getStorePackage(POWERSHELL_PFN);
|
||||
if (gaPackage)
|
||||
{
|
||||
out.emplace_back(PowershellCoreProfileGenerator::PowerShellInstance{
|
||||
gaPackage.Id().Version().Major,
|
||||
PowershellCoreProfileGenerator::PowerShellFlags::Store,
|
||||
gaPath / PWSH_EXE,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Finds a powershell instance that's just a pwsh.exe in a folder.
|
||||
// - This function cannot determine the version number of such a powershell instance.
|
||||
// Arguments:
|
||||
// - directory: the directory under which to search
|
||||
// - flags: flags to apply to all found instances
|
||||
// - out: the list into which to accumulate these instances.
|
||||
static void _accumulatePwshExeInDirectory(const std::wstring_view directory, const PowerShellFlags flags, std::vector<PowerShellInstance>& out)
|
||||
{
|
||||
const std::filesystem::path root{ wil::ExpandEnvironmentStringsW<std::wstring>(directory.data()) };
|
||||
const auto pwshPath = root / PWSH_EXE;
|
||||
if (std::filesystem::exists(pwshPath))
|
||||
// Function Description:
|
||||
// - Finds a powershell instance that's just a pwsh.exe in a folder.
|
||||
// - This function cannot determine the version number of such a powershell instance.
|
||||
// Arguments:
|
||||
// - directory: the directory under which to search
|
||||
// - flags: flags to apply to all found instances
|
||||
// - out: the list into which to accumulate these instances.
|
||||
static void _accumulatePwshExeInDirectory(const std::wstring_view directory, const PowershellCoreProfileGenerator::PowerShellFlags flags, std::vector<PowershellCoreProfileGenerator::PowerShellInstance>& out)
|
||||
{
|
||||
out.emplace_back(PowerShellInstance{ 0 /* we can't tell */, flags, pwshPath });
|
||||
const std::filesystem::path root{ wil::ExpandEnvironmentStringsW<std::wstring>(directory.data()) };
|
||||
const auto pwshPath = root / PWSH_EXE;
|
||||
if (std::filesystem::exists(pwshPath))
|
||||
{
|
||||
out.emplace_back(PowershellCoreProfileGenerator::PowerShellInstance{ 0 /* we can't tell */, flags, pwshPath });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Builds a comprehensive priority-ordered list of powershell instances.
|
||||
// Return value:
|
||||
// - a comprehensive priority-ordered list of powershell instances.
|
||||
static std::vector<PowerShellInstance> _collectPowerShellInstances()
|
||||
{
|
||||
std::vector<PowerShellInstance> versions;
|
||||
// Function Description:
|
||||
// - Builds a comprehensive priority-ordered list of powershell instances.
|
||||
// Return value:
|
||||
// - a comprehensive priority-ordered list of powershell instances.
|
||||
static std::vector<PowershellCoreProfileGenerator::PowerShellInstance> _collectPowerShellInstances()
|
||||
{
|
||||
std::vector<PowershellCoreProfileGenerator::PowerShellInstance> versions;
|
||||
|
||||
_accumulateTraditionalLayoutPowerShellInstancesInDirectory(L"%ProgramFiles%\\PowerShell", PowerShellFlags::None, versions);
|
||||
_accumulateTraditionalLayoutPowerShellInstancesInDirectory(L"%ProgramFiles%\\PowerShell", PowershellCoreProfileGenerator::PowerShellFlags::None, versions);
|
||||
|
||||
#if defined(_M_AMD64) || defined(_M_ARM64) // No point in looking for WOW if we're not somewhere it exists
|
||||
_accumulateTraditionalLayoutPowerShellInstancesInDirectory(L"%ProgramFiles(x86)%\\PowerShell", PowerShellFlags::WOWx86, versions);
|
||||
_accumulateTraditionalLayoutPowerShellInstancesInDirectory(L"%ProgramFiles(x86)%\\PowerShell", PowershellCoreProfileGenerator::PowerShellFlags::WOWx86, versions);
|
||||
#endif
|
||||
|
||||
#if defined(_M_ARM64) // no point in looking for WOA if we're not on ARM64
|
||||
_accumulateTraditionalLayoutPowerShellInstancesInDirectory(L"%ProgramFiles(Arm)%\\PowerShell", PowerShellFlags::WOWARM, versions);
|
||||
_accumulateTraditionalLayoutPowerShellInstancesInDirectory(L"%ProgramFiles(Arm)%\\PowerShell", PowershellCoreProfileGenerator::PowerShellFlags::WOWARM, versions);
|
||||
#endif
|
||||
|
||||
_accumulateStorePowerShellInstances(versions);
|
||||
_accumulateStorePowerShellInstances(versions);
|
||||
|
||||
_accumulatePwshExeInDirectory(L"%USERPROFILE%\\.dotnet\\tools", PowerShellFlags::Dotnet, versions);
|
||||
_accumulatePwshExeInDirectory(L"%USERPROFILE%\\scoop\\shims", PowerShellFlags::Scoop, versions);
|
||||
_accumulatePwshExeInDirectory(L"%USERPROFILE%\\.dotnet\\tools", PowershellCoreProfileGenerator::PowerShellFlags::Dotnet, versions);
|
||||
_accumulatePwshExeInDirectory(L"%USERPROFILE%\\scoop\\shims", PowershellCoreProfileGenerator::PowerShellFlags::Scoop, versions);
|
||||
|
||||
std::sort(versions.rbegin(), versions.rend()); // sort in reverse (best first)
|
||||
std::sort(versions.rbegin(), versions.rend()); // sort in reverse (best first)
|
||||
|
||||
return versions;
|
||||
}
|
||||
return versions;
|
||||
}
|
||||
|
||||
// Legacy GUIDs:
|
||||
// - PowerShell Core 574e775e-4f2a-5b96-ac1e-a2962a402336
|
||||
static constexpr winrt::guid PowershellCoreGuid{ 0x574e775e, 0x4f2a, 0x5b96, { 0xac, 0x1e, 0xa2, 0x96, 0x2a, 0x40, 0x23, 0x36 } };
|
||||
// Legacy GUIDs:
|
||||
// - PowerShell Core 574e775e-4f2a-5b96-ac1e-a2962a402336
|
||||
static constexpr winrt::guid PowershellCoreGuid{ 0x574e775e, 0x4f2a, 0x5b96, { 0xac, 0x1e, 0xa2, 0x96, 0x2a, 0x40, 0x23, 0x36 } };
|
||||
|
||||
std::wstring_view PowershellCoreProfileGenerator::GetNamespace() const noexcept
|
||||
{
|
||||
return PowershellCoreGeneratorNamespace;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Checks if pwsh is installed, and if it is, creates a profile to launch it.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a vector with the PowerShell Core profile, if available.
|
||||
void PowershellCoreProfileGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
|
||||
{
|
||||
const auto psInstances = _collectPowerShellInstances();
|
||||
auto first = true;
|
||||
|
||||
for (const auto& psI : psInstances)
|
||||
std::wstring_view PowershellCoreProfileGenerator::GetNamespace() const noexcept
|
||||
{
|
||||
const auto name = psI.Name();
|
||||
auto profile{ CreateDynamicProfile(name) };
|
||||
return PowershellCoreGeneratorNamespace;
|
||||
}
|
||||
|
||||
const auto& unquotedCommandline = psI.executablePath.native();
|
||||
std::wstring quotedCommandline;
|
||||
quotedCommandline.reserve(unquotedCommandline.size() + 2);
|
||||
quotedCommandline.push_back(L'"');
|
||||
quotedCommandline.append(unquotedCommandline);
|
||||
quotedCommandline.push_back(L'"');
|
||||
profile->Commandline(winrt::hstring{ quotedCommandline });
|
||||
std::wstring_view PowershellCoreProfileGenerator::GetDisplayName() const noexcept
|
||||
{
|
||||
return RS_(L"PowershellCoreProfileGeneratorDisplayName");
|
||||
}
|
||||
|
||||
profile->StartingDirectory(winrt::hstring{ DEFAULT_STARTING_DIRECTORY });
|
||||
profile->DefaultAppearance().DarkColorSchemeName(L"Campbell");
|
||||
profile->DefaultAppearance().LightColorSchemeName(L"Campbell");
|
||||
profile->Icon(winrt::hstring{ WI_IsFlagSet(psI.flags, PowerShellFlags::Preview) ? POWERSHELL_PREVIEW_ICON : POWERSHELL_ICON });
|
||||
std::wstring_view PowershellCoreProfileGenerator::GetIcon() const noexcept
|
||||
{
|
||||
return GENERATOR_POWERSHELL_ICON;
|
||||
}
|
||||
|
||||
if (first)
|
||||
// Method Description:
|
||||
// - Checks if pwsh is installed, and if it is, creates a profile to launch it.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a vector with the PowerShell Core profile, if available.
|
||||
void PowershellCoreProfileGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles)
|
||||
{
|
||||
GetPowerShellInstances();
|
||||
auto first = true;
|
||||
|
||||
for (const auto& psI : _powerShellInstances)
|
||||
{
|
||||
// Give the first ("algorithmically best") profile the official, and original, "PowerShell Core" GUID.
|
||||
// This will turn the anchored default profile into "PowerShell Core Latest for Native Architecture through Store"
|
||||
// (or the closest approximation thereof). It may choose a preview instance as the "best" if it is a higher version.
|
||||
profile->Guid(PowershellCoreGuid);
|
||||
profile->Name(winrt::hstring{ POWERSHELL_PREFERRED_PROFILE_NAME });
|
||||
const auto name = psI.Name();
|
||||
auto profile{ CreateDynamicProfile(name) };
|
||||
|
||||
first = false;
|
||||
const auto& unquotedCommandline = psI.executablePath.native();
|
||||
std::wstring quotedCommandline;
|
||||
quotedCommandline.reserve(unquotedCommandline.size() + 2);
|
||||
quotedCommandline.push_back(L'"');
|
||||
quotedCommandline.append(unquotedCommandline);
|
||||
quotedCommandline.push_back(L'"');
|
||||
profile->Commandline(winrt::hstring{ quotedCommandline });
|
||||
|
||||
profile->StartingDirectory(winrt::hstring{ DEFAULT_STARTING_DIRECTORY });
|
||||
profile->DefaultAppearance().DarkColorSchemeName(L"Campbell");
|
||||
profile->DefaultAppearance().LightColorSchemeName(L"Campbell");
|
||||
profile->Icon(winrt::hstring{ WI_IsFlagSet(psI.flags, PowerShellFlags::Preview) ? POWERSHELL_PREVIEW_ICON : POWERSHELL_ICON });
|
||||
|
||||
if (first)
|
||||
{
|
||||
// Give the first ("algorithmically best") profile the official, and original, "PowerShell Core" GUID.
|
||||
// This will turn the anchored default profile into "PowerShell Core Latest for Native Architecture through Store"
|
||||
// (or the closest approximation thereof). It may choose a preview instance as the "best" if it is a higher version.
|
||||
profile->Guid(PowershellCoreGuid);
|
||||
profile->Name(winrt::hstring{ POWERSHELL_PREFERRED_PROFILE_NAME });
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
profiles.emplace_back(std::move(profile));
|
||||
}
|
||||
}
|
||||
|
||||
profiles.emplace_back(std::move(profile));
|
||||
std::vector<PowershellCoreProfileGenerator::PowerShellInstance> PowershellCoreProfileGenerator::GetPowerShellInstances() noexcept
|
||||
{
|
||||
if (_powerShellInstances.empty())
|
||||
{
|
||||
_powerShellInstances = _collectPowerShellInstances();
|
||||
}
|
||||
return _powerShellInstances;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Returns the thing it's named for.
|
||||
// Return value:
|
||||
// - the thing it says in the name
|
||||
const std::wstring_view PowershellCoreProfileGenerator::GetPreferredPowershellProfileName()
|
||||
{
|
||||
return POWERSHELL_PREFERRED_PROFILE_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Returns the thing it's named for.
|
||||
// Return value:
|
||||
// - the thing it says in the name
|
||||
const std::wstring_view PowershellCoreProfileGenerator::GetPreferredPowershellProfileName()
|
||||
{
|
||||
return POWERSHELL_PREFERRED_PROFILE_NAME;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,60 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
public:
|
||||
static const std::wstring_view GetPreferredPowershellProfileName();
|
||||
|
||||
enum PowerShellFlags
|
||||
{
|
||||
None = 0,
|
||||
|
||||
// These flags are used as a sort key, so they encode some native ordering.
|
||||
// They are ordered such that the "most important" flags have the largest
|
||||
// impact on the sort space. For example, since we want Preview to be very polar
|
||||
// we give it the highest flag value.
|
||||
// The "ideal" powershell instance has 0 flags (stable, native, Program Files location)
|
||||
//
|
||||
// With this ordering, the sort space ends up being (for PowerShell 6)
|
||||
// (numerically greater values are on the left; this is flipped in the final sort)
|
||||
//
|
||||
// <-- Less Valued .................................... More Valued -->
|
||||
// | All instances of PS 6 | All PS7 |
|
||||
// | Preview | Stable | ~~~ |
|
||||
// | Non-Native | Native | Non-Native | Native | ~~~ |
|
||||
// | Trd | Pack | Trd | Pack | Trd | Pack | Trd | Pack | ~~~ |
|
||||
// (where Pack is a stand-in for store, scoop, dotnet, though they have their own orders,
|
||||
// and Trd is a stand-in for "Traditional" (Program Files))
|
||||
//
|
||||
// In short, flags with larger magnitudes are pushed further down (therefore valued less)
|
||||
|
||||
// distribution method (choose one)
|
||||
Store = 1 << 0, // distributed via the store
|
||||
Scoop = 1 << 1, // installed via Scoop
|
||||
Dotnet = 1 << 2, // installed as a dotnet global tool
|
||||
Traditional = 1 << 3, // installed in traditional Program Files locations
|
||||
|
||||
// native architecture (choose one)
|
||||
WOWARM = 1 << 4, // non-native (Windows-on-Windows, ARM variety)
|
||||
WOWx86 = 1 << 5, // non-native (Windows-on-Windows, x86 variety)
|
||||
|
||||
// build type (choose one)
|
||||
Preview = 1 << 6, // preview version
|
||||
};
|
||||
|
||||
struct PowerShellInstance
|
||||
{
|
||||
int majorVersion; // 0 = we don't know, sort last.
|
||||
PowerShellFlags flags;
|
||||
std::filesystem::path executablePath;
|
||||
|
||||
constexpr bool operator<(const PowerShellInstance& second) const;
|
||||
std::wstring Name() const;
|
||||
};
|
||||
|
||||
std::wstring_view GetNamespace() const noexcept override;
|
||||
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const override;
|
||||
std::wstring_view GetDisplayName() const noexcept override;
|
||||
std::wstring_view GetIcon() const noexcept override;
|
||||
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) override;
|
||||
std::vector<PowerShellInstance> GetPowerShellInstances() noexcept;
|
||||
|
||||
private:
|
||||
std::vector<PowerShellInstance> _powerShellInstances;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "PowershellInstallationProfileGenerator.h"
|
||||
#include "DynamicProfileUtils.h"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
||||
static constexpr std::wstring_view POWERSHELL_ICON{ L"ms-appx:///ProfileIcons/pwsh.png" };
|
||||
static constexpr std::wstring_view GENERATOR_POWERSHELL_ICON{ L"ms-appx:///ProfileGeneratorIcons/PowerShell.png" };
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
{
|
||||
std::wstring_view PowershellInstallationProfileGenerator::Namespace{ L"Windows.Terminal.InstallPowerShell" };
|
||||
|
||||
std::wstring_view PowershellInstallationProfileGenerator::GetNamespace() const noexcept
|
||||
{
|
||||
return Namespace;
|
||||
}
|
||||
|
||||
std::wstring_view PowershellInstallationProfileGenerator::GetDisplayName() const noexcept
|
||||
{
|
||||
return RS_(L"PowerShellInstallationProfileGeneratorDisplayName");
|
||||
}
|
||||
|
||||
std::wstring_view PowershellInstallationProfileGenerator::GetIcon() const noexcept
|
||||
{
|
||||
return GENERATOR_POWERSHELL_ICON;
|
||||
}
|
||||
|
||||
void PowershellInstallationProfileGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles)
|
||||
{
|
||||
auto profile{ CreateDynamicProfile(RS_(L"PowerShellInstallationProfileName")) };
|
||||
profile->Commandline(winrt::hstring{ fmt::format(FMT_COMPILE(L"cmd /k winget install --interactive --id Microsoft.PowerShell --source winget & echo. & echo {} & exit"), RS_(L"PowerShellInstallationInstallerGuidance")) });
|
||||
profile->Icon(winrt::hstring{ POWERSHELL_ICON });
|
||||
profile->CloseOnExit(CloseOnExitMode::Never);
|
||||
|
||||
profiles.emplace_back(std::move(profile));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- PowershellInstallationProfileGenerator
|
||||
|
||||
Abstract:
|
||||
- This is the dynamic profile generator for a PowerShell stub. Checks if pwsh is
|
||||
installed, and if it is NOT installed, creates a profile that installs the
|
||||
latest PowerShell.
|
||||
|
||||
Author(s):
|
||||
- Carlos Zamora - March 2025
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IDynamicProfileGenerator.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
{
|
||||
class PowershellInstallationProfileGenerator final : public IDynamicProfileGenerator
|
||||
{
|
||||
public:
|
||||
static std::wstring_view Namespace;
|
||||
std::wstring_view GetNamespace() const noexcept override;
|
||||
std::wstring_view GetDisplayName() const noexcept override;
|
||||
std::wstring_view GetIcon() const noexcept override;
|
||||
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) override;
|
||||
};
|
||||
};
|
||||
@@ -740,4 +740,309 @@
|
||||
<data name="OpenCWDCommandKey" xml:space="preserve">
|
||||
<value>Open current working directory</value>
|
||||
</data>
|
||||
<data name="CopyText" xml:space="preserve">
|
||||
<value>Copy Text</value>
|
||||
</data>
|
||||
<data name="PasteText" xml:space="preserve">
|
||||
<value>Paste Text</value>
|
||||
</data>
|
||||
<data name="OpenNewTabDropdown" xml:space="preserve">
|
||||
<value>Open New Tab Dropdown</value>
|
||||
</data>
|
||||
<data name="DuplicateTab" xml:space="preserve">
|
||||
<value>Duplicate Tab</value>
|
||||
</data>
|
||||
<data name="NewTab" xml:space="preserve">
|
||||
<value>New Tab</value>
|
||||
</data>
|
||||
<data name="CloseWindow" xml:space="preserve">
|
||||
<value>Close Window</value>
|
||||
</data>
|
||||
<data name="CloseTab" xml:space="preserve">
|
||||
<value>Close Tab</value>
|
||||
</data>
|
||||
<data name="ClosePane" xml:space="preserve">
|
||||
<value>Close Pane</value>
|
||||
</data>
|
||||
<data name="NextTab" xml:space="preserve">
|
||||
<value>Next Tab</value>
|
||||
</data>
|
||||
<data name="PrevTab" xml:space="preserve">
|
||||
<value>Previous Tab</value>
|
||||
</data>
|
||||
<data name="SendInput" xml:space="preserve">
|
||||
<value>Send Input</value>
|
||||
</data>
|
||||
<data name="SplitPane" xml:space="preserve">
|
||||
<value>Split Pane</value>
|
||||
</data>
|
||||
<data name="ToggleSplitOrientation" xml:space="preserve">
|
||||
<value>Toggle Split Orientation</value>
|
||||
</data>
|
||||
<data name="TogglePaneZoom" xml:space="preserve">
|
||||
<value>Toggle Pane Zoom</value>
|
||||
</data>
|
||||
<data name="SwitchToTab" xml:space="preserve">
|
||||
<value>Switch To Tab</value>
|
||||
</data>
|
||||
<data name="AdjustFontSize" xml:space="preserve">
|
||||
<value>Adjust Font Size</value>
|
||||
</data>
|
||||
<data name="ResetFontSize" xml:space="preserve">
|
||||
<value>Reset Font Size</value>
|
||||
</data>
|
||||
<data name="ScrollUp" xml:space="preserve">
|
||||
<value>Scroll Up</value>
|
||||
</data>
|
||||
<data name="ScrollDown" xml:space="preserve">
|
||||
<value>Scroll Down</value>
|
||||
</data>
|
||||
<data name="ScrollUpPage" xml:space="preserve">
|
||||
<value>Scroll Up Page</value>
|
||||
</data>
|
||||
<data name="ScrollDownPage" xml:space="preserve">
|
||||
<value>Scroll Down Page</value>
|
||||
</data>
|
||||
<data name="ScrollToTop" xml:space="preserve">
|
||||
<value>Scroll To Top</value>
|
||||
</data>
|
||||
<data name="ScrollToBottom" xml:space="preserve">
|
||||
<value>Scroll To Bottom</value>
|
||||
</data>
|
||||
<data name="ScrollToMark" xml:space="preserve">
|
||||
<value>Scroll To Mark</value>
|
||||
</data>
|
||||
<data name="AddMark" xml:space="preserve">
|
||||
<value>Add Mark</value>
|
||||
</data>
|
||||
<data name="ClearMark" xml:space="preserve">
|
||||
<value>Clear Mark</value>
|
||||
</data>
|
||||
<data name="ClearAllMarks" xml:space="preserve">
|
||||
<value>Clear All Marks</value>
|
||||
</data>
|
||||
<data name="ResizePane" xml:space="preserve">
|
||||
<value>Resize Pane</value>
|
||||
</data>
|
||||
<data name="MoveFocus" xml:space="preserve">
|
||||
<value>Move Focus</value>
|
||||
</data>
|
||||
<data name="MovePane" xml:space="preserve">
|
||||
<value>Move Pane</value>
|
||||
</data>
|
||||
<data name="SwapPane" xml:space="preserve">
|
||||
<value>Swap Pane</value>
|
||||
</data>
|
||||
<data name="Find" xml:space="preserve">
|
||||
<value>Find</value>
|
||||
</data>
|
||||
<data name="ToggleShaderEffects" xml:space="preserve">
|
||||
<value>Toggle Shader Effects</value>
|
||||
</data>
|
||||
<data name="ToggleFocusMode" xml:space="preserve">
|
||||
<value>Toggle Focus Mode</value>
|
||||
</data>
|
||||
<data name="ToggleFullscreen" xml:space="preserve">
|
||||
<value>Toggle Fullscreen</value>
|
||||
</data>
|
||||
<data name="ToggleAlwaysOnTop" xml:space="preserve">
|
||||
<value>Toggle Always On Top</value>
|
||||
</data>
|
||||
<data name="OpenSettings" xml:space="preserve">
|
||||
<value>Open Settings</value>
|
||||
</data>
|
||||
<data name="SetFocusMode" xml:space="preserve">
|
||||
<value>Set Focus Mode</value>
|
||||
</data>
|
||||
<data name="SetFullScreen" xml:space="preserve">
|
||||
<value>Set Full Screen</value>
|
||||
</data>
|
||||
<data name="SetMaximized" xml:space="preserve">
|
||||
<value>Set Maximized</value>
|
||||
</data>
|
||||
<data name="SetColorScheme" xml:space="preserve">
|
||||
<value>Set Color Scheme</value>
|
||||
</data>
|
||||
<data name="SetTabColor" xml:space="preserve">
|
||||
<value>Set Tab Color</value>
|
||||
</data>
|
||||
<data name="OpenTabColorPicker" xml:space="preserve">
|
||||
<value>Open Tab Color Picker</value>
|
||||
</data>
|
||||
<data name="RenameTab" xml:space="preserve">
|
||||
<value>Rename Tab</value>
|
||||
</data>
|
||||
<data name="OpenTabRenamer" xml:space="preserve">
|
||||
<value>Open Tab Renamer</value>
|
||||
</data>
|
||||
<data name="ExecuteCommandline" xml:space="preserve">
|
||||
<value>Execute Command Line</value>
|
||||
</data>
|
||||
<data name="ToggleCommandPalette" xml:space="preserve">
|
||||
<value>Toggle Command Palette</value>
|
||||
</data>
|
||||
<data name="CloseOtherTabs" xml:space="preserve">
|
||||
<value>Close Other Tabs</value>
|
||||
</data>
|
||||
<data name="CloseTabsAfter" xml:space="preserve">
|
||||
<value>Close Tabs After</value>
|
||||
</data>
|
||||
<data name="TabSearch" xml:space="preserve">
|
||||
<value>Tab Search</value>
|
||||
</data>
|
||||
<data name="MoveTab" xml:space="preserve">
|
||||
<value>Move Tab</value>
|
||||
</data>
|
||||
<data name="BreakIntoDebugger" xml:space="preserve">
|
||||
<value>Break Into Debugger</value>
|
||||
</data>
|
||||
<data name="TogglePaneReadOnly" xml:space="preserve">
|
||||
<value>Toggle Pane Read-Only</value>
|
||||
</data>
|
||||
<data name="EnablePaneReadOnly" xml:space="preserve">
|
||||
<value>Enable Pane Read-Only</value>
|
||||
</data>
|
||||
<data name="DisablePaneReadOnly" xml:space="preserve">
|
||||
<value>Disable Pane Read-Only</value>
|
||||
</data>
|
||||
<data name="FindMatch" xml:space="preserve">
|
||||
<value>Find Match</value>
|
||||
</data>
|
||||
<data name="NewWindow" xml:space="preserve">
|
||||
<value>New Window</value>
|
||||
</data>
|
||||
<data name="IdentifyWindow" xml:space="preserve">
|
||||
<value>Identify Window</value>
|
||||
</data>
|
||||
<data name="IdentifyWindows" xml:space="preserve">
|
||||
<value>Identify Windows</value>
|
||||
</data>
|
||||
<data name="RenameWindow" xml:space="preserve">
|
||||
<value>Rename Window</value>
|
||||
</data>
|
||||
<data name="OpenWindowRenamer" xml:space="preserve">
|
||||
<value>Open Window Renamer</value>
|
||||
</data>
|
||||
<data name="DisplayWorkingDirectory" xml:space="preserve">
|
||||
<value>Display Working Directory</value>
|
||||
</data>
|
||||
<data name="SearchForText" xml:space="preserve">
|
||||
<value>Search For Text</value>
|
||||
</data>
|
||||
<data name="GlobalSummon" xml:space="preserve">
|
||||
<value>Global Summon</value>
|
||||
</data>
|
||||
<data name="QuakeMode" xml:space="preserve">
|
||||
<value>Quake Mode</value>
|
||||
</data>
|
||||
<data name="FocusPane" xml:space="preserve">
|
||||
<value>Focus Pane</value>
|
||||
</data>
|
||||
<data name="OpenSystemMenu" xml:space="preserve">
|
||||
<value>Open System Menu</value>
|
||||
</data>
|
||||
<data name="ExportBuffer" xml:space="preserve">
|
||||
<value>Export Buffer</value>
|
||||
</data>
|
||||
<data name="ClearBuffer" xml:space="preserve">
|
||||
<value>Clear Buffer</value>
|
||||
</data>
|
||||
<data name="MultipleActions" xml:space="preserve">
|
||||
<value>Multiple Actions</value>
|
||||
</data>
|
||||
<data name="Quit" xml:space="preserve">
|
||||
<value>Quit</value>
|
||||
</data>
|
||||
<data name="AdjustOpacity" xml:space="preserve">
|
||||
<value>Adjust Opacity</value>
|
||||
</data>
|
||||
<data name="RestoreLastClosed" xml:space="preserve">
|
||||
<value>Restore Last Closed</value>
|
||||
</data>
|
||||
<data name="SelectAll" xml:space="preserve">
|
||||
<value>Select All</value>
|
||||
</data>
|
||||
<data name="SelectCommand" xml:space="preserve">
|
||||
<value>Select Command</value>
|
||||
</data>
|
||||
<data name="SelectOutput" xml:space="preserve">
|
||||
<value>Select Output</value>
|
||||
</data>
|
||||
<data name="MarkMode" xml:space="preserve">
|
||||
<value>Mark Mode</value>
|
||||
</data>
|
||||
<data name="ToggleBlockSelection" xml:space="preserve">
|
||||
<value>Toggle Block Selection</value>
|
||||
</data>
|
||||
<data name="SwitchSelectionEndpoint" xml:space="preserve">
|
||||
<value>Switch Selection Endpoint</value>
|
||||
</data>
|
||||
<data name="Suggestions" xml:space="preserve">
|
||||
<value>Suggestions</value>
|
||||
</data>
|
||||
<data name="ColorSelection" xml:space="preserve">
|
||||
<value>Color Selection</value>
|
||||
</data>
|
||||
<data name="ShowContextMenu" xml:space="preserve">
|
||||
<value>Show Context Menu</value>
|
||||
</data>
|
||||
<data name="ExpandSelectionToWord" xml:space="preserve">
|
||||
<value>Expand Selection To Word</value>
|
||||
</data>
|
||||
<data name="CloseOtherPanes" xml:space="preserve">
|
||||
<value>Close Other Panes</value>
|
||||
</data>
|
||||
<data name="RestartConnection" xml:space="preserve">
|
||||
<value>Restart Connection</value>
|
||||
</data>
|
||||
<data name="ToggleBroadcastInput" xml:space="preserve">
|
||||
<value>Toggle Broadcast Input</value>
|
||||
</data>
|
||||
<data name="OpenScratchpad" xml:space="preserve">
|
||||
<value>Open Scratchpad</value>
|
||||
</data>
|
||||
<data name="OpenAbout" xml:space="preserve">
|
||||
<value>Open About</value>
|
||||
</data>
|
||||
<data name="QuickFix" xml:space="preserve">
|
||||
<value>Quick Fix</value>
|
||||
</data>
|
||||
<data name="OpenCWD" xml:space="preserve">
|
||||
<value>Open Current Working Directory</value>
|
||||
</data>
|
||||
<data name="WslDistroGeneratorDisplayName" xml:space="preserve">
|
||||
<value>WSL Distro Profile Generator</value>
|
||||
<comment>The display name of a dynamic profile generator for WSL distros</comment>
|
||||
</data>
|
||||
<data name="PowershellCoreProfileGeneratorDisplayName" xml:space="preserve">
|
||||
<value>PowerShell Profile Generator</value>
|
||||
<comment>The display name of a dynamic profile generator for PowerShell</comment>
|
||||
</data>
|
||||
<data name="PowershellInstallationProfileGeneratorDisplayName" xml:space="preserve">
|
||||
<value>PowerShell Installation Generator</value>
|
||||
<comment>The display name of a dynamic profile generator that installs the latest PowerShell</comment>
|
||||
</data>
|
||||
<data name="PowershellInstallationProfileName" xml:space="preserve">
|
||||
<value>Install Latest PowerShell</value>
|
||||
<comment>The display name of a profile generated by the PowerShellInstallationProfileGenerator. This profile installs the latest PowerShell.</comment>
|
||||
</data>
|
||||
<data name="AzureCloudShellGeneratorDisplayName" xml:space="preserve">
|
||||
<value>Azure Cloud Shell Profile Generator</value>
|
||||
<comment>The display name of a dynamic profile generator for Azure Cloud Shell</comment>
|
||||
</data>
|
||||
<data name="VisualStudioGeneratorDisplayName" xml:space="preserve">
|
||||
<value>Visual Studio Profile Generator</value>
|
||||
<comment>The display name of a dynamic profile generator for Visual Studio</comment>
|
||||
</data>
|
||||
<data name="SshHostGeneratorDisplayName" xml:space="preserve">
|
||||
<value>SSH Host Profile Generator</value>
|
||||
<comment>The display name of a dynamic profile generator for SSH hosts</comment>
|
||||
</data>
|
||||
<data name="PowerShellInstallationInstallerGuidance" xml:space="preserve">
|
||||
<value>Restart Windows Terminal to apply the new profile.</value>
|
||||
<comment>Guidance displayed by the installer directing the user to restart the app.</comment>
|
||||
</data>
|
||||
<data name="PowerShellInstallationProfileJsonComment" xml:space="preserve">
|
||||
<value>This profile only appears if PowerShell is not installed</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
|
||||
#include "DynamicProfileUtils.h"
|
||||
#include <LibraryResources.h>
|
||||
|
||||
static constexpr std::wstring_view SshHostGeneratorNamespace{ L"Windows.Terminal.SSH" };
|
||||
|
||||
static constexpr std::wstring_view PROFILE_TITLE_PREFIX = L"SSH - ";
|
||||
static constexpr std::wstring_view PROFILE_ICON_PATH = L"ms-appx:///ProfileIcons/{550ce7b8-d500-50ad-8a1a-c400c3262db3}.png";
|
||||
static constexpr std::wstring_view GENERATOR_ICON_PATH = L"ms-appx:///ProfileGeneratorIcons/SSH.png";
|
||||
|
||||
// OpenSSH is installed under System32 when installed via Optional Features
|
||||
static constexpr std::wstring_view SSH_EXE_PATH1 = L"%SystemRoot%\\System32\\OpenSSH\\ssh.exe";
|
||||
@@ -132,13 +134,23 @@ std::wstring_view SshHostGenerator::GetNamespace() const noexcept
|
||||
return SshHostGeneratorNamespace;
|
||||
}
|
||||
|
||||
std::wstring_view SshHostGenerator::GetDisplayName() const noexcept
|
||||
{
|
||||
return RS_(L"SshHostGeneratorDisplayName");
|
||||
}
|
||||
|
||||
std::wstring_view SshHostGenerator::GetIcon() const noexcept
|
||||
{
|
||||
return GENERATOR_ICON_PATH;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Generate a list of profiles for each detected OpenSSH host.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <A list of SSH host profiles.>
|
||||
void SshHostGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
|
||||
void SshHostGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles)
|
||||
{
|
||||
std::wstring sshExePath;
|
||||
if (_tryFindSshExePath(sshExePath))
|
||||
|
||||
@@ -24,7 +24,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
{
|
||||
public:
|
||||
std::wstring_view GetNamespace() const noexcept override;
|
||||
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const override;
|
||||
std::wstring_view GetDisplayName() const noexcept override;
|
||||
std::wstring_view GetIcon() const noexcept override;
|
||||
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) override;
|
||||
|
||||
private:
|
||||
static const std::wregex _configKeyValueRegex;
|
||||
|
||||
@@ -6,17 +6,29 @@
|
||||
#include "VisualStudioGenerator.h"
|
||||
#include "VsDevCmdGenerator.h"
|
||||
#include "VsDevShellGenerator.h"
|
||||
#include <LibraryResources.h>
|
||||
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
std::wstring_view VisualStudioGenerator::Namespace{ L"Windows.Terminal.VisualStudio" };
|
||||
static constexpr std::wstring_view IconPath{ L"ms-appx:///ProfileGeneratorIcons/VisualStudio.png" };
|
||||
|
||||
std::wstring_view VisualStudioGenerator::GetNamespace() const noexcept
|
||||
{
|
||||
return Namespace;
|
||||
}
|
||||
|
||||
void VisualStudioGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
|
||||
std::wstring_view VisualStudioGenerator::GetDisplayName() const noexcept
|
||||
{
|
||||
return RS_(L"VisualStudioGeneratorDisplayName");
|
||||
}
|
||||
|
||||
std::wstring_view VisualStudioGenerator::GetIcon() const noexcept
|
||||
{
|
||||
return IconPath;
|
||||
}
|
||||
|
||||
void VisualStudioGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles)
|
||||
{
|
||||
const auto instances = VsSetupConfiguration::QueryInstances();
|
||||
|
||||
|
||||
@@ -28,7 +28,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
public:
|
||||
static std::wstring_view Namespace;
|
||||
std::wstring_view GetNamespace() const noexcept override;
|
||||
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const override;
|
||||
std::wstring_view GetDisplayName() const noexcept override;
|
||||
std::wstring_view GetIcon() const noexcept override;
|
||||
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) override;
|
||||
|
||||
class IVisualStudioProfileGenerator
|
||||
{
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
|
||||
#include "DynamicProfileUtils.h"
|
||||
#include <LibraryResources.h>
|
||||
|
||||
static constexpr std::wstring_view WslHomeDirectory{ L"~" };
|
||||
static constexpr std::wstring_view DockerDistributionPrefix{ L"docker-desktop" };
|
||||
static constexpr std::wstring_view RancherDistributionPrefix{ L"rancher-desktop" };
|
||||
static constexpr std::wstring_view IconPath{ L"ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png" };
|
||||
static constexpr std::wstring_view GeneratorIconPath{ L"ms-appx:///ProfileGeneratorIcons/WSL.png" };
|
||||
|
||||
// The WSL entries are structured as such:
|
||||
// HKCU\Software\Microsoft\Windows\CurrentVersion\Lxss
|
||||
@@ -47,6 +50,16 @@ std::wstring_view WslDistroGenerator::GetNamespace() const noexcept
|
||||
return WslGeneratorNamespace;
|
||||
}
|
||||
|
||||
std::wstring_view WslDistroGenerator::GetDisplayName() const noexcept
|
||||
{
|
||||
return RS_(L"WslDistroGeneratorDisplayName");
|
||||
}
|
||||
|
||||
std::wstring_view WslDistroGenerator::GetIcon() const noexcept
|
||||
{
|
||||
return GeneratorIconPath;
|
||||
}
|
||||
|
||||
static winrt::com_ptr<implementation::Profile> makeProfile(const std::wstring& distName)
|
||||
{
|
||||
const auto WSLDistro{ CreateDynamicProfile(distName) };
|
||||
@@ -65,7 +78,7 @@ static winrt::com_ptr<implementation::Profile> makeProfile(const std::wstring& d
|
||||
{
|
||||
WSLDistro->StartingDirectory(winrt::hstring{ DEFAULT_STARTING_DIRECTORY });
|
||||
}
|
||||
WSLDistro->Icon(L"ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png");
|
||||
WSLDistro->Icon(winrt::hstring{ IconPath });
|
||||
WSLDistro->PathTranslationStyle(winrt::Microsoft::Terminal::Control::PathTranslationStyle::WSL);
|
||||
return WSLDistro;
|
||||
}
|
||||
@@ -226,7 +239,7 @@ static bool getWslNames(const wil::unique_hkey& wslRootKey,
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - A list of WSL profiles.
|
||||
void WslDistroGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
|
||||
void WslDistroGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles)
|
||||
{
|
||||
auto wslRootKey{ openWslRegKey() };
|
||||
if (wslRootKey)
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
{
|
||||
public:
|
||||
std::wstring_view GetNamespace() const noexcept override;
|
||||
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const override;
|
||||
std::wstring_view GetDisplayName() const noexcept override;
|
||||
std::wstring_view GetIcon() const noexcept override;
|
||||
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) override;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -77,4 +77,36 @@ namespace winrt::Microsoft::Terminal::UI::implementation
|
||||
{
|
||||
return fontWeight.Weight;
|
||||
}
|
||||
|
||||
double Converters::MaxValueFromPaddingString(const winrt::hstring& paddingString)
|
||||
{
|
||||
std::wstring buffer;
|
||||
double maxVal = 0;
|
||||
|
||||
auto& errnoRef = errno; // Nonzero cost, pay it once
|
||||
|
||||
// Get padding values till we run out of delimiter separated values in the stream
|
||||
// Non-numeral values detected will default to 0
|
||||
// std::stod will throw invalid_argument exception if the input is an invalid double value
|
||||
// std::stod will throw out_of_range exception if the input value is more than DBL_MAX
|
||||
for (const auto& part : til::split_iterator{ std::wstring_view{ paddingString }, L',' })
|
||||
{
|
||||
buffer.assign(part);
|
||||
|
||||
// wcstod handles whitespace prefix (which is ignored) & stops the
|
||||
// scan when first char outside the range of radix is encountered.
|
||||
// We'll be permissive till the extent that stod function allows us to be by default
|
||||
// Ex. a value like 100.3#535w2 will be read as 100.3, but ;df25 will fail
|
||||
errnoRef = 0;
|
||||
wchar_t* end;
|
||||
const double val = wcstod(buffer.c_str(), &end);
|
||||
|
||||
if (end != buffer.c_str() && errnoRef != ERANGE)
|
||||
{
|
||||
maxVal = std::max(maxVal, val);
|
||||
}
|
||||
}
|
||||
|
||||
return maxVal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace winrt::Microsoft::Terminal::UI::implementation
|
||||
static winrt::Windows::UI::Text::FontWeight DoubleToFontWeight(double value);
|
||||
static winrt::Windows::UI::Xaml::Media::SolidColorBrush ColorToBrush(winrt::Windows::UI::Color color);
|
||||
static double FontWeightToDouble(winrt::Windows::UI::Text::FontWeight fontWeight);
|
||||
static double MaxValueFromPaddingString(const winrt::hstring& paddingString);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -26,5 +26,6 @@ namespace Microsoft.Terminal.UI
|
||||
static Windows.UI.Text.FontWeight DoubleToFontWeight(Double value);
|
||||
static Windows.UI.Xaml.Media.SolidColorBrush ColorToBrush(Windows.UI.Color color);
|
||||
static Double FontWeightToDouble(Windows.UI.Text.FontWeight fontWeight);
|
||||
static Double MaxValueFromPaddingString(String paddingString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,11 @@
|
||||
<description>Enables the dynamic profile generator for OpenSSH config files</description>
|
||||
<id>9031</id>
|
||||
<stage>AlwaysDisabled</stage>
|
||||
<alwaysEnabledBrandingTokens>
|
||||
<brandingToken>Dev</brandingToken>
|
||||
<brandingToken>Canary</brandingToken>
|
||||
<brandingToken>Preview</brandingToken>
|
||||
</alwaysEnabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
@@ -197,4 +202,16 @@
|
||||
<alwaysDisabledReleaseTokens/>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_PowerShellInstallerProfileGenerator</name>
|
||||
<description>Enables the PowerShell Installer Dynamic Profile Generator</description>
|
||||
<id>18639</id>
|
||||
<stage>AlwaysDisabled</stage>
|
||||
<alwaysEnabledBrandingTokens>
|
||||
<brandingToken>Dev</brandingToken>
|
||||
<brandingToken>Canary</brandingToken>
|
||||
<brandingToken>Preview</brandingToken>
|
||||
</alwaysEnabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
</featureStaging>
|
||||
|
||||
Reference in New Issue
Block a user