diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 2b3fc7d3..b37e25b2 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -1884,15 +1884,13 @@ void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) { if (pWindow == m_pLastWindow) { const auto* const ACTIVECOLOR = !pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL); - setBorderColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying() >= 0 ? - CGradientValueData(CColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying())) : - *ACTIVECOLOR); + setBorderColor(pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying().m_vColors.empty() ? *ACTIVECOLOR : + pWindow->m_sSpecialRenderData.activeBorderColor.toUnderlying()); } else { const auto* const INACTIVECOLOR = !pWindow->m_sGroupData.pNextWindow ? (!pWindow->m_sGroupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL); - setBorderColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying() >= 0 ? - CGradientValueData(CColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying())) : - *INACTIVECOLOR); + setBorderColor(pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying().m_vColors.empty() ? *INACTIVECOLOR : + pWindow->m_sSpecialRenderData.inactiveBorderColor.toUnderlying()); } } diff --git a/src/Window.cpp b/src/Window.cpp index 818b4089..5a50713b 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -614,14 +614,42 @@ void CWindow::applyDynamicRule(const SWindowRule& r) { m_sAdditionalConfigData.animationStyle = STYLE; } else if (r.szRule.starts_with("bordercolor")) { try { - std::string colorPart = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find_first_of(' ') + 1)); + // Each vector will only get used if it has at least one color + CGradientValueData activeBorderGradient = {}; + CGradientValueData inactiveBorderGradient = {}; + bool active = true; + CVarList colorsAndAngles = CVarList(removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find_first_of(' ') + 1)), 0, 's', true); - if (colorPart.contains(' ')) { - // we have a space, 2 values - m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart.substr(0, colorPart.find_first_of(' '))); - m_sSpecialRenderData.inactiveBorderColor = configStringToInt(colorPart.substr(colorPart.find_first_of(' ') + 1)); - } else { - m_sSpecialRenderData.activeBorderColor = configStringToInt(colorPart); + // Basic form has only two colors, everything else can be parsed as a gradient + if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) { + m_sSpecialRenderData.activeBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[0]))); + m_sSpecialRenderData.inactiveBorderColor = CGradientValueData(CColor(configStringToInt(colorsAndAngles[1]))); + return; + } + + for (auto& token : colorsAndAngles) { + // The first angle, or an explicit "0deg", splits the two gradients + if (active && token.contains("deg")) { + activeBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); + active = false; + } else if (token.contains("deg")) + inactiveBorderGradient.m_fAngle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0); + else if (active) + activeBorderGradient.m_vColors.push_back(configStringToInt(token)); + else + inactiveBorderGradient.m_vColors.push_back(configStringToInt(token)); + } + + // Includes sanity checks for the number of colors in each gradient + if (activeBorderGradient.m_vColors.size() > 10 || inactiveBorderGradient.m_vColors.size() > 10) + Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", r.szRule); + else if (activeBorderGradient.m_vColors.empty()) + Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", r.szRule); + else if (inactiveBorderGradient.m_vColors.empty()) + m_sSpecialRenderData.activeBorderColor = activeBorderGradient; + else { + m_sSpecialRenderData.activeBorderColor = activeBorderGradient; + m_sSpecialRenderData.inactiveBorderColor = inactiveBorderGradient; } } catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r.szRule, e.what()); } } else if (r.szRule == "dimaround") { @@ -651,8 +679,8 @@ void CWindow::applyDynamicRule(const SWindowRule& r) { } void CWindow::updateDynamicRules() { - m_sSpecialRenderData.activeBorderColor = -1; - m_sSpecialRenderData.inactiveBorderColor = -1; + m_sSpecialRenderData.activeBorderColor = CGradientValueData(); + m_sSpecialRenderData.inactiveBorderColor = CGradientValueData(); m_sSpecialRenderData.alpha = 1.f; m_sSpecialRenderData.alphaInactive = -1.f; m_sAdditionalConfigData.forceNoBlur = false; diff --git a/src/Window.hpp b/src/Window.hpp index d700af7e..bbe7dc07 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -106,13 +106,13 @@ class CWindowOverridableVar { }; struct SWindowSpecialRenderData { - CWindowOverridableVar alphaOverride = false; - CWindowOverridableVar alpha = 1.f; - CWindowOverridableVar alphaInactiveOverride = false; - CWindowOverridableVar alphaInactive = -1.f; // -1 means unset + CWindowOverridableVar alphaOverride = false; + CWindowOverridableVar alpha = 1.f; + CWindowOverridableVar alphaInactiveOverride = false; + CWindowOverridableVar alphaInactive = -1.f; // -1 means unset - CWindowOverridableVar activeBorderColor = -1; // -1 means unset - CWindowOverridableVar inactiveBorderColor = -1; // -1 means unset + CWindowOverridableVar activeBorderColor = CGradientValueData(); // empty color vector means unset + CWindowOverridableVar inactiveBorderColor = CGradientValueData(); // empty color vector means unset // set by the layout CWindowOverridableVar borderSize = -1; // -1 means unset diff --git a/src/config/ConfigDataValues.hpp b/src/config/ConfigDataValues.hpp index f6bf9881..14b5e51d 100644 --- a/src/config/ConfigDataValues.hpp +++ b/src/config/ConfigDataValues.hpp @@ -16,6 +16,7 @@ class ICustomConfigValueData { class CGradientValueData : public ICustomConfigValueData { public: + CGradientValueData(){}; CGradientValueData(CColor col) { m_vColors.push_back(col); }; diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 490f14c9..79855d27 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -1138,9 +1138,9 @@ std::string dispatchSetProp(std::string request) { } else if (PROP == "alphainactive") { PWINDOW->m_sSpecialRenderData.alphaInactive.forceSetIgnoreLocked(std::stof(VAL), lock); } else if (PROP == "activebordercolor") { - PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock); + PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock); } else if (PROP == "inactivebordercolor") { - PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock); + PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(CGradientValueData(CColor(configStringToInt(VAL))), lock); } else if (PROP == "forcergbx") { PWINDOW->m_sAdditionalConfigData.forceRGBX.forceSetIgnoreLocked(configStringToInt(VAL), lock); } else if (PROP == "bordersize") {