mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-19 03:10:49 +00:00
Compare commits
5 Commits
dev/migrie
...
dev/cazamo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
054afcc59d | ||
|
|
56aa5fc73f | ||
|
|
3ee13c679f | ||
|
|
87d9fdb1da | ||
|
|
7cbfb5784a |
1
.github/actions/spelling/expect/expect.txt
vendored
1
.github/actions/spelling/expect/expect.txt
vendored
@@ -1105,7 +1105,6 @@ NOSIZE
|
||||
NOSNAPSHOT
|
||||
NOTHOUSANDS
|
||||
NOTICKS
|
||||
notif
|
||||
NOTIMEOUTIFNOTHUNG
|
||||
NOTIMPL
|
||||
NOTOPMOST
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "ColorSchemes.h"
|
||||
#include "EditColorScheme.h"
|
||||
#include "AddProfile.h"
|
||||
#include "Profiles.h"
|
||||
#include "InteractionViewModel.h"
|
||||
#include "LaunchViewModel.h"
|
||||
#include "NewTabMenuViewModel.h"
|
||||
@@ -102,7 +103,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
MainPage::MainPage(const CascadiaSettings& settings) :
|
||||
_settingsSource{ settings },
|
||||
_settingsClone{ settings.Copy() },
|
||||
_profileVMs{ single_threaded_observable_vector<Editor::ProfileViewModel>() }
|
||||
_profilesPageVM{ winrt::make<ProfilesPageViewModel>() }
|
||||
{
|
||||
InitializeComponent();
|
||||
_UpdateBackgroundForMica();
|
||||
@@ -156,8 +157,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
});
|
||||
|
||||
_SetupProfilesPageEventHandling();
|
||||
|
||||
// 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
|
||||
// that VM into the appearance VMs within the profiles. The Profiles VM owns the per-profile list itself.
|
||||
_InitializeProfilesList();
|
||||
|
||||
// Apply icons and tooltips (GH#19688, long names may be truncated) to static nav items
|
||||
@@ -205,28 +208,24 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
_UpdateBackgroundForMica();
|
||||
|
||||
// Deduce information about the currently selected item
|
||||
IInspectable lastBreadcrumb;
|
||||
const auto size = _breadcrumbs.Size();
|
||||
if (size > 0)
|
||||
// Capture data about where we are right now, so we can re-navigate to the same
|
||||
// place after we rebuild all the settings.
|
||||
IInspectable destination{ nullptr };
|
||||
auto subPage = BreadcrumbSubPage::None;
|
||||
hstring parentNavTag{};
|
||||
if (const auto size = _breadcrumbs.Size(); size > 0)
|
||||
{
|
||||
lastBreadcrumb = _breadcrumbs.GetAt(size - 1);
|
||||
const auto& crumb = _breadcrumbs.GetAt(size - 1).as<Breadcrumb>();
|
||||
destination = crumb->Tag();
|
||||
subPage = crumb->SubPage();
|
||||
// If we were inside the Profiles section, remember that so we can restore
|
||||
// the "Profiles ›" prefix on the rebuilt navigation.
|
||||
if (_RootCrumbIsProfilesBreadcrumb())
|
||||
{
|
||||
parentNavTag = hstring{ profilesTag };
|
||||
}
|
||||
}
|
||||
|
||||
// Collect only the first items out of the menu item source, the static
|
||||
// ones that we don't want to regenerate.
|
||||
//
|
||||
// By manipulating a MenuItemsSource this way, rather than manipulating the
|
||||
// MenuItems directly, we avoid a crash in WinUI.
|
||||
//
|
||||
// By making the vector only _originalNumItems big to start, GetMany
|
||||
// will only fill that number of elements out of the current source.
|
||||
std::vector<IInspectable> menuItemsSTL(_originalNumItems, nullptr);
|
||||
_menuItemSource.GetMany(0, menuItemsSTL);
|
||||
// now, just stick them back in.
|
||||
_menuItemSource.ReplaceAll(menuItemsSTL);
|
||||
|
||||
// Repopulate profile-related menu items
|
||||
_InitializeProfilesList();
|
||||
|
||||
// Update the Nav State with the new version of the settings
|
||||
@@ -236,64 +235,43 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_extensionsVM.UpdateSettings(_settingsClone, _colorSchemesPageVM);
|
||||
_profileDefaultsVM = nullptr; // Lazy-loaded upon navigation
|
||||
|
||||
// Now that the menuItems are repopulated,
|
||||
// refresh the current page using the breadcrumb data we collected before the refresh
|
||||
if (const auto& crumb{ lastBreadcrumb.try_as<Breadcrumb>() }; crumb && crumb->Tag())
|
||||
if (const auto& profileTag{ destination.try_as<Editor::ProfileViewModel>() })
|
||||
{
|
||||
bool foundNavigationParams = false;
|
||||
auto destination = crumb->Tag();
|
||||
auto subPage = crumb->SubPage();
|
||||
for (const auto& item : _menuItemSource)
|
||||
// Find the new profile VM by guid
|
||||
if (const auto newProfileVM = _FindProfileViewModelByGuid(profileTag.OriginalProfileGuid()))
|
||||
{
|
||||
const auto menuItem = item.try_as<MUX::Controls::NavigationViewItem>();
|
||||
if (!menuItem)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& tag = menuItem.Tag();
|
||||
if (const auto& stringTag{ tag.try_as<hstring>() })
|
||||
{
|
||||
if (const auto& destString{ destination.try_as<hstring>() })
|
||||
{
|
||||
foundNavigationParams = (*stringTag == *destString);
|
||||
}
|
||||
else if (destination.try_as<Editor::FolderEntryViewModel>() && *stringTag == newTabMenuTag)
|
||||
{
|
||||
foundNavigationParams = true;
|
||||
subPage = BreadcrumbSubPage::NewTabMenu_Folder;
|
||||
}
|
||||
else if (destination.try_as<Editor::ExtensionPackageViewModel>() && *stringTag == extensionsTag)
|
||||
{
|
||||
foundNavigationParams = true;
|
||||
subPage = BreadcrumbSubPage::Extensions_Extension;
|
||||
}
|
||||
}
|
||||
else if (const auto& profileTag{ tag.try_as<ProfileViewModel>() })
|
||||
{
|
||||
const auto destProfile = destination.try_as<ProfileViewModel>();
|
||||
if (destProfile && profileTag->OriginalProfileGuid() == destProfile->OriginalProfileGuid())
|
||||
{
|
||||
// Use the new profile VM from the refreshed menu items
|
||||
destination = tag;
|
||||
foundNavigationParams = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundNavigationParams)
|
||||
{
|
||||
// found the one that was selected before the refresh
|
||||
_Navigate(destination, subPage);
|
||||
return;
|
||||
}
|
||||
destination = newProfileVM;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fall back to the Profiles landing page
|
||||
destination = box_value(profilesTag);
|
||||
subPage = BreadcrumbSubPage::None;
|
||||
parentNavTag = {};
|
||||
}
|
||||
}
|
||||
else if (destination.try_as<Editor::FolderEntryViewModel>())
|
||||
{
|
||||
destination = box_value(newTabMenuTag);
|
||||
subPage = BreadcrumbSubPage::NewTabMenu_Folder;
|
||||
}
|
||||
else if (destination.try_as<Editor::ExtensionPackageViewModel>())
|
||||
{
|
||||
destination = box_value(extensionsTag);
|
||||
subPage = BreadcrumbSubPage::Extensions_Extension;
|
||||
}
|
||||
else if (!destination.try_as<hstring>())
|
||||
{
|
||||
// Couldn't find a meaningful previous page. Fall back to the first menu item.
|
||||
if (_menuItemSource && _menuItemSource.Size() > 0)
|
||||
{
|
||||
destination = _menuItemSource.GetAt(0).as<MUX::Controls::NavigationViewItem>().Tag();
|
||||
subPage = BreadcrumbSubPage::None;
|
||||
parentNavTag = {};
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find the selected item, fall back to first menu item
|
||||
// This happens when the selected item was a profile which doesn't exist in the new configuration
|
||||
// We can use menuItemsSTL here because the only things they miss are profile entries.
|
||||
const auto& firstItem{ _menuItemSource.GetAt(0).as<MUX::Controls::NavigationViewItem>() };
|
||||
_Navigate(firstItem.Tag(), BreadcrumbSubPage::None);
|
||||
_Navigate(destination, subPage, {}, parentNavTag);
|
||||
|
||||
_UpdateSearchIndex();
|
||||
}
|
||||
@@ -325,12 +303,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
// can be empty to indicate that we should create a fresh profile
|
||||
void MainPage::_AddProfileHandler(winrt::guid profileGuid)
|
||||
{
|
||||
uint32_t insertIndex;
|
||||
auto selectedItem{ SettingsNav().SelectedItem() };
|
||||
if (_menuItemSource)
|
||||
{
|
||||
_menuItemSource.IndexOf(selectedItem, insertIndex);
|
||||
}
|
||||
if (profileGuid != winrt::guid{})
|
||||
{
|
||||
// if we were given a non-empty guid, we want to duplicate the corresponding profile
|
||||
@@ -338,13 +310,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
if (profile)
|
||||
{
|
||||
const auto duplicated = _settingsClone.DuplicateProfile(profile);
|
||||
_CreateAndNavigateToNewProfile(insertIndex, duplicated);
|
||||
_CreateAndNavigateToNewProfile(duplicated);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we were given an empty guid, create a new profile
|
||||
_CreateAndNavigateToNewProfile(insertIndex, nullptr);
|
||||
_CreateAndNavigateToNewProfile(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +423,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
else if (_colorSchemesPageVM.CurrentPage() == ColorSchemesSubPage::Base)
|
||||
{
|
||||
_Navigate(boxedTag, BreadcrumbSubPage::None);
|
||||
// Preserve the current root context so that "Profiles > Color schemes" still shows the Profiles prefix.
|
||||
const hstring inheritedParentNavTag = _RootCrumbIsProfilesBreadcrumb() ? hstring{ profilesTag } : hstring{};
|
||||
_Navigate(boxedTag, BreadcrumbSubPage::None, {}, inheritedParentNavTag);
|
||||
}
|
||||
}
|
||||
else if (settingName == L"CurrentSchemeName")
|
||||
@@ -521,6 +495,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
if (currentPage == ProfileSubPage::Base)
|
||||
{
|
||||
_breadcrumbs.Clear();
|
||||
_AppendProfilesRootCrumb();
|
||||
_breadcrumbs.Append(winrt::make<Breadcrumb>(breadcrumbTag, breadcrumbText, BreadcrumbSubPage::None));
|
||||
}
|
||||
_NavigateToProfileSubPage(profile, currentPage, breadcrumbTag, {});
|
||||
@@ -535,7 +510,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
// a view model object (i.e. ProfileViewModel, ColorSchemeViewModel, etc.) for dynamic pages
|
||||
// - subPage: the sub page to navigate to, used for pages that have multiple sub pages (i.e. Profile > Appearance/Terminal/Advanced)
|
||||
// - elementToFocus: the name of the element to focus on the target page
|
||||
void MainPage::_Navigate(const IInspectable& vm, BreadcrumbSubPage subPage, hstring elementToFocus)
|
||||
// - parentNavTag: optional nav tag of the parent page when this navigation is invoked from inside another
|
||||
// landing page. Used by the Profiles landing page to keep itself selected and to prepend a
|
||||
// "Profiles" breadcrumb when entering Color schemes / a profile / Defaults / Add Profile from there.
|
||||
void MainPage::_Navigate(const IInspectable& vm, BreadcrumbSubPage subPage, hstring elementToFocus, const hstring& parentNavTag)
|
||||
{
|
||||
_PreNavigateHelper();
|
||||
|
||||
@@ -616,8 +594,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, RS_(L"Nav_Extensions/Content"), BreadcrumbSubPage::None));
|
||||
}
|
||||
}
|
||||
else if (*clickedItemTag == profilesTag)
|
||||
{
|
||||
contentFrame().Navigate(xaml_typename<Editor::Profiles>(), winrt::make<NavigateToPageArgs>(_profilesPageVM, *this, elementToFocus));
|
||||
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, RS_(L"Nav_Profiles/Content"), BreadcrumbSubPage::None));
|
||||
}
|
||||
else if (*clickedItemTag == globalProfileTag)
|
||||
{
|
||||
_AppendProfilesRootCrumb();
|
||||
|
||||
// lazy load profile defaults VM
|
||||
if (!_profileDefaultsVM)
|
||||
{
|
||||
@@ -636,9 +621,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
// Register handler for future user-driven sub-page changes
|
||||
_SetupProfileEventHandling(_profileDefaultsVM);
|
||||
|
||||
// Keep the Profiles nav item selected.
|
||||
selectedNavTag = profilesTag;
|
||||
}
|
||||
else if (*clickedItemTag == colorSchemesTag)
|
||||
{
|
||||
// Color Schemes page is accessible from root level and within Profiles,
|
||||
// so we need to prepend the root crumb in the second case.
|
||||
if (parentNavTag == profilesTag)
|
||||
{
|
||||
_AppendProfilesRootCrumb();
|
||||
selectedNavTag = profilesTag;
|
||||
}
|
||||
|
||||
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, RS_(L"Nav_ColorSchemes/Content"), BreadcrumbSubPage::None));
|
||||
contentFrame().Navigate(xaml_typename<Editor::ColorSchemes>(), winrt::make<NavigateToPageArgs>(_colorSchemesPageVM, *this, elementToFocus));
|
||||
|
||||
@@ -654,47 +650,62 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
else if (*clickedItemTag == addProfileTag)
|
||||
{
|
||||
_AppendProfilesRootCrumb();
|
||||
|
||||
auto addProfileState{ winrt::make<AddProfilePageNavigationState>(_settingsClone) };
|
||||
addProfileState.AddNew({ get_weak(), &MainPage::_AddProfileHandler });
|
||||
contentFrame().Navigate(xaml_typename<Editor::AddProfile>(), winrt::make<NavigateToPageArgs>(addProfileState, *this, elementToFocus));
|
||||
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, RS_(L"Nav_AddNewProfile/Content"), BreadcrumbSubPage::None));
|
||||
|
||||
// Keep the Profiles nav item selected.
|
||||
selectedNavTag = profilesTag;
|
||||
}
|
||||
}
|
||||
else if (const auto& profile = vm.try_as<Editor::ProfileViewModel>())
|
||||
{
|
||||
_AppendProfilesRootCrumb();
|
||||
selectedNavTag = profilesTag;
|
||||
|
||||
if (profile.Orphaned())
|
||||
{
|
||||
contentFrame().Navigate(xaml_typename<Editor::Profiles_Base_Orphaned>(), winrt::make<NavigateToPageArgs>(profile, *this, elementToFocus));
|
||||
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, profile.Name(), BreadcrumbSubPage::None));
|
||||
profile.CurrentPage(ProfileSubPage::Base);
|
||||
_SetupProfileEventHandling(profile);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set CurrentPage before registering the handler to avoid double-navigation
|
||||
const ProfileSubPage profileSubPage = ProfileSubPageFromBreadcrumb(subPage);
|
||||
profile.CurrentPage(profileSubPage);
|
||||
|
||||
// Navigate directly to the correct sub-page
|
||||
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, profile.Name(), BreadcrumbSubPage::None));
|
||||
_NavigateToProfileSubPage(profile, profileSubPage, vm, elementToFocus);
|
||||
|
||||
if (const auto profileNavItem = _FindProfileNavItem(profile.OriginalProfileGuid()))
|
||||
else
|
||||
{
|
||||
SettingsNav().SelectedItem(profileNavItem);
|
||||
}
|
||||
// Set CurrentPage before registering the handler to avoid double-navigation
|
||||
const ProfileSubPage profileSubPage = ProfileSubPageFromBreadcrumb(subPage);
|
||||
profile.CurrentPage(profileSubPage);
|
||||
|
||||
// Register handler for future user-driven sub-page changes
|
||||
_SetupProfileEventHandling(profile);
|
||||
// Navigate directly to the correct sub-page
|
||||
_breadcrumbs.Append(winrt::make<Breadcrumb>(vm, profile.Name(), BreadcrumbSubPage::None));
|
||||
_NavigateToProfileSubPage(profile, profileSubPage, vm, elementToFocus);
|
||||
|
||||
// Register handler for future user-driven sub-page changes
|
||||
_SetupProfileEventHandling(profile);
|
||||
}
|
||||
}
|
||||
else if (const auto& colorSchemeVM = vm.try_as<Editor::ColorSchemeViewModel>())
|
||||
{
|
||||
selectedNavTag = colorSchemesTag;
|
||||
const auto boxedColorSchemesTag = box_value(colorSchemesTag);
|
||||
|
||||
// Suppress the handler to avoid double-navigation
|
||||
_colorSchemesPageViewModelChangedRevoker.revoke();
|
||||
|
||||
// Color Schemes page is accessible from within Profiles and root level,
|
||||
// so we need to prepend the root crumb in the first case.
|
||||
if (parentNavTag == profilesTag)
|
||||
{
|
||||
_AppendProfilesRootCrumb();
|
||||
selectedNavTag = profilesTag;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedNavTag = colorSchemesTag;
|
||||
}
|
||||
|
||||
_breadcrumbs.Append(winrt::make<Breadcrumb>(boxedColorSchemesTag, RS_(L"Nav_ColorSchemes/Content"), BreadcrumbSubPage::None));
|
||||
|
||||
if (subPage == BreadcrumbSubPage::None)
|
||||
@@ -813,26 +824,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
|
||||
// Select the appropriate nav item
|
||||
// NOTE: profiles are special in that they have their own nav item, so those are handled in the profile branch above
|
||||
if (!selectedNavTag.empty())
|
||||
{
|
||||
for (auto&& menuItem : _menuItemSource)
|
||||
{
|
||||
if (const auto& navViewItem{ menuItem.try_as<MUX::Controls::NavigationViewItem>() })
|
||||
{
|
||||
if (const auto& tag{ navViewItem.Tag() })
|
||||
{
|
||||
if (const auto& stringTag{ tag.try_as<hstring>() })
|
||||
{
|
||||
if (*stringTag == selectedNavTag)
|
||||
{
|
||||
SettingsNav().SelectedItem(navViewItem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_SelectNavItemByTag(selectedNavTag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,7 +849,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
if (gsl::narrow_cast<uint32_t>(args.Index()) < (_breadcrumbs.Size() - 1))
|
||||
{
|
||||
const auto crumb = args.Item().as<Breadcrumb>();
|
||||
_Navigate(crumb->Tag(), crumb->SubPage());
|
||||
// If the breadcrumb chain is rooted at the Profiles landing page, preserve
|
||||
// that context so navigating to sub pages (i.e. Color schemes) via a back-breadcrumb
|
||||
// keeps the "Profiles ›" prefix.
|
||||
const hstring parentNavTag = _RootCrumbIsProfilesBreadcrumb() ? hstring{ profilesTag } : hstring{};
|
||||
_Navigate(crumb->Tag(), crumb->SubPage(), {}, parentNavTag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -872,35 +870,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_MoveXamlParsedNavItemsIntoItemSource();
|
||||
}
|
||||
|
||||
// Manually create a NavigationViewItem and view model for each profile
|
||||
// and keep a reference to them in a map so that we
|
||||
// can easily modify the correct one when the associated
|
||||
// profile changes.
|
||||
_profileVMs.Clear();
|
||||
// Populate the per-profile view models on the Profiles VM. The Profiles landing
|
||||
// page (and the search index) read this same list back through the VM.
|
||||
const auto& profileVMs = _profilesPageVM.Profiles();
|
||||
profileVMs.Clear();
|
||||
for (const auto& profile : _settingsClone.AllProfiles())
|
||||
{
|
||||
if (!profile.Deleted())
|
||||
{
|
||||
auto profileVM = _viewModelForProfile(profile, _settingsClone, Dispatcher());
|
||||
profileVM.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
|
||||
auto navItem = _CreateProfileNavViewItem(profileVM);
|
||||
_menuItemSource.Append(navItem);
|
||||
profileVM.DeleteProfileRequested({ this, &MainPage::_DeleteProfile });
|
||||
profileVMs.Append(profileVM);
|
||||
}
|
||||
}
|
||||
|
||||
// Top off (the end of the nav view) with the Add Profile item
|
||||
MUX::Controls::NavigationViewItem addProfileItem;
|
||||
const auto addProfileText = RS_(L"Nav_AddNewProfile/Content");
|
||||
addProfileItem.Content(box_value(addProfileText));
|
||||
addProfileItem.Tag(box_value(addProfileTag));
|
||||
WUX::Controls::ToolTipService::SetToolTip(addProfileItem, box_value(addProfileText));
|
||||
|
||||
FontIcon icon;
|
||||
// This is the "Add" symbol
|
||||
icon.Glyph(NavTagIconMap[addProfileTag]);
|
||||
addProfileItem.Icon(icon);
|
||||
|
||||
_menuItemSource.Append(addProfileItem);
|
||||
}
|
||||
|
||||
// BODGY
|
||||
@@ -936,79 +919,17 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
SettingsNav().MenuItemsSource(_menuItemSource);
|
||||
}
|
||||
|
||||
void MainPage::_CreateAndNavigateToNewProfile(const uint32_t index, const Model::Profile& profile)
|
||||
void MainPage::_CreateAndNavigateToNewProfile(const Model::Profile& profile)
|
||||
{
|
||||
const auto newProfile{ profile ? profile : _settingsClone.CreateNewProfile() };
|
||||
const auto profileViewModel{ _viewModelForProfile(newProfile, _settingsClone, Dispatcher()) };
|
||||
profileViewModel.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
|
||||
const auto navItem{ _CreateProfileNavViewItem(profileViewModel) };
|
||||
profileViewModel.DeleteProfileRequested({ this, &MainPage::_DeleteProfile });
|
||||
|
||||
if (_menuItemSource)
|
||||
{
|
||||
_menuItemSource.InsertAt(index, navItem);
|
||||
}
|
||||
_profilesPageVM.Profiles().Append(profileViewModel);
|
||||
|
||||
// Select and navigate to the new profile
|
||||
_Navigate(profileViewModel, BreadcrumbSubPage::None);
|
||||
}
|
||||
|
||||
static MUX::Controls::InfoBadge _createGlyphIconBadge(wil::zwstring_view glyph)
|
||||
{
|
||||
MUX::Controls::InfoBadge badge;
|
||||
MUX::Controls::FontIconSource icon;
|
||||
icon.FontFamily(winrt::Windows::UI::Xaml::Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
|
||||
icon.FontSize(12);
|
||||
icon.Glyph(glyph);
|
||||
badge.IconSource(icon);
|
||||
return badge;
|
||||
}
|
||||
|
||||
MUX::Controls::NavigationViewItem MainPage::_CreateProfileNavViewItem(const Editor::ProfileViewModel& profile)
|
||||
{
|
||||
MUX::Controls::NavigationViewItem profileNavItem;
|
||||
profileNavItem.Content(box_value(profile.Name()));
|
||||
profileNavItem.Tag(box_value<Editor::ProfileViewModel>(profile));
|
||||
profileNavItem.Icon(UI::IconPathConverter::IconWUX(profile.EvaluatedIcon()));
|
||||
WUX::Controls::ToolTipService::SetToolTip(profileNavItem, box_value(profile.Name()));
|
||||
|
||||
if (profile.Orphaned())
|
||||
{
|
||||
profileNavItem.InfoBadge(_createGlyphIconBadge(L"\xE7BA") /* Warning Triangle */);
|
||||
}
|
||||
else if (profile.Hidden())
|
||||
{
|
||||
profileNavItem.InfoBadge(_createGlyphIconBadge(L"\xED1A") /* Hide */);
|
||||
}
|
||||
|
||||
// Update the menu item when the icon/name changes
|
||||
auto weakMenuItem{ make_weak(profileNavItem) };
|
||||
profile.PropertyChanged([weakMenuItem](const auto&, const WUX::Data::PropertyChangedEventArgs& args) {
|
||||
if (auto menuItem{ weakMenuItem.get() })
|
||||
{
|
||||
const auto& tag{ menuItem.Tag().as<Editor::ProfileViewModel>() };
|
||||
if (args.PropertyName() == L"Icon")
|
||||
{
|
||||
menuItem.Icon(UI::IconPathConverter::IconWUX(tag.EvaluatedIcon()));
|
||||
}
|
||||
else if (args.PropertyName() == L"Name")
|
||||
{
|
||||
menuItem.Content(box_value(tag.Name()));
|
||||
WUX::Controls::ToolTipService::SetToolTip(menuItem, box_value(tag.Name()));
|
||||
}
|
||||
else if (args.PropertyName() == L"Hidden")
|
||||
{
|
||||
menuItem.InfoBadge(tag.Hidden() ? _createGlyphIconBadge(L"\xED1A") /* Hide */ : nullptr);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add an event handler for when the user wants to delete a profile.
|
||||
profile.DeleteProfileRequested({ this, &MainPage::_DeleteProfile });
|
||||
|
||||
// Register the VM so that it appears in the search index
|
||||
_profileVMs.Append(profile);
|
||||
|
||||
return profileNavItem;
|
||||
_Navigate(profileViewModel);
|
||||
}
|
||||
|
||||
void MainPage::_DeleteProfile(const IInspectable /*sender*/, const Editor::DeleteProfileEventArgs& args)
|
||||
@@ -1025,33 +946,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// remove selected item
|
||||
uint32_t index;
|
||||
auto selectedItem{ SettingsNav().SelectedItem() };
|
||||
if (_menuItemSource)
|
||||
// Remove the profile VM
|
||||
const auto& profileVMs = _profilesPageVM.Profiles();
|
||||
for (uint32_t i = 0; i < profileVMs.Size(); ++i)
|
||||
{
|
||||
_menuItemSource.IndexOf(selectedItem, index);
|
||||
_menuItemSource.RemoveAt(index);
|
||||
|
||||
// Remove it from the list of VMs
|
||||
auto profileVM = selectedItem.as<MUX::Controls::NavigationViewItem>().Tag().as<Editor::ProfileViewModel>();
|
||||
uint32_t vmIndex;
|
||||
if (_menuItemSource.IndexOf(profileVM, vmIndex))
|
||||
if (profileVMs.GetAt(i).OriginalProfileGuid() == guid)
|
||||
{
|
||||
_profileVMs.RemoveAt(vmIndex);
|
||||
profileVMs.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
|
||||
// navigate to the profile next to this one
|
||||
const auto newSelectedItem{ _menuItemSource.GetAt(index < _menuItemSource.Size() - 1 ? index : index - 1) };
|
||||
const auto newTag = newSelectedItem.as<MUX::Controls::NavigationViewItem>().Tag();
|
||||
if (const auto profileViewModel = newTag.try_as<ProfileViewModel>())
|
||||
{
|
||||
profileViewModel->FocusDeleteButton(true);
|
||||
}
|
||||
_Navigate(newTag, BreadcrumbSubPage::None);
|
||||
// Since we are navigating to a new profile after deletion, scroll up to the top
|
||||
SettingsMainPage_ScrollViewer().ChangeView(nullptr, 0.0, nullptr);
|
||||
}
|
||||
|
||||
// Go back to the Profiles landing page
|
||||
_Navigate(box_value(profilesTag));
|
||||
SettingsMainPage_ScrollViewer().ChangeView(nullptr, 0.0, nullptr);
|
||||
}
|
||||
|
||||
IObservableVector<IInspectable> MainPage::Breadcrumbs() noexcept
|
||||
@@ -1061,29 +969,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
void MainPage::_NavigateToProfileHandler(const IInspectable& /*sender*/, winrt::guid profileGuid)
|
||||
{
|
||||
if (const auto profileNavItem = _FindProfileNavItem(profileGuid))
|
||||
if (const auto profileVM = _FindProfileViewModelByGuid(profileGuid))
|
||||
{
|
||||
_Navigate(profileNavItem.Tag(), BreadcrumbSubPage::None);
|
||||
_Navigate(profileVM);
|
||||
}
|
||||
// Silently fail if the profile wasn't found
|
||||
}
|
||||
|
||||
MUX::Controls::NavigationViewItem MainPage::_FindProfileNavItem(winrt::guid profileGuid) const
|
||||
Editor::ProfileViewModel MainPage::_FindProfileViewModelByGuid(winrt::guid profileGuid) const
|
||||
{
|
||||
for (auto&& menuItem : _menuItemSource)
|
||||
for (const auto& profileVM : _profilesPageVM.Profiles())
|
||||
{
|
||||
if (const auto& navViewItem{ menuItem.try_as<MUX::Controls::NavigationViewItem>() })
|
||||
if (profileVM.OriginalProfileGuid() == profileGuid)
|
||||
{
|
||||
if (const auto& tag{ navViewItem.Tag() })
|
||||
{
|
||||
if (const auto& profileTag{ tag.try_as<ProfileViewModel>() })
|
||||
{
|
||||
if (profileTag->OriginalProfileGuid() == profileGuid)
|
||||
{
|
||||
return navViewItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
return profileVM;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@@ -1094,6 +993,62 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
_Navigate(box_value(hstring{ colorSchemesTag }), BreadcrumbSubPage::ColorSchemes_Edit);
|
||||
}
|
||||
|
||||
void MainPage::_AppendProfilesRootCrumb()
|
||||
{
|
||||
_breadcrumbs.Append(winrt::make<Breadcrumb>(box_value(profilesTag), RS_(L"Nav_Profiles/Content"), BreadcrumbSubPage::None));
|
||||
}
|
||||
|
||||
bool MainPage::_RootCrumbIsProfilesBreadcrumb() const
|
||||
{
|
||||
if (_breadcrumbs.Size() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto rootTag = _breadcrumbs.GetAt(0).as<Breadcrumb>()->Tag().try_as<hstring>();
|
||||
return rootTag && *rootTag == profilesTag;
|
||||
}
|
||||
|
||||
void MainPage::_SelectNavItemByTag(std::wstring_view tag)
|
||||
{
|
||||
if (!_menuItemSource)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (auto&& menuItem : _menuItemSource)
|
||||
{
|
||||
if (const auto& navViewItem{ menuItem.try_as<MUX::Controls::NavigationViewItem>() })
|
||||
{
|
||||
if (const auto& itemTag{ navViewItem.Tag() })
|
||||
{
|
||||
if (const auto& stringTag{ itemTag.try_as<hstring>() })
|
||||
{
|
||||
if (*stringTag == tag)
|
||||
{
|
||||
SettingsNav().SelectedItem(navViewItem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainPage::_SetupProfilesPageEventHandling()
|
||||
{
|
||||
_profilesPageVM.OpenDefaultsRequested([this](const auto&, const auto&) {
|
||||
_Navigate(box_value(globalProfileTag));
|
||||
});
|
||||
_profilesPageVM.OpenColorSchemesRequested([this](const auto&, const auto&) {
|
||||
_Navigate(box_value(colorSchemesTag), {}, {}, hstring{ profilesTag });
|
||||
});
|
||||
_profilesPageVM.AddProfileRequested([this](const auto&, const auto&) {
|
||||
_Navigate(box_value(addProfileTag));
|
||||
});
|
||||
_profilesPageVM.OpenProfileRequested([this](const auto&, const Editor::ProfileViewModel& profile) {
|
||||
_Navigate(profile);
|
||||
});
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Media::Brush MainPage::BackgroundBrush()
|
||||
{
|
||||
return SettingsNav().Background();
|
||||
@@ -1196,7 +1151,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
co_return;
|
||||
}
|
||||
_currentSearch = SearchIndex::Instance().SearchAsync(sanitizedQuery,
|
||||
_profileVMs.GetView(),
|
||||
_profilesPageVM.Profiles().GetView(),
|
||||
get_self<implementation::NewTabMenuViewModel>(_newTabMenuPageVM)->FolderTreeFlatList().GetView(),
|
||||
_colorSchemesPageVM.AllColorSchemes().GetView(),
|
||||
_extensionsVM.ExtensionPackages().GetView(),
|
||||
|
||||
@@ -84,21 +84,25 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
std::optional<HWND> _hostingHwnd;
|
||||
|
||||
void _InitializeProfilesList();
|
||||
void _CreateAndNavigateToNewProfile(const uint32_t index, const Model::Profile& profile);
|
||||
winrt::Microsoft::UI::Xaml::Controls::NavigationViewItem _CreateProfileNavViewItem(const Editor::ProfileViewModel& profile);
|
||||
void _CreateAndNavigateToNewProfile(const Model::Profile& profile);
|
||||
void _DeleteProfile(const Windows::Foundation::IInspectable sender, const Editor::DeleteProfileEventArgs& args);
|
||||
void _AddProfileHandler(const winrt::guid profileGuid);
|
||||
|
||||
void _SetupProfileEventHandling(const winrt::Microsoft::Terminal::Settings::Editor::ProfileViewModel profile);
|
||||
void _SetupColorSchemesEventHandling();
|
||||
void _SetupActionsEventHandling();
|
||||
void _SetupProfilesPageEventHandling();
|
||||
void _NavigateToProfileSubPage(const Editor::ProfileViewModel& profile, ProfileSubPage page, const IInspectable& breadcrumbTag, const hstring& elementToFocus);
|
||||
|
||||
void _PreNavigateHelper();
|
||||
void _Navigate(const IInspectable& vm, BreadcrumbSubPage subPage, hstring elementToFocus = {});
|
||||
void _Navigate(const IInspectable& vm, BreadcrumbSubPage subPage = BreadcrumbSubPage::None, hstring elementToFocus = {}, const hstring& parentNavTag = {});
|
||||
void _NavigateToProfileHandler(const IInspectable& sender, winrt::guid profileGuid);
|
||||
void _NavigateToColorSchemeHandler(const IInspectable& sender, const IInspectable& args);
|
||||
Microsoft::UI::Xaml::Controls::NavigationViewItem _FindProfileNavItem(winrt::guid profileGuid) const;
|
||||
Editor::ProfileViewModel _FindProfileViewModelByGuid(winrt::guid profileGuid) const;
|
||||
|
||||
void _AppendProfilesRootCrumb();
|
||||
bool _RootCrumbIsProfilesBreadcrumb() const;
|
||||
void _SelectNavItemByTag(std::wstring_view tag);
|
||||
|
||||
void _UpdateBackgroundForMica();
|
||||
void _MoveXamlParsedNavItemsIntoItemSource();
|
||||
@@ -106,11 +110,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
safe_void_coroutine _UpdateSearchIndex();
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Editor::ProfileViewModel _profileDefaultsVM{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Editor::ProfileViewModel> _profileVMs{ nullptr };
|
||||
winrt::Microsoft::Terminal::Settings::Editor::ColorSchemesPageViewModel _colorSchemesPageVM{ nullptr };
|
||||
winrt::Microsoft::Terminal::Settings::Editor::ActionsViewModel _actionsVM{ nullptr };
|
||||
winrt::Microsoft::Terminal::Settings::Editor::NewTabMenuViewModel _newTabMenuPageVM{ nullptr };
|
||||
winrt::Microsoft::Terminal::Settings::Editor::ExtensionsViewModel _extensionsVM{ nullptr };
|
||||
winrt::Microsoft::Terminal::Settings::Editor::ProfilesPageViewModel _profilesPageVM{ nullptr };
|
||||
|
||||
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable>> _currentSearch{ nullptr };
|
||||
|
||||
|
||||
@@ -192,11 +192,9 @@
|
||||
x:Uid="Nav_Extensions"
|
||||
Tag="Extensions_Nav" />
|
||||
|
||||
<muxc:NavigationViewItemHeader x:Uid="Nav_Profiles" />
|
||||
|
||||
<muxc:NavigationViewItem x:Name="BaseLayerMenuItem"
|
||||
x:Uid="Nav_ProfileDefaults"
|
||||
Tag="GlobalProfile_Nav" />
|
||||
<muxc:NavigationViewItem x:Name="ProfilesNavItem"
|
||||
x:Uid="Nav_Profiles"
|
||||
Tag="Profiles_Nav" />
|
||||
</muxc:NavigationView.MenuItems>
|
||||
|
||||
<muxc:NavigationView.FooterMenuItems>
|
||||
|
||||
@@ -119,6 +119,10 @@
|
||||
<DependentUpon>ColorSchemesPageViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Profiles.h">
|
||||
<DependentUpon>Profiles.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RenderingViewModel.h">
|
||||
<DependentUpon>RenderingViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -231,6 +235,9 @@
|
||||
<Page Include="Profiles_Base.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Profiles.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Profiles_Base_Orphaned.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
@@ -331,6 +338,10 @@
|
||||
<DependentUpon>ColorSchemesPageViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Profiles.cpp">
|
||||
<DependentUpon>Profiles.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RenderingViewModel.cpp">
|
||||
<DependentUpon>RenderingViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -456,6 +467,10 @@
|
||||
<Midl Include="TerminalColorConverters.idl" />
|
||||
<Midl Include="ColorSchemeViewModel.idl" />
|
||||
<Midl Include="ColorSchemesPageViewModel.idl" />
|
||||
<Midl Include="Profiles.idl">
|
||||
<DependentUpon>Profiles.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="RenderingViewModel.idl" />
|
||||
<Midl Include="InteractionViewModel.idl" />
|
||||
<Midl Include="GlobalAppearanceViewModel.idl" />
|
||||
|
||||
@@ -57,5 +57,6 @@
|
||||
<Page Include="NullableColorPicker.xaml" />
|
||||
<Page Include="IconPicker.xaml" />
|
||||
<Page Include="NewTabMenu.xaml" />
|
||||
<Page Include="Profiles.xaml" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -17,6 +17,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
inline constexpr std::wstring_view actionsTag{ L"Actions_Nav" };
|
||||
inline constexpr std::wstring_view newTabMenuTag{ L"NewTabMenu_Nav" };
|
||||
inline constexpr std::wstring_view extensionsTag{ L"Extensions_Nav" };
|
||||
inline constexpr std::wstring_view profilesTag{ L"Profiles_Nav" };
|
||||
inline constexpr std::wstring_view globalProfileTag{ L"GlobalProfile_Nav" };
|
||||
inline constexpr std::wstring_view addProfileTag{ L"AddProfile" };
|
||||
inline constexpr std::wstring_view colorSchemesTag{ L"ColorSchemes_Nav" };
|
||||
@@ -33,6 +34,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
std::pair{ actionsTag, L"\xE765" }, /* Keyboard Classic */
|
||||
std::pair{ newTabMenuTag, L"\xE71D" }, /* All Apps */
|
||||
std::pair{ extensionsTag, L"\xEA86" }, /* Puzzle */
|
||||
std::pair{ profilesTag, L"\xE7EE" }, /* Other User */
|
||||
std::pair{ globalProfileTag, L"\xE81E" }, /* Map Layers */
|
||||
std::pair{ addProfileTag, L"\xE710" }, /* Add */
|
||||
std::pair{ openJsonTag, L"\xE713" }, /* Settings */
|
||||
|
||||
94
src/cascadia/TerminalSettingsEditor/Profiles.cpp
Normal file
94
src/cascadia/TerminalSettingsEditor/Profiles.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "Profiles.h"
|
||||
#include "Profiles.g.cpp"
|
||||
#include "ProfilesPageViewModel.g.cpp"
|
||||
#include "ProfileViewModel.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
|
||||
{
|
||||
Profiles::Profiles()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Automation::AutomationProperties::SetName(DefaultsNavigator(), RS_(L"Profiles_DefaultsNavigator_Title/Text"));
|
||||
Automation::AutomationProperties::SetName(ColorSchemesNavigator(), RS_(L"Profiles_ColorSchemesNavigator_Title/Text"));
|
||||
Automation::AutomationProperties::SetName(AddProfileButton(), RS_(L"Profiles_AddProfileButton/Text"));
|
||||
}
|
||||
|
||||
void Profiles::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
const auto args = e.Parameter().as<Editor::NavigateToPageArgs>();
|
||||
_ViewModel = args.ViewModel().as<Editor::ProfilesPageViewModel>();
|
||||
BringIntoViewWhenLoaded(args.ElementToFocus());
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalSettingsEditorProvider,
|
||||
"NavigatedToPage",
|
||||
TraceLoggingDescription("Event emitted when the user navigates to a page in the settings UI"),
|
||||
TraceLoggingValue("profilesLanding", "PageId", "The identifier of the page that was navigated to"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
|
||||
}
|
||||
|
||||
void Profiles::Defaults_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
|
||||
{
|
||||
_ViewModel.RequestOpenDefaults();
|
||||
}
|
||||
|
||||
void Profiles::ColorSchemes_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
|
||||
{
|
||||
_ViewModel.RequestOpenColorSchemes();
|
||||
}
|
||||
|
||||
void Profiles::AddProfile_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*args*/)
|
||||
{
|
||||
_ViewModel.RequestAddProfile();
|
||||
}
|
||||
|
||||
void Profiles::Profile_Click(const IInspectable& sender, const RoutedEventArgs& /*args*/)
|
||||
{
|
||||
// Profile navigators are buttons whose DataContext is the bound ProfileViewModel.
|
||||
if (const auto element = sender.try_as<FrameworkElement>())
|
||||
{
|
||||
if (const auto profile = element.DataContext().try_as<Editor::ProfileViewModel>())
|
||||
{
|
||||
_ViewModel.RequestOpenProfile(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProfilesPageViewModel::ProfilesPageViewModel()
|
||||
{
|
||||
_setProfiles(single_threaded_observable_vector<Editor::ProfileViewModel>());
|
||||
}
|
||||
|
||||
void ProfilesPageViewModel::RequestOpenDefaults()
|
||||
{
|
||||
OpenDefaultsRequested.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
void ProfilesPageViewModel::RequestOpenColorSchemes()
|
||||
{
|
||||
OpenColorSchemesRequested.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
void ProfilesPageViewModel::RequestAddProfile()
|
||||
{
|
||||
AddProfileRequested.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
void ProfilesPageViewModel::RequestOpenProfile(const Editor::ProfileViewModel& profile)
|
||||
{
|
||||
OpenProfileRequested.raise(*this, profile);
|
||||
}
|
||||
|
||||
}
|
||||
67
src/cascadia/TerminalSettingsEditor/Profiles.h
Normal file
67
src/cascadia/TerminalSettingsEditor/Profiles.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- Profiles.h
|
||||
|
||||
Abstract:
|
||||
- The Profiles landing page in the Settings UI. The page hosts entry points to
|
||||
the list of individual profiles among other profile-related settings.
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Profiles.g.h"
|
||||
#include "ProfilesPageViewModel.g.h"
|
||||
#include "ProfileViewModel.h"
|
||||
#include "Utils.h"
|
||||
#include "ViewModelHelpers.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct ProfilesPageViewModel : ProfilesPageViewModelT<ProfilesPageViewModel>, ViewModelHelper<ProfilesPageViewModel>
|
||||
{
|
||||
public:
|
||||
ProfilesPageViewModel();
|
||||
|
||||
void RequestOpenDefaults();
|
||||
void RequestOpenColorSchemes();
|
||||
void RequestAddProfile();
|
||||
void RequestOpenProfile(const Editor::ProfileViewModel& profile);
|
||||
|
||||
// DON'T YOU DARE ADD A `WINRT_CALLBACK(PropertyChanged` TO A CLASS DERIVED FROM ViewModelHelper. Do this instead:
|
||||
using ViewModelHelper<ProfilesPageViewModel>::PropertyChanged;
|
||||
|
||||
WINRT_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::ProfileViewModel>, Profiles, _propertyChangedHandlers, nullptr);
|
||||
|
||||
public:
|
||||
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::IInspectable> OpenDefaultsRequested;
|
||||
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::IInspectable> OpenColorSchemesRequested;
|
||||
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::IInspectable> AddProfileRequested;
|
||||
til::typed_event<Windows::Foundation::IInspectable, Editor::ProfileViewModel> OpenProfileRequested;
|
||||
};
|
||||
|
||||
struct Profiles : public HasScrollViewer<Profiles>, ProfilesT<Profiles>
|
||||
{
|
||||
public:
|
||||
Profiles();
|
||||
|
||||
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
|
||||
|
||||
void Defaults_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void ColorSchemes_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void AddProfile_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void Profile_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
|
||||
til::property_changed_event PropertyChanged;
|
||||
WINRT_PROPERTY(Editor::ProfilesPageViewModel, ViewModel, nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(Profiles);
|
||||
BASIC_FACTORY(ProfilesPageViewModel);
|
||||
}
|
||||
31
src/cascadia/TerminalSettingsEditor/Profiles.idl
Normal file
31
src/cascadia/TerminalSettingsEditor/Profiles.idl
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "ProfileViewModel.idl";
|
||||
import "ColorSchemeViewModel.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
runtimeclass ProfilesPageViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
ProfilesPageViewModel();
|
||||
|
||||
Windows.Foundation.Collections.IObservableVector<ProfileViewModel> Profiles { get; };
|
||||
|
||||
void RequestOpenDefaults();
|
||||
void RequestOpenColorSchemes();
|
||||
void RequestAddProfile();
|
||||
void RequestOpenProfile(ProfileViewModel profile);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, IInspectable> OpenDefaultsRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, IInspectable> OpenColorSchemesRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, IInspectable> AddProfileRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, ProfileViewModel> OpenProfileRequested;
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass Profiles : Windows.UI.Xaml.Controls.Page
|
||||
{
|
||||
Profiles();
|
||||
ProfilesPageViewModel ViewModel { get; };
|
||||
}
|
||||
}
|
||||
136
src/cascadia/TerminalSettingsEditor/Profiles.xaml
Normal file
136
src/cascadia/TerminalSettingsEditor/Profiles.xaml
Normal file
@@ -0,0 +1,136 @@
|
||||
<!--
|
||||
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.Profiles"
|
||||
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>
|
||||
|
||||
<DataTemplate x:Key="ProfileNavigatorTemplate"
|
||||
x:DataType="local:ProfileViewModel">
|
||||
<Button HorizontalAlignment="Stretch"
|
||||
AutomationProperties.Name="{x:Bind Name, Mode=OneWay}"
|
||||
Click="Profile_Click"
|
||||
Style="{StaticResource NavigatorButtonStyle}">
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ContentControl Grid.Column="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Bind IconPreview, Mode=OneWay}"
|
||||
IsTabStop="False" />
|
||||
<TextBlock Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||
Text="{x:Bind Name, Mode=OneWay}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<muxc:InfoBadge Grid.Column="2"
|
||||
Margin="0,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{x:Bind Hidden, Mode=OneWay}">
|
||||
<muxc:InfoBadge.IconSource>
|
||||
<muxc:FontIconSource FontFamily="Segoe Fluent Icons, Segoe MDL2 Assets"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
</muxc:InfoBadge.IconSource>
|
||||
</muxc:InfoBadge>
|
||||
<muxc:InfoBadge Grid.Column="2"
|
||||
Margin="0,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{x:Bind Orphaned, Mode=OneWay}">
|
||||
<muxc:InfoBadge.IconSource>
|
||||
<muxc:FontIconSource FontFamily="Segoe Fluent Icons, Segoe MDL2 Assets"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
</muxc:InfoBadge.IconSource>
|
||||
</muxc:InfoBadge>
|
||||
</Grid>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
|
||||
<!-- General Profile Settings -->
|
||||
<TextBlock x:Uid="Profiles_GeneralSettingsHeader"
|
||||
Margin="0,0,0,4"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
|
||||
<!-- Defaults navigator -->
|
||||
<Button x:Name="DefaultsNavigator"
|
||||
Click="Defaults_Click"
|
||||
Style="{StaticResource NavigatorButtonStyle}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock x:Uid="Profiles_DefaultsNavigator_Title"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
<TextBlock x:Uid="Profiles_DefaultsNavigator_Description"
|
||||
Margin="0,2,0,0"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Color schemes navigator -->
|
||||
<Button x:Name="ColorSchemesNavigator"
|
||||
Click="ColorSchemes_Click"
|
||||
Style="{StaticResource NavigatorButtonStyle}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock x:Uid="Profiles_ColorSchemesNavigator_Title"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
<TextBlock x:Uid="Profiles_ColorSchemesNavigator_Description"
|
||||
Margin="0,2,0,0"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Terminal profiles section -->
|
||||
<TextBlock x:Uid="Profiles_TerminalProfilesHeader"
|
||||
Style="{StaticResource TextBlockSubHeaderStyle}" />
|
||||
|
||||
<!-- Add Profile accent button -->
|
||||
<Button x:Name="AddProfileButton"
|
||||
Margin="0,4,0,0"
|
||||
Click="AddProfile_Click"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon FontSize="{StaticResource StandardIconSize}"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="Profiles_AddProfileButton"
|
||||
Style="{StaticResource IconButtonTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- List of profiles. Each is a NavigatorButtonStyle button rendered by ProfileNavigatorTemplate. -->
|
||||
<ItemsControl Margin="0,0,0,16"
|
||||
ItemTemplate="{StaticResource ProfileNavigatorTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.Profiles, Mode=OneWay}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</Page>
|
||||
@@ -1291,7 +1291,39 @@
|
||||
</data>
|
||||
<data name="Nav_Profiles.Content" xml:space="preserve">
|
||||
<value>Profiles</value>
|
||||
<comment>Header for the "profiles" menu items. This acts as a divider to introduce profile-related menu items.</comment>
|
||||
<comment>Header for the "profiles" menu item. Navigates to a landing page that lists profile-related settings (defaults, color schemes, the list of terminal profiles, and the add-profile button).</comment>
|
||||
</data>
|
||||
<data name="Nav_Profiles.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Profiles</value>
|
||||
<comment>Tooltip for the "profiles" menu item. Navigates to a landing page that lists profile-related settings.</comment>
|
||||
</data>
|
||||
<data name="Profiles_GeneralSettingsHeader.Text" xml:space="preserve">
|
||||
<value>General Profile Settings</value>
|
||||
<comment>Section header on the Profiles landing page. Introduces general-purpose profile settings such as the defaults shared by all profiles and the color schemes that profiles can use.</comment>
|
||||
</data>
|
||||
<data name="Profiles_TerminalProfilesHeader.Text" xml:space="preserve">
|
||||
<value>Terminal profiles</value>
|
||||
<comment>Section header on the Profiles landing page. Introduces the list of individual terminal profiles configured by the user.</comment>
|
||||
</data>
|
||||
<data name="Profiles_DefaultsNavigator_Title.Text" xml:space="preserve">
|
||||
<value>Defaults</value>
|
||||
<comment>Title for the "Defaults" navigator on the Profiles landing page. Clicking this opens the page where defaults shared by all profiles can be edited.</comment>
|
||||
</data>
|
||||
<data name="Profiles_DefaultsNavigator_Description.Text" xml:space="preserve">
|
||||
<value>Changes made here apply to all profiles unless overridden by a specific profile. These settings do not affect the Windows Terminal appearance.</value>
|
||||
<comment>Help text describing the "Defaults" navigator on the Profiles landing page. Clarifies that the defaults apply to every profile but are not the same as the global Windows Terminal appearance settings.</comment>
|
||||
</data>
|
||||
<data name="Profiles_ColorSchemesNavigator_Title.Text" xml:space="preserve">
|
||||
<value>Color schemes</value>
|
||||
<comment>Title for the "Color schemes" navigator on the Profiles landing page. Clicking this opens the same page reachable from the top-level Color schemes nav item.</comment>
|
||||
</data>
|
||||
<data name="Profiles_ColorSchemesNavigator_Description.Text" xml:space="preserve">
|
||||
<value>Add, edit, or remove the color schemes that your profiles can use.</value>
|
||||
<comment>Help text describing the "Color schemes" navigator on the Profiles landing page.</comment>
|
||||
</data>
|
||||
<data name="Profiles_AddProfileButton.Text" xml:space="preserve">
|
||||
<value>Add a new profile</value>
|
||||
<comment>Text on the accent-colored button on the Profiles landing page. Clicking this opens the page where users can add a new profile.</comment>
|
||||
</data>
|
||||
<data name="Globals_LaunchModeFocus.Content" xml:space="preserve">
|
||||
<value>Focus</value>
|
||||
|
||||
@@ -66,8 +66,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
hstring runtimeObjContext{};
|
||||
if (const auto profileVM = runtimeObj.try_as<Editor::ProfileViewModel>())
|
||||
{
|
||||
// No runtimeObjContext: profile name and icon should be enough
|
||||
runtimeObjLabel = profileVM.Name();
|
||||
runtimeObjContext = RS_(L"Nav_Profiles/Content");
|
||||
}
|
||||
else if (const auto colorSchemeVM = runtimeObj.try_as<Editor::ColorSchemeViewModel>())
|
||||
{
|
||||
@@ -258,6 +258,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
localizedEntry.DisplayTextNeutral = EnglishOnlyResourceLoader().GetLocalizedString(entry.ResourceName);
|
||||
}
|
||||
if (!entry.SecondaryLabelResourceName.empty())
|
||||
{
|
||||
localizedEntry.SecondaryLabelLocalized = GetLibraryResourceString(entry.SecondaryLabelResourceName);
|
||||
}
|
||||
localizedIndex.emplace_back(std::move(localizedEntry));
|
||||
}
|
||||
return localizedIndex;
|
||||
@@ -342,7 +346,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
if (bestScore >= MinimumMatchScore)
|
||||
{
|
||||
scoredResults.emplace_back(bestScore, winrt::make<FilteredSearchResult>(index, &entry));
|
||||
scoredResults.emplace_back(bestScore, winrt::make<FilteredSearchResult>(index, &entry, nullptr, std::nullopt, entry.SecondaryLabelLocalized));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
winrt::hstring DisplayTextLocalized;
|
||||
std::optional<winrt::hstring> DisplayTextNeutral = std::nullopt;
|
||||
// No "Neutral" copy of SecondaryLabelLocalized: unlike DisplayText, the secondary
|
||||
// label is display-only (rendered in the search result row's caption). It is never
|
||||
// passed to the fuzzy matcher, so a single localized string is sufficient.
|
||||
winrt::hstring SecondaryLabelLocalized;
|
||||
const IndexEntry* Entry = nullptr;
|
||||
|
||||
std::array<std::pair<std::optional<winrt::hstring>, int>, 2> GetSearchableFields() const;
|
||||
|
||||
@@ -85,6 +85,7 @@ $ClassMap = @{
|
||||
ResourceName = "Nav_ProfileDefaults/Content"
|
||||
NavigationParam = "GlobalProfile_Nav"
|
||||
SubPage = "BreadcrumbSubPage::None"
|
||||
SecondaryLabel = "Nav_Profiles/Content"
|
||||
}
|
||||
"Microsoft::Terminal::Settings::Editor::Profiles_Appearance" = @{
|
||||
ResourceName = "Nav_ProfileDefaults/Content"
|
||||
@@ -105,6 +106,12 @@ $ClassMap = @{
|
||||
ResourceName = "Nav_AddNewProfile/Content"
|
||||
NavigationParam = "AddProfile"
|
||||
SubPage = "BreadcrumbSubPage::None"
|
||||
SecondaryLabel = "Nav_Profiles/Content"
|
||||
}
|
||||
"Microsoft::Terminal::Settings::Editor::Profiles" = @{
|
||||
ResourceName = "Nav_Profiles/Content"
|
||||
NavigationParam = "Profiles_Nav"
|
||||
SubPage = "BreadcrumbSubPage::None"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +163,7 @@ foreach ($xamlFile in Get-ChildItem -Path $SourceDir -Filter *.xaml)
|
||||
NavigationParam = $ClassMap[$pageClass].NavigationParam
|
||||
SubPage = $ClassMap[$pageClass].SubPage
|
||||
ElementName = $null # No specific element to navigate to, for the page itself
|
||||
SecondaryLabel = $ClassMap[$pageClass].SecondaryLabel # Resource name for the result's sub-text (i.e. parent page name); $null if none
|
||||
File = $filename
|
||||
}
|
||||
}
|
||||
@@ -189,6 +197,7 @@ foreach ($xamlFile in Get-ChildItem -Path $SourceDir -Filter *.xaml)
|
||||
NavigationParam = $ClassMap[$pageClass].NavigationParam
|
||||
SubPage = $ClassMap[$pageClass].SubPage
|
||||
ElementName = "AddNewButton"
|
||||
SecondaryLabel = $ClassMap[$pageClass].SecondaryLabel
|
||||
File = $filename
|
||||
}
|
||||
}
|
||||
@@ -285,8 +294,9 @@ function FormatEntry($e)
|
||||
$formattedResourceName = 'USES_RESOURCE(L"{0}")' -f $e.ResourceName
|
||||
$formattedNavigationParam = 'L"{0}"' -f $e.NavigationParam # null Navigation param resolves to empty string
|
||||
$formattedElementName = 'L"{0}"' -f $e.ElementName
|
||||
$formattedSecondaryLabel = [string]::IsNullOrEmpty($e.SecondaryLabel) ? 'L""' : ('USES_RESOURCE(L"{0}")' -f $e.SecondaryLabel)
|
||||
|
||||
return " IndexEntry{{ {0}, {1}, {2}, {3} }}, // {4}" -f ($formattedResourceName, $formattedNavigationParam, $e.SubPage, $formattedElementName, $e.File)
|
||||
return " IndexEntry{{ {0}, {1}, {2}, {3}, {4} }}, // {5}" -f ($formattedResourceName, $formattedNavigationParam, $e.SubPage, $formattedElementName, $formattedSecondaryLabel, $e.File)
|
||||
}
|
||||
|
||||
function FormatEntries($es) {
|
||||
@@ -294,7 +304,7 @@ function FormatEntries($es) {
|
||||
}
|
||||
|
||||
# Sort and remove duplicates
|
||||
$entries = $entries | Sort-Object ResourceName, ParentPage, NavigationParam, SubPage, ElementName, File -Unique
|
||||
$entries = $entries | Sort-Object ResourceName, ParentPage, NavigationParam, SubPage, ElementName, SecondaryLabel, File -Unique
|
||||
|
||||
$buildTimeEntries = @()
|
||||
$profileEntries = @()
|
||||
@@ -350,6 +360,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
// x:Name of the SettingContainer to navigate to on the page (i.e. "DefaultProfile")
|
||||
wil::zwstring_view ElementName;
|
||||
|
||||
// Resource name of the search result's secondary label (i.e. parent page name like "Nav_Profiles/Content").
|
||||
// Empty if the entry has no secondary label.
|
||||
// NOTE: wrapped in USES_RESOURCE() like ResourceName when non-empty.
|
||||
wil::zwstring_view SecondaryLabelResourceName;
|
||||
};
|
||||
|
||||
const std::array<IndexEntry, $($buildTimeEntries.Count)>& LoadBuildTimeIndex();
|
||||
|
||||
Reference in New Issue
Block a user