Merge remote-tracking branch 'origin/main' into dev/migrie/f/elevation-warning

This commit is contained in:
Mike Griese 2021-08-31 16:16:02 -05:00
commit ccbcb425da
28 changed files with 403 additions and 45 deletions

View file

@ -268,6 +268,7 @@
"movePane",
"swapPane",
"moveTab",
"multipleActions",
"newTab",
"newWindow",
"nextTab",
@ -825,6 +826,24 @@
],
"required": [ "direction" ]
},
"MultipleActionsAction": {
"description": "Arguments for the multiple actions command",
"allOf": [
{ "$ref": "#/definitions/ShortcutAction" },
{
"properties": {
"action": { "type": "string", "pattern": "multipleActions" },
"actions" : {
"$ref": "#/definitions/ShortcutAction",
"type": "array",
"minItems": 1,
"description": "A list of other actions."
}
}
}
],
"required": [ "actions" ]
},
"CommandPaletteAction": {
"description": "Arguments for a commandPalette action",
"allOf": [

View file

@ -874,4 +874,21 @@ namespace winrt::TerminalApp::implementation
}
}
}
void TerminalPage::_HandleMultipleActions(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (args)
{
if (const auto& realArgs = args.ActionArgs().try_as<MultipleActionsArgs>())
{
for (const auto& action : realArgs.Actions())
{
_actionDispatch->DoAction(action);
}
args.Handled(true);
}
}
}
}

View file

@ -244,6 +244,17 @@ namespace winrt::TerminalApp::implementation
_PreviewActionHandlers(*this, actionPaletteItem.Command());
}
}
else if (_currentMode == CommandPaletteMode::CommandlineMode)
{
if (filteredCommand)
{
SearchBoxPlaceholderText(filteredCommand.Item().Name());
}
else
{
SearchBoxPlaceholderText(RS_(L"CmdPalCommandlinePrompt"));
}
}
}
void CommandPalette::_previewKeyDownHandler(IInspectable const& /*sender*/,
@ -364,6 +375,17 @@ namespace winrt::TerminalApp::implementation
_searchBox().PasteFromClipboard();
e.Handled(true);
}
else if (key == VirtualKey::Right && _currentMode == CommandPaletteMode::CommandlineMode)
{
if (const auto command{ _filteredActionsView().SelectedItem().try_as<winrt::TerminalApp::FilteredCommand>() })
{
_searchBox().Text(command.Item().Name());
_searchBox().Select(_searchBox().Text().size(), 0);
_searchBox().Focus(FocusState::Programmatic);
_filteredActionsView().SelectedIndex(-1);
e.Handled(true);
}
}
}
// Method Description:
@ -1219,6 +1241,13 @@ namespace winrt::TerminalApp::implementation
IVector<TerminalApp::FilteredCommand> CommandPalette::_loadRecentCommands()
{
const auto recentCommands = ApplicationState::SharedInstance().RecentCommands();
// If this is the first time we've opened the commandline mode and
// there aren't any recent commands, then just return an empty vector.
if (!recentCommands)
{
return single_threaded_vector<TerminalApp::FilteredCommand>();
}
std::vector<TerminalApp::FilteredCommand> parsedCommands;
parsedCommands.reserve(std::min(recentCommands.Size(), CommandLineHistoryLength));
@ -1246,7 +1275,9 @@ namespace winrt::TerminalApp::implementation
void CommandPalette::_updateRecentCommands(const hstring& command)
{
const auto recentCommands = ApplicationState::SharedInstance().RecentCommands();
const auto countToCopy = std::min(recentCommands.Size(), CommandLineHistoryLength - 1);
// If there aren't and recent commands already in the state, then we
// don't need to copy any.
const auto countToCopy = std::min(recentCommands ? recentCommands.Size() : 0, CommandLineHistoryLength - 1);
std::vector<hstring> newRecentCommands{ countToCopy + 1 };
til::at(newRecentCommands, 0) = command;
if (countToCopy)

View file

@ -697,4 +697,16 @@
<data name="DropPathTabSplit.Text" xml:space="preserve">
<value>Split the window and start in given directory</value>
</data>
<data name="ExportTabText" xml:space="preserve">
<value>Export Text</value>
</data>
<data name="ExportFailure" xml:space="preserve">
<value>Failed to export terminal content</value>
</data>
<data name="ExportSuccess" xml:space="preserve">
<value>Successfully exported terminal content</value>
</data>
<data name="PlainText" xml:space="preserve">
<value>Plain Text</value>
</data>
</root>

View file

@ -28,6 +28,9 @@ using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::System;
using namespace winrt::Windows::ApplicationModel::DataTransfer;
using namespace winrt::Windows::UI::Text;
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::Storage::Pickers;
using namespace winrt::Windows::Storage::Provider;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
@ -171,6 +174,16 @@ namespace winrt::TerminalApp::implementation
}
});
newTabImpl->ExportTabRequested([weakTab, weakThis{ get_weak() }]() {
auto page{ weakThis.get() };
auto tab{ weakTab.get() };
if (page && tab)
{
page->_ExportTab(*tab);
}
});
auto tabViewItem = newTabImpl->TabViewItem();
_tabView.TabItems().Append(tabViewItem);
@ -427,6 +440,45 @@ namespace winrt::TerminalApp::implementation
CATCH_LOG();
}
// Method Description:
// - Exports the content of the Terminal Buffer inside the tab
// Arguments:
// - tab: tab to export
winrt::fire_and_forget TerminalPage::_ExportTab(const TerminalTab& tab)
{
try
{
if (const auto control{ tab.GetActiveTerminalControl() })
{
const FileSavePicker savePicker;
savePicker.as<IInitializeWithWindow>()->Initialize(*_hostingHwnd);
savePicker.SuggestedStartLocation(PickerLocationId::Downloads);
const auto fileChoices = single_threaded_vector<hstring>({ L".txt" });
savePicker.FileTypeChoices().Insert(RS_(L"PlainText"), fileChoices);
savePicker.SuggestedFileName(control.Title());
const StorageFile file = co_await savePicker.PickSaveFileAsync();
if (file != nullptr)
{
const auto buffer = control.ReadEntireBuffer();
CachedFileManager::DeferUpdates(file);
co_await FileIO::WriteTextAsync(file, buffer);
const auto status = co_await CachedFileManager::CompleteUpdatesAsync(file);
switch (status)
{
case FileUpdateStatus::Complete:
case FileUpdateStatus::CompleteAndRenamed:
_ShowControlNoticeDialog(RS_(L"NoticeInfo"), RS_(L"ExportSuccess"));
break;
default:
_ShowControlNoticeDialog(RS_(L"NoticeError"), RS_(L"ExportFailure"));
}
}
}
}
CATCH_LOG();
}
// Method Description:
// - Removes the tab (both TerminalControl and XAML) after prompting for approval
// Arguments:

View file

@ -222,6 +222,7 @@ namespace winrt::TerminalApp::implementation
void _DuplicateTab(const TerminalTab& tab);
void _SplitTab(TerminalTab& tab);
winrt::fire_and_forget _ExportTab(const TerminalTab& tab);
winrt::Windows::Foundation::IAsyncAction _HandleCloseTabRequested(winrt::TerminalApp::TabBase tab);
void _CloseTabAtIndex(uint32_t index);

View file

@ -1211,6 +1211,23 @@ namespace winrt::TerminalApp::implementation
splitTabMenuItem.Icon(splitTabSymbol);
}
Controls::MenuFlyoutItem exportTabMenuItem;
{
// "Split Tab"
Controls::FontIcon exportTabSymbol;
exportTabSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
exportTabSymbol.Glyph(L"\xE74E"); // Save
exportTabMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->_ExportTabRequestedHandlers();
}
});
exportTabMenuItem.Text(RS_(L"ExportTabText"));
exportTabMenuItem.Icon(exportTabSymbol);
}
// Build the menu
Controls::MenuFlyout contextMenuFlyout;
Controls::MenuFlyoutSeparator menuSeparator;
@ -1218,6 +1235,7 @@ namespace winrt::TerminalApp::implementation
contextMenuFlyout.Items().Append(renameTabMenuItem);
contextMenuFlyout.Items().Append(duplicateTabMenuItem);
contextMenuFlyout.Items().Append(splitTabMenuItem);
contextMenuFlyout.Items().Append(exportTabMenuItem);
contextMenuFlyout.Items().Append(menuSeparator);
// GH#5750 - When the context menu is dismissed with ESC, toss the focus
@ -1592,4 +1610,5 @@ namespace winrt::TerminalApp::implementation
DEFINE_EVENT(TerminalTab, TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
DEFINE_EVENT(TerminalTab, DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
DEFINE_EVENT(TerminalTab, SplitTabRequested, _SplitTabRequestedHandlers, winrt::delegate<>);
DEFINE_EVENT(TerminalTab, ExportTabRequested, _ExportTabRequestedHandlers, winrt::delegate<>);
}

View file

@ -103,6 +103,7 @@ namespace winrt::TerminalApp::implementation
DECLARE_EVENT(TabRaiseVisualBell, _TabRaiseVisualBellHandlers, winrt::delegate<>);
DECLARE_EVENT(DuplicateRequested, _DuplicateRequestedHandlers, winrt::delegate<>);
DECLARE_EVENT(SplitTabRequested, _SplitTabRequestedHandlers, winrt::delegate<>);
DECLARE_EVENT(ExportTabRequested, _ExportTabRequestedHandlers, winrt::delegate<>);
TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable);
private:

View file

@ -56,6 +56,9 @@
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
#include <winrt/Microsoft.Terminal.Settings.Editor.h>
#include <winrt/Microsoft.Terminal.Settings.Model.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Provider.h>
#include <winrt/Windows.Storage.Pickers.h>
#include <windows.ui.xaml.media.dxinterop.h>

View file

@ -1499,4 +1499,32 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_updatePatternLocations->Run();
}
hstring ControlCore::ReadEntireBuffer() const
{
auto terminalLock = _terminal->LockForWriting();
const auto& textBuffer = _terminal->GetTextBuffer();
std::wstringstream ss;
const auto lastRow = textBuffer.GetLastNonSpaceCharacter().Y;
for (auto rowIndex = 0; rowIndex <= lastRow; rowIndex++)
{
const auto& row = textBuffer.GetRowByOffset(rowIndex);
auto rowText = row.GetText();
const auto strEnd = rowText.find_last_not_of(UNICODE_SPACE);
if (strEnd != std::string::npos)
{
rowText.erase(strEnd + 1);
ss << rowText;
}
if (!row.WasWrapForced())
{
ss << UNICODE_CARRIAGERETURN << UNICODE_LINEFEED;
}
}
return hstring(ss.str());
}
}

View file

@ -144,6 +144,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool IsInReadOnlyMode() const;
void ToggleReadOnlyMode();
hstring ReadEntireBuffer() const;
// -------------------------------- WinRT Events ---------------------------------
// clang-format off
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);

View file

@ -81,6 +81,8 @@ namespace Microsoft.Terminal.Control
Boolean CursorOn;
void EnablePainting();
String ReadEntireBuffer();
event FontSizeChangedEventArgs FontSizeChanged;
event Windows.Foundation.TypedEventHandler<Object, CopyToClipboardEventArgs> CopyToClipboard;

View file

@ -2586,4 +2586,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_playWarningBell->Run();
}
hstring TermControl::ReadEntireBuffer() const
{
return _core.ReadEntireBuffer();
}
}

View file

@ -107,6 +107,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
static unsigned int GetPointerUpdateKind(const winrt::Windows::UI::Input::PointerPoint point);
static Windows::UI::Xaml::Thickness ParseThicknessFromPadding(const hstring padding);
hstring ReadEntireBuffer() const;
// -------------------------------- WinRT Events ---------------------------------
// clang-format off
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);

View file

@ -67,5 +67,7 @@ namespace Microsoft.Terminal.Control
Boolean ReadOnly { get; };
void ToggleReadOnly();
String ReadEntireBuffer();
}
}

View file

@ -198,4 +198,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
globals.Language(currentLanguage);
}
}
bool GlobalAppearance::FeatureTrayIconEnabled() const noexcept
{
return Feature_TrayIcon::IsEnabled();
}
}

View file

@ -25,6 +25,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
bool FeatureTrayIconEnabled() const noexcept;
WINRT_PROPERTY(Editor::GlobalAppearancePageNavigationState, State, nullptr);
GETSET_BINDABLE_ENUM_SETTING(Theme, winrt::Windows::UI::Xaml::ElementTheme, State().Globals, Theme);
GETSET_BINDABLE_ENUM_SETTING(TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, State().Globals, TabWidthMode);

View file

@ -25,5 +25,7 @@ namespace Microsoft.Terminal.Settings.Editor
IInspectable CurrentTabWidthMode;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> TabWidthModeList { get; };
Boolean FeatureTrayIconEnabled { get; };
}
}

View file

@ -84,6 +84,18 @@
<local:SettingContainer x:Uid="Globals_DisableAnimationsReversed">
<ToggleSwitch IsOn="{x:Bind local:Converters.InvertBoolean(State.Globals.DisableAnimations), BindBack=State.Globals.SetInvertedDisableAnimationsValue, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Always Show Tray Icon -->
<local:SettingContainer x:Uid="Globals_AlwaysShowTrayIcon"
Visibility="{x:Bind FeatureTrayIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.AlwaysShowTrayIcon, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Minimize To Tray -->
<local:SettingContainer x:Uid="Globals_MinimizeToTray"
Visibility="{x:Bind FeatureTrayIconEnabled}">
<ToggleSwitch IsOn="{x:Bind State.Globals.MinimizeToTray, Mode=TwoWay}" />
</local:SettingContainer>
</StackPanel>
</ScrollViewer>
</Page>

View file

@ -1090,6 +1090,14 @@
<value>Pane animations</value>
<comment>Header for a control to toggle animations on panes. "Enabled" value enables the animations.</comment>
</data>
<data name="Globals_AlwaysShowTrayIcon.Header" xml:space="preserve">
<value>Always display an icon in the notification area</value>
<comment>Header for a control to toggle whether the tray icon should always be shown.</comment>
</data>
<data name="Globals_MinimizeToTray.Header" xml:space="preserve">
<value>Hide Terminal in the notification area when it is minimized</value>
<comment>Header for a control to toggle whether the terminal should hide itself in the tray instead of the taskbar when minimized.</comment>
</data>
<data name="SettingContainer_OverrideMessageBaseLayer" xml:space="preserve">
<value>Reset to inherited value.</value>
<comment>This button will remove a user's customization from a given setting, restoring it to the value that the profile inherited. This is a text label on a button.</comment>

View file

@ -65,6 +65,7 @@ static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" };
static constexpr std::string_view GlobalSummonKey{ "globalSummon" };
static constexpr std::string_view QuakeModeKey{ "quakeMode" };
static constexpr std::string_view FocusPaneKey{ "focusPane" };
static constexpr std::string_view MultipleActionsKey{ "multipleActions" };
static constexpr std::string_view ActionKey{ "action" };
@ -366,6 +367,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::GlobalSummon, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") },
{ ShortcutAction::FocusPane, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::MultipleActions, L"" }, // Intentionally omitted, must be generated by GenerateName
};
}();

View file

@ -35,3 +35,34 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
{
BASIC_FACTORY(ActionAndArgs);
}
namespace Microsoft::Terminal::Settings::Model::JsonUtils
{
using namespace winrt::Microsoft::Terminal::Settings::Model;
template<>
struct ConversionTrait<ActionAndArgs>
{
ActionAndArgs FromJson(const Json::Value& json)
{
std::vector<SettingsLoadWarnings> v;
return *implementation::ActionAndArgs::FromJson(json, v);
}
bool CanConvert(const Json::Value& json) const
{
// commands without args might just be a string
return json.isString() || json.isObject();
}
Json::Value ToJson(const ActionAndArgs& val)
{
return implementation::ActionAndArgs::ToJson(val);
}
std::string TypeDescription() const
{
return "ActionAndArgs";
}
};
}

View file

@ -34,6 +34,7 @@
#include "RenameWindowArgs.g.cpp"
#include "GlobalSummonArgs.g.cpp"
#include "FocusPaneArgs.g.cpp"
#include "MultipleActionsArgs.g.cpp"
#include <LibraryResources.h>
@ -687,4 +688,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Id())
};
}
winrt::hstring MultipleActionsArgs::GenerateName() const
{
return L"";
}
}

View file

@ -36,6 +36,7 @@
#include "RenameWindowArgs.g.h"
#include "GlobalSummonArgs.g.h"
#include "FocusPaneArgs.g.h"
#include "MultipleActionsArgs.g.h"
#include "../../cascadia/inc/cppwinrt_utils.h"
#include "JsonUtils.h"
@ -1754,6 +1755,53 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
};
struct MultipleActionsArgs : public MultipleActionsArgsT<MultipleActionsArgs>
{
MultipleActionsArgs() = default;
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<ActionAndArgs>, Actions);
static constexpr std::string_view ActionsKey{ "actions" };
public:
hstring GenerateName() const;
bool Equals(const IActionArgs& other)
{
auto otherAsUs = other.try_as<MultipleActionsArgs>();
if (otherAsUs)
{
return otherAsUs->_Actions == _Actions;
}
return false;
};
static FromJsonResult FromJson(const Json::Value& json)
{
// LOAD BEARING: Not using make_self here _will_ break you in the future!
auto args = winrt::make_self<MultipleActionsArgs>();
JsonUtils::GetValueForKey(json, ActionsKey, args->_Actions);
return { *args, {} };
}
static Json::Value ToJson(const IActionArgs& val)
{
if (!val)
{
return {};
}
Json::Value json{ Json::ValueType::objectValue };
const auto args{ get_self<MultipleActionsArgs>(val) };
JsonUtils::SetValueForKey(json, ActionsKey, args->_Actions);
return json;
}
IActionArgs Copy() const
{
auto copy{ winrt::make_self<MultipleActionsArgs>() };
copy->_Actions = _Actions;
return *copy;
}
size_t Hash() const
{
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Actions);
}
};
}
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
@ -1778,4 +1826,5 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
BASIC_FACTORY(FocusPaneArgs);
BASIC_FACTORY(PrevTabArgs);
BASIC_FACTORY(NextTabArgs);
BASIC_FACTORY(MultipleActionsArgs);
}

View file

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "Command.idl";
namespace Microsoft.Terminal.Settings.Model
{
interface IActionArgs
@ -308,4 +310,10 @@ namespace Microsoft.Terminal.Settings.Model
FocusPaneArgs(UInt32 Id);
UInt32 Id { get; };
};
[default_interface] runtimeclass MultipleActionsArgs : IActionArgs
{
MultipleActionsArgs();
Windows.Foundation.Collections.IVector<ActionAndArgs> Actions;
}
}

View file

@ -78,7 +78,8 @@
ON_ALL_ACTIONS(OpenWindowRenamer) \
ON_ALL_ACTIONS(GlobalSummon) \
ON_ALL_ACTIONS(QuakeMode) \
ON_ALL_ACTIONS(FocusPane)
ON_ALL_ACTIONS(FocusPane) \
ON_ALL_ACTIONS(MultipleActions)
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \
@ -109,4 +110,5 @@
ON_ALL_ACTIONS_WITH_ARGS(SplitPane) \
ON_ALL_ACTIONS_WITH_ARGS(SwitchToTab) \
ON_ALL_ACTIONS_WITH_ARGS(ToggleCommandPalette) \
ON_ALL_ACTIONS_WITH_ARGS(FocusPane)
ON_ALL_ACTIONS_WITH_ARGS(FocusPane) \
ON_ALL_ACTIONS_WITH_ARGS(MultipleActions)

View file

@ -55,48 +55,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
return fmt::format("{}[]", ConversionTrait<GUID>{}.TypeDescription());
}
};
template<typename T>
struct ConversionTrait<winrt::Windows::Foundation::Collections::IVector<T>>
{
winrt::Windows::Foundation::Collections::IVector<T> FromJson(const Json::Value& json) const
{
ConversionTrait<T> trait;
std::vector<T> val;
val.reserve(json.size());
for (const auto& element : json)
{
val.push_back(trait.FromJson(element));
}
return winrt::single_threaded_vector(move(val));
}
bool CanConvert(const Json::Value& json) const
{
ConversionTrait<T> trait;
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) -> bool { return trait.CanConvert(json); });
}
Json::Value ToJson(const winrt::Windows::Foundation::Collections::IVector<T>& val)
{
ConversionTrait<T> trait;
Json::Value json{ Json::arrayValue };
for (const auto& key : val)
{
json.append(trait.ToJson(key));
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("vector ({})", ConversionTrait<T>{}.TypeDescription());
}
};
}
using namespace ::Microsoft::Terminal::Settings::Model;

View file

@ -177,6 +177,47 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
}
};
template<typename T>
struct ConversionTrait<std::vector<T>>
{
std::vector<T> FromJson(const Json::Value& json)
{
std::vector<T> val;
val.reserve(json.size());
ConversionTrait<T> trait;
for (const auto& element : json)
{
val.push_back(trait.FromJson(element));
}
return val;
}
bool CanConvert(const Json::Value& json) const
{
ConversionTrait<T> trait;
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) mutable -> bool { return trait.CanConvert(json); });
}
Json::Value ToJson(const std::vector<T>& val)
{
Json::Value json{ Json::arrayValue };
ConversionTrait<T> trait;
for (const auto& v : val)
{
json.append(trait.ToJson(v));
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("{}[]", ConversionTrait<T>{}.TypeDescription());
}
};
template<typename T>
struct ConversionTrait<std::unordered_map<std::string, T>>
{
@ -259,6 +300,42 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
}
};
template<typename T>
struct ConversionTrait<winrt::Windows::Foundation::Collections::IVector<T>>
{
winrt::Windows::Foundation::Collections::IVector<T> FromJson(const Json::Value& json)
{
ConversionTrait<std::vector<T>> trait;
return winrt::single_threaded_vector<T>(std::move(trait.FromJson(json)));
}
bool CanConvert(const Json::Value& json) const
{
ConversionTrait<std::vector<T>> trait;
return trait.CanConvert(json);
}
Json::Value ToJson(const winrt::Windows::Foundation::Collections::IVector<T>& val)
{
Json::Value json{ Json::arrayValue };
if (val)
{
ConversionTrait<T> trait;
for (const auto& v : val)
{
json.append(trait.ToJson(v));
}
}
return json;
}
std::string TypeDescription() const
{
return fmt::format("{}[]", ConversionTrait<T>{}.TypeDescription());
}
};
template<typename T>
struct ConversionTrait<winrt::Windows::Foundation::Collections::IMap<winrt::hstring, T>>
{