Introduces the following UI controls to the ColorSchemes page: - "Add new" button - next to dropdown selector - adds a new color scheme named ("Color Scheme #" where # is the number of color schemes you have) - "Rename" Button - next to the selector - replaces the ComboBox with a TextBox and the accept/cancel buttons appear - "Delete" button - bottom of the page - opens flyout, when confirmed, deletes the current color scheme and selects another one This also adds a Delete button to the Profiles page. The Hide checkbox was moved above the Delete button. ## References #1564 - Settings UI #6800 - Settings UI Completion Epic ## Detailed Description of the Pull Request / Additional comments **Color Schemes:** - Deleting a color scheme selects another one from the list available - Rename replaces the combobox with a textbox to allow editing - The Add New button creates a new color scheme named "Color Scheme X" where X is the number of schemes defined - In-box color schemes cannot be deleted **Profile:** - Deleting a profile selects another one from the list available - the rename button does not exist (yet), because it needs a modification to the NavigationView's Header Template - The delete button is disabled for in-box profiles (CMD and Windows Powershell) and dynamic profiles ## Validation Steps Performed **Color Schemes - Add New** ✅ Creates a new color scheme named "Color Scheme X" (X being the number of color schemes) ✅ The new color scheme can be renamed/deleted/modified **Color Schemes - Rename** ✅ You cannot rename an in-box color scheme ✅ The rename button has a tooltip ✅ Clicking the rename button replaces the combobox with a textbox ✅ Accept --> changes name ✅ Cancel --> does not change the name ✅ accepting/cancelling the rename operation updates the combo box appropriately **Color Schemes - Delete** ✅ Clicking delete produces a flyout to confirm deletion ✅ Deleting a color scheme removes it from the list and select the one under it ✅ Deleting the last color scheme selects the last available color scheme after it's deleted ✅ In-box color schemes have the delete button disabled, and a disclaimer appears next to it **Profile- Delete** ✅ Base layer presents a disclaimer at the top, and hides the delete button ✅ Dynamic and in-box profiles disable the delete button and show the appropriate disclaimer next to the disabled button ✅ Clicking delete produces a flyout to confirm deletion ✅ Regular profiles have a delete button that is styled appropriately ✅ Clicking the delete profile button opens a content dialog. Confirmation deletes the profile and navigates to the profile indexed under it (deleting the last one redirects to the last one) ## Demo Refer to this post [here](https://github.com/microsoft/terminal/pull/8403#issuecomment-747545651. Confirmation flyout demo: https://github.com/microsoft/terminal/pull/8403#issuecomment-747657842
265 lines
9.8 KiB
C++
265 lines
9.8 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
#include "ColorSchemes.h"
|
|
#include "ColorTableEntry.g.cpp"
|
|
#include "ColorSchemes.g.cpp"
|
|
#include "ColorSchemesPageNavigationState.g.cpp"
|
|
|
|
#include <LibraryResources.h>
|
|
|
|
using namespace winrt;
|
|
using namespace winrt::Windows::UI;
|
|
using namespace winrt::Windows::UI::Xaml;
|
|
using namespace winrt::Windows::UI::Xaml::Navigation;
|
|
using namespace winrt::Windows::UI::Xaml::Controls;
|
|
using namespace winrt::Windows::UI::Xaml::Media;
|
|
using namespace winrt::Windows::Foundation;
|
|
using namespace winrt::Windows::Foundation::Collections;
|
|
|
|
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
|
{
|
|
static const std::array<hstring, 16> TableColorNames = {
|
|
RS_(L"ColorScheme_Black/Header"),
|
|
RS_(L"ColorScheme_Red/Header"),
|
|
RS_(L"ColorScheme_Green/Header"),
|
|
RS_(L"ColorScheme_Yellow/Header"),
|
|
RS_(L"ColorScheme_Blue/Header"),
|
|
RS_(L"ColorScheme_Purple/Header"),
|
|
RS_(L"ColorScheme_Cyan/Header"),
|
|
RS_(L"ColorScheme_White/Header"),
|
|
RS_(L"ColorScheme_BrightBlack/Header"),
|
|
RS_(L"ColorScheme_BrightRed/Header"),
|
|
RS_(L"ColorScheme_BrightGreen/Header"),
|
|
RS_(L"ColorScheme_BrightYellow/Header"),
|
|
RS_(L"ColorScheme_BrightBlue/Header"),
|
|
RS_(L"ColorScheme_BrightPurple/Header"),
|
|
RS_(L"ColorScheme_BrightCyan/Header"),
|
|
RS_(L"ColorScheme_BrightWhite/Header")
|
|
};
|
|
|
|
static const std::array<std::wstring, 9> InBoxSchemes = {
|
|
L"Campbell",
|
|
L"Campbell Powershell",
|
|
L"Vintage",
|
|
L"One Half Dark",
|
|
L"One Half Light",
|
|
L"Solarized Dark",
|
|
L"Solarized Light",
|
|
L"Tango Dark",
|
|
L"Tango Light"
|
|
};
|
|
|
|
ColorSchemes::ColorSchemes() :
|
|
_ColorSchemeList{ single_threaded_observable_vector<Model::ColorScheme>() },
|
|
_CurrentColorTable{ single_threaded_observable_vector<Editor::ColorTableEntry>() }
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
void ColorSchemes::OnNavigatedTo(const NavigationEventArgs& e)
|
|
{
|
|
_State = e.Parameter().as<Editor::ColorSchemesPageNavigationState>();
|
|
_UpdateColorSchemeList();
|
|
|
|
// Initialize our color table view model with 16 dummy colors
|
|
// so that on a ColorScheme selection change, we can loop through
|
|
// each ColorTableEntry and just change its color. Performing a
|
|
// clear and 16 appends doesn't seem to update the color pickers
|
|
// very accurately.
|
|
for (uint8_t i = 0; i < TableColorNames.size(); ++i)
|
|
{
|
|
auto entry = winrt::make<ColorTableEntry>(i, Windows::UI::Color{ 0, 0, 0, 0 });
|
|
_CurrentColorTable.Append(entry);
|
|
}
|
|
}
|
|
|
|
// Function Description:
|
|
// - Called when a different color scheme is selected. Updates our current
|
|
// color scheme and updates our currently modifiable color table.
|
|
// Arguments:
|
|
// - args: The selection changed args that tells us what's the new color scheme selected.
|
|
// Return Value:
|
|
// - <none>
|
|
void ColorSchemes::ColorSchemeSelectionChanged(IInspectable const& /*sender*/,
|
|
SelectionChangedEventArgs const& args)
|
|
{
|
|
// Update the color scheme this page is modifying
|
|
const auto colorScheme{ args.AddedItems().GetAt(0).try_as<Model::ColorScheme>() };
|
|
CurrentColorScheme(colorScheme);
|
|
_UpdateColorTable(colorScheme);
|
|
|
|
// Set the text disclaimer for the text box
|
|
hstring disclaimer{};
|
|
const std::wstring schemeName{ colorScheme.Name() };
|
|
if (std::find(std::begin(InBoxSchemes), std::end(InBoxSchemes), schemeName) != std::end(InBoxSchemes))
|
|
{
|
|
// load disclaimer for in-box profiles
|
|
disclaimer = RS_(L"ColorScheme_DeleteButtonDisclaimerInBox");
|
|
}
|
|
DeleteButtonDisclaimer().Text(disclaimer);
|
|
|
|
// Update the state of the page
|
|
_PropertyChangedHandlers(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"CanDeleteCurrentScheme" });
|
|
IsRenaming(false);
|
|
}
|
|
|
|
// Function Description:
|
|
// - Updates the list of all color schemes available to choose from.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void ColorSchemes::_UpdateColorSchemeList()
|
|
{
|
|
// Surprisingly, though this is called every time we navigate to the page,
|
|
// the list does not keep growing on each navigation.
|
|
const auto& colorSchemeMap{ _State.Globals().ColorSchemes() };
|
|
for (const auto& pair : colorSchemeMap)
|
|
{
|
|
_ColorSchemeList.Append(pair.Value());
|
|
}
|
|
}
|
|
|
|
// Function Description:
|
|
// - Called when a ColorPicker control has selected a new color. This is specifically
|
|
// called by color pickers assigned to a color table entry. It takes the index
|
|
// that's been stuffed in the Tag property of the color picker and uses it
|
|
// to update the color table accordingly.
|
|
// Arguments:
|
|
// - sender: the color picker that raised this event.
|
|
// - args: the args that contains the new color that was picked.
|
|
// Return Value:
|
|
// - <none>
|
|
void ColorSchemes::ColorPickerChanged(IInspectable const& sender,
|
|
ColorChangedEventArgs const& args)
|
|
{
|
|
if (auto picker = sender.try_as<ColorPicker>())
|
|
{
|
|
if (auto tag = picker.Tag())
|
|
{
|
|
auto index = winrt::unbox_value<uint8_t>(tag);
|
|
CurrentColorScheme().SetColorTableEntry(index, args.NewColor());
|
|
_CurrentColorTable.GetAt(index).Color(args.NewColor());
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ColorSchemes::CanDeleteCurrentScheme() const
|
|
{
|
|
if (const auto scheme{ CurrentColorScheme() })
|
|
{
|
|
// Only allow this color scheme to be deleted if it's not provided in-box
|
|
const std::wstring myName{ scheme.Name() };
|
|
return std::find(std::begin(InBoxSchemes), std::end(InBoxSchemes), myName) == std::end(InBoxSchemes);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ColorSchemes::DeleteConfirmation_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
|
{
|
|
const auto schemeName{ CurrentColorScheme().Name() };
|
|
_State.Globals().RemoveColorScheme(schemeName);
|
|
|
|
const auto removedSchemeIndex{ ColorSchemeComboBox().SelectedIndex() };
|
|
if (static_cast<uint32_t>(removedSchemeIndex) < _ColorSchemeList.Size() - 1)
|
|
{
|
|
// select same index
|
|
ColorSchemeComboBox().SelectedIndex(removedSchemeIndex + 1);
|
|
}
|
|
else
|
|
{
|
|
// select last color scheme (avoid out of bounds error)
|
|
ColorSchemeComboBox().SelectedIndex(removedSchemeIndex - 1);
|
|
}
|
|
_ColorSchemeList.RemoveAt(removedSchemeIndex);
|
|
DeleteButton().Flyout().Hide();
|
|
}
|
|
|
|
void ColorSchemes::AddNew_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
|
{
|
|
// Give the new scheme a distinct name
|
|
const hstring schemeName{ fmt::format(L"Color Scheme {}", _State.Globals().ColorSchemes().Size() + 1) };
|
|
Model::ColorScheme scheme{ schemeName };
|
|
|
|
// Add the new color scheme
|
|
_State.Globals().AddColorScheme(scheme);
|
|
|
|
// Update current page
|
|
_ColorSchemeList.Append(scheme);
|
|
ColorSchemeComboBox().SelectedItem(scheme);
|
|
}
|
|
|
|
// Function Description:
|
|
// - Pre-populates/focuses the name TextBox, updates the UI
|
|
// Arguments:
|
|
// - <unused>
|
|
// Return Value:
|
|
// - <none>
|
|
void ColorSchemes::Rename_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
|
{
|
|
NameBox().Text(CurrentColorScheme().Name());
|
|
IsRenaming(true);
|
|
NameBox().Focus(FocusState::Programmatic);
|
|
NameBox().SelectAll();
|
|
}
|
|
|
|
void ColorSchemes::RenameAccept_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
|
{
|
|
_RenameCurrentScheme(NameBox().Text());
|
|
}
|
|
|
|
void ColorSchemes::RenameCancel_Click(IInspectable const& /*sender*/, RoutedEventArgs const& /*e*/)
|
|
{
|
|
IsRenaming(false);
|
|
}
|
|
|
|
void ColorSchemes::NameBox_PreviewKeyDown(IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e)
|
|
{
|
|
if (e.OriginalKey() == winrt::Windows::System::VirtualKey::Enter)
|
|
{
|
|
_RenameCurrentScheme(NameBox().Text());
|
|
e.Handled(true);
|
|
}
|
|
else if (e.OriginalKey() == winrt::Windows::System::VirtualKey::Escape)
|
|
{
|
|
IsRenaming(false);
|
|
e.Handled(true);
|
|
}
|
|
}
|
|
|
|
void ColorSchemes::_RenameCurrentScheme(hstring newName)
|
|
{
|
|
CurrentColorScheme().Name(newName);
|
|
IsRenaming(false);
|
|
|
|
// The color scheme is renamed appropriately, but the ComboBox still shows the old name (until you open it)
|
|
// We need to manually force the ComboBox to refresh itself.
|
|
const auto selectedIndex{ ColorSchemeComboBox().SelectedIndex() };
|
|
ColorSchemeComboBox().SelectedIndex((selectedIndex + 1) % ColorSchemeList().Size());
|
|
ColorSchemeComboBox().SelectedIndex(selectedIndex);
|
|
}
|
|
|
|
// Function Description:
|
|
// - Updates the currently modifiable color table based on the given current color scheme.
|
|
// Arguments:
|
|
// - colorScheme: the color scheme to retrieve the color table from
|
|
// Return Value:
|
|
// - <none>
|
|
void ColorSchemes::_UpdateColorTable(const Model::ColorScheme& colorScheme)
|
|
{
|
|
for (uint8_t i = 0; i < TableColorNames.size(); ++i)
|
|
{
|
|
_CurrentColorTable.GetAt(i).Color(colorScheme.Table()[i]);
|
|
}
|
|
}
|
|
|
|
ColorTableEntry::ColorTableEntry(uint8_t index, Windows::UI::Color color)
|
|
{
|
|
Name(TableColorNames[index]);
|
|
Index(winrt::box_value<uint8_t>(index));
|
|
Color(color);
|
|
}
|
|
}
|