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:
PankajBhojwani 2021-05-04 21:15:25 -07:00 committed by GitHub
parent cb55cec275
commit b53bd672d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 462 additions and 31 deletions

View file

@ -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);
}
}

View 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());
}
}
}

View 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);
}

View 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; };
}
}

View 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="&#xE710;" />
<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="&#xE8C8;" />
<TextBlock x:Uid="AddProfile_DuplicateTextBlock"
Style="{StaticResource IconButtonTextBlockStyle}" />
</StackPanel>
</Button.Content>
</Button>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Page>

View file

@ -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);

View file

@ -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);

View file

@ -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">

View file

@ -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>

View file

@ -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.

View file

@ -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;

View file

@ -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; };

View file

@ -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)

View file

@ -16,7 +16,8 @@ namespace Microsoft.Terminal.Settings.Model
Custom = 0,
InBox,
Generated,
Fragment
Fragment,
ProfilesDefaults
};
enum CloseOnExitMode

View file

@ -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>