mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 14:19:45 +00:00
Compare commits
19 Commits
v1.24.2812
...
dev/cazamo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87946e9f01 | ||
|
|
6959e7ea28 | ||
|
|
f25e6fe2d1 | ||
|
|
e9f83fc4eb | ||
|
|
c371c484a3 | ||
|
|
1072d69fb7 | ||
|
|
b472d9fed9 | ||
|
|
7fcdeaac40 | ||
|
|
2766f21d31 | ||
|
|
0af4eb0e21 | ||
|
|
b0a7ef1d39 | ||
|
|
7cdbb7c795 | ||
|
|
5afc9bc86a | ||
|
|
6b83fa705a | ||
|
|
e6c43c0d4c | ||
|
|
75d02c29bd | ||
|
|
f11515e692 | ||
|
|
7f0c9e5374 | ||
|
|
019bb766db |
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>
|
||||
|
||||
@@ -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}" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
351
src/cascadia/TerminalSettingsEditor/Extensions.cpp
Normal file
351
src/cascadia/TerminalSettingsEditor/Extensions.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
// 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->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 }
|
||||
{
|
||||
UpdateSettings(settings, colorSchemesPageVM);
|
||||
|
||||
PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) {
|
||||
const auto viewModelProperty{ args.PropertyName() };
|
||||
if (viewModelProperty == L"CurrentExtensionPackage")
|
||||
{
|
||||
// Update the views to reflect the current extension package, if one is selected.
|
||||
// Otherwise, show components from all extensions
|
||||
_profilesModifiedView.Clear();
|
||||
_profilesAddedView.Clear();
|
||||
_colorSchemesAddedView.Clear();
|
||||
|
||||
// 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())
|
||||
{
|
||||
for (const auto& profile : ext.ProfilesModified())
|
||||
{
|
||||
_profilesModifiedView.Append(profile);
|
||||
}
|
||||
for (const auto& profile : ext.ProfilesAdded())
|
||||
{
|
||||
_profilesAddedView.Append(profile);
|
||||
}
|
||||
for (const auto& scheme : ext.ColorSchemesAdded())
|
||||
{
|
||||
_colorSchemesAddedView.Append(scheme);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (const auto currentExtensionPackage = CurrentExtensionPackage())
|
||||
{
|
||||
addPackageContentsToView(currentExtensionPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& extPkg : _extensionPackages)
|
||||
{
|
||||
addPackageContentsToView(extPkg);
|
||||
}
|
||||
}
|
||||
|
||||
_NotifyChanges(L"IsExtensionView", L"CurrentExtensionPackageIdentifierTemplate");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ExtensionsViewModel::UpdateSettings(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM)
|
||||
{
|
||||
_settings = settings;
|
||||
_colorSchemesPageVM = colorSchemesPageVM;
|
||||
_CurrentExtensionPackage = nullptr;
|
||||
|
||||
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);
|
||||
extensionPackages.push_back(*extPkgVM);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
extPkgVM->FragmentExtensions().Append(winrt::make<FragmentExtensionViewModel>(fragExt, currentProfilesModified, currentProfilesAdded, currentColorSchemesAdded));
|
||||
}
|
||||
}
|
||||
|
||||
_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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Returns the accessible name for the extension package with the disabled state (if disabled) in the following format:
|
||||
// "<DisplayName?>, <Source>: <Disabled?>"
|
||||
hstring ExtensionPackageViewModel::AccessibleNameWithStatus() const noexcept
|
||||
{
|
||||
if (Enabled())
|
||||
{
|
||||
return AccessibleName();
|
||||
}
|
||||
return hstring{ fmt::format(FMT_COMPILE(L"{}: {}"), AccessibleName(), RS_(L"Extension_StateDisabled/Text")) };
|
||||
}
|
||||
|
||||
hstring FragmentProfileViewModel::AccessibleName() const noexcept
|
||||
{
|
||||
return hstring{ fmt::format(FMT_COMPILE(L"{}, {}"), Profile().Name(), SourceName()) };
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
180
src/cascadia/TerminalSettingsEditor/Extensions.h
Normal file
180
src/cascadia/TerminalSettingsEditor/Extensions.h
Normal file
@@ -0,0 +1,180 @@
|
||||
// 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:
|
||||
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 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;
|
||||
};
|
||||
|
||||
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>() } {}
|
||||
|
||||
Model::ExtensionPackage Package() const noexcept { return _package; }
|
||||
hstring Scope() const noexcept;
|
||||
bool Enabled() const;
|
||||
void Enabled(bool val);
|
||||
hstring AccessibleName() const noexcept;
|
||||
hstring AccessibleNameWithStatus() 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 } {}
|
||||
|
||||
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 } {}
|
||||
|
||||
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);
|
||||
}
|
||||
80
src/cascadia/TerminalSettingsEditor/Extensions.idl
Normal file
80
src/cascadia/TerminalSettingsEditor/Extensions.idl
Normal file
@@ -0,0 +1,80 @@
|
||||
// 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; };
|
||||
}
|
||||
|
||||
[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; };
|
||||
String AccessibleNameWithStatus { 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;
|
||||
}
|
||||
}
|
||||
493
src/cascadia/TerminalSettingsEditor/Extensions.xaml
Normal file
493
src/cascadia/TerminalSettingsEditor/Extensions.xaml
Normal file
@@ -0,0 +1,493 @@
|
||||
<!--
|
||||
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">
|
||||
<TextBlock Text="{x:Bind Package.Source}" />
|
||||
</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}" />
|
||||
|
||||
<TextBlock x:Uid="Extension_StateDisabled"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource SecondaryTextBlockStyle}"
|
||||
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(Enabled)}" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="ComplexExtensionNavigatorTemplate"
|
||||
x:DataType="local:ExtensionPackageViewModel">
|
||||
<Button AutomationProperties.Name="{x:Bind AccessibleNameWithStatus}"
|
||||
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}" />
|
||||
|
||||
<TextBlock x:Uid="Extension_StateDisabled"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource SecondaryTextBlockStyle}"
|
||||
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(Enabled)}" />
|
||||
</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}">
|
||||
<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 Spacing="20"
|
||||
Style="{StaticResource SettingsStackStyle}">
|
||||
|
||||
<!-- [Root View Only] -->
|
||||
<!-- Set margin.bottom to -20 because the next stack panel gets the 20 from the root's spacing property -->
|
||||
<StackPanel MaxWidth="{StaticResource StandardControlMaxWidth}"
|
||||
Margin="0,0,0,-20"
|
||||
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}" />
|
||||
<TextBlock x:Uid="Extensions_NoActiveExtensionsDisclaimer"
|
||||
Style="{StaticResource ItalicDisclaimerStyle}"
|
||||
Visibility="{x:Bind ViewModel.NoExtensionPackages, Mode=OneWay}" />
|
||||
<ItemsControl x:Name="ActiveExtensionsList"
|
||||
IsTabStop="False"
|
||||
ItemTemplateSelector="{StaticResource ExtensionPackageNavigatorTemplateSelector}"
|
||||
ItemsSource="{x:Bind ViewModel.ExtensionPackages}"
|
||||
XYFocusKeyboardNavigation="Enabled" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- [Extension View Only] -->
|
||||
<!-- Set margin.top to -20 because the previous stack panel gets the 20 from the root's spacing property -->
|
||||
<StackPanel MaxWidth="{StaticResource StandardControlMaxWidth}"
|
||||
Margin="0,-20,0,0"
|
||||
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>
|
||||
<TextBlock x:Uid="Extensions_ModifiedProfilesHeader"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
<TextBlock x:Uid="Extensions_NoModifiedProfilesDisclaimer"
|
||||
Style="{StaticResource ItalicDisclaimerStyle}"
|
||||
Visibility="{x:Bind ViewModel.NoProfilesModified, Mode=OneWay}" />
|
||||
<ItemsControl x:Name="ModifiedProfilesList"
|
||||
IsTabStop="False"
|
||||
ItemTemplate="{StaticResource FragmentProfileViewModelTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.ProfilesModified, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Grouping: Added Profiles -->
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="Extensions_AddedProfilesHeader"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
<TextBlock x:Uid="Extensions_NoAddedProfilesDisclaimer"
|
||||
Style="{StaticResource ItalicDisclaimerStyle}"
|
||||
Visibility="{x:Bind ViewModel.NoProfilesAdded, Mode=OneWay}" />
|
||||
<ItemsControl x:Name="AddedProfilesList"
|
||||
IsTabStop="False"
|
||||
ItemTemplate="{StaticResource FragmentProfileViewModelTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.ProfilesAdded, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Grouping: Added Color Schemes -->
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="Extensions_AddedColorSchemesHeader"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
<TextBlock x:Uid="Extensions_NoAddedColorSchemesDisclaimer"
|
||||
Style="{StaticResource ItalicDisclaimerStyle}"
|
||||
Visibility="{x:Bind ViewModel.NoSchemesAdded, Mode=OneWay}" />
|
||||
<ItemsControl x:Name="AddedColorSchemesList"
|
||||
IsTabStop="False"
|
||||
ItemTemplate="{StaticResource FragmentColorSchemeViewModelTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.ColorSchemesAdded, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Page>
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Compatibility.h"
|
||||
#include "Rendering.h"
|
||||
#include "RenderingViewModel.h"
|
||||
#include "Extensions.h"
|
||||
#include "Actions.h"
|
||||
#include "ProfileViewModel.h"
|
||||
#include "GlobalAppearance.h"
|
||||
@@ -44,6 +45,7 @@ static const std::wstring_view renderingTag{ L"Rendering_Nav" };
|
||||
static const std::wstring_view compatibilityTag{ L"Compatibility_Nav" };
|
||||
static const std::wstring_view actionsTag{ L"Actions_Nav" };
|
||||
static const std::wstring_view newTabMenuTag{ L"NewTabMenu_Nav" };
|
||||
static const std::wstring_view extensionsTag{ L"Extensions_Nav" };
|
||||
static const std::wstring_view globalProfileTag{ L"GlobalProfile_Nav" };
|
||||
static const std::wstring_view addProfileTag{ L"AddProfile" };
|
||||
static const std::wstring_view colorSchemesTag{ L"ColorSchemes_Nav" };
|
||||
@@ -111,6 +113,33 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
});
|
||||
|
||||
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,6 +190,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
// Update the Nav State with the new version of the settings
|
||||
_colorSchemesPageVM.UpdateSettings(_settingsClone);
|
||||
_newTabMenuPageVM.UpdateSettings(_settingsClone);
|
||||
_extensionsVM.UpdateSettings(_settingsClone, _colorSchemesPageVM);
|
||||
|
||||
// We'll update the profile in the _profilesNavState whenever we actually navigate to one
|
||||
|
||||
@@ -182,7 +212,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;
|
||||
}
|
||||
}
|
||||
@@ -197,6 +227,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>() })
|
||||
{
|
||||
@@ -445,6 +486,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()) };
|
||||
@@ -575,6 +631,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::OpenJsonTapped(const IInspectable& /*sender*/, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& /*args*/)
|
||||
{
|
||||
const auto window = CoreWindow::GetForCurrentThread();
|
||||
@@ -621,6 +711,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);
|
||||
@@ -818,6 +912,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();
|
||||
|
||||
@@ -45,6 +45,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;
|
||||
|
||||
@@ -70,16 +71,21 @@ 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::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 _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,8 @@ namespace Microsoft.Terminal.Settings.Editor
|
||||
Profile_Terminal,
|
||||
Profile_Advanced,
|
||||
ColorSchemes_Edit,
|
||||
NewTabMenu_Folder
|
||||
NewTabMenu_Folder,
|
||||
Extensions_Extension
|
||||
};
|
||||
|
||||
runtimeclass Breadcrumb : Windows.Foundation.IStringable
|
||||
@@ -42,6 +45,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="" />
|
||||
@@ -155,6 +156,17 @@
|
||||
</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" />
|
||||
|
||||
<muxc:NavigationViewItem x:Name="BaseLayerMenuItem"
|
||||
|
||||
@@ -125,6 +125,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>
|
||||
@@ -199,6 +203,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>
|
||||
@@ -309,6 +316,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>
|
||||
@@ -408,6 +419,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>
|
||||
|
||||
@@ -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,10 @@
|
||||
<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_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>
|
||||
@@ -2340,4 +2344,67 @@
|
||||
<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_NoActiveExtensionsDisclaimer.Text" xml:space="preserve">
|
||||
<value>None</value>
|
||||
<comment>Text displayed when no extensions are available. Shown in place of a list of entries.</comment>
|
||||
</data>
|
||||
<data name="Extensions_NoModifiedProfilesDisclaimer.Text" xml:space="preserve">
|
||||
<value>None</value>
|
||||
<comment>Text displayed when no profiles were modified. Shown in place of a list of entries.</comment>
|
||||
</data>
|
||||
<data name="Extensions_NoAddedColorSchemesDisclaimer.Text" xml:space="preserve">
|
||||
<value>None</value>
|
||||
<comment>Text displayed when no color schemes were added. Shown in place of a list of entries.</comment>
|
||||
</data>
|
||||
<data name="Extensions_NoAddedProfilesDisclaimer.Text" xml:space="preserve">
|
||||
<value>None</value>
|
||||
<comment>Text displayed when no profiles were added. Shown in place of a list of entries.</comment>
|
||||
</data>
|
||||
<data name="Extensions_DisclaimerHyperlink.Content" xml:space="preserve">
|
||||
<value>Learn more about fragment 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="Extension_StateDisabled.Text" xml:space="preserve">
|
||||
<value>Disabled</value>
|
||||
<comment>Text displayed when an extension is disabled</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>
|
||||
@@ -179,13 +179,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 +233,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 +346,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=""
|
||||
|
||||
@@ -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,6 +25,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
{
|
||||
public:
|
||||
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) const override;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -113,6 +113,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 +177,11 @@ IObservableVector<Model::Profile> CascadiaSettings::ActiveProfiles() const noexc
|
||||
return _activeProfiles;
|
||||
}
|
||||
|
||||
IVectorView<Model::ExtensionPackage> CascadiaSettings::Extensions() const noexcept
|
||||
{
|
||||
return _extensionPackages.GetView();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns the globally configured keybindings
|
||||
// Arguments:
|
||||
|
||||
@@ -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;
|
||||
@@ -70,6 +96,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:
|
||||
@@ -88,13 +115,14 @@ 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, FragmentScope scope, std::wstring_view jsonFilename, bool applyToSettings);
|
||||
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);
|
||||
bool _addOrMergeUserColorScheme(const winrt::com_ptr<implementation::ColorScheme>& colorScheme);
|
||||
void _executeGenerator(const IDynamicProfileGenerator& generator);
|
||||
winrt::com_ptr<implementation::ExtensionPackage> _registerFragment(const winrt::Microsoft::Terminal::Settings::Model::FragmentSettings& fragment, FragmentScope scope);
|
||||
|
||||
std::unordered_set<winrt::hstring, til::transparent_hstring_hash, til::transparent_hstring_equal_to> _ignoredNamespaces;
|
||||
std::set<std::string> themesChangeLog;
|
||||
@@ -127,6 +155,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() const noexcept;
|
||||
void WriteSettingsToDisk();
|
||||
Json::Value ToJson() const;
|
||||
Model::Profile ProfileDefaults() const;
|
||||
@@ -184,6 +213,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 = winrt::single_threaded_vector<Model::ExtensionPackage>();
|
||||
std::set<std::string> _themesChangeLog{};
|
||||
|
||||
// load errors
|
||||
@@ -199,6 +229,69 @@ 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; }
|
||||
hstring Json() const noexcept { return _json; }
|
||||
|
||||
private:
|
||||
winrt::guid _profileGuid;
|
||||
hstring _json;
|
||||
};
|
||||
|
||||
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; }
|
||||
WINRT_PROPERTY(bool, Conflict, false);
|
||||
|
||||
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 Json() const noexcept { return _json; }
|
||||
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; }
|
||||
|
||||
// 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 _json;
|
||||
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
|
||||
|
||||
@@ -8,6 +8,12 @@ import "DefaultTerminal.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Model
|
||||
{
|
||||
enum FragmentScope
|
||||
{
|
||||
User,
|
||||
Machine
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass CascadiaSettings {
|
||||
static CascadiaSettings LoadDefaults();
|
||||
static CascadiaSettings LoadAll();
|
||||
@@ -38,6 +44,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 +63,36 @@ 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; };
|
||||
Boolean Conflict { 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; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,17 +246,18 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings()
|
||||
{
|
||||
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, bool applyToSettings) {
|
||||
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, scope, fragExtPath.filename().wstring(), applyToSettings);
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
@@ -278,9 +279,12 @@ 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
|
||||
!_ignoredNamespaces.contains(std::wstring_view{ source })); // applyToSettings
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,11 +316,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 +335,16 @@ 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,
|
||||
!_ignoredNamespaces.contains(std::wstring_view{ packageName })); // applyToSettings
|
||||
|
||||
auto extPkg = extensionPackageMap[packageName];
|
||||
extPkg->Icon(package.Logo().AbsoluteUri());
|
||||
extPkg->DisplayName(package.DisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,7 +355,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, FragmentScope::User, L"filename.json", true);
|
||||
}
|
||||
|
||||
// Call this method before passing SettingsLoader to the CascadiaSettings constructor.
|
||||
@@ -724,15 +734,24 @@ 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)
|
||||
// - scope: The scope of the fragment file (user or machine).
|
||||
// - jsonFilename: The filename of the JSON file being parsed.
|
||||
// - applyToSettings: If true, the parsed settings will be applied to the user settings. Otherwise, load the fragment for the settings UI, but don't apply it.
|
||||
void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::string_view& content, ParsedSettings& settings, FragmentScope scope, std::wstring_view jsonFilename, bool applyToSettings)
|
||||
{
|
||||
auto json = _parseJson(content);
|
||||
|
||||
Json::StreamWriterBuilder styledWriter;
|
||||
styledWriter["indentation"] = " ";
|
||||
styledWriter["commentStyle"] = "All";
|
||||
auto fragmentSettings = winrt::make_self<FragmentSettings>(source, hstring{ til::u8u16(Json::writeString(styledWriter, json.root)) }, hstring{ jsonFilename });
|
||||
|
||||
settings.clear();
|
||||
|
||||
{
|
||||
settings.globals = winrt::make_self<GlobalAppSettings>();
|
||||
|
||||
std::vector<Model::FragmentColorSchemeEntry> fragmentColorSchemes;
|
||||
for (const auto& schemeJson : json.colorSchemes)
|
||||
{
|
||||
try
|
||||
@@ -740,19 +759,27 @@ 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 (applyToSettings)
|
||||
{
|
||||
// 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));
|
||||
}
|
||||
fragmentColorSchemes.emplace_back(winrt::make<FragmentColorSchemeEntry>(scheme->Name(), hstring{ til::u8u16(Json::writeString(styledWriter, schemeJson)) }));
|
||||
}
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
fragmentSettings->ColorSchemes(fragmentColorSchemes.empty() ? nullptr : single_threaded_vector<Model::FragmentColorSchemeEntry>(std::move(fragmentColorSchemes)));
|
||||
|
||||
// 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 (applyToSettings)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
@@ -760,52 +787,77 @@ void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::str
|
||||
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 (applyToSettings)
|
||||
{
|
||||
_appendProfile(std::move(profile), guid, settings);
|
||||
}
|
||||
destinationSet.emplace_back(winrt::make<FragmentProfileEntry>(guid, hstring{ til::u8u16(Json::writeString(styledWriter, profileJson)) }));
|
||||
}
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
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)));
|
||||
}
|
||||
|
||||
for (const auto& fragmentProfile : settings.profiles)
|
||||
if (applyToSettings)
|
||||
{
|
||||
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);
|
||||
if (!_addOrMergeUserColorScheme(fragmentColorScheme))
|
||||
{
|
||||
// Color scheme wasn't added because it conflicted with a non-user created scheme.
|
||||
// Mark the fragment's color scheme as conflicting.
|
||||
for (auto schemeEntry : fragmentSettings->ColorSchemes())
|
||||
{
|
||||
if (schemeEntry.ColorSchemeName() == fragmentColorScheme->Name())
|
||||
{
|
||||
get_self<FragmentColorSchemeEntry>(schemeEntry)->Conflict(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
_registerFragment(std::move(*fragmentSettings), scope);
|
||||
}
|
||||
|
||||
SettingsLoader::JsonSettings SettingsLoader::_parseJson(const std::string_view& content)
|
||||
@@ -905,7 +957,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,9 +984,12 @@ 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.
|
||||
@@ -941,31 +997,67 @@ void SettingsLoader::_addOrMergeUserColorScheme(const winrt::com_ptr<implementat
|
||||
void SettingsLoader::_executeGenerator(const IDynamicProfileGenerator& generator)
|
||||
{
|
||||
const auto generatorNamespace = generator.GetNamespace();
|
||||
if (_ignoredNamespaces.contains(generatorNamespace))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto previousSize = inboxSettings.profiles.size();
|
||||
|
||||
std::vector<winrt::com_ptr<implementation::Profile>> generatedProfiles;
|
||||
try
|
||||
{
|
||||
generator.GenerateProfiles(inboxSettings.profiles);
|
||||
generator.GenerateProfiles(generatedProfiles);
|
||||
}
|
||||
CATCH_LOG_MSG("Dynamic Profile Namespace: \"%.*s\"", gsl::narrow<int>(generatorNamespace.size()), generatorNamespace.data())
|
||||
|
||||
// These are needed for the FragmentSettings object
|
||||
std::vector<Model::FragmentProfileEntry> profileEntries;
|
||||
Json::Value profilesListJson{ Json::ValueType::arrayValue };
|
||||
Json::StreamWriterBuilder styledWriter;
|
||||
styledWriter["indentation"] = " ";
|
||||
|
||||
// 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)
|
||||
const winrt::hstring source{ generatorNamespace };
|
||||
for (const auto& profile : generatedProfiles)
|
||||
{
|
||||
const winrt::hstring source{ generatorNamespace };
|
||||
profile->Origin(OriginTag::Generated);
|
||||
profile->Source(source);
|
||||
|
||||
for (const auto& profile : std::span(inboxSettings.profiles).subspan(previousSize))
|
||||
const auto profileJson = profile->ToJson();
|
||||
profilesListJson.append(profileJson);
|
||||
profileEntries.push_back(winrt::make<FragmentProfileEntry>(profile->Guid(), hstring{ til::u8u16(Json::writeString(styledWriter, profileJson)) }));
|
||||
}
|
||||
|
||||
if (!_ignoredNamespaces.contains(generatorNamespace))
|
||||
{
|
||||
// Add generated profiles to the user settings
|
||||
for (auto& profile : generatedProfiles)
|
||||
{
|
||||
profile->Origin(OriginTag::Generated);
|
||||
profile->Source(source);
|
||||
inboxSettings.profiles.push_back(profile);
|
||||
}
|
||||
}
|
||||
|
||||
// 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{ generatorNamespace }, hstring{ til::u8u16(Json::writeString(styledWriter, 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() });
|
||||
}
|
||||
|
||||
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:
|
||||
@@ -1258,6 +1350,11 @@ CascadiaSettings::CascadiaSettings(SettingsLoader&& loader) :
|
||||
_warnings = winrt::single_threaded_vector(std::move(warnings));
|
||||
_themesChangeLog = std::move(loader.userSettings.themesChangeLog);
|
||||
|
||||
for (auto [_, extPkg] : loader.extensionPackageMap)
|
||||
{
|
||||
_extensionPackages.Append(*extPkg);
|
||||
}
|
||||
|
||||
_resolveDefaultProfile();
|
||||
_resolveNewTabMenuProfiles();
|
||||
_validateSettings();
|
||||
|
||||
@@ -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 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) const = 0;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
#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
|
||||
@@ -294,6 +296,16 @@ std::wstring_view PowershellCoreProfileGenerator::GetNamespace() const noexcept
|
||||
return PowershellCoreGeneratorNamespace;
|
||||
}
|
||||
|
||||
std::wstring_view PowershellCoreProfileGenerator::GetDisplayName() const noexcept
|
||||
{
|
||||
return RS_(L"PowershellCoreProfileGeneratorDisplayName");
|
||||
}
|
||||
|
||||
std::wstring_view PowershellCoreProfileGenerator::GetIcon() const noexcept
|
||||
{
|
||||
return GENERATOR_POWERSHELL_ICON;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Checks if pwsh is installed, and if it is, creates a profile to launch it.
|
||||
// Arguments:
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
static const std::wstring_view GetPreferredPowershellProfileName();
|
||||
|
||||
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) const override;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -740,4 +740,24 @@
|
||||
<data name="OpenCWDCommandKey" xml:space="preserve">
|
||||
<value>Open current working directory</value>
|
||||
</data>
|
||||
</root>
|
||||
<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="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>
|
||||
</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,6 +134,16 @@ 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:
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
{
|
||||
public:
|
||||
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) const override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -6,16 +6,28 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
const auto instances = VsSetupConfiguration::QueryInstances();
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
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) const 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;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model
|
||||
{
|
||||
public:
|
||||
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) const override;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user