Skip to content

Commit 3f31d56

Browse files
committed
PRE-MERGE #18559 Add Extensions page to Settings UI
2 parents 3accdcf + 7cdbb7c commit 3f31d56

19 files changed

+1444
-67
lines changed

src/cascadia/TerminalSettingsEditor/AddProfile.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
<IconSourceElement Grid.Column="0"
6060
Width="16"
6161
Height="16"
62-
IconSource="{x:Bind mtu:IconPathConverter.IconSourceWUX(Icon), Mode=OneTime}" />
62+
IconSource="{x:Bind mtu:IconPathConverter.IconSourceWUX(EvaluatedIcon), Mode=OneTime}" />
6363

6464
<TextBlock Grid.Column="1"
6565
Text="{x:Bind Name}" />

src/cascadia/TerminalSettingsEditor/CommonResources.xaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,7 +1199,7 @@
11991199
<Setter Property="HorizontalAlignment" Value="Stretch" />
12001200
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
12011201
<Setter Property="VerticalAlignment" Value="Stretch" />
1202-
<Setter Property="HorizontalContentAlignment" Value="Left" />
1202+
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
12031203
<Setter Property="VerticalContentAlignment" Value="Center" />
12041204
<Setter Property="Template">
12051205
<Setter.Value>
@@ -1222,7 +1222,8 @@
12221222
Content="{TemplateBinding Content}"
12231223
ContentTemplate="{TemplateBinding ContentTemplate}"
12241224
ContentTransitions="{TemplateBinding ContentTransitions}" />
1225-
<FontIcon Margin="20,0,8,0"
1225+
<FontIcon Grid.Column="1"
1226+
Margin="20,0,8,0"
12261227
HorizontalAlignment="Right"
12271228
FontSize="10"
12281229
FontWeight="Black"
Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
#include "pch.h"
5+
#include "Extensions.h"
6+
#include "Extensions.g.cpp"
7+
#include "ExtensionPackageViewModel.g.cpp"
8+
#include "ExtensionsViewModel.g.cpp"
9+
#include "FragmentProfileViewModel.g.cpp"
10+
11+
#include <LibraryResources.h>
12+
#include "..\WinRTUtils\inc\Utils.h"
13+
14+
using namespace winrt::Windows::Foundation;
15+
using namespace winrt::Windows::Foundation::Collections;
16+
using namespace winrt::Windows::UI::Xaml;
17+
using namespace winrt::Windows::UI::Xaml::Controls;
18+
using namespace winrt::Windows::UI::Xaml::Navigation;
19+
20+
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
21+
{
22+
Extensions::Extensions()
23+
{
24+
InitializeComponent();
25+
}
26+
27+
void Extensions::OnNavigatedTo(const NavigationEventArgs& e)
28+
{
29+
_ViewModel = e.Parameter().as<Editor::ExtensionsViewModel>();
30+
}
31+
32+
void Extensions::ExtensionLoaded(const IInspectable& sender, const RoutedEventArgs& /*args*/)
33+
{
34+
const auto& toggleSwitch = sender.as<Controls::ToggleSwitch>();
35+
const auto& extensionSource = toggleSwitch.Tag().as<hstring>();
36+
toggleSwitch.IsOn(_ViewModel.GetExtensionState(extensionSource));
37+
}
38+
39+
void Extensions::ExtensionToggled(const IInspectable& sender, const RoutedEventArgs& /*args*/)
40+
{
41+
const auto& toggleSwitch = sender.as<Controls::ToggleSwitch>();
42+
const auto& extensionSource = toggleSwitch.Tag().as<hstring>();
43+
_ViewModel.SetExtensionState(extensionSource, toggleSwitch.IsOn());
44+
}
45+
46+
void Extensions::ExtensionNavigator_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/)
47+
{
48+
const auto source = sender.as<Controls::Button>().Tag().as<hstring>();
49+
_ViewModel.CurrentExtensionSource(source);
50+
}
51+
52+
void Extensions::NavigateToProfile_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/)
53+
{
54+
const auto& profileGuid = sender.as<Controls::Button>().Tag().as<guid>();
55+
get_self<ExtensionsViewModel>(_ViewModel)->NavigateToProfile(profileGuid);
56+
}
57+
58+
void Extensions::NavigateToColorScheme_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/)
59+
{
60+
const auto& schemeVM = sender.as<Controls::Button>().Tag().as<Editor::ColorSchemeViewModel>();
61+
get_self<ExtensionsViewModel>(_ViewModel)->NavigateToColorScheme(schemeVM);
62+
}
63+
64+
ExtensionsViewModel::ExtensionsViewModel(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM) :
65+
_settings{ settings },
66+
_colorSchemesPageVM{ colorSchemesPageVM }
67+
{
68+
UpdateSettings(settings, colorSchemesPageVM);
69+
70+
PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) {
71+
const auto viewModelProperty{ args.PropertyName() };
72+
if (viewModelProperty == L"CurrentExtensionSource")
73+
{
74+
// Update the views to reflect the current extension source, if one is selected.
75+
// Otherwise, show components from all extensions
76+
_profilesModifiedView.Clear();
77+
_profilesAddedView.Clear();
78+
_colorSchemesAddedView.Clear();
79+
80+
const auto currentExtensionSource = CurrentExtensionSource();
81+
for (const auto& ext : _fragmentExtensions)
82+
{
83+
// No extension selected --> show all enabled extension components
84+
// Otherwise, only show the ones for the selected extension
85+
if (const auto extSrc = ext.Fragment().Source(); (currentExtensionSource.empty() && GetExtensionState(extSrc)) || extSrc == currentExtensionSource)
86+
{
87+
for (const auto& profile : ext.ProfilesModified())
88+
{
89+
_profilesModifiedView.Append(profile);
90+
}
91+
for (const auto& profile : ext.ProfilesAdded())
92+
{
93+
_profilesAddedView.Append(profile);
94+
}
95+
for (const auto& scheme : ext.ColorSchemesAdded())
96+
{
97+
_colorSchemesAddedView.Append(scheme);
98+
}
99+
}
100+
}
101+
102+
_NotifyChanges(L"IsExtensionView", L"CurrentExtensionFragments");
103+
}
104+
});
105+
}
106+
107+
void ExtensionsViewModel::UpdateSettings(const Model::CascadiaSettings& settings, const Editor::ColorSchemesPageViewModel& colorSchemesPageVM)
108+
{
109+
_settings = settings;
110+
_colorSchemesPageVM = colorSchemesPageVM;
111+
_extensionSources.clear();
112+
_CurrentExtensionSource.clear();
113+
114+
std::vector<Model::FragmentSettings> extensions;
115+
extensions.reserve(settings.FragmentExtensions().Size() + settings.DynamicProfileGenerators().Size());
116+
for (auto ext : settings.FragmentExtensions())
117+
{
118+
extensions.push_back(ext);
119+
}
120+
for (auto ext : settings.DynamicProfileGenerators())
121+
{
122+
extensions.push_back(ext);
123+
}
124+
125+
std::vector<Editor::FragmentExtensionViewModel> extensionVMs;
126+
extensionVMs.reserve(extensions.size());
127+
128+
// these vectors track components all extensions successfully added
129+
std::vector<Editor::FragmentProfileViewModel> profilesModifiedTotal;
130+
std::vector<Editor::FragmentProfileViewModel> profilesAddedTotal;
131+
std::vector<Editor::FragmentColorSchemeViewModel> colorSchemesAddedTotal;
132+
for (const auto& fragExt : extensions)
133+
{
134+
const auto extensionEnabled = GetExtensionState(fragExt.Source());
135+
136+
// these vectors track everything the current extension attempted to bring in
137+
std::vector<Editor::FragmentProfileViewModel> currentProfilesModified;
138+
std::vector<Editor::FragmentProfileViewModel> currentProfilesAdded;
139+
std::vector<Editor::FragmentColorSchemeViewModel> currentColorSchemesAdded;
140+
141+
for (const auto&& entry : fragExt.ModifiedProfilesView())
142+
{
143+
// Ensure entry successfully modifies a profile before creating and registering the object
144+
if (const auto& deducedProfile = _settings.FindProfile(entry.ProfileGuid()))
145+
{
146+
auto vm = winrt::make<FragmentProfileViewModel>(entry, fragExt, deducedProfile);
147+
currentProfilesModified.push_back(vm);
148+
if (extensionEnabled)
149+
{
150+
profilesModifiedTotal.push_back(vm);
151+
}
152+
}
153+
}
154+
155+
for (const auto&& entry : fragExt.NewProfilesView())
156+
{
157+
// Ensure entry successfully points to a profile before creating and registering the object.
158+
// The profile may have been removed by the user.
159+
if (const auto& deducedProfile = _settings.FindProfile(entry.ProfileGuid()))
160+
{
161+
auto vm = winrt::make<FragmentProfileViewModel>(entry, fragExt, deducedProfile);
162+
currentProfilesAdded.push_back(vm);
163+
if (extensionEnabled)
164+
{
165+
profilesAddedTotal.push_back(vm);
166+
}
167+
}
168+
}
169+
170+
for (const auto&& entry : fragExt.ColorSchemesView())
171+
{
172+
for (const auto& schemeVM : _colorSchemesPageVM.AllColorSchemes())
173+
{
174+
if (schemeVM.Name() == entry.ColorSchemeName())
175+
{
176+
auto vm = winrt::make<FragmentColorSchemeViewModel>(entry, fragExt, schemeVM);
177+
currentColorSchemesAdded.push_back(vm);
178+
if (extensionEnabled)
179+
{
180+
colorSchemesAddedTotal.push_back(vm);
181+
}
182+
}
183+
}
184+
}
185+
186+
_extensionSources.insert(fragExt.Source());
187+
extensionVMs.push_back(winrt::make<FragmentExtensionViewModel>(fragExt, currentProfilesModified, currentProfilesAdded, currentColorSchemesAdded));
188+
}
189+
190+
_fragmentExtensions = single_threaded_observable_vector<Editor::FragmentExtensionViewModel>(std::move(extensionVMs));
191+
_profilesModifiedView = single_threaded_observable_vector<Editor::FragmentProfileViewModel>(std::move(profilesModifiedTotal));
192+
_profilesAddedView = single_threaded_observable_vector<Editor::FragmentProfileViewModel>(std::move(profilesAddedTotal));
193+
_colorSchemesAddedView = single_threaded_observable_vector<Editor::FragmentColorSchemeViewModel>(std::move(colorSchemesAddedTotal));
194+
}
195+
196+
IVector<IInspectable> ExtensionsViewModel::CurrentExtensionFragments() const noexcept
197+
{
198+
std::vector<IInspectable> fragmentExtensionVMs;
199+
for (auto&& extVM : _fragmentExtensions)
200+
{
201+
if (_CurrentExtensionSource.empty() || extVM.Fragment().Source() == _CurrentExtensionSource)
202+
{
203+
fragmentExtensionVMs.push_back(extVM);
204+
}
205+
}
206+
return winrt::single_threaded_vector<IInspectable>(std::move(fragmentExtensionVMs));
207+
}
208+
209+
hstring ExtensionsViewModel::CurrentExtensionScope() const noexcept
210+
{
211+
if (!_CurrentExtensionSource.empty())
212+
{
213+
for (auto&& extVM : _fragmentExtensions)
214+
{
215+
const auto& fragExt = extVM.Fragment();
216+
if (fragExt.Source() == _CurrentExtensionSource)
217+
{
218+
return fragExt.Scope() == Model::FragmentScope::User ? RS_(L"Extensions_ScopeUser") : RS_(L"Extensions_ScopeSystem");
219+
}
220+
}
221+
}
222+
return hstring{};
223+
}
224+
225+
IObservableVector<Editor::ExtensionPackageViewModel> ExtensionsViewModel::ExtensionPackages() const noexcept
226+
{
227+
std::vector<Editor::ExtensionPackageViewModel> extensionPackages;
228+
for (auto&& extSrc : _extensionSources)
229+
{
230+
extensionPackages.push_back(winrt::make<ExtensionPackageViewModel>(extSrc, GetExtensionState(extSrc)));
231+
}
232+
return winrt::single_threaded_observable_vector<Editor::ExtensionPackageViewModel>(std::move(extensionPackages));
233+
}
234+
235+
// Returns true if the extension is enabled, false otherwise
236+
bool ExtensionsViewModel::GetExtensionState(hstring extensionSource) const
237+
{
238+
if (const auto& disabledExtensions = _DisabledProfileSources())
239+
{
240+
uint32_t ignored;
241+
return !disabledExtensions.IndexOf(extensionSource, ignored);
242+
}
243+
// "disabledProfileSources" not defined --> all extensions are enabled
244+
return true;
245+
}
246+
247+
// Enable/Disable an extension
248+
void ExtensionsViewModel::SetExtensionState(hstring extensionSource, bool enableExt)
249+
{
250+
// get the current status of the extension
251+
uint32_t idx;
252+
bool currentlyEnabled = true;
253+
const auto& disabledExtensions = _DisabledProfileSources();
254+
if (disabledExtensions)
255+
{
256+
currentlyEnabled = !disabledExtensions.IndexOf(extensionSource, idx);
257+
}
258+
259+
// current status mismatches the desired status,
260+
// update the list of disabled extensions
261+
if (currentlyEnabled != enableExt)
262+
{
263+
// If we're disabling an extension and we don't have "disabledProfileSources" defined,
264+
// create it in the model directly
265+
if (!disabledExtensions && !enableExt)
266+
{
267+
std::vector<hstring> disabledProfileSources{ extensionSource };
268+
_settings.GlobalSettings().DisabledProfileSources(single_threaded_vector<hstring>(std::move(disabledProfileSources)));
269+
return;
270+
}
271+
272+
// Update the list of disabled extensions
273+
if (enableExt)
274+
{
275+
disabledExtensions.RemoveAt(idx);
276+
}
277+
else
278+
{
279+
disabledExtensions.Append(extensionSource);
280+
}
281+
}
282+
}
283+
284+
void ExtensionsViewModel::NavigateToProfile(const guid profileGuid)
285+
{
286+
NavigateToProfileRequested.raise(*this, profileGuid);
287+
}
288+
289+
void ExtensionsViewModel::NavigateToColorScheme(const Editor::ColorSchemeViewModel& schemeVM)
290+
{
291+
_colorSchemesPageVM.CurrentScheme(schemeVM);
292+
NavigateToColorSchemeRequested.raise(*this, nullptr);
293+
}
294+
295+
hstring ExtensionPackageViewModel::AccessibleName() const noexcept
296+
{
297+
if (_enabled)
298+
{
299+
return _source;
300+
}
301+
return hstring{ fmt::format(L"{}: {}", _source, RS_(L"Extension_StateDisabled/Text")) };
302+
}
303+
}

0 commit comments

Comments
 (0)