diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index de11231a313..9ee309a4927 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -1,4 +1,8 @@ apc +Apc +bsd +breadcrumb +breadcrumbs calt ccmp changelog diff --git a/src/cascadia/TerminalSettingsEditor/Appearances.xaml b/src/cascadia/TerminalSettingsEditor/Appearances.xaml index cab2ba73b87..a4e61bd95a2 100644 --- a/src/cascadia/TerminalSettingsEditor/Appearances.xaml +++ b/src/cascadia/TerminalSettingsEditor/Appearances.xaml @@ -65,7 +65,7 @@ HasSettingValue="{x:Bind Appearance.HasFontFace, Mode=OneWay}" SettingOverrideSource="{x:Bind Appearance.FontFaceOverrideSource, Mode=OneWay}" Visibility="{x:Bind Appearance.IsDefault, Mode=OneWay}"> - + Copy it into an STL vector to simplify our code and reduce COM overhead. std::vector menuItemsSTL(menuItems.Size(), nullptr); @@ -129,8 +131,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // We'll update the profile in the _profilesNavState whenever we actually navigate to one // now that the menuItems are repopulated, - // refresh the current page using the SelectedItem data we collected before the refresh - if (selectedItemTag) + // refresh the current page using the breadcrumb data we collected before the refresh + if (const auto& crumb{ lastBreadcrumb.try_as() }) { for (const auto& item : menuItems) { @@ -140,26 +142,26 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { if (const auto& stringTag{ tag.try_as() }) { - if (const auto& selectedItemStringTag{ selectedItemTag.try_as() }) + if (const auto& breadcrumbStringTag{ crumb->Tag().try_as() }) { - if (stringTag == selectedItemStringTag) + if (stringTag == breadcrumbStringTag) { // found the one that was selected before the refresh SettingsNav().SelectedItem(item); - _Navigate(*stringTag); + _Navigate(*stringTag, crumb->SubPage()); return; } } } else if (const auto& profileTag{ tag.try_as() }) { - if (const auto& selectedItemProfileTag{ selectedItemTag.try_as() }) + if (const auto& breadcrumbProfileTag{ crumb->Tag().try_as() }) { - if (profileTag->OriginalProfileGuid() == selectedItemProfileTag->OriginalProfileGuid()) + if (profileTag->OriginalProfileGuid() == breadcrumbProfileTag->OriginalProfileGuid()) { // found the one that was selected before the refresh SettingsNav().SelectedItem(item); - _Navigate(*profileTag); + _Navigate(*profileTag, crumb->SubPage()); return; } } @@ -174,7 +176,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // We can use menuItemsSTL here because the only things they miss are profile entries. const auto& firstItem{ menuItemsSTL.at(0).as() }; SettingsNav().SelectedItem(firstItem); - _Navigate(unbox_value(firstItem.Tag())); + _Navigate(unbox_value(firstItem.Tag()), BreadcrumbSubPage::None); } void MainPage::SetHostingWindow(uint64_t hostingWindow) noexcept @@ -243,7 +245,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // Manually navigate because setting the selected item programmatically doesn't trigger ItemInvoked. if (const auto tag = initialItem.as().Tag()) { - _Navigate(unbox_value(tag)); + _Navigate(unbox_value(tag), BreadcrumbSubPage::None); } } } @@ -267,47 +269,96 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation if (const auto navString = clickedItemContainer.Tag().try_as()) { - _Navigate(*navString); + _Navigate(*navString, BreadcrumbSubPage::None); } else if (const auto profile = clickedItemContainer.Tag().try_as()) { // Navigate to a page with the given profile - _Navigate(profile); + _Navigate(profile, BreadcrumbSubPage::None); } } } - void MainPage::_Navigate(hstring clickedItemTag) + void MainPage::_PreNavigateHelper() + { + _profileViewModelChangedRevoker.revoke(); + _breadcrumbs.Clear(); + } + + void MainPage::_SetupProfileEventHandling(const Editor::ProfilePageNavigationState state) + { + // Add an event handler to navigate to Profiles_Appearance or Profiles_Advanced + // Some notes on this: + // - At first we tried putting another frame inside Profiles.xaml and having that + // frame default to showing Profiles_Base. This allowed the logic for navigation + // to Profiles_Advanced/Profiles_Appearance to live within Profiles.cpp. + // - However, the header for the SUI lives in MainPage.xaml (because that's where + // the whole NavigationView is) and so the BreadcrumbBar needs to be in MainPage.xaml. + // We decided that it's better for the owner of the BreadcrumbBar to also be responsible + // for navigation, so the navigation to Profiles_Advanced/Profiles_Appearance from + // Profiles_Base got moved here. + const auto profile = state.Profile(); + + // If this is the base layer, the breadcrumb tag should be the globalProfileTag instead of the + // ProfileViewModel, because the navigation menu item for this profile is the globalProfileTag. + // See MainPage::UpdateSettings for why this matters + const auto breadcrumbTag = profile.IsBaseLayer() ? box_value(globalProfileTag) : box_value(profile); + const auto breadcrumbText = profile.IsBaseLayer() ? RS_(L"Nav_ProfileDefaults/Content") : profile.Name(); + _profileViewModelChangedRevoker = profile.PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& args) { + const auto settingName{ args.PropertyName() }; + if (settingName == L"CurrentPage") + { + const auto currentPage = profile.CurrentPage(); + if (currentPage == ProfileSubPage::Base) + { + contentFrame().Navigate(xaml_typename(), state); + _breadcrumbs.Clear(); + const auto crumb = winrt::make(breadcrumbTag, breadcrumbText, BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); + } + else if (currentPage == ProfileSubPage::Appearance) + { + contentFrame().Navigate(xaml_typename(), state); + const auto crumb = winrt::make(breadcrumbTag, RS_(L"Profile_Appearance/Header"), BreadcrumbSubPage::Profile_Appearance); + _breadcrumbs.Append(crumb); + } + else if (currentPage == ProfileSubPage::Advanced) + { + contentFrame().Navigate(xaml_typename(), state); + const auto crumb = winrt::make(breadcrumbTag, RS_(L"Profile_Advanced/Header"), BreadcrumbSubPage::Profile_Advanced); + _breadcrumbs.Append(crumb); + } + } + }); + } + + void MainPage::_Navigate(hstring clickedItemTag, BreadcrumbSubPage subPage) { + _PreNavigateHelper(); + if (clickedItemTag == launchTag) { contentFrame().Navigate(xaml_typename(), winrt::make(_settingsClone)); + const auto crumb = winrt::make(box_value(clickedItemTag), RS_(L"Nav_Launch/Content"), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); } else if (clickedItemTag == interactionTag) { contentFrame().Navigate(xaml_typename(), winrt::make(_settingsClone.GlobalSettings())); + const auto crumb = winrt::make(box_value(clickedItemTag), RS_(L"Nav_Interaction/Content"), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); } else if (clickedItemTag == renderingTag) { contentFrame().Navigate(xaml_typename(), winrt::make(_settingsClone.GlobalSettings())); + const auto crumb = winrt::make(box_value(clickedItemTag), RS_(L"Nav_Rendering/Content"), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); } else if (clickedItemTag == actionsTag) { - if constexpr (Feature_EditableActionsPage::IsEnabled()) - { - contentFrame().Navigate(xaml_typename(), winrt::make(_settingsClone)); - } - else - { - auto actionsState{ winrt::make(_settingsClone) }; - actionsState.OpenJson([weakThis = get_weak()](auto&&, auto&& arg) { - if (auto self{ weakThis.get() }) - { - self->_OpenJsonHandlers(nullptr, arg); - } - }); - contentFrame().Navigate(xaml_typename(), actionsState); - } + contentFrame().Navigate(xaml_typename(), winrt::make(_settingsClone)); + const auto crumb = winrt::make(box_value(clickedItemTag), RS_(L"Nav_Actions/Content"), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); } else if (clickedItemTag == globalProfileTag) { @@ -317,21 +368,41 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation _settingsClone.GlobalSettings().ColorSchemes(), *this) }; - contentFrame().Navigate(xaml_typename(), state); + _SetupProfileEventHandling(state); + + contentFrame().Navigate(xaml_typename(), state); + const auto crumb = winrt::make(box_value(clickedItemTag), RS_(L"Nav_ProfileDefaults/Content"), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); + + // If we were given a label, make sure we are on the correct sub-page + if (subPage == BreadcrumbSubPage::Profile_Appearance) + { + profileVM.CurrentPage(ProfileSubPage::Appearance); + } + else if (subPage == BreadcrumbSubPage::Profile_Advanced) + { + profileVM.CurrentPage(ProfileSubPage::Advanced); + } } else if (clickedItemTag == colorSchemesTag) { contentFrame().Navigate(xaml_typename(), _colorSchemesNavState); + const auto crumb = winrt::make(box_value(clickedItemTag), RS_(L"Nav_ColorSchemes/Content"), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); } else if (clickedItemTag == globalAppearanceTag) { contentFrame().Navigate(xaml_typename(), winrt::make(_settingsClone.GlobalSettings())); + const auto crumb = winrt::make(box_value(clickedItemTag), RS_(L"Nav_Appearance/Content"), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); } else if (clickedItemTag == addProfileTag) { auto addProfileState{ winrt::make(_settingsClone) }; addProfileState.AddNew({ get_weak(), &MainPage::_AddProfileHandler }); contentFrame().Navigate(xaml_typename(), addProfileState); + const auto crumb = winrt::make(box_value(clickedItemTag), RS_(L"Nav_AddNewProfile/Content"), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); } } @@ -340,16 +411,37 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // - NOTE: this does not update the selected item. // Arguments: // - profile - the profile object we are getting a view of - void MainPage::_Navigate(const Editor::ProfileViewModel& profile) + void MainPage::_Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage) { auto state{ winrt::make(profile, _settingsClone.GlobalSettings().ColorSchemes(), *this) }; + _PreNavigateHelper(); + // Add an event handler for when the user wants to delete a profile. profile.DeleteProfile({ this, &MainPage::_DeleteProfile }); - contentFrame().Navigate(xaml_typename(), state); + _SetupProfileEventHandling(state); + + contentFrame().Navigate(xaml_typename(), state); + const auto crumb = winrt::make(box_value(profile), profile.Name(), BreadcrumbSubPage::None); + _breadcrumbs.Append(crumb); + + // Set the profile's 'CurrentPage' to the correct one, if this requires further navigation, the + // event handler will do it + if (subPage == BreadcrumbSubPage::None) + { + profile.CurrentPage(ProfileSubPage::Base); + } + else if (subPage == BreadcrumbSubPage::Profile_Appearance) + { + profile.CurrentPage(ProfileSubPage::Appearance); + } + else if (subPage == BreadcrumbSubPage::Profile_Advanced) + { + profile.CurrentPage(ProfileSubPage::Advanced); + } } void MainPage::OpenJsonTapped(IInspectable const& /*sender*/, Windows::UI::Xaml::Input::TappedRoutedEventArgs const& /*args*/) @@ -383,6 +475,20 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation UpdateSettings(_settingsSource); } + void MainPage::BreadcrumbBar_ItemClicked(Microsoft::UI::Xaml::Controls::BreadcrumbBar const& /*sender*/, Microsoft::UI::Xaml::Controls::BreadcrumbBarItemClickedEventArgs const& args) + { + const auto tag = args.Item().as()->Tag(); + const auto subPage = args.Item().as()->SubPage(); + if (const auto profileViewModel = tag.try_as()) + { + _Navigate(*profileViewModel, subPage); + } + else + { + _Navigate(tag.as(), subPage); + } + } + void MainPage::_InitializeProfilesList() { const auto menuItems = SettingsNav().MenuItems(); @@ -422,7 +528,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // Select and navigate to the new profile SettingsNav().SelectedItem(navItem); - _Navigate(profileViewModel); + _Navigate(profileViewModel, BreadcrumbSubPage::None); } MUX::Controls::NavigationViewItem MainPage::_CreateProfileNavViewItem(const Editor::ProfileViewModel& profile) @@ -482,11 +588,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // navigate to the profile next to this one const auto newSelectedItem{ menuItems.GetAt(index < menuItems.Size() - 1 ? index : index - 1) }; SettingsNav().SelectedItem(newSelectedItem); - _Navigate(newSelectedItem.try_as().Tag().try_as()); + _Navigate(newSelectedItem.try_as().Tag().try_as(), BreadcrumbSubPage::None); } - bool MainPage::ShowBaseLayerMenuItem() const noexcept + IObservableVector MainPage::Breadcrumbs() noexcept { - return Feature_ShowProfileDefaultsInSettings::IsEnabled(); + return _breadcrumbs; } } diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.h b/src/cascadia/TerminalSettingsEditor/MainPage.h index 1c0152ce0b6..f63586aa3b0 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.h +++ b/src/cascadia/TerminalSettingsEditor/MainPage.h @@ -4,10 +4,23 @@ #pragma once #include "MainPage.g.h" +#include "Breadcrumb.g.h" #include "Utils.h" namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { + struct Breadcrumb : BreadcrumbT + { + Breadcrumb(IInspectable tag, winrt::hstring label, BreadcrumbSubPage subPage) : + _Tag{ tag }, + _Label{ label }, + _SubPage{ subPage } {} + + WINRT_PROPERTY(IInspectable, Tag); + WINRT_PROPERTY(winrt::hstring, Label); + WINRT_PROPERTY(BreadcrumbSubPage, SubPage); + }; + struct MainPage : MainPageT { MainPage() = delete; @@ -21,16 +34,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void SettingsNav_ItemInvoked(Microsoft::UI::Xaml::Controls::NavigationView const& sender, Microsoft::UI::Xaml::Controls::NavigationViewItemInvokedEventArgs const& args); void SaveButton_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); void ResetButton_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); + void BreadcrumbBar_ItemClicked(Microsoft::UI::Xaml::Controls::BreadcrumbBar const& sender, Microsoft::UI::Xaml::Controls::BreadcrumbBarItemClickedEventArgs const& args); void SetHostingWindow(uint64_t hostingWindow) noexcept; bool TryPropagateHostingWindow(IInspectable object) noexcept; uint64_t GetHostingWindow() const noexcept; - bool ShowBaseLayerMenuItem() const noexcept; + Windows::Foundation::Collections::IObservableVector Breadcrumbs() noexcept; TYPED_EVENT(OpenJson, Windows::Foundation::IInspectable, Model::SettingsTarget); private: + Windows::Foundation::Collections::IObservableVector _breadcrumbs; Model::CascadiaSettings _settingsSource; Model::CascadiaSettings _settingsClone; @@ -42,10 +57,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void _DeleteProfile(const Windows::Foundation::IInspectable sender, const Editor::DeleteProfileEventArgs& args); void _AddProfileHandler(const winrt::guid profileGuid); - void _Navigate(hstring clickedItemTag); - void _Navigate(const Editor::ProfileViewModel& profile); + void _SetupProfileEventHandling(const winrt::Microsoft::Terminal::Settings::Editor::ProfilePageNavigationState state); + + void _PreNavigateHelper(); + void _Navigate(hstring clickedItemTag, BreadcrumbSubPage subPage); + void _Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage); winrt::Microsoft::Terminal::Settings::Editor::ColorSchemesPageNavigationState _colorSchemesNavState{ nullptr }; + + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _profileViewModelChangedRevoker; }; } diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.idl b/src/cascadia/TerminalSettingsEditor/MainPage.idl index 6c368713a73..4c771419753 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.idl +++ b/src/cascadia/TerminalSettingsEditor/MainPage.idl @@ -13,6 +13,20 @@ namespace Microsoft.Terminal.Settings.Editor UInt64 GetHostingWindow(); } + enum BreadcrumbSubPage + { + None = 0, + Profile_Appearance, + Profile_Advanced + }; + + runtimeclass Breadcrumb + { + IInspectable Tag; + String Label; + BreadcrumbSubPage SubPage; + } + [default_interface] runtimeclass MainPage : Windows.UI.Xaml.Controls.Page, IHostedInWindow { MainPage(Microsoft.Terminal.Settings.Model.CascadiaSettings settings); @@ -24,6 +38,6 @@ namespace Microsoft.Terminal.Settings.Editor // Let's just smuggle the HWND in as a UInt64 :| void SetHostingWindow(UInt64 window); - Boolean ShowBaseLayerMenuItem { get; }; + Windows.Foundation.Collections.IObservableVector Breadcrumbs { get; }; } } diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.xaml b/src/cascadia/TerminalSettingsEditor/MainPage.xaml index 0c0e5a412f5..1487d150e4d 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.xaml +++ b/src/cascadia/TerminalSettingsEditor/MainPage.xaml @@ -6,6 +6,7 @@ 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:muxc="using:Microsoft.UI.Xaml.Controls" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" @@ -36,12 +37,31 @@ + + + + + + + + + + 28 + 11,4,12,0 + SemiBold + 16 + + + + @@ -112,7 +132,7 @@ - + diff --git a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj index e3d66548482..184ab6a98ef 100644 --- a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj +++ b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj @@ -70,13 +70,25 @@ MainPage.xaml - Profiles.xaml + Profiles.idl Code ProfileViewModel.idl Code + + Profiles_Base.xaml + Code + + + Profiles_Advanced.xaml + Code + + + Profiles_Appearance.xaml + Code + Appearances.xaml Code @@ -122,7 +134,13 @@ Designer - + + Designer + + + Designer + + Designer @@ -174,13 +192,25 @@ - Profiles.xaml + Profiles.idl Code ProfileViewModel.idl Code + + Profiles_Base.xaml + Code + + + Profiles_Advanced.xaml + Code + + + Profiles_Appearance.xaml + Code + Appearances.xaml Code @@ -243,10 +273,21 @@ MainPage.xaml - Profiles.xaml Code + + Profiles_Base.xaml + Code + + + Profiles_Advanced.xaml + Code + + + Profiles_Appearance.xaml + Code + Appearances.xaml Code diff --git a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj.filters b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj.filters index 9ce0ebd1436..015795e88bd 100644 --- a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj.filters +++ b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj.filters @@ -35,7 +35,9 @@ - + + + diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index d7e05e2b2ab..baa44c2bb15 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -70,7 +70,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { Windows::Foundation::Collections::IObservableVector ProfileViewModel::_MonospaceFontList{ nullptr }; Windows::Foundation::Collections::IObservableVector ProfileViewModel::_FontList{ nullptr }; - ProfilesPivots ProfileViewModel::_LastActivePivot{ ProfilesPivots::General }; ProfileViewModel::ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& appSettings) : _profile{ profile }, diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h index b27c308d7a5..674c20724ed 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.h @@ -19,9 +19,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation static Windows::Foundation::Collections::IObservableVector CompleteFontList() noexcept { return _FontList; }; static Windows::Foundation::Collections::IObservableVector MonospaceFontList() noexcept { return _MonospaceFontList; }; - static ProfilesPivots LastActivePivot() noexcept { return _LastActivePivot; }; - static void LastActivePivot(Editor::ProfilesPivots val) noexcept { _LastActivePivot = val; }; - ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& settings); Model::TerminalSettings TermSettings() const; void DeleteProfile(); @@ -77,6 +74,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void DeleteUnfocusedAppearance(); bool AtlasEngineAvailable() const noexcept; + VIEW_MODEL_OBSERVABLE_PROPERTY(ProfileSubPage, CurrentPage); + PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, Guid); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, ConnectionType); OBSERVABLE_PROJECTED_SETTING(_profile, Name); @@ -122,7 +121,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation static Windows::Foundation::Collections::IObservableVector _MonospaceFontList; static Windows::Foundation::Collections::IObservableVector _FontList; - static ProfilesPivots _LastActivePivot; static Editor::Font _GetFont(com_ptr localizedFamilyNames); diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl index 7215989d626..2c4ff5a920a 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl @@ -18,9 +18,9 @@ namespace Microsoft.Terminal.Settings.Editor Guid ProfileGuid { get; }; } - enum ProfilesPivots + enum ProfileSubPage { - General = 0, + Base = 0, Appearance = 1, Advanced = 2 }; @@ -29,7 +29,6 @@ namespace Microsoft.Terminal.Settings.Editor { static Windows.Foundation.Collections.IObservableVector CompleteFontList { get; }; static Windows.Foundation.Collections.IObservableVector MonospaceFontList { get; }; - static ProfilesPivots LastActivePivot; Microsoft.Terminal.Settings.Model.TerminalSettings TermSettings { get; }; @@ -54,6 +53,7 @@ namespace Microsoft.Terminal.Settings.Editor Boolean CanDeleteProfile { get; }; Boolean IsBaseLayer; + ProfileSubPage CurrentPage; Boolean UseParentProcessDirectory; Boolean UseCustomStartingDirectory { get; }; AppearanceViewModel DefaultAppearance { get; }; diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.cpp b/src/cascadia/TerminalSettingsEditor/Profiles.cpp index b558f2f0294..71f54af117d 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.cpp +++ b/src/cascadia/TerminalSettingsEditor/Profiles.cpp @@ -5,7 +5,6 @@ #include "Profiles.h" #include "PreviewConnection.h" -#include "Profiles.g.cpp" #include #include "..\WinRTUtils\inc\Utils.h" @@ -21,156 +20,4 @@ using namespace winrt::Microsoft::Terminal::Settings::Model; namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { - Profiles::Profiles() : - _previewControl{ Control::TermControl(Model::TerminalSettings{}, make()) } - { - InitializeComponent(); - - const auto startingDirCheckboxTooltip{ ToolTipService::GetToolTip(StartingDirectoryUseParentCheckbox()) }; - Automation::AutomationProperties::SetFullDescription(StartingDirectoryUseParentCheckbox(), unbox_value(startingDirCheckboxTooltip)); - - Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"Profile_DeleteButton/Text")); - - _previewControl.IsEnabled(false); - _previewControl.AllowFocusWhenDisabled(false); - ControlPreview().Child(_previewControl); - } - - void Profiles::OnNavigatedTo(const NavigationEventArgs& e) - { - auto state{ e.Parameter().as() }; - _Profile = state.Profile(); - - // generate the font list, if we don't have one - if (!ProfileViewModel::CompleteFontList() || !ProfileViewModel::MonospaceFontList()) - { - ProfileViewModel::UpdateFontList(); - } - - // Check the use parent directory box if the starting directory is empty - if (_Profile.StartingDirectory().empty()) - { - StartingDirectoryUseParentCheckbox().IsChecked(true); - } - - // Subscribe to some changes in the view model - // These changes should force us to update our own set of "Current" members, - // and propagate those changes to the UI - _ViewModelChangedRevoker = _Profile.PropertyChanged(winrt::auto_revoke, [=](auto&&, auto&&) { - _previewControl.Settings(_Profile.TermSettings()); - _previewControl.UpdateSettings(); - }); - - // The Appearances object handles updating the values in the settings UI, but - // we still need to listen to the changes here just to update the preview control - _AppearanceViewModelChangedRevoker = _Profile.DefaultAppearance().PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& /*args*/) { - _previewControl.Settings(_Profile.TermSettings()); - _previewControl.UpdateSettings(); - }); - - // Navigate to the pivot in the provided navigation state - ProfilesPivot().SelectedIndex(static_cast(ProfileViewModel::LastActivePivot())); - - _previewControl.Settings(_State.Profile().TermSettings()); - // There is a possibility that the control has not fully initialized yet, - // so wait for it to initialize before updating the settings (so we know - // that the renderer is set up) - _previewControl.Initialized([&](auto&& /*s*/, auto&& /*e*/) { - _previewControl.UpdateSettings(); - }); - } - - void Profiles::OnNavigatedFrom(const NavigationEventArgs& /*e*/) - { - _ViewModelChangedRevoker.revoke(); - _AppearanceViewModelChangedRevoker.revoke(); - } - - void Profiles::DeleteConfirmation_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/) - { - winrt::get_self(_Profile)->DeleteProfile(); - } - - void Profiles::CreateUnfocusedAppearance_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/) - { - _Profile.CreateUnfocusedAppearance(); - } - - void Profiles::DeleteUnfocusedAppearance_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/) - { - _Profile.DeleteUnfocusedAppearance(); - } - - fire_and_forget Profiles::Icon_Click(IInspectable const&, RoutedEventArgs const&) - { - auto lifetime = get_strong(); - - const auto parentHwnd{ reinterpret_cast(winrt::get_self(_Profile)->WindowRoot().GetHostingWindow()) }; - auto file = co_await OpenImagePicker(parentHwnd); - if (!file.empty()) - { - _Profile.Icon(file); - } - } - - fire_and_forget Profiles::Commandline_Click(IInspectable const&, RoutedEventArgs const&) - { - auto lifetime = get_strong(); - - static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = { - { L"Executable Files (*.exe, *.cmd, *.bat)", L"*.exe;*.cmd;*.bat" }, - { L"All Files (*.*)", L"*.*" } - }; - - static constexpr winrt::guid clientGuidExecutables{ 0x2E7E4331, 0x0800, 0x48E6, { 0xB0, 0x17, 0xA1, 0x4C, 0xD8, 0x73, 0xDD, 0x58 } }; - const auto parentHwnd{ reinterpret_cast(winrt::get_self(_Profile)->WindowRoot().GetHostingWindow()) }; - auto path = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) { - THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExecutables)); - try - { - auto folderShellItem{ winrt::capture(&SHGetKnownFolderItem, FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, nullptr) }; - dialog->SetDefaultFolder(folderShellItem.get()); - } - CATCH_LOG(); // non-fatal - THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedFileTypes), supportedFileTypes)); - THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed - THROW_IF_FAILED(dialog->SetDefaultExtension(L"exe;cmd;bat")); - }); - - if (!path.empty()) - { - _Profile.Commandline(path); - } - } - - fire_and_forget Profiles::StartingDirectory_Click(IInspectable const&, RoutedEventArgs const&) - { - auto lifetime = get_strong(); - const auto parentHwnd{ reinterpret_cast(winrt::get_self(_Profile)->WindowRoot().GetHostingWindow()) }; - auto folder = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) { - static constexpr winrt::guid clientGuidFolderPicker{ 0xAADAA433, 0xB04D, 0x4BAE, { 0xB1, 0xEA, 0x1E, 0x6C, 0xD1, 0xCD, 0xA6, 0x8B } }; - THROW_IF_FAILED(dialog->SetClientGuid(clientGuidFolderPicker)); - try - { - auto folderShellItem{ winrt::capture(&SHGetKnownFolderItem, FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, nullptr) }; - dialog->SetDefaultFolder(folderShellItem.get()); - } - CATCH_LOG(); // non-fatal - - DWORD flags{}; - THROW_IF_FAILED(dialog->GetOptions(&flags)); - THROW_IF_FAILED(dialog->SetOptions(flags | FOS_PICKFOLDERS)); // folders only - }); - - if (!folder.empty()) - { - _Profile.StartingDirectory(folder); - } - } - - void Profiles::Pivot_SelectionChanged(Windows::Foundation::IInspectable const& /*sender*/, - RoutedEventArgs const& /*e*/) - { - ProfileViewModel::LastActivePivot(static_cast(ProfilesPivot().SelectedIndex())); - } } diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.h b/src/cascadia/TerminalSettingsEditor/Profiles.h index 6213bcbe69d..8b245b8fe28 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.h +++ b/src/cascadia/TerminalSettingsEditor/Profiles.h @@ -3,7 +3,6 @@ #pragma once -#include "Profiles.g.h" #include "ProfilePageNavigationState.g.h" #include "ProfileViewModel.h" #include "Utils.h" @@ -35,39 +34,4 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); }; - - struct Profiles : public HasScrollViewer, ProfilesT - { - public: - Profiles(); - - void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); - void OnNavigatedFrom(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); - - fire_and_forget Commandline_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); - fire_and_forget StartingDirectory_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); - fire_and_forget Icon_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); - void DeleteConfirmation_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); - void Pivot_SelectionChanged(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); - void CreateUnfocusedAppearance_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); - void DeleteUnfocusedAppearance_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); - - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); - - WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); - - private: - void _UpdateBIAlignmentControl(const int32_t val); - - std::array _BIAlignmentButtons; - Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; - Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _AppearanceViewModelChangedRevoker; - - Microsoft::Terminal::Control::TermControl _previewControl; - }; }; - -namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation -{ - BASIC_FACTORY(Profiles); -} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.idl b/src/cascadia/TerminalSettingsEditor/Profiles.idl index 545579f2365..a3a1dafd1cf 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.idl +++ b/src/cascadia/TerminalSettingsEditor/Profiles.idl @@ -10,12 +10,4 @@ namespace Microsoft.Terminal.Settings.Editor { ProfileViewModel Profile { get; }; }; - - [default_interface] runtimeclass Profiles : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged - { - Profiles(); - ProfileViewModel Profile { get; }; - - Windows.UI.Xaml.Controls.Slider OpacitySlider { get; }; - } } diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.xaml b/src/cascadia/TerminalSettingsEditor/Profiles.xaml deleted file mode 100644 index 354d01f41dc..00000000000 --- a/src/cascadia/TerminalSettingsEditor/Profiles.xaml +++ /dev/null @@ -1,496 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp new file mode 100644 index 00000000000..79c0edffd76 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "Profiles_Advanced.h" +#include "Profiles_Advanced.g.cpp" + +#include "EnumEntry.h" +#include +#include "..\WinRTUtils\inc\Utils.h" + +using namespace winrt::Windows::UI::Xaml::Navigation; + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + Profiles_Advanced::Profiles_Advanced() + { + InitializeComponent(); + } + + void Profiles_Advanced::OnNavigatedTo(const NavigationEventArgs& e) + { + auto state{ e.Parameter().as() }; + _Profile = state.Profile(); + } + + void Profiles_Advanced::OnNavigatedFrom(const NavigationEventArgs& /*e*/) + { + _ViewModelChangedRevoker.revoke(); + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.h b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.h new file mode 100644 index 00000000000..1acd81d647a --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.h @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "Profiles_Advanced.g.h" +#include "ViewModelHelpers.h" +#include "Utils.h" + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + struct Profiles_Advanced : public HasScrollViewer, Profiles_AdvancedT + { + public: + Profiles_Advanced(); + + void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + void OnNavigatedFrom(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); + + private: + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; + }; +}; + +namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation +{ + BASIC_FACTORY(Profiles_Advanced); +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.idl b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.idl new file mode 100644 index 00000000000..44a1cbca91a --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.idl @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "Profiles.idl"; + +namespace Microsoft.Terminal.Settings.Editor +{ + [default_interface] runtimeclass Profiles_Advanced : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + Profiles_Advanced(); + ProfileViewModel Profile { get; }; + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml new file mode 100644 index 00000000000..517cc3f9113 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Advanced.xaml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp new file mode 100644 index 00000000000..8ca25d19faa --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "Profiles_Appearance.h" +#include "Profiles_Appearance.g.cpp" +#include "Profiles.h" +#include "PreviewConnection.h" +#include "EnumEntry.h" + +#include +#include "..\WinRTUtils\inc\Utils.h" + +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Navigation; + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + Profiles_Appearance::Profiles_Appearance() : + _previewControl{ Control::TermControl(Model::TerminalSettings{}, nullptr, make()) } + { + InitializeComponent(); + + _previewControl.IsEnabled(false); + _previewControl.AllowFocusWhenDisabled(false); + ControlPreview().Child(_previewControl); + } + + void Profiles_Appearance::OnNavigatedTo(const NavigationEventArgs& e) + { + auto state{ e.Parameter().as() }; + _Profile = state.Profile(); + + // generate the font list, if we don't have one + if (_Profile.CompleteFontList() || !_Profile.MonospaceFontList()) + { + ProfileViewModel::UpdateFontList(); + } + + // Subscribe to some changes in the view model + // These changes should force us to update our own set of "Current" members, + // and propagate those changes to the UI + _ViewModelChangedRevoker = _Profile.PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& /*args*/) { + _previewControl.UpdateControlSettings(_Profile.TermSettings(), _Profile.TermSettings()); + }); + + // The Appearances object handles updating the values in the settings UI, but + // we still need to listen to the changes here just to update the preview control + _AppearanceViewModelChangedRevoker = _Profile.DefaultAppearance().PropertyChanged(winrt::auto_revoke, [=](auto&&, const PropertyChangedEventArgs& /*args*/) { + _previewControl.UpdateControlSettings(_Profile.TermSettings(), _Profile.TermSettings()); + }); + + // There is a possibility that the control has not fully initialized yet, + // so wait for it to initialize before updating the settings (so we know + // that the renderer is set up) + _previewControl.Initialized([&](auto&& /*s*/, auto&& /*e*/) { + _previewControl.UpdateControlSettings(_Profile.TermSettings(), _Profile.TermSettings()); + }); + } + + void Profiles_Appearance::OnNavigatedFrom(const NavigationEventArgs& /*e*/) + { + _ViewModelChangedRevoker.revoke(); + _AppearanceViewModelChangedRevoker.revoke(); + } + + void Profiles_Appearance::CreateUnfocusedAppearance_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/) + { + _Profile.CreateUnfocusedAppearance(); + } + + void Profiles_Appearance::DeleteUnfocusedAppearance_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/) + { + _Profile.DeleteUnfocusedAppearance(); + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h new file mode 100644 index 00000000000..f85efca9c23 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "Profiles_Appearance.g.h" +#include "Utils.h" + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + struct Profiles_Appearance : public HasScrollViewer, Profiles_AppearanceT + { + public: + Profiles_Appearance(); + + void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + void OnNavigatedFrom(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + + void CreateUnfocusedAppearance_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + void DeleteUnfocusedAppearance_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); + + private: + Microsoft::Terminal::Control::TermControl _previewControl; + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _AppearanceViewModelChangedRevoker; + }; +}; + +namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation +{ + BASIC_FACTORY(Profiles_Appearance); +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.idl b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.idl new file mode 100644 index 00000000000..dff812912a7 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.idl @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "Profiles.idl"; + +namespace Microsoft.Terminal.Settings.Editor +{ + [default_interface] runtimeclass Profiles_Appearance : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + Profiles_Appearance(); + ProfileViewModel Profile { get; }; + + Windows.UI.Xaml.Controls.Slider OpacitySlider { get; }; + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.xaml new file mode 100644 index 00000000000..56ea4904380 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Appearance.xaml @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp new file mode 100644 index 00000000000..6c31b619b56 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "Profiles_Base.h" +#include "Profiles_Base.g.cpp" +#include "Profiles.h" + +#include +#include "..\WinRTUtils\inc\Utils.h" + +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_Base::Profiles_Base() + { + InitializeComponent(); + + const auto startingDirCheckboxTooltip{ ToolTipService::GetToolTip(StartingDirectoryUseParentCheckbox()) }; + Automation::AutomationProperties::SetFullDescription(StartingDirectoryUseParentCheckbox(), unbox_value(startingDirCheckboxTooltip)); + + Automation::AutomationProperties::SetName(DeleteButton(), RS_(L"Profile_DeleteButton/Text")); + } + + void Profiles_Base::OnNavigatedTo(const NavigationEventArgs& e) + { + auto state{ e.Parameter().as() }; + _Profile = state.Profile(); + + // Check the use parent directory box if the starting directory is empty + if (_Profile.StartingDirectory().empty()) + { + StartingDirectoryUseParentCheckbox().IsChecked(true); + } + } + + void Profiles_Base::OnNavigatedFrom(const NavigationEventArgs& /*e*/) + { + _ViewModelChangedRevoker.revoke(); + } + + void Profiles_Base::Appearance_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*args*/) + { + _Profile.CurrentPage(ProfileSubPage::Appearance); + } + + void Profiles_Base::Advanced_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*args*/) + { + _Profile.CurrentPage(ProfileSubPage::Advanced); + } + + void Profiles_Base::DeleteConfirmation_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/) + { + winrt::get_self(_Profile)->DeleteProfile(); + } + + fire_and_forget Profiles_Base::Commandline_Click(IInspectable const&, RoutedEventArgs const&) + { + auto lifetime = get_strong(); + + static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = { + { L"Executable Files (*.exe, *.cmd, *.bat)", L"*.exe;*.cmd;*.bat" }, + { L"All Files (*.*)", L"*.*" } + }; + + static constexpr winrt::guid clientGuidExecutables{ 0x2E7E4331, 0x0800, 0x48E6, { 0xB0, 0x17, 0xA1, 0x4C, 0xD8, 0x73, 0xDD, 0x58 } }; + const auto parentHwnd{ reinterpret_cast(winrt::get_self(_Profile)->WindowRoot().GetHostingWindow()) }; + auto path = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) { + THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExecutables)); + try + { + auto folderShellItem{ winrt::capture(&SHGetKnownFolderItem, FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, nullptr) }; + dialog->SetDefaultFolder(folderShellItem.get()); + } + CATCH_LOG(); // non-fatal + THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedFileTypes), supportedFileTypes)); + THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed + THROW_IF_FAILED(dialog->SetDefaultExtension(L"exe;cmd;bat")); + }); + + if (!path.empty()) + { + _Profile.Commandline(path); + } + } + + fire_and_forget Profiles_Base::Icon_Click(IInspectable const&, RoutedEventArgs const&) + { + auto lifetime = get_strong(); + + const auto parentHwnd{ reinterpret_cast(winrt::get_self(_Profile)->WindowRoot().GetHostingWindow()) }; + auto file = co_await OpenImagePicker(parentHwnd); + if (!file.empty()) + { + _Profile.Icon(file); + } + } + + fire_and_forget Profiles_Base::StartingDirectory_Click(IInspectable const&, RoutedEventArgs const&) + { + auto lifetime = get_strong(); + const auto parentHwnd{ reinterpret_cast(winrt::get_self(_Profile)->WindowRoot().GetHostingWindow()) }; + auto folder = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) { + static constexpr winrt::guid clientGuidFolderPicker{ 0xAADAA433, 0xB04D, 0x4BAE, { 0xB1, 0xEA, 0x1E, 0x6C, 0xD1, 0xCD, 0xA6, 0x8B } }; + THROW_IF_FAILED(dialog->SetClientGuid(clientGuidFolderPicker)); + try + { + auto folderShellItem{ winrt::capture(&SHGetKnownFolderItem, FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, nullptr) }; + dialog->SetDefaultFolder(folderShellItem.get()); + } + CATCH_LOG(); // non-fatal + + DWORD flags{}; + THROW_IF_FAILED(dialog->GetOptions(&flags)); + THROW_IF_FAILED(dialog->SetOptions(flags | FOS_PICKFOLDERS)); // folders only + }); + + if (!folder.empty()) + { + _Profile.StartingDirectory(folder); + } + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.h b/src/cascadia/TerminalSettingsEditor/Profiles_Base.h new file mode 100644 index 00000000000..778a2f675cc --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.h @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "Profiles_Base.g.h" +#include "ViewModelHelpers.h" +#include "Utils.h" + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + struct Profiles_Base : public HasScrollViewer, Profiles_BaseT + { + public: + Profiles_Base(); + + void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + void OnNavigatedFrom(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e); + + fire_and_forget StartingDirectory_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + fire_and_forget Icon_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + fire_and_forget Commandline_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + void Appearance_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + void Advanced_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + void DeleteConfirmation_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e); + + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); + + private: + Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; + }; +}; + +namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation +{ + BASIC_FACTORY(Profiles_Base); +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.idl b/src/cascadia/TerminalSettingsEditor/Profiles_Base.idl new file mode 100644 index 00000000000..a0cd1b88aba --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.idl @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "Profiles.idl"; + +namespace Microsoft.Terminal.Settings.Editor +{ + [default_interface] runtimeclass Profiles_Base : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged + { + Profiles_Base(); + ProfileViewModel Profile { get; }; + } +} diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Base.xaml new file mode 100644 index 00000000000..2d7c75ff944 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.xaml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index faf020bbddf..9d11d8c3844 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -1110,6 +1110,10 @@ Cursor Header for a group of settings that control the appearance of the cursor. Presented near "Profile_CursorHeight" and other keys starting with "Profile_Cursor". + + Additional settings + Header for the buttons that navigate to additional settings for the profile. + Text Header for a group of settings that control the appearance of text in the app. diff --git a/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml b/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml index 252eaa30a63..7e6906b34f1 100644 --- a/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml +++ b/src/cascadia/TerminalSettingsEditor/SettingContainerStyle.xaml @@ -18,6 +18,11 @@ + #0F000000 + + + #19000000 + + @@ -67,7 +79,7 @@ - + @@ -88,6 +100,7 @@