diff --git a/src/cascadia/TerminalControl/IControlAppearance.idl b/src/cascadia/TerminalControl/IControlAppearance.idl index fca4be44d1f..e8ed4daaeac 100644 --- a/src/cascadia/TerminalControl/IControlAppearance.idl +++ b/src/cascadia/TerminalControl/IControlAppearance.idl @@ -19,5 +19,7 @@ namespace Microsoft.Terminal.Control Boolean RetroTerminalEffect { get; }; String PixelShaderPath { get; }; String PixelShaderImagePath { get; }; + + // NOTE! When adding something here, make sure to update ControlProperties.h too! }; } diff --git a/src/cascadia/TerminalControl/IControlSettings.idl b/src/cascadia/TerminalControl/IControlSettings.idl index 51eaf34f90d..4d5badb454a 100644 --- a/src/cascadia/TerminalControl/IControlSettings.idl +++ b/src/cascadia/TerminalControl/IControlSettings.idl @@ -66,5 +66,7 @@ namespace Microsoft.Terminal.Control Boolean UseBackgroundImageForWindow { get; }; Boolean RightClickContextMenu { get; }; Boolean RepositionCursorWithMouse { get; }; + + // NOTE! When adding something here, make sure to update ControlProperties.h too! }; } diff --git a/src/cascadia/TerminalCore/ICoreAppearance.idl b/src/cascadia/TerminalCore/ICoreAppearance.idl index 4ff3c60e5a9..d8ad5a9f674 100644 --- a/src/cascadia/TerminalCore/ICoreAppearance.idl +++ b/src/cascadia/TerminalCore/ICoreAppearance.idl @@ -122,5 +122,7 @@ namespace Microsoft.Terminal.Core Boolean IntenseIsBold; Boolean IntenseIsBright; AdjustTextMode AdjustIndistinguishableColors; + + // NOTE! When adding something here, make sure to update ControlProperties.h too! }; } diff --git a/src/cascadia/TerminalCore/ICoreSettings.idl b/src/cascadia/TerminalCore/ICoreSettings.idl index ab3f0e5cf83..115fdce5a62 100644 --- a/src/cascadia/TerminalCore/ICoreSettings.idl +++ b/src/cascadia/TerminalCore/ICoreSettings.idl @@ -27,7 +27,9 @@ namespace Microsoft.Terminal.Core Windows.Foundation.IReference StartingTabColor; Boolean AutoMarkPrompts; + Boolean RainbowSuggestions; + // NOTE! When adding something here, make sure to update ControlProperties.h too! }; } diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index c1b0436797d..24327e9eb5d 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -95,6 +95,7 @@ void Terminal::UpdateSettings(ICoreSettings settings) _startingTitle = settings.StartingTitle(); _trimBlockSelection = settings.TrimBlockSelection(); _autoMarkPrompts = settings.AutoMarkPrompts(); + _rainbowSuggestions = settings.RainbowSuggestions(); _getTerminalInput().ForceDisableWin32InputMode(settings.ForceVTInput()); @@ -1586,7 +1587,7 @@ til::point Terminal::GetViewportRelativeCursorPosition() const noexcept void Terminal::PreviewText(std::wstring_view input) { - // Our suggestion text is default-on-default, in italics. + // Our default suggestion text is default-on-default, in italics. static constexpr TextAttribute previewAttrs{ CharacterAttributes::Italics, TextColor{}, TextColor{}, 0u, TextColor{} }; auto lock = LockForWriting(); @@ -1626,11 +1627,43 @@ void Terminal::PreviewText(std::wstring_view input) // pad it out preview.insert(originalSize, expectedLenTillEnd - originalSize, L' '); } - snippetPreview.text = til::visualize_nonspace_control_codes(preview); + // Build our composition data + // The text is just the trimmed command, with the spaces at the end. + snippetPreview.text = til::visualize_nonspace_control_codes(preview); + + // The attributes depend on the $profile:experimental.rainbowSuggestions setting:. const auto len = snippetPreview.text.size(); snippetPreview.attributes.clear(); - snippetPreview.attributes.emplace_back(len, previewAttrs); + + if (_rainbowSuggestions) + { + // Let's do something fun. + + // Use the actual text length for the number of steps, not including the + // trailing spaces. + const float increment = 1.0f / originalSize; + for (auto i = 0u; i < originalSize; i++) + { + const auto color = til::color::from_hue(increment * i); + TextAttribute curr = previewAttrs; + curr.SetForeground(color); + snippetPreview.attributes.emplace_back(1, curr); + } + + if (originalSize < len) + { + TextAttribute curr; + snippetPreview.attributes.emplace_back(len - originalSize, curr); + } + } + else + { + // Default: + // Use the default attribute we defined above. + snippetPreview.attributes.emplace_back(len, previewAttrs); + } + snippetPreview.cursorPos = len; _activeBuffer().NotifyPaintFrame(); } diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index a52a5722da5..367ee14816f 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -360,6 +360,7 @@ class Microsoft::Terminal::Core::Terminal final : bool _suppressApplicationTitle = false; bool _trimBlockSelection = false; bool _autoMarkPrompts = false; + bool _rainbowSuggestions = false; size_t _taskbarState = 0; size_t _taskbarProgress = 0; diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index f0e30684c53..e2cbff9c02a 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -96,7 +96,8 @@ Author(s): X(bool, AutoMarkPrompts, "autoMarkPrompts", false) \ X(bool, ShowMarks, "showMarksOnScrollbar", false) \ X(bool, RepositionCursorWithMouse, "experimental.repositionCursorWithMouse", false) \ - X(bool, ReloadEnvironmentVariables, "compatibility.reloadEnvironmentVariables", true) + X(bool, ReloadEnvironmentVariables, "compatibility.reloadEnvironmentVariables", true) \ + X(bool, RainbowSuggestions, "experimental.rainbowSuggestions", false) // Intentionally omitted Profile settings: // * Name diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl index 2d6d1ab609c..e99ab3c0b86 100644 --- a/src/cascadia/TerminalSettingsModel/Profile.idl +++ b/src/cascadia/TerminalSettingsModel/Profile.idl @@ -87,6 +87,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_PROFILE_SETTING(Boolean, RepositionCursorWithMouse); INHERITABLE_PROFILE_SETTING(Boolean, ReloadEnvironmentVariables); + INHERITABLE_PROFILE_SETTING(Boolean, RainbowSuggestions); } } diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp index 29b9bb811fe..4afdcc3f25a 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp @@ -343,10 +343,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation _ShowMarks = Feature_ScrollbarMarks::IsEnabled() && profile.ShowMarks(); _RightClickContextMenu = profile.RightClickContextMenu(); - _RepositionCursorWithMouse = profile.RepositionCursorWithMouse(); - _ReloadEnvironmentVariables = profile.ReloadEnvironmentVariables(); + _RainbowSuggestions = profile.RainbowSuggestions(); } // Method Description: diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 33658427f94..81bb8758989 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -112,6 +112,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, bool, IntenseIsBright); INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Core::AdjustTextMode, AdjustIndistinguishableColors, Core::AdjustTextMode::Never); + INHERITABLE_SETTING(Model::TerminalSettings, bool, RainbowSuggestions, false); // ------------------------ End of Core Settings ----------------------- diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 63d8de231d3..bdf4fbfd53a 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -1128,25 +1128,6 @@ void AppHost::_stopFrameTimer() // is called as the `_frameTimer` Tick callback, roughly 60 times per second. void AppHost::_updateFrameColor(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { - // First, a couple helper functions: - static const auto saturateAndToColor = [](const float a, const float b, const float c) -> til::color { - return til::color{ - base::saturated_cast(255.f * std::clamp(a, 0.f, 1.f)), - base::saturated_cast(255.f * std::clamp(b, 0.f, 1.f)), - base::saturated_cast(255.f * std::clamp(c, 0.f, 1.f)) - }; - }; - - // Helper for converting a hue [0, 1) to an RGB value. - // Credit to https://www.chilliant.com/rgb2hsv.html - static const auto hueToRGB = [&](const float H) -> til::color { - float R = abs(H * 6 - 3) - 1; - float G = 2 - abs(H * 6 - 2); - float B = 2 - abs(H * 6 - 4); - return saturateAndToColor(R, G, B); - }; - - // Now, the main body of work. // - Convert the time delta between when we were started and now, to a hue. This will cycle us through all the colors. // - Convert that hue to an RGB value. // - Set the frame's color to that RGB color. @@ -1154,7 +1135,7 @@ void AppHost::_updateFrameColor(const winrt::Windows::Foundation::IInspectable&, const std::chrono::duration delta{ now - _started }; const auto seconds = delta.count() / 4; // divide by four, to make the effect slower. Otherwise it flashes way to fast. float ignored; - const auto color = hueToRGB(modf(seconds, &ignored)); + const auto color = til::color::from_hue(modf(seconds, &ignored)); _frameColorHelper(_window->GetHandle(), color); } diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index f848f401b6b..a3dcb87e9d0 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -49,7 +49,8 @@ X(winrt::hstring, StartingTitle) \ X(bool, DetectURLs, true) \ X(bool, AutoMarkPrompts) \ - X(bool, RepositionCursorWithMouse, false) + X(bool, RepositionCursorWithMouse, false) \ + X(bool, RainbowSuggestions) // --------------------------- Control Settings --------------------------- // All of these settings are defined in IControlSettings. diff --git a/src/inc/til/color.h b/src/inc/til/color.h index 81dd2d9b6b1..85ec527d023 100644 --- a/src/inc/til/color.h +++ b/src/inc/til/color.h @@ -186,6 +186,20 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" } #endif + // Helper for converting a hue [0, 1) to an RGB value. + // Credit to https://www.chilliant.com/rgb2hsv.html + static til::color from_hue(float hue) + { + const float R = abs(hue * 6 - 3) - 1; + const float G = 2 - abs(hue * 6 - 2); + const float B = 2 - abs(hue * 6 - 4); + return color{ + base::saturated_cast(255.f * std::clamp(R, 0.f, 1.f)), + base::saturated_cast(255.f * std::clamp(G, 0.f, 1.f)), + base::saturated_cast(255.f * std::clamp(B, 0.f, 1.f)) + }; + } + constexpr bool operator==(const til::color& other) const { return abgr == other.abgr;