Create a new page for "Add new profile" in the SUI (#9352)
- Whenever we add a new profile setting from now on we have to update `Profile::CopySettings` _and_ `CascadiaSettings::DuplicateProfile` 👎 Notes from bug bash (checked bugs have been resolved): - [ ] The duplicate list can be very long if you have profiles - [x] DH: "Create new" seems too vague. "New empty profile" or something seems a little clearer to me. - [x] There is no deduplication counter for name - [x] Crash when your settings file is corrupt and we had to fall back to the defaults and you duplicate a profile - [x] Crash due to #10003 ## PR Checklist * [x] Closes #9121
This commit is contained in:
parent
cb55cec275
commit
b53bd672d7
|
@ -36,6 +36,7 @@ namespace SettingsModelLocalTests
|
|||
TEST_METHOD(LayerProfileProperties);
|
||||
TEST_METHOD(LayerProfileIcon);
|
||||
TEST_METHOD(LayerProfilesOnArray);
|
||||
TEST_METHOD(DuplicateProfileTest);
|
||||
|
||||
TEST_CLASS_SETUP(ClassSetup)
|
||||
{
|
||||
|
@ -307,4 +308,22 @@ namespace SettingsModelLocalTests
|
|||
VERIFY_ARE_EQUAL(L"profile4", settings->_allProfiles.GetAt(0).Name());
|
||||
}
|
||||
|
||||
void ProfileTests::DuplicateProfileTest()
|
||||
{
|
||||
const std::string profile0String{ R"({
|
||||
"name" : "profile0",
|
||||
"backgroundImage" : "some//path"
|
||||
})" };
|
||||
|
||||
const auto profile0Json = VerifyParseSucceeded(profile0String);
|
||||
|
||||
auto settings = winrt::make_self<implementation::CascadiaSettings>();
|
||||
|
||||
settings->_LayerOrCreateProfile(profile0Json);
|
||||
auto duplicatedProfile = settings->DuplicateProfile(*settings->_FindMatchingProfile(profile0Json));
|
||||
duplicatedProfile.Name(L"profile0");
|
||||
|
||||
const auto duplicatedJson = winrt::get_self<implementation::Profile>(duplicatedProfile)->ToJson();
|
||||
VERIFY_ARE_EQUAL(profile0Json, duplicatedJson);
|
||||
}
|
||||
}
|
||||
|
|
42
src/cascadia/TerminalSettingsEditor/AddProfile.cpp
Normal file
42
src/cascadia/TerminalSettingsEditor/AddProfile.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AddProfile.h"
|
||||
#include "AddProfile.g.cpp"
|
||||
#include "AddProfilePageNavigationState.g.cpp"
|
||||
#include "EnumEntry.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
AddProfile::AddProfile()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
void AddProfile::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
_State = e.Parameter().as<Editor::AddProfilePageNavigationState>();
|
||||
}
|
||||
|
||||
void AddProfile::AddNewClick(const IInspectable& /*sender*/,
|
||||
const Windows::UI::Xaml::RoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
_State.RequestAddNew();
|
||||
}
|
||||
|
||||
void AddProfile::DuplicateClick(const IInspectable& /*sender*/,
|
||||
const Windows::UI::Xaml::RoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
if (const auto selected = Profiles().SelectedItem())
|
||||
{
|
||||
_State.RequestDuplicate(selected.try_as<Model::Profile>().Guid());
|
||||
}
|
||||
}
|
||||
}
|
63
src/cascadia/TerminalSettingsEditor/AddProfile.h
Normal file
63
src/cascadia/TerminalSettingsEditor/AddProfile.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- AddProfile.h
|
||||
|
||||
Abstract:
|
||||
- This creates the 'add new profile' page in the settings UI and handles
|
||||
user interaction with it, raising events to the main page as necessary
|
||||
|
||||
|
||||
Author(s):
|
||||
- Pankaj Bhojwani - March 2021
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AddProfile.g.h"
|
||||
#include "AddProfilePageNavigationState.g.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct AddProfilePageNavigationState : AddProfilePageNavigationStateT<AddProfilePageNavigationState>
|
||||
{
|
||||
public:
|
||||
AddProfilePageNavigationState(const Model::CascadiaSettings& settings) :
|
||||
_Settings{ settings } {}
|
||||
|
||||
void RequestAddNew()
|
||||
{
|
||||
_AddNewHandlers(winrt::guid{});
|
||||
}
|
||||
|
||||
void RequestDuplicate(GUID profile)
|
||||
{
|
||||
_AddNewHandlers(profile);
|
||||
}
|
||||
|
||||
WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr)
|
||||
WINRT_CALLBACK(AddNew, AddNewArgs);
|
||||
};
|
||||
|
||||
struct AddProfile : AddProfileT<AddProfile>
|
||||
{
|
||||
public:
|
||||
AddProfile();
|
||||
|
||||
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
|
||||
|
||||
void AddNewClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
void DuplicateClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
|
||||
WINRT_PROPERTY(Editor::AddProfilePageNavigationState, State, nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(AddProfile);
|
||||
}
|
21
src/cascadia/TerminalSettingsEditor/AddProfile.idl
Normal file
21
src/cascadia/TerminalSettingsEditor/AddProfile.idl
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
delegate void AddNewArgs(Guid profile);
|
||||
|
||||
runtimeclass AddProfilePageNavigationState
|
||||
{
|
||||
Microsoft.Terminal.Settings.Model.CascadiaSettings Settings;
|
||||
void RequestAddNew();
|
||||
void RequestDuplicate(Guid profile);
|
||||
event AddNewArgs AddNew;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass AddProfile : Windows.UI.Xaml.Controls.Page
|
||||
{
|
||||
AddProfile();
|
||||
AddProfilePageNavigationState State { get; };
|
||||
}
|
||||
}
|
87
src/cascadia/TerminalSettingsEditor/AddProfile.xaml
Normal file
87
src/cascadia/TerminalSettingsEditor/AddProfile.xaml
Normal file
|
@ -0,0 +1,87 @@
|
|||
<!--
|
||||
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.AddProfile"
|
||||
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:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="CommonResources.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<model:IconPathConverter x:Key="IconSourceConverter" />
|
||||
</ResourceDictionary>
|
||||
</Page.Resources>
|
||||
|
||||
<ScrollViewer>
|
||||
<StackPanel Style="{StaticResource SettingsStackStyle}">
|
||||
<Button x:Uid="AddProfile_AddNewButton"
|
||||
AutomationProperties.AutomationId="AddProfile_AddNewButton"
|
||||
AutomationProperties.Name="{Binding Tag, RelativeSource={RelativeSource Self}}"
|
||||
Click="AddNewClick"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon FontSize="{StaticResource StandardIconSize}"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="AddProfile_AddNewTextBlock"
|
||||
Style="{StaticResource IconButtonTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
<StackPanel Margin="{StaticResource StandardControlMargin}">
|
||||
<local:SettingContainer x:Uid="AddProfile_Duplicate">
|
||||
<muxc:RadioButtons x:Name="Profiles"
|
||||
ItemsSource="{x:Bind State.Settings.AllProfiles, Mode=OneWay}">
|
||||
<muxc:RadioButtons.ItemTemplate>
|
||||
<DataTemplate x:DataType="model:Profile">
|
||||
<Grid HorizontalAlignment="Stretch"
|
||||
ColumnSpacing="8">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<!-- icon -->
|
||||
<ColumnDefinition Width="16" />
|
||||
<!-- profile name -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<IconSourceElement Grid.Column="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
IconSource="{x:Bind Icon, Mode=OneWay, Converter={StaticResource IconSourceConverter}}" />
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{x:Bind Name}" />
|
||||
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</muxc:RadioButtons.ItemTemplate>
|
||||
</muxc:RadioButtons>
|
||||
</local:SettingContainer>
|
||||
<Button x:Uid="AddProfile_DuplicateButton"
|
||||
Margin="{StaticResource StandardControlMargin}"
|
||||
AutomationProperties.AutomationId="AddProfile_DuplicateButton"
|
||||
AutomationProperties.Name="{Binding Tag, RelativeSource={RelativeSource Self}}"
|
||||
Click="DuplicateClick"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<Button.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon FontSize="{StaticResource StandardIconSize}"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="AddProfile_DuplicateTextBlock"
|
||||
Style="{StaticResource IconButtonTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Page>
|
|
@ -11,6 +11,7 @@
|
|||
#include "Profiles.h"
|
||||
#include "GlobalAppearance.h"
|
||||
#include "ColorSchemes.h"
|
||||
#include "AddProfile.h"
|
||||
#include "..\types\inc\utils.hpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
@ -122,8 +123,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
// refresh the current page using the SelectedItem data we collected before the refresh
|
||||
if (selectedItemTag)
|
||||
{
|
||||
const auto& selectedItemStringTag{ selectedItemTag.try_as<hstring>() };
|
||||
const auto& selectedItemProfileTag{ selectedItemTag.try_as<ProfileViewModel>() };
|
||||
for (const auto& item : menuItems)
|
||||
{
|
||||
if (const auto& menuItem{ item.try_as<MUX::Controls::NavigationViewItem>() })
|
||||
|
@ -132,22 +131,28 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
{
|
||||
if (const auto& stringTag{ tag.try_as<hstring>() })
|
||||
{
|
||||
if (stringTag == selectedItemStringTag)
|
||||
if (const auto& selectedItemStringTag{ selectedItemTag.try_as<hstring>() })
|
||||
{
|
||||
// found the one that was selected before the refresh
|
||||
SettingsNav().SelectedItem(item);
|
||||
_Navigate(*stringTag);
|
||||
co_return;
|
||||
if (stringTag == selectedItemStringTag)
|
||||
{
|
||||
// found the one that was selected before the refresh
|
||||
SettingsNav().SelectedItem(item);
|
||||
_Navigate(*stringTag);
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (const auto& profileTag{ tag.try_as<ProfileViewModel>() })
|
||||
{
|
||||
if (profileTag->Guid() == selectedItemProfileTag->Guid())
|
||||
if (const auto& selectedItemProfileTag{ selectedItemTag.try_as<ProfileViewModel>() })
|
||||
{
|
||||
// found the one that was selected before the refresh
|
||||
SettingsNav().SelectedItem(item);
|
||||
_Navigate(*profileTag);
|
||||
co_return;
|
||||
if (profileTag->Guid() == selectedItemProfileTag->Guid())
|
||||
{
|
||||
// found the one that was selected before the refresh
|
||||
SettingsNav().SelectedItem(item);
|
||||
_Navigate(*profileTag);
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,6 +187,34 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a new profile and navigates to it in the Settings UI
|
||||
// Arguments:
|
||||
// - profileGuid: the guid of the profile we want to duplicate,
|
||||
// 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() };
|
||||
auto menuItems{ SettingsNav().MenuItems() };
|
||||
menuItems.IndexOf(selectedItem, insertIndex);
|
||||
if (profileGuid != winrt::guid{})
|
||||
{
|
||||
// if we were given a non-empty guid, we want to duplicate the corresponding profile
|
||||
const auto profile = _settingsClone.FindProfile(profileGuid);
|
||||
if (profile)
|
||||
{
|
||||
const auto duplicated = _settingsClone.DuplicateProfile(profile);
|
||||
_CreateAndNavigateToNewProfile(insertIndex, duplicated);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we were given an empty guid, create a new profile
|
||||
_CreateAndNavigateToNewProfile(insertIndex, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t MainPage::GetHostingWindow() const noexcept
|
||||
{
|
||||
return reinterpret_cast<uint64_t>(_hostingHwnd.value_or(nullptr));
|
||||
|
@ -227,18 +260,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
|
||||
if (const auto navString = clickedItemContainer.Tag().try_as<hstring>())
|
||||
{
|
||||
if (navString == addProfileTag)
|
||||
{
|
||||
// "AddProfile" needs to create a new profile before we can navigate to it
|
||||
uint32_t insertIndex;
|
||||
SettingsNav().MenuItems().IndexOf(clickedItemContainer, insertIndex);
|
||||
_CreateAndNavigateToNewProfile(insertIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, navigate to the page
|
||||
_Navigate(*navString);
|
||||
}
|
||||
_Navigate(*navString);
|
||||
}
|
||||
else if (const auto profile = clickedItemContainer.Tag().try_as<Editor::ProfileViewModel>())
|
||||
{
|
||||
|
@ -281,6 +303,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
{
|
||||
contentFrame().Navigate(xaml_typename<Editor::GlobalAppearance>(), winrt::make<GlobalAppearancePageNavigationState>(_settingsClone.GlobalSettings()));
|
||||
}
|
||||
else if (clickedItemTag == addProfileTag)
|
||||
{
|
||||
auto addProfileState{ winrt::make<AddProfilePageNavigationState>(_settingsClone) };
|
||||
addProfileState.AddNew({ get_weak(), &MainPage::_AddProfileHandler });
|
||||
contentFrame().Navigate(xaml_typename<Editor::AddProfile>(), addProfileState);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -348,7 +376,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
MUX::Controls::NavigationViewItem addProfileItem;
|
||||
addProfileItem.Content(box_value(RS_(L"Nav_AddNewProfile/Content")));
|
||||
addProfileItem.Tag(box_value(addProfileTag));
|
||||
addProfileItem.SelectsOnInvoked(false);
|
||||
|
||||
FontIcon icon;
|
||||
// This is the "Add" symbol
|
||||
|
@ -358,9 +385,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
SettingsNav().MenuItems().Append(addProfileItem);
|
||||
}
|
||||
|
||||
void MainPage::_CreateAndNavigateToNewProfile(const uint32_t index)
|
||||
void MainPage::_CreateAndNavigateToNewProfile(const uint32_t index, const Model::Profile& profile)
|
||||
{
|
||||
const auto newProfile{ _settingsClone.CreateNewProfile() };
|
||||
const auto newProfile{ profile ? profile : _settingsClone.CreateNewProfile() };
|
||||
const auto profileViewModel{ _viewModelForProfile(newProfile) };
|
||||
const auto navItem{ _CreateProfileNavViewItem(profileViewModel) };
|
||||
SettingsNav().MenuItems().InsertAt(index, navItem);
|
||||
|
|
|
@ -35,9 +35,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|||
std::optional<HWND> _hostingHwnd;
|
||||
|
||||
void _InitializeProfilesList();
|
||||
void _CreateAndNavigateToNewProfile(const uint32_t index);
|
||||
void _CreateAndNavigateToNewProfile(const uint32_t index, const Model::Profile& profile);
|
||||
winrt::Microsoft::UI::Xaml::Controls::NavigationViewItem _CreateProfileNavViewItem(const Editor::ProfileViewModel& profile);
|
||||
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);
|
||||
|
|
|
@ -38,6 +38,9 @@
|
|||
<ClInclude Include="Actions.h">
|
||||
<DependentUpon>Actions.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AddProfile.h">
|
||||
<DependentUpon>AddProfile.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ColorToBrushConverter.h">
|
||||
<DependentUpon>Converters.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
|
@ -105,6 +108,9 @@
|
|||
<Page Include="Actions.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="AddProfile.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="CommonResources.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
|
@ -138,6 +144,9 @@
|
|||
<ClCompile Include="Actions.cpp">
|
||||
<DependentUpon>Actions.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AddProfile.cpp">
|
||||
<DependentUpon>AddProfile.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ColorToBrushConverter.cpp">
|
||||
<DependentUpon>Converters.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
|
@ -206,6 +215,10 @@
|
|||
<DependentUpon>Actions.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="AddProfile.idl">
|
||||
<DependentUpon>AddProfile.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="Converters.idl" />
|
||||
<Midl Include="EnumEntry.idl" />
|
||||
<Midl Include="GlobalAppearance.idl">
|
||||
|
|
|
@ -117,6 +117,24 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AddProfile_AddNewButton.Tag" xml:space="preserve">
|
||||
<value>Create New Button</value>
|
||||
</data>
|
||||
<data name="AddProfile_AddNewTextBlock.Text" xml:space="preserve">
|
||||
<value>New empty profile</value>
|
||||
<comment>Button label that creates a new profile with default settings.</comment>
|
||||
</data>
|
||||
<data name="AddProfile_Duplicate.Header" xml:space="preserve">
|
||||
<value>Duplicate a profile</value>
|
||||
<comment>This is the header for a control that lets the user duplicate one of their existing profiles.</comment>
|
||||
</data>
|
||||
<data name="AddProfile_DuplicateButton.Tag" xml:space="preserve">
|
||||
<value>Duplicate Button</value>
|
||||
</data>
|
||||
<data name="AddProfile_DuplicateTextBlock.Text" xml:space="preserve">
|
||||
<value>Duplicate</value>
|
||||
<comment>Button label that duplicates the selected profile and navigates to the duplicate's page.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_Background.Text" xml:space="preserve">
|
||||
<value>Background</value>
|
||||
<comment>This is the header for a control that lets the user select the background color for text displayed on the screen.</comment>
|
||||
|
@ -790,8 +808,8 @@
|
|||
<comment>{Locked="⚠"} A disclaimer that appears when the unsaved changes to the settings are present.</comment>
|
||||
</data>
|
||||
<data name="Nav_AddNewProfile.Content" xml:space="preserve">
|
||||
<value>Add new</value>
|
||||
<comment>Header for the "add new" menu item. This creates a new profile and navigates to that page.</comment>
|
||||
<value>Add a new profile</value>
|
||||
<comment>Header for the "add new" menu item. This navigates to the page where users can add a new profile.</comment>
|
||||
</data>
|
||||
<data name="Nav_Profiles.Content" xml:space="preserve">
|
||||
<value>Profiles</value>
|
||||
|
|
|
@ -249,6 +249,137 @@ winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::CreateNew
|
|||
return *newProfile;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Duplicate a new profile based off another profile's settings
|
||||
// - This differs from Profile::Copy because it also copies over settings
|
||||
// that were not defined in the json (for example, settings that were
|
||||
// defined in one of the parents)
|
||||
// - This will not duplicate settings that were defined in profiles.defaults
|
||||
// however, because we do not want the json blob generated from the new profile
|
||||
// to contain those settings
|
||||
// Arguments:
|
||||
// - source: the Profile object we are duplicating (must not be null)
|
||||
// Return Value:
|
||||
// - a reference to the new profile
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile CascadiaSettings::DuplicateProfile(Model::Profile source)
|
||||
{
|
||||
THROW_HR_IF_NULL(E_INVALIDARG, source);
|
||||
|
||||
winrt::com_ptr<Profile> duplicated;
|
||||
if (_userDefaultProfileSettings)
|
||||
{
|
||||
duplicated = _userDefaultProfileSettings->CreateChild();
|
||||
}
|
||||
else
|
||||
{
|
||||
duplicated = winrt::make_self<Profile>();
|
||||
}
|
||||
_allProfiles.Append(*duplicated);
|
||||
|
||||
if (!source.Hidden())
|
||||
{
|
||||
_activeProfiles.Append(*duplicated);
|
||||
}
|
||||
|
||||
winrt::hstring newName{ fmt::format(L"{} ({})", source.Name(), RS_(L"CopySuffix")) };
|
||||
|
||||
// Check if this name already exists and if so, append a number
|
||||
for (uint32_t candidateIndex = 0; candidateIndex < _allProfiles.Size() + 1; ++candidateIndex)
|
||||
{
|
||||
if (std::none_of(begin(_allProfiles), end(_allProfiles), [&](auto&& profile) { return profile.Name() == newName; }))
|
||||
{
|
||||
break;
|
||||
}
|
||||
// There is a theoretical unsigned integer wraparound, which is OK
|
||||
newName = fmt::format(L"{} ({} {})", source.Name(), RS_(L"CopySuffix"), candidateIndex + 2);
|
||||
}
|
||||
duplicated->Name(winrt::hstring(newName));
|
||||
|
||||
#define DUPLICATE_SETTING_MACRO(settingName) \
|
||||
if (source.Has##settingName() || \
|
||||
(source.##settingName##OverrideSource() != nullptr && source.##settingName##OverrideSource().Origin() != OriginTag::ProfilesDefaults)) \
|
||||
{ \
|
||||
duplicated->##settingName(source.##settingName()); \
|
||||
}
|
||||
|
||||
#define DUPLICATE_APPEARANCE_SETTING_MACRO(settingName) \
|
||||
if (source.DefaultAppearance().Has##settingName() || \
|
||||
(source.DefaultAppearance().##settingName##OverrideSource() != nullptr && source.DefaultAppearance().##settingName##OverrideSource().SourceProfile().Origin() != OriginTag::ProfilesDefaults)) \
|
||||
{ \
|
||||
duplicated->DefaultAppearance().##settingName(source.DefaultAppearance().##settingName()); \
|
||||
}
|
||||
|
||||
DUPLICATE_SETTING_MACRO(Hidden);
|
||||
DUPLICATE_SETTING_MACRO(Icon);
|
||||
DUPLICATE_SETTING_MACRO(CloseOnExit);
|
||||
DUPLICATE_SETTING_MACRO(TabTitle);
|
||||
DUPLICATE_SETTING_MACRO(TabColor);
|
||||
DUPLICATE_SETTING_MACRO(SuppressApplicationTitle);
|
||||
DUPLICATE_SETTING_MACRO(UseAcrylic);
|
||||
DUPLICATE_SETTING_MACRO(AcrylicOpacity);
|
||||
DUPLICATE_SETTING_MACRO(ScrollState);
|
||||
DUPLICATE_SETTING_MACRO(FontFace);
|
||||
DUPLICATE_SETTING_MACRO(FontSize);
|
||||
DUPLICATE_SETTING_MACRO(FontWeight);
|
||||
DUPLICATE_SETTING_MACRO(Padding);
|
||||
DUPLICATE_SETTING_MACRO(Commandline);
|
||||
DUPLICATE_SETTING_MACRO(StartingDirectory);
|
||||
DUPLICATE_SETTING_MACRO(AntialiasingMode);
|
||||
DUPLICATE_SETTING_MACRO(ForceFullRepaintRendering);
|
||||
DUPLICATE_SETTING_MACRO(SoftwareRendering);
|
||||
DUPLICATE_SETTING_MACRO(HistorySize);
|
||||
DUPLICATE_SETTING_MACRO(SnapOnInput);
|
||||
DUPLICATE_SETTING_MACRO(AltGrAliasing);
|
||||
DUPLICATE_SETTING_MACRO(BellStyle);
|
||||
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(ColorSchemeName);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(Foreground);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(Background);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(SelectionBackground);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(CursorColor);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(PixelShaderPath);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(BackgroundImagePath);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(BackgroundImageOpacity);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(BackgroundImageStretchMode);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(BackgroundImageAlignment);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(RetroTerminalEffect);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(CursorShape);
|
||||
DUPLICATE_APPEARANCE_SETTING_MACRO(CursorHeight);
|
||||
|
||||
// UnfocusedAppearance is treated as a single setting,
|
||||
// but requires a little more legwork to duplicate properly
|
||||
if (source.HasUnfocusedAppearance() ||
|
||||
(source.UnfocusedAppearanceOverrideSource() != nullptr && source.UnfocusedAppearanceOverrideSource().Origin() != OriginTag::ProfilesDefaults))
|
||||
{
|
||||
// First, get a com_ptr to the source's unfocused appearance
|
||||
// We need this to be able to call CopyAppearance (it is alright to simply call CopyAppearance here
|
||||
// instead of needing a separate function like DuplicateAppearance since UnfocusedAppearance is treated
|
||||
// as a single setting)
|
||||
winrt::com_ptr<AppearanceConfig> sourceUnfocusedAppearanceImpl;
|
||||
sourceUnfocusedAppearanceImpl.copy_from(winrt::get_self<AppearanceConfig>(source.UnfocusedAppearance()));
|
||||
|
||||
// Get a weak ref to the duplicate profile so we can provide a source profile to the new UnfocusedAppearance
|
||||
// we are about to create
|
||||
const auto weakRefToDuplicated = weak_ref<Model::Profile>(*duplicated);
|
||||
auto duplicatedUnfocusedAppearanceImpl = AppearanceConfig::CopyAppearance(sourceUnfocusedAppearanceImpl, weakRefToDuplicated);
|
||||
|
||||
// Make sure to add the default appearance of the duplicated profile as a parent to the duplicate's UnfocusedAppearance
|
||||
winrt::com_ptr<AppearanceConfig> duplicatedDefaultAppearanceImpl;
|
||||
duplicatedDefaultAppearanceImpl.copy_from(winrt::get_self<AppearanceConfig>(duplicated->DefaultAppearance()));
|
||||
duplicatedUnfocusedAppearanceImpl->InsertParent(duplicatedDefaultAppearanceImpl);
|
||||
|
||||
// Finally, set the duplicate's UnfocusedAppearance
|
||||
duplicated->UnfocusedAppearance(*duplicatedUnfocusedAppearanceImpl);
|
||||
}
|
||||
|
||||
if (source.HasConnectionType())
|
||||
{
|
||||
duplicated->ConnectionType(source.ConnectionType());
|
||||
}
|
||||
|
||||
return *duplicated;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Gets our list of warnings we found during loading. These are things that we
|
||||
// knew were bad when we called `_ValidateSettings` last.
|
||||
|
|
|
@ -98,6 +98,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
winrt::guid GetProfileForArgs(const Model::NewTerminalArgs& newTerminalArgs) const;
|
||||
|
||||
Model::Profile DuplicateProfile(Model::Profile source);
|
||||
void RefreshDefaultTerminals();
|
||||
|
||||
static bool IsDefaultTerminalAvailable() noexcept;
|
||||
|
|
|
@ -31,6 +31,8 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
Windows.Foundation.Collections.IObservableVector<Profile> AllProfiles { get; };
|
||||
Windows.Foundation.Collections.IObservableVector<Profile> ActiveProfiles { get; };
|
||||
|
||||
Profile DuplicateProfile(Profile sourceProfile);
|
||||
|
||||
KeyMapping KeyMap { get; };
|
||||
|
||||
Windows.Foundation.Collections.IVectorView<SettingsLoadWarnings> Warnings { get; };
|
||||
|
|
|
@ -1033,6 +1033,7 @@ void CascadiaSettings::_ApplyDefaultsFromUserSettings()
|
|||
|
||||
_userDefaultProfileSettings = winrt::make_self<Profile>();
|
||||
_userDefaultProfileSettings->LayerJson(defaultSettings);
|
||||
_userDefaultProfileSettings->Origin(OriginTag::ProfilesDefaults);
|
||||
|
||||
const auto numOfProfiles{ _allProfiles.Size() };
|
||||
for (uint32_t profileIndex = 0; profileIndex < numOfProfiles; ++profileIndex)
|
||||
|
|
|
@ -16,7 +16,8 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
Custom = 0,
|
||||
InBox,
|
||||
Generated,
|
||||
Fragment
|
||||
Fragment,
|
||||
ProfilesDefaults
|
||||
};
|
||||
|
||||
enum CloseOnExitMode
|
||||
|
|
|
@ -154,6 +154,10 @@
|
|||
<value>Close tabs after index {0}</value>
|
||||
<comment>{0} will be replaced with a number</comment>
|
||||
</data>
|
||||
<data name="CopySuffix" xml:space="preserve">
|
||||
<value>Copy</value>
|
||||
<comment>The suffix we add to the name of a duplicated profile.</comment>
|
||||
</data>
|
||||
<data name="MoveTabCommandKey" xml:space="preserve">
|
||||
<value>Move tab {0}</value>
|
||||
<comment>{0} will be replaced with a "forward" / "backward"</comment>
|
||||
|
|
Loading…
Reference in a new issue