From 9d771f5aa6340b1ce0032cd2723e9442e76d40c7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 3 Aug 2020 10:46:00 -0500 Subject: [PATCH 1/7] This works to initialize the tabs with the current color, but doesn't hot-reload the color --- .../TerminalApp/AppActionHandlers.cpp | 4 +- src/cascadia/TerminalApp/Profile.cpp | 10 + src/cascadia/TerminalApp/Profile.h | 1 + src/cascadia/TerminalApp/Tab.cpp | 202 +++++++++++------- src/cascadia/TerminalApp/Tab.h | 11 +- src/cascadia/TerminalApp/TerminalPage.cpp | 20 +- src/cascadia/TerminalControl/TermControl.cpp | 6 + src/cascadia/TerminalControl/TermControl.h | 2 + src/cascadia/TerminalControl/TermControl.idl | 2 + src/cascadia/TerminalCore/Terminal.cpp | 14 ++ src/cascadia/TerminalCore/Terminal.hpp | 3 + src/cascadia/TerminalCore/pch.h | 1 + .../TerminalSettings/ICoreSettings.idl | 2 + .../TerminalSettings/terminalsettings.h | 2 + src/types/inc/utils.hpp | 24 +++ 15 files changed, 215 insertions(+), 89 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 63a2743d80b..edc9c642000 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -290,11 +290,11 @@ namespace winrt::TerminalApp::implementation { if (tabColor.has_value()) { - activeTab->SetTabColor(tabColor.value()); + activeTab->SetRuntimeTabColor(tabColor.value()); } else { - activeTab->ResetTabColor(); + activeTab->ResetRuntimeTabColor(); } } args.Handled(true); diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index 7cd80a65b11..7d85745bb19 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -52,6 +52,7 @@ static constexpr std::string_view BackgroundImageStretchModeKey{ "backgroundImag static constexpr std::string_view BackgroundImageAlignmentKey{ "backgroundImageAlignment" }; static constexpr std::string_view RetroTerminalEffectKey{ "experimental.retroTerminalEffect" }; static constexpr std::string_view AntialiasingModeKey{ "antialiasingMode" }; +static constexpr std::string_view TabColorKey{ "tabColor" }; Profile::Profile() : Profile(std::nullopt) @@ -231,6 +232,13 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map cr{ c }; + terminalSettings.TabColor(cr); + } + return terminalSettings; } @@ -404,6 +412,8 @@ void Profile::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, BackgroundImageAlignmentKey, _backgroundImageAlignment); JsonUtils::GetValueForKey(json, RetroTerminalEffectKey, _retroTerminalEffect); JsonUtils::GetValueForKey(json, AntialiasingModeKey, _antialiasingMode); + + JsonUtils::GetValueForKey(json, TabColorKey, _tabColor); } void Profile::SetFontFace(std::wstring fontFace) noexcept diff --git a/src/cascadia/TerminalApp/Profile.h b/src/cascadia/TerminalApp/Profile.h index 17edad98bb6..c1d11cc068c 100644 --- a/src/cascadia/TerminalApp/Profile.h +++ b/src/cascadia/TerminalApp/Profile.h @@ -117,6 +117,7 @@ class TerminalApp::Profile final std::optional _selectionBackground; std::optional _cursorColor; std::optional _tabTitle; + std::optional _tabColor; bool _suppressApplicationTitle; int32_t _historySize; bool _snapOnInput; diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index 51e54be09ee..bd559f02079 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -8,6 +8,7 @@ #include "Tab.g.cpp" #include "Utils.h" #include "ColorHelper.h" +#include "../../types/inc/utils.hpp" using namespace winrt; using namespace winrt::Windows::UI::Xaml; @@ -15,6 +16,7 @@ using namespace winrt::Windows::UI::Core; using namespace winrt::Microsoft::Terminal::Settings; using namespace winrt::Microsoft::Terminal::TerminalControl; using namespace winrt::Windows::System; +using namespace ::Microsoft::Console; namespace winrt { @@ -55,6 +57,7 @@ namespace winrt::TerminalApp::implementation }); _UpdateTitle(); + _RecalculateAndApplyTabColor(); } // Method Description: @@ -480,6 +483,7 @@ namespace winrt::TerminalApp::implementation if (tab && sender != tab->_activePane) { tab->_UpdateActivePane(sender); + tab->_RecalculateAndApplyTabColor(); } }); } @@ -530,14 +534,14 @@ namespace winrt::TerminalApp::implementation _tabColorPickup.ColorSelected([weakThis](auto newTabColor) { if (auto tab{ weakThis.get() }) { - tab->SetTabColor(newTabColor); + tab->SetRuntimeTabColor(newTabColor); } }); _tabColorPickup.ColorCleared([weakThis]() { if (auto tab{ weakThis.get() }) { - tab->ResetTabColor(); + tab->ResetRuntimeTabColor(); } }); @@ -707,114 +711,164 @@ namespace winrt::TerminalApp::implementation // - The tab's color, if any std::optional Tab::GetTabColor() { - return _tabColor; + const auto currControlColor{ GetActiveTerminalControl().TabColor() }; + std::optional controlTabColor; + if (currControlColor != nullptr) + { + controlTabColor = currControlColor.Value(); + } + return Utils::CoalesceOptionalsOrNot(_runtimeTabColor, + controlTabColor, + _themeTabColor, + std::optional(std::nullopt)); } // Method Description: - // - Sets the tab background color to the color chosen by the user + // - Sets the runtime tab background color to the color chosen by the user // - Sets the tab foreground color depending on the luminance of // the background color // Arguments: - // - color: the shiny color the user picked for their tab + // - color: the color the user picked for their tab // Return Value: // - - void Tab::SetTabColor(const winrt::Windows::UI::Color& color) + void Tab::SetRuntimeTabColor(const winrt::Windows::UI::Color& color) + { + _runtimeTabColor.emplace(color); + _RecalculateAndApplyTabColor(); + } + + // Method Description: + // - This function dispatches a function to the UI thread to recalculate + // what this tab's current background color should be. If a color is set, + // it will apply the given color to the tab's background. Otherwise, it + // will clear the tab's background color. + // Arguments: + // - + // Return Value: + // - + void Tab::_RecalculateAndApplyTabColor() { auto weakThis{ get_weak() }; - _tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis, color]() { + _tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() { auto ptrTab = weakThis.get(); if (!ptrTab) return; auto tab{ ptrTab }; - Media::SolidColorBrush selectedTabBrush{}; - Media::SolidColorBrush deselectedTabBrush{}; - Media::SolidColorBrush fontBrush{}; - Media::SolidColorBrush hoverTabBrush{}; - // calculate the luminance of the current color and select a font - // color based on that - // see https://www.w3.org/TR/WCAG20/#relativeluminancedef - if (TerminalApp::ColorHelper::IsBrightColor(color)) + + std::optional currentColor = tab->GetTabColor(); + if (currentColor.has_value()) { - fontBrush.Color(winrt::Windows::UI::Colors::Black()); + tab->_ApplyTabColor(currentColor.value()); } else { - fontBrush.Color(winrt::Windows::UI::Colors::White()); + tab->_ClearTabBackgroundColor(); } - - hoverTabBrush.Color(TerminalApp::ColorHelper::GetAccentColor(color)); - selectedTabBrush.Color(color); - - // currently if a tab has a custom color, a deselected state is - // signified by using the same color with a bit ot transparency - auto deselectedTabColor = color; - deselectedTabColor.A = 64; - deselectedTabBrush.Color(deselectedTabColor); - - // currently if a tab has a custom color, a deselected state is - // signified by using the same color with a bit ot transparency - tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush); - tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush); - tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush); - tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush); - tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush); - tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush); - tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush); - tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush); - tab->_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush); - - tab->_RefreshVisualState(); - - tab->_tabColor.emplace(color); - tab->_colorSelected(color); }); } // Method Description: - // Clear the custom color of the tab, if any + // - Applies the given color to the background of this tab's TabViewItem. + // - Sets the tab foreground color depending on the luminance of // the background color + // - This method should only be called on the UI thread. + // Arguments: + // - color: the color the user picked for their tab + // Return Value: + // - + void Tab::_ApplyTabColor(const winrt::Windows::UI::Color& color) + { + Media::SolidColorBrush selectedTabBrush{}; + Media::SolidColorBrush deselectedTabBrush{}; + Media::SolidColorBrush fontBrush{}; + Media::SolidColorBrush hoverTabBrush{}; + // calculate the luminance of the current color and select a font + // color based on that + // see https://www.w3.org/TR/WCAG20/#relativeluminancedef + if (TerminalApp::ColorHelper::IsBrightColor(color)) + { + fontBrush.Color(winrt::Windows::UI::Colors::Black()); + } + else + { + fontBrush.Color(winrt::Windows::UI::Colors::White()); + } + + hoverTabBrush.Color(TerminalApp::ColorHelper::GetAccentColor(color)); + selectedTabBrush.Color(color); + + // currently if a tab has a custom color, a deselected state is + // signified by using the same color with a bit ot transparency + auto deselectedTabColor = color; + deselectedTabColor.A = 64; + deselectedTabBrush.Color(deselectedTabColor); + + // currently if a tab has a custom color, a deselected state is + // signified by using the same color with a bit ot transparency + _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush); + _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush); + _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush); + _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush); + _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush); + _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush); + _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush); + _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush); + _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush); + + _RefreshVisualState(); + + _colorSelected(color); + } + + // Method Description: + // - Clear the custom runtime color of the tab, if any color is set. This + // will re-apply whatever the tab's base color should be (either the color + // from the control, the theme, or the default tab color.) // Arguments: // - // Return Value: // - - void Tab::ResetTabColor() + void Tab::ResetRuntimeTabColor() { - auto weakThis{ get_weak() }; + _runtimeTabColor.reset(); + _RecalculateAndApplyTabColor(); + } - _tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() { - auto ptrTab = weakThis.get(); - if (!ptrTab) - return; + // Method Description: + // - Clear out any color we've set for the TabViewItem. + // - This method should only be called on the UI thread. + // Arguments: + // - + // Return Value: + // - + void Tab::_ClearTabBackgroundColor() + { + winrt::hstring keys[] = { + L"TabViewItemHeaderBackground", + L"TabViewItemHeaderBackgroundSelected", + L"TabViewItemHeaderBackgroundPointerOver", + L"TabViewItemHeaderForeground", + L"TabViewItemHeaderForegroundSelected", + L"TabViewItemHeaderForegroundPointerOver", + L"TabViewItemHeaderBackgroundPressed", + L"TabViewItemHeaderForegroundPressed", + L"TabViewButtonForegroundActiveTab" + }; - auto tab{ ptrTab }; - winrt::hstring keys[] = { - L"TabViewItemHeaderBackground", - L"TabViewItemHeaderBackgroundSelected", - L"TabViewItemHeaderBackgroundPointerOver", - L"TabViewItemHeaderForeground", - L"TabViewItemHeaderForegroundSelected", - L"TabViewItemHeaderForegroundPointerOver", - L"TabViewItemHeaderBackgroundPressed", - L"TabViewItemHeaderForegroundPressed", - L"TabViewButtonForegroundActiveTab" - }; - - // simply clear any of the colors in the tab's dict - for (auto keyString : keys) + // simply clear any of the colors in the tab's dict + for (auto keyString : keys) + { + auto key = winrt::box_value(keyString); + if (_tabViewItem.Resources().HasKey(key)) { - auto key = winrt::box_value(keyString); - if (tab->_tabViewItem.Resources().HasKey(key)) - { - tab->_tabViewItem.Resources().Remove(key); - } + _tabViewItem.Resources().Remove(key); } + } - tab->_RefreshVisualState(); - tab->_tabColor.reset(); - tab->_colorCleared(); - }); + _RefreshVisualState(); + _colorCleared(); } // Method Description: diff --git a/src/cascadia/TerminalApp/Tab.h b/src/cascadia/TerminalApp/Tab.h index a6752fe6bbe..3269d9bba6b 100644 --- a/src/cascadia/TerminalApp/Tab.h +++ b/src/cascadia/TerminalApp/Tab.h @@ -57,8 +57,8 @@ namespace winrt::TerminalApp::implementation std::optional GetTabColor(); - void SetTabColor(const winrt::Windows::UI::Color& color); - void ResetTabColor(); + void SetRuntimeTabColor(const winrt::Windows::UI::Color& color); + void ResetRuntimeTabColor(); void ActivateColorPicker(); WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler); @@ -75,7 +75,8 @@ namespace winrt::TerminalApp::implementation std::shared_ptr _activePane{ nullptr }; winrt::hstring _lastIconPath{}; winrt::TerminalApp::ColorPickupFlyout _tabColorPickup{}; - std::optional _tabColor{}; + std::optional _themeTabColor{}; + std::optional _runtimeTabColor{}; bool _focused{ false }; winrt::Microsoft::UI::Xaml::Controls::TabViewItem _tabViewItem{ nullptr }; @@ -102,6 +103,10 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget _UpdateTitle(); void _ConstructTabRenameBox(const winrt::hstring& tabText); + void _RecalculateAndApplyTabColor(); + void _ApplyTabColor(const winrt::Windows::UI::Color& color); + void _ClearTabBackgroundColor(); + friend class ::TerminalAppLocalTests::TabTests; }; } diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index fd613d8a82e..dbeec9885a6 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1818,16 +1818,16 @@ namespace winrt::TerminalApp::implementation // Raise an event that our title changed _titleChangeHandlers(*this, tab->GetActiveTitle()); - // Raise an event that our titlebar color changed - std::optional color = tab->GetTabColor(); - if (color.has_value()) - { - _SetNonClientAreaColors(color.value()); - } - else - { - _ClearNonClientAreaColors(); - } + // // Raise an event that our titlebar color changed + // std::optional color = tab->GetTabColor(); + // if (color.has_value()) + // { + // _SetNonClientAreaColors(color.value()); + // } + // else + // { + // _ClearNonClientAreaColors(); + // } } CATCH_LOG(); } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 784275fa324..31b2c5874d0 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2827,6 +2827,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _renderer->ResetErrorStateAndResume(); } + Windows::Foundation::IReference TermControl::TabColor() noexcept + { + auto coreColor = _terminal->GetTabColor(); + return coreColor.has_value() ? Windows::Foundation::IReference(coreColor.value()) : nullptr; + } + // -------------------------------- WinRT Events --------------------------------- // Winrt events need a method for adding a callback to the event and removing the callback. // These macros will define them both for you. diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 783ca85d967..b1244b8ed2f 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -109,6 +109,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const winrt::hstring& padding, const uint32_t dpi); + Windows::Foundation::IReference TabColor() noexcept; + // clang-format off // -------------------------------- WinRT Events --------------------------------- DECLARE_EVENT(TitleChanged, _titleChangedHandlers, TerminalControl::TitleChangedEventArgs); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index d5454e50578..5cebfac26e1 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -70,5 +70,7 @@ namespace Microsoft.Terminal.TerminalControl void ResetFontSize(); void ToggleRetroEffect(); + + Windows.Foundation.IReference TabColor { get; }; } } diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index e7719507099..9355609999a 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -147,6 +147,15 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting _terminalInput->ForceDisableWin32InputMode(settings.ForceVTInput()); + if (settings.TabColor() == nullptr) + { + _tabColor = std::nullopt; + } + else + { + _tabColor = til::color(settings.TabColor().Value() | 0xff000000); + } + // TODO:MSFT:21327402 - if HistorySize has changed, resize the buffer so we // have a smaller scrollback. We should do this carefully - if the new buffer // size is smaller than where the mutable viewport currently is, we'll want @@ -956,3 +965,8 @@ bool Terminal::IsCursorBlinkingAllowed() const noexcept const auto& cursor = _buffer->GetCursor(); return cursor.IsBlinkingAllowed(); } + +const std::optional Terminal::GetTabColor() const noexcept +{ + return _tabColor; +} diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index ac8d59d3e02..ade99237496 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -176,6 +176,8 @@ class Microsoft::Terminal::Core::Terminal final : void SetCursorOn(const bool isOn); bool IsCursorBlinkingAllowed() const noexcept; + const std::optional GetTabColor() const noexcept; + #pragma region TextSelection // These methods are defined in TerminalSelection.cpp enum class SelectionExpansionMode @@ -205,6 +207,7 @@ class Microsoft::Terminal::Core::Terminal final : std::optional _title; std::wstring _startingTitle; + std::optional _tabColor; std::array _colorTable; COLORREF _defaultFg; diff --git a/src/cascadia/TerminalCore/pch.h b/src/cascadia/TerminalCore/pch.h index e03399daf91..58fe68df1da 100644 --- a/src/cascadia/TerminalCore/pch.h +++ b/src/cascadia/TerminalCore/pch.h @@ -4,3 +4,4 @@ #pragma once #include +#include "winrt/Windows.Foundation.h" diff --git a/src/cascadia/TerminalSettings/ICoreSettings.idl b/src/cascadia/TerminalSettings/ICoreSettings.idl index 3bc4834592d..8e3360ae9c8 100644 --- a/src/cascadia/TerminalSettings/ICoreSettings.idl +++ b/src/cascadia/TerminalSettings/ICoreSettings.idl @@ -34,6 +34,8 @@ namespace Microsoft.Terminal.Settings String WordDelimiters; Boolean ForceVTInput; + + Windows.Foundation.IReference TabColor; }; } diff --git a/src/cascadia/TerminalSettings/terminalsettings.h b/src/cascadia/TerminalSettings/terminalsettings.h index 1bd1aad98ae..8e82c0bfeae 100644 --- a/src/cascadia/TerminalSettings/terminalsettings.h +++ b/src/cascadia/TerminalSettings/terminalsettings.h @@ -54,6 +54,8 @@ namespace winrt::Microsoft::Terminal::Settings::implementation GETSET_PROPERTY(hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS); GETSET_PROPERTY(bool, CopyOnSelect, false); + GETSET_PROPERTY(Windows::Foundation::IReference, TabColor, nullptr); + // ------------------------ End of Core Settings ----------------------- GETSET_PROPERTY(hstring, ProfileName); diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index 5e22a25127e..f9bcf52b7fe 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -124,4 +124,28 @@ namespace Microsoft::Console::Utils // few more times. return t1.value_or(CoalesceOptionals(std::forward(t2)...)); } + + // Method Description: + // - Base case provided to handle the last argument to CoalesceOptionals() + template + std::optional CoalesceOptionalsOrNot(const std::optional& base) + { + return base; + } + + // Method Description: + // - Base case provided to handle the last argument to CoalesceOptionals(..., nullopt) + template + std::optional CoalesceOptionalsOrNot(const std::nullopt_t& base) + { + return base; + } + + // Method Description: + // - Returns the value from the first populated optional, or the last one (if none of the previous had a value) + template + std::optional CoalesceOptionalsOrNot(const std::optional& t1, Ts&&... t2) + { + return t1.has_value() ? t1 : CoalesceOptionalsOrNot(std::forward(t2)...); + } } From fb20323d3393352efcf862b7824381160bca1097 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 3 Aug 2020 11:26:17 -0500 Subject: [PATCH 2/7] damn hot-reloading is slick as heck --- src/cascadia/TerminalApp/Tab.cpp | 10 ++++++++++ src/cascadia/TerminalControl/TermControl.cpp | 7 +++++++ src/cascadia/TerminalControl/TermControl.h | 2 ++ src/cascadia/TerminalControl/TermControl.idl | 1 + src/cascadia/TerminalCore/Terminal.cpp | 6 ++++++ src/cascadia/TerminalCore/Terminal.hpp | 2 ++ 6 files changed, 28 insertions(+) diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index bd559f02079..84113809157 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -439,6 +439,16 @@ namespace winrt::TerminalApp::implementation _rootPane->Relayout(); } }); + + control.TabColorChanged([weakThis](auto&&, auto&&) { + if (auto tab{ weakThis.get() }) + { + // The control's tabColor changed, but it is not necessarily the + // active control in this tab. We'll just recalculate the + // current color anyways. + tab->_RecalculateAndApplyTabColor(); + } + }); } // Method Description: diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 31b2c5874d0..8c917fe4fb6 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -88,6 +88,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation auto pfnTitleChanged = std::bind(&TermControl::_TerminalTitleChanged, this, std::placeholders::_1); _terminal->SetTitleChangedCallback(pfnTitleChanged); + auto pfnTabColorChanged = std::bind(&TermControl::_TerminalTabColorChanged, this, std::placeholders::_1); + _terminal->SetTabColorChangedCallback(pfnTabColorChanged); + auto pfnBackgroundColorChanged = std::bind(&TermControl::_BackgroundColorChanged, this, std::placeholders::_1); _terminal->SetBackgroundCallback(pfnBackgroundColorChanged); @@ -2057,6 +2060,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation { _titleChangedHandlers(winrt::hstring{ wstr }); } + void TermControl::_TerminalTabColorChanged(const std::optional /*color*/) + { + _TabColorChangedHandlers(*this, nullptr); + } void TermControl::_CopyToClipboard(const std::wstring_view& wstr) { diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index b1244b8ed2f..7cc8b0c26aa 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -122,6 +122,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation TYPED_EVENT(ConnectionStateChanged, TerminalControl::TermControl, IInspectable); TYPED_EVENT(Initialized, TerminalControl::TermControl, Windows::UI::Xaml::RoutedEventArgs); + TYPED_EVENT(TabColorChanged, IInspectable, IInspectable); // clang-format on private: @@ -223,6 +224,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation void _DoResizeUnderLock(const double newWidth, const double newHeight); void _RefreshSizeUnderLock(); void _TerminalTitleChanged(const std::wstring_view& wstr); + void _TerminalTabColorChanged(const std::optional color); void _CopyToClipboard(const std::wstring_view& wstr); void _TerminalScrollPositionChanged(const int viewTop, const int viewHeight, const int bufferSize); void _TerminalCursorPositionChanged(); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 5cebfac26e1..dd301b119ef 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -72,5 +72,6 @@ namespace Microsoft.Terminal.TerminalControl void ToggleRetroEffect(); Windows.Foundation.IReference TabColor { get; }; + event Windows.Foundation.TypedEventHandler TabColorChanged; } } diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 9355609999a..59de8004755 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -155,6 +155,7 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting { _tabColor = til::color(settings.TabColor().Value() | 0xff000000); } + _pfnTabColorChanged(_tabColor); // TODO:MSFT:21327402 - if HistorySize has changed, resize the buffer so we // have a smaller scrollback. We should do this carefully - if the new buffer @@ -909,6 +910,11 @@ void Terminal::SetTitleChangedCallback(std::function)> pfn) noexcept +{ + _pfnTabColorChanged.swap(pfn); +} + void Terminal::SetCopyToClipboardCallback(std::function pfn) noexcept { _pfnCopyToClipboard.swap(pfn); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index ade99237496..7d93c1b2277 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -168,6 +168,7 @@ class Microsoft::Terminal::Core::Terminal final : void SetWriteInputCallback(std::function pfn) noexcept; void SetTitleChangedCallback(std::function pfn) noexcept; + void SetTabColorChangedCallback(std::function)> pfn) noexcept; void SetCopyToClipboardCallback(std::function pfn) noexcept; void SetScrollPositionChangedCallback(std::function pfn) noexcept; void SetCursorPositionChangedCallback(std::function pfn) noexcept; @@ -201,6 +202,7 @@ class Microsoft::Terminal::Core::Terminal final : std::function _pfnScrollPositionChanged; std::function _pfnBackgroundColorChanged; std::function _pfnCursorPositionChanged; + std::function)> _pfnTabColorChanged; std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine; std::unique_ptr<::Microsoft::Console::VirtualTerminal::TerminalInput> _terminalInput; From 694938702233ce1b40f0930d52d35783cd5d315a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 3 Aug 2020 11:44:48 -0500 Subject: [PATCH 3/7] nits from things I forgot before submitting the PR --- src/cascadia/TerminalApp/Profile.cpp | 5 ++--- src/cascadia/TerminalApp/Tab.cpp | 15 +++++++++++++++ src/cascadia/TerminalApp/TerminalPage.cpp | 11 ----------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index 7d85745bb19..fd439739449 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -234,9 +234,8 @@ TerminalSettings Profile::CreateTerminalSettings(const std::unordered_map cr{ c }; - terminalSettings.TabColor(cr); + winrt::Windows::Foundation::IReference colorRef{ _tabColor.value() }; + terminalSettings.TabColor(colorRef); } return terminalSettings; diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index 84113809157..efc7684ff2e 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -727,6 +727,21 @@ namespace winrt::TerminalApp::implementation { controlTabColor = currControlColor.Value(); } + + // A Tab's color will be the result of layering a variety of sources, + // from the bottom up: + // + // Color | | Set by + // -------------------- | -- | -- + // Runtime Color | _optional_ | Color Picker / `setTabColor` action + // Control Tab Color | _optional_ | Profile's `tabColor`, or a color set by VT + // Theme Tab Background | _optional_ | `tab.backgroundColor` in the theme + // Tab Default Color | **default** | TabView in XAML + // + // CoalesceOptionalsOrNot will get us the first of these values that's + // actually set, with nullopt being our sentinel for "use the default + // tabview color" (and clear out any colors we've set). + return Utils::CoalesceOptionalsOrNot(_runtimeTabColor, controlTabColor, _themeTabColor, diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index dbeec9885a6..dcc551f1fed 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1817,17 +1817,6 @@ namespace winrt::TerminalApp::implementation // Raise an event that our title changed _titleChangeHandlers(*this, tab->GetActiveTitle()); - - // // Raise an event that our titlebar color changed - // std::optional color = tab->GetTabColor(); - // if (color.has_value()) - // { - // _SetNonClientAreaColors(color.value()); - // } - // else - // { - // _ClearNonClientAreaColors(); - // } } CATCH_LOG(); } From daf7e63bfdf1fc0773ce20f8533641f72d7c5ee9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 3 Aug 2020 16:05:25 -0500 Subject: [PATCH 4/7] fix the compilation of the unittests --- src/cascadia/UnitTests_TerminalCore/MockTermSettings.h | 3 +++ src/cascadia/UnitTests_TerminalCore/precomp.h | 1 + 2 files changed, 4 insertions(+) diff --git a/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h index 9d91488075c..1082d25dfe9 100644 --- a/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h +++ b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h @@ -6,6 +6,7 @@ #include "DefaultSettings.h" #include "winrt/Microsoft.Terminal.Settings.h" +#include "../inc/cppwinrt_utils.h" using namespace winrt::Microsoft::Terminal::Settings; @@ -63,6 +64,8 @@ namespace TerminalCoreUnitTests // other unimplemented methods void SetColorTableEntry(int32_t /* index */, uint32_t /* value */) {} + GETSET_PROPERTY(winrt::Windows::Foundation::IReference, TabColor, nullptr); + private: int32_t _historySize; int32_t _initialRows; diff --git a/src/cascadia/UnitTests_TerminalCore/precomp.h b/src/cascadia/UnitTests_TerminalCore/precomp.h index 53dbb752420..7e6feac953f 100644 --- a/src/cascadia/UnitTests_TerminalCore/precomp.h +++ b/src/cascadia/UnitTests_TerminalCore/precomp.h @@ -45,3 +45,4 @@ Author(s): #define CON_USERPRIVAPI_INDIRECT #define CON_DPIAPI_INDIRECT #endif +#include "winrt/Windows.Foundation.h" From 969c807e93bbb32e11175ca3c229ca6537e37d20 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 4 Aug 2020 12:23:09 -0500 Subject: [PATCH 5/7] all that just for a _test to pass_? --- src/cascadia/TerminalCore/Terminal.cpp | 5 +- src/cascadia/UnitTests_TerminalCore/precomp.h | 54 +++++++++++-------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index d92dfb8a911..17bb24876e9 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -155,7 +155,10 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting { _tabColor = til::color(settings.TabColor().Value() | 0xff000000); } - _pfnTabColorChanged(_tabColor); + if (_pfnTabColorChanged) + { + _pfnTabColorChanged(_tabColor); + } // TODO:MSFT:21327402 - if HistorySize has changed, resize the buffer so we // have a smaller scrollback. We should do this carefully - if the new buffer diff --git a/src/cascadia/UnitTests_TerminalCore/precomp.h b/src/cascadia/UnitTests_TerminalCore/precomp.h index 7e6feac953f..2a55dfc7ffc 100644 --- a/src/cascadia/UnitTests_TerminalCore/precomp.h +++ b/src/cascadia/UnitTests_TerminalCore/precomp.h @@ -17,32 +17,40 @@ Author(s): #pragma once -// -// This header and define are needed so that the console host code can build in -// this test binary. - -// Block minwindef.h min/max macros to prevent conflict -#define NOMINMAX - -// This includes a lot of common headers needed by both the host and the propsheet -// including: windows.h, winuser, ntstatus, assert, and the DDK -#include "HostAndPropsheetIncludes.h" -// - +#define BLOCK_TIL // This includes support libraries from the CRT, STL, WIL, and GSL #include "LibraryIncludes.h" - -#ifdef BUILDING_INSIDE_WINIDE -#define DbgRaiseAssertionFailure() __int2c() +// This is inexplicable, but for whatever reason, cppwinrt conflicts with the +// SDK definition of this function, so the only fix is to undef it. +// from WinBase.h +// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime +#ifdef GetCurrentTime +#undef GetCurrentTime #endif -#include +#include +#include +#include -// Comment to build against the private SDK. -#define CON_BUILD_PUBLIC +#include +#include "consoletaeftemplates.hpp" -#ifdef CON_BUILD_PUBLIC -#define CON_USERPRIVAPI_INDIRECT -#define CON_DPIAPI_INDIRECT -#endif -#include "winrt/Windows.Foundation.h" +#include +#include +#include + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#include "til.h" + +// +// These are needed because the roundtrip tests included in this library also +// re-use some conhost code that depends on these. + +#include "conddkrefs.h" +// From ntdef.h, but that can't be included or it'll fight over PROBE_ALIGNMENT and other such arch specific defs +typedef _Return_type_success_(return >= 0) LONG NTSTATUS; +/*lint -save -e624 */ // Don't complain about different typedefs. +typedef NTSTATUS* PNTSTATUS; +/*lint -restore */ // Resume checking for different typedefs. +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +// From 0b8433320b4a8f6a7e1957c5b108767f25ee9131 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 4 Aug 2020 15:04:39 -0500 Subject: [PATCH 6/7] Eh let's just move this to it's own file now, that's easier --- src/cascadia/TerminalApp/CascadiaSettings.cpp | 4 +- src/cascadia/TerminalApp/Tab.cpp | 10 +-- src/inc/til.h | 1 + src/inc/til/coalesce.h | 61 ++++++++++++++ src/til/ut_til/CoalesceTests.cpp | 84 +++++++++++++++++++ src/til/ut_til/til.unit.tests.vcxproj | 3 +- src/types/inc/utils.hpp | 52 ------------ 7 files changed, 154 insertions(+), 61 deletions(-) create mode 100644 src/inc/til/coalesce.h create mode 100644 src/til/ut_til/CoalesceTests.cpp diff --git a/src/cascadia/TerminalApp/CascadiaSettings.cpp b/src/cascadia/TerminalApp/CascadiaSettings.cpp index 96e947e952d..8ebe69428ff 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettings.cpp @@ -227,7 +227,7 @@ void CascadiaSettings::_ResolveDefaultProfile() { const auto unparsedDefaultProfile{ GlobalSettings().UnparsedDefaultProfile() }; auto maybeParsedDefaultProfile{ _GetProfileGuidByName(unparsedDefaultProfile) }; - auto defaultProfileGuid{ Utils::CoalesceOptionals(maybeParsedDefaultProfile, GUID{}) }; + auto defaultProfileGuid{ til::CoalesceOptionals(maybeParsedDefaultProfile, GUID{}) }; GlobalSettings().DefaultProfile(defaultProfileGuid); } @@ -566,7 +566,7 @@ GUID CascadiaSettings::_GetProfileForArgs(const NewTerminalArgs& newTerminalArgs profileByName = _GetProfileGuidByName(newTerminalArgs.Profile()); } - return Utils::CoalesceOptionals(profileByName, profileByIndex, _globals.DefaultProfile()); + return til::CoalesceOptionals(profileByName, profileByIndex, _globals.DefaultProfile()); } // Method Description: diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index efc7684ff2e..89b83156598 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -8,7 +8,6 @@ #include "Tab.g.cpp" #include "Utils.h" #include "ColorHelper.h" -#include "../../types/inc/utils.hpp" using namespace winrt; using namespace winrt::Windows::UI::Xaml; @@ -16,7 +15,6 @@ using namespace winrt::Windows::UI::Core; using namespace winrt::Microsoft::Terminal::Settings; using namespace winrt::Microsoft::Terminal::TerminalControl; using namespace winrt::Windows::System; -using namespace ::Microsoft::Console; namespace winrt { @@ -742,10 +740,10 @@ namespace winrt::TerminalApp::implementation // actually set, with nullopt being our sentinel for "use the default // tabview color" (and clear out any colors we've set). - return Utils::CoalesceOptionalsOrNot(_runtimeTabColor, - controlTabColor, - _themeTabColor, - std::optional(std::nullopt)); + return til::CoalesceOptionalsOrNot(_runtimeTabColor, + controlTabColor, + _themeTabColor, + std::optional(std::nullopt)); } // Method Description: diff --git a/src/inc/til.h b/src/inc/til.h index e9504535708..36e10ad0e6f 100644 --- a/src/inc/til.h +++ b/src/inc/til.h @@ -16,6 +16,7 @@ #include "til/bitmap.h" #include "til/u8u16convert.h" #include "til/spsc.h" +#include "til/coalesce.h" namespace til // Terminal Implementation Library. Also: "Today I Learned" { diff --git a/src/inc/til/coalesce.h b/src/inc/til/coalesce.h new file mode 100644 index 00000000000..46ce7090315 --- /dev/null +++ b/src/inc/til/coalesce.h @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +namespace til +{ + // Method Description: + // - Base case provided to handle the last argument to CoalesceOptionals() + template + T CoalesceOptionals(const T& base) + { + return base; + } + + // Method Description: + // - Base case provided to throw an assertion if you call CoalesceOptionals(opt, opt, opt) + template + T CoalesceOptionals(const std::optional& base) + { + static_assert(false, "CoalesceOptionals must be passed a base non-optional value to be used if all optionals are empty"); + return T{}; + } + + // Method Description: + // - Returns the value from the first populated optional, or a base value if none were populated. + template + T CoalesceOptionals(const std::optional& t1, Ts&&... t2) + { + // Initially, I wanted to check "has_value" and short-circuit out so that we didn't + // evaluate value_or for every single optional, but has_value/value emits exception handling + // code that value_or doesn't. Less exception handling is cheaper than calling value_or a + // few more times. + return t1.value_or(CoalesceOptionals(std::forward(t2)...)); + } + + // Method Description: + // - Base case provided to handle the last argument to CoalesceOptionals() + template + std::optional CoalesceOptionalsOrNot(const std::optional& base) + { + return base; + } + + // Method Description: + // - Base case provided to handle the last argument to CoalesceOptionals(..., nullopt) + template + std::optional CoalesceOptionalsOrNot(const std::nullopt_t& base) + { + return base; + } + + // Method Description: + // - Returns the value from the first populated optional, or the last one (if none of the previous had a value) + template + std::optional CoalesceOptionalsOrNot(const std::optional& t1, Ts&&... t2) + { + return t1.has_value() ? t1 : CoalesceOptionalsOrNot(std::forward(t2)...); + } + +} diff --git a/src/til/ut_til/CoalesceTests.cpp b/src/til/ut_til/CoalesceTests.cpp new file mode 100644 index 00000000000..2b74e663cd0 --- /dev/null +++ b/src/til/ut_til/CoalesceTests.cpp @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" +#include "WexTestClass.h" + +using namespace WEX::Common; +using namespace WEX::Logging; +using namespace WEX::TestExecution; + +class CoalesceTests +{ + TEST_CLASS(CoalesceTests); + + TEST_METHOD(CoalesceFirstValue); + TEST_METHOD(CoalesceMiddleValue); + TEST_METHOD(CoalesceDefaultValue); + + TEST_METHOD(CoalesceOrNotFirstValue); + TEST_METHOD(CoalesceOrNotMiddleValue); + TEST_METHOD(CoalesceOrNotDefaultValue); + TEST_METHOD(CoalesceOrNotDefaultIsNullopt); +}; + +void CoalesceTests::CoalesceFirstValue() +{ + int result = til::CoalesceOptionals(std::optional(1), + std::optional(2), + std::optional(3), + 4); + VERIFY_ARE_EQUAL(1, result); +} +void CoalesceTests::CoalesceMiddleValue() +{ + int result = til::CoalesceOptionals(std::optional(std::nullopt), + std::optional(2), + std::optional(3), + 4); + VERIFY_ARE_EQUAL(2, result); +} +void CoalesceTests::CoalesceDefaultValue() +{ + int result = til::CoalesceOptionals(std::optional(std::nullopt), + std::optional(std::nullopt), + std::optional(std::nullopt), + 4); + VERIFY_ARE_EQUAL(4, result); +} + +void CoalesceTests::CoalesceOrNotFirstValue() +{ + std::optional result = til::CoalesceOptionalsOrNot(std::optional(1), + std::optional(2), + std::optional(3), + std::optional(4)); + VERIFY_IS_TRUE(result.has_value()); + VERIFY_ARE_EQUAL(1, result.value()); +} +void CoalesceTests::CoalesceOrNotMiddleValue() +{ + std::optional result = til::CoalesceOptionalsOrNot(std::optional(std::nullopt), + std::optional(2), + std::optional(3), + std::optional(4)); + VERIFY_IS_TRUE(result.has_value()); + VERIFY_ARE_EQUAL(2, result.value()); +} +void CoalesceTests::CoalesceOrNotDefaultValue() +{ + std::optional result = til::CoalesceOptionalsOrNot(std::optional(std::nullopt), + std::optional(std::nullopt), + std::optional(std::nullopt), + std::optional(4)); + VERIFY_IS_TRUE(result.has_value()); + VERIFY_ARE_EQUAL(4, result.value()); +} +void CoalesceTests::CoalesceOrNotDefaultIsNullopt() +{ + std::optional result = til::CoalesceOptionalsOrNot(std::optional(std::nullopt), + std::optional(std::nullopt), + std::optional(std::nullopt), + std::optional(std::nullopt)); + VERIFY_IS_FALSE(result.has_value()); +} diff --git a/src/til/ut_til/til.unit.tests.vcxproj b/src/til/ut_til/til.unit.tests.vcxproj index 82b7908bb7c..52a07246b81 100644 --- a/src/til/ut_til/til.unit.tests.vcxproj +++ b/src/til/ut_til/til.unit.tests.vcxproj @@ -18,6 +18,7 @@ + Create @@ -36,4 +37,4 @@ - \ No newline at end of file + diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index f9bcf52b7fe..05ba610e9d5 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -96,56 +96,4 @@ namespace Microsoft::Console::Utils GUID CreateV5Uuid(const GUID& namespaceGuid, const gsl::span name); - // Method Description: - // - Base case provided to handle the last argument to CoalesceOptionals() - template - T CoalesceOptionals(const T& base) - { - return base; - } - - // Method Description: - // - Base case provided to throw an assertion if you call CoalesceOptionals(opt, opt, opt) - template - T CoalesceOptionals(const std::optional& base) - { - static_assert(false, "CoalesceOptionals must be passed a base non-optional value to be used if all optionals are empty"); - return T{}; - } - - // Method Description: - // - Returns the value from the first populated optional, or a base value if none were populated. - template - T CoalesceOptionals(const std::optional& t1, Ts&&... t2) - { - // Initially, I wanted to check "has_value" and short-circuit out so that we didn't - // evaluate value_or for every single optional, but has_value/value emits exception handling - // code that value_or doesn't. Less exception handling is cheaper than calling value_or a - // few more times. - return t1.value_or(CoalesceOptionals(std::forward(t2)...)); - } - - // Method Description: - // - Base case provided to handle the last argument to CoalesceOptionals() - template - std::optional CoalesceOptionalsOrNot(const std::optional& base) - { - return base; - } - - // Method Description: - // - Base case provided to handle the last argument to CoalesceOptionals(..., nullopt) - template - std::optional CoalesceOptionalsOrNot(const std::nullopt_t& base) - { - return base; - } - - // Method Description: - // - Returns the value from the first populated optional, or the last one (if none of the previous had a value) - template - std::optional CoalesceOptionalsOrNot(const std::optional& t1, Ts&&... t2) - { - return t1.has_value() ? t1 : CoalesceOptionalsOrNot(std::forward(t2)...); - } } From 0edc1d88fe34cb1a15d0f0217b2368c150fbafd9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 6 Aug 2020 16:45:49 -0500 Subject: [PATCH 7/7] Rename this to be _simpler_ --- src/cascadia/TerminalApp/CascadiaSettings.cpp | 4 +- src/cascadia/TerminalApp/Tab.cpp | 10 ++-- src/inc/til/coalesce.h | 26 ++++----- src/til/ut_til/CoalesceTests.cpp | 56 +++++++++---------- 4 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/cascadia/TerminalApp/CascadiaSettings.cpp b/src/cascadia/TerminalApp/CascadiaSettings.cpp index 8ebe69428ff..32c19867c41 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettings.cpp @@ -227,7 +227,7 @@ void CascadiaSettings::_ResolveDefaultProfile() { const auto unparsedDefaultProfile{ GlobalSettings().UnparsedDefaultProfile() }; auto maybeParsedDefaultProfile{ _GetProfileGuidByName(unparsedDefaultProfile) }; - auto defaultProfileGuid{ til::CoalesceOptionals(maybeParsedDefaultProfile, GUID{}) }; + auto defaultProfileGuid{ til::coalesce_value(maybeParsedDefaultProfile, GUID{}) }; GlobalSettings().DefaultProfile(defaultProfileGuid); } @@ -566,7 +566,7 @@ GUID CascadiaSettings::_GetProfileForArgs(const NewTerminalArgs& newTerminalArgs profileByName = _GetProfileGuidByName(newTerminalArgs.Profile()); } - return til::CoalesceOptionals(profileByName, profileByIndex, _globals.DefaultProfile()); + return til::coalesce_value(profileByName, profileByIndex, _globals.DefaultProfile()); } // Method Description: diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index 89b83156598..0764acedccd 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -736,14 +736,14 @@ namespace winrt::TerminalApp::implementation // Theme Tab Background | _optional_ | `tab.backgroundColor` in the theme // Tab Default Color | **default** | TabView in XAML // - // CoalesceOptionalsOrNot will get us the first of these values that's + // coalesce will get us the first of these values that's // actually set, with nullopt being our sentinel for "use the default // tabview color" (and clear out any colors we've set). - return til::CoalesceOptionalsOrNot(_runtimeTabColor, - controlTabColor, - _themeTabColor, - std::optional(std::nullopt)); + return til::coalesce(_runtimeTabColor, + controlTabColor, + _themeTabColor, + std::optional(std::nullopt)); } // Method Description: diff --git a/src/inc/til/coalesce.h b/src/inc/til/coalesce.h index 46ce7090315..24153869a67 100644 --- a/src/inc/til/coalesce.h +++ b/src/inc/til/coalesce.h @@ -6,46 +6,46 @@ namespace til { // Method Description: - // - Base case provided to handle the last argument to CoalesceOptionals() + // - Base case provided to handle the last argument to coalesce_value() template - T CoalesceOptionals(const T& base) + T coalesce_value(const T& base) { return base; } // Method Description: - // - Base case provided to throw an assertion if you call CoalesceOptionals(opt, opt, opt) + // - Base case provided to throw an assertion if you call coalesce_value(opt, opt, opt) template - T CoalesceOptionals(const std::optional& base) + T coalesce_value(const std::optional& base) { - static_assert(false, "CoalesceOptionals must be passed a base non-optional value to be used if all optionals are empty"); + static_assert(false, "coalesce_value must be passed a base non-optional value to be used if all optionals are empty"); return T{}; } // Method Description: // - Returns the value from the first populated optional, or a base value if none were populated. template - T CoalesceOptionals(const std::optional& t1, Ts&&... t2) + T coalesce_value(const std::optional& t1, Ts&&... t2) { // Initially, I wanted to check "has_value" and short-circuit out so that we didn't // evaluate value_or for every single optional, but has_value/value emits exception handling // code that value_or doesn't. Less exception handling is cheaper than calling value_or a // few more times. - return t1.value_or(CoalesceOptionals(std::forward(t2)...)); + return t1.value_or(coalesce_value(std::forward(t2)...)); } // Method Description: - // - Base case provided to handle the last argument to CoalesceOptionals() + // - Base case provided to handle the last argument to coalesce_value() template - std::optional CoalesceOptionalsOrNot(const std::optional& base) + std::optional coalesce(const std::optional& base) { return base; } // Method Description: - // - Base case provided to handle the last argument to CoalesceOptionals(..., nullopt) + // - Base case provided to handle the last argument to coalesce_value(..., nullopt) template - std::optional CoalesceOptionalsOrNot(const std::nullopt_t& base) + std::optional coalesce(const std::nullopt_t& base) { return base; } @@ -53,9 +53,9 @@ namespace til // Method Description: // - Returns the value from the first populated optional, or the last one (if none of the previous had a value) template - std::optional CoalesceOptionalsOrNot(const std::optional& t1, Ts&&... t2) + std::optional coalesce(const std::optional& t1, Ts&&... t2) { - return t1.has_value() ? t1 : CoalesceOptionalsOrNot(std::forward(t2)...); + return t1.has_value() ? t1 : coalesce(std::forward(t2)...); } } diff --git a/src/til/ut_til/CoalesceTests.cpp b/src/til/ut_til/CoalesceTests.cpp index 2b74e663cd0..76c2e9c31ab 100644 --- a/src/til/ut_til/CoalesceTests.cpp +++ b/src/til/ut_til/CoalesceTests.cpp @@ -24,61 +24,61 @@ class CoalesceTests void CoalesceTests::CoalesceFirstValue() { - int result = til::CoalesceOptionals(std::optional(1), - std::optional(2), - std::optional(3), - 4); + int result = til::coalesce_value(std::optional(1), + std::optional(2), + std::optional(3), + 4); VERIFY_ARE_EQUAL(1, result); } void CoalesceTests::CoalesceMiddleValue() { - int result = til::CoalesceOptionals(std::optional(std::nullopt), - std::optional(2), - std::optional(3), - 4); + int result = til::coalesce_value(std::optional(std::nullopt), + std::optional(2), + std::optional(3), + 4); VERIFY_ARE_EQUAL(2, result); } void CoalesceTests::CoalesceDefaultValue() { - int result = til::CoalesceOptionals(std::optional(std::nullopt), - std::optional(std::nullopt), - std::optional(std::nullopt), - 4); + int result = til::coalesce_value(std::optional(std::nullopt), + std::optional(std::nullopt), + std::optional(std::nullopt), + 4); VERIFY_ARE_EQUAL(4, result); } void CoalesceTests::CoalesceOrNotFirstValue() { - std::optional result = til::CoalesceOptionalsOrNot(std::optional(1), - std::optional(2), - std::optional(3), - std::optional(4)); + std::optional result = til::coalesce(std::optional(1), + std::optional(2), + std::optional(3), + std::optional(4)); VERIFY_IS_TRUE(result.has_value()); VERIFY_ARE_EQUAL(1, result.value()); } void CoalesceTests::CoalesceOrNotMiddleValue() { - std::optional result = til::CoalesceOptionalsOrNot(std::optional(std::nullopt), - std::optional(2), - std::optional(3), - std::optional(4)); + std::optional result = til::coalesce(std::optional(std::nullopt), + std::optional(2), + std::optional(3), + std::optional(4)); VERIFY_IS_TRUE(result.has_value()); VERIFY_ARE_EQUAL(2, result.value()); } void CoalesceTests::CoalesceOrNotDefaultValue() { - std::optional result = til::CoalesceOptionalsOrNot(std::optional(std::nullopt), - std::optional(std::nullopt), - std::optional(std::nullopt), - std::optional(4)); + std::optional result = til::coalesce(std::optional(std::nullopt), + std::optional(std::nullopt), + std::optional(std::nullopt), + std::optional(4)); VERIFY_IS_TRUE(result.has_value()); VERIFY_ARE_EQUAL(4, result.value()); } void CoalesceTests::CoalesceOrNotDefaultIsNullopt() { - std::optional result = til::CoalesceOptionalsOrNot(std::optional(std::nullopt), - std::optional(std::nullopt), - std::optional(std::nullopt), - std::optional(std::nullopt)); + std::optional result = til::coalesce(std::optional(std::nullopt), + std::optional(std::nullopt), + std::optional(std::nullopt), + std::optional(std::nullopt)); VERIFY_IS_FALSE(result.has_value()); }