diff --git a/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp b/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp index e9106f68783..f1568677953 100644 --- a/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsAppAdapterLib/TerminalSettings.cpp @@ -6,6 +6,8 @@ #include "winrt/Windows.UI.ViewManagement.h" #include "../../types/inc/colorTable.hpp" +#include + using namespace winrt::Microsoft::Terminal::Control; using namespace winrt::Microsoft::Terminal::Settings; using namespace Microsoft::Console::Utils; @@ -211,16 +213,61 @@ namespace winrt::Microsoft::Terminal::Settings winrt::Windows::UI::Xaml::ElementTheme::Light; } + // GH#9422: The special "_random" token causes us to randomly select + // a color scheme from the set of available schemes each time a new + // tab or pane is created. + static constexpr std::wstring_view RandomSchemeToken{ L"_random" }; + + // Helper to pick a random scheme from the available schemes map. + const auto pickRandomScheme = [&]() -> Model::ColorScheme { + const auto size = schemes.Size(); + if (size == 0) + { + return nullptr; + } + // Use a random_device for non-deterministic seeding so each + // tab gets a truly random color scheme. + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dist(0, size - 1); + const auto index = dist(gen); + + uint32_t i = 0; + for (const auto& [name, scheme] : schemes) + { + if (i == index) + { + return scheme; + } + ++i; + } + return nullptr; + }; + switch (requestedTheme) { case winrt::Windows::UI::Xaml::ElementTheme::Light: - if (const auto scheme = schemes.TryLookup(appearance.LightColorSchemeName())) + if (appearance.LightColorSchemeName() == RandomSchemeToken) + { + if (const auto scheme = pickRandomScheme()) + { + ApplyColorScheme(scheme); + } + } + else if (const auto scheme = schemes.TryLookup(appearance.LightColorSchemeName())) { ApplyColorScheme(scheme); } break; case winrt::Windows::UI::Xaml::ElementTheme::Dark: - if (const auto scheme = schemes.TryLookup(appearance.DarkColorSchemeName())) + if (appearance.DarkColorSchemeName() == RandomSchemeToken) + { + if (const auto scheme = pickRandomScheme()) + { + ApplyColorScheme(scheme); + } + } + else if (const auto scheme = schemes.TryLookup(appearance.DarkColorSchemeName())) { ApplyColorScheme(scheme); } diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index 9897629f350..744deb1501b 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -23,6 +23,10 @@ using namespace winrt::Microsoft::Terminal::Control; using namespace winrt::Windows::Foundation::Collections; using namespace Microsoft::Console; +// The special token used to indicate that a random color scheme should be +// selected each time a new tab or pane is created. GH#9422 +static constexpr std::wstring_view RandomSchemeToken{ L"_random" }; + // Creating a child of a profile requires us to copy certain // required attributes. This method handles those attributes. // @@ -466,13 +470,20 @@ void CascadiaSettings::_validateAllSchemesExist() { for (const auto& appearance : std::array{ profile.DefaultAppearance(), profile.UnfocusedAppearance() }) { - if (appearance && !colorSchemes.HasKey(appearance.DarkColorSchemeName())) + // GH#9422: Don't clear the color scheme name if it's set to + // "_random" - that's a special token that will be resolved + // at runtime to a randomly chosen scheme. + if (appearance && + appearance.DarkColorSchemeName() != RandomSchemeToken && + !colorSchemes.HasKey(appearance.DarkColorSchemeName())) { // Clear the user set dark color scheme. We'll just fallback instead. appearance.ClearDarkColorSchemeName(); foundInvalidDarkScheme = true; } - if (appearance && !colorSchemes.HasKey(appearance.LightColorSchemeName())) + if (appearance && + appearance.LightColorSchemeName() != RandomSchemeToken && + !colorSchemes.HasKey(appearance.LightColorSchemeName())) { // Clear the user set light color scheme. We'll just fallback instead. appearance.ClearLightColorSchemeName();