From e25a6a559a422cf3ae0cbd56242caef98a6189ec Mon Sep 17 00:00:00 2001 From: Bennch Date: Fri, 16 Jan 2026 21:23:45 +0100 Subject: [PATCH] Add button hover effect and inactive bar color support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds two new features to hyprbars: 1. Button hover background effect: - Added `button_hover_bg_color` config option for Windows 11-style hover effect - Hover rectangles are properly centered on button icons - Close button (rightmost) extends to window edge - All buttons include half padding on each side for even spacing - Hover effect scales with bar_button_padding setting 2. Separate bar colors for active/inactive windows: - Added `bar_color_inactive` config option - Bar color now changes based on window focus state - Matches Hyprland's border behavior (active/inactive) - Smooth animated transition between colors - Falls back to `bar_color` if `bar_color_inactive` is not set Config example: plugin:hyprbars:button_hover_bg_color = rgba(ffffff22) plugin:hyprbars:bar_color = rgba(33333388) plugin:hyprbars:bar_color_inactive = rgba(2E2E32FF) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- hyprbars/barDeco.cpp | 75 ++++++++++++++++++++++++++++++++++---------- hyprbars/main.cpp | 2 ++ 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/hyprbars/barDeco.cpp b/hyprbars/barDeco.cpp index feb973f..1a19fe8 100644 --- a/hyprbars/barDeco.cpp +++ b/hyprbars/barDeco.cpp @@ -519,6 +519,7 @@ void CHyprBar::renderBarButtonsText(CBox* barBox, const float scale, const float static auto* const PBARPADDING = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_padding")->getDataStaticPtr(); static auto* const PALIGNBUTTONS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment")->getDataStaticPtr(); static auto* const PICONONHOVER = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:icon_on_hover")->getDataStaticPtr(); + static auto* const PHOVERBGCOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:button_hover_bg_color")->getDataStaticPtr(); const bool BUTTONSRIGHT = std::string{*PALIGNBUTTONS} != "left"; const auto visibleCount = getVisibleButtonCount(PBARBUTTONPADDING, PBARPADDING, Vector2D{barBox->w, barBox->h}, scale); @@ -552,11 +553,44 @@ void CHyprBar::renderBarButtonsText(CBox* barBox, const float scale, const float CBox pos = {barBox->x + (BUTTONSRIGHT ? barBox->width - offset - scaledButtonSize : offset), barBox->y + (barBox->height - scaledButtonSize) / 2.0, scaledButtonSize, scaledButtonSize}; + // Check if this button is being hovered + bool currentBit = (m_iButtonHoverState & (1 << i)) != 0; + + // Draw hover background rectangle if hovering + if (currentBit && **PHOVERBGCOLOR > 0) { + const auto hoverColor = CHyprColor(**PHOVERBGCOLOR); + // Create consistent hover box based on bar height + const float hoverSize = barBox->height * 0.9; // 90% of bar height for nice padding + + // Special case: first button (index 0, rightmost = Close) extends to edge + const bool isFirstButton = (i == 0); + const bool isLastButton = (i == visibleCount - 1); + float hoverX, hoverWidth; + + if (BUTTONSRIGHT && isFirstButton) { + // Close button (rightmost): include half padding on left, extend to edge on right + hoverX = pos.x - scaledButtonsPad / 2.0; + hoverWidth = barBox->x + barBox->width - hoverX; + } else if (BUTTONSRIGHT && isLastButton) { + // Leftmost button: include half padding on left and right + hoverWidth = scaledButtonSize + scaledButtonsPad; + hoverX = pos.x - scaledButtonsPad / 2.0; + } else { + // Middle buttons: include half padding on both sides + hoverWidth = scaledButtonSize + scaledButtonsPad; + hoverX = pos.x - scaledButtonsPad / 2.0; + } + + CBox hoverBox = {hoverX, + barBox->y + (barBox->height - hoverSize) / 2.0, + hoverWidth, + hoverSize}; + g_pHyprOpenGL->renderRect(hoverBox, hoverColor, {.round = 3}); // Rounded corners + } + if (!**PICONONHOVER || (**PICONONHOVER && m_iButtonHoverState > 0)) g_pHyprOpenGL->renderTexture(button.iconTex, pos, {.a = a}); offset += scaledButtonsPad + scaledButtonSize; - - bool currentBit = (m_iButtonHoverState & (1 << i)) != 0; if (hovering != currentBit) { m_iButtonHoverState ^= (1 << i); // damage to get rid of some artifacts when icons are "hidden" @@ -588,24 +622,31 @@ void CHyprBar::draw(PHLMONITOR pMonitor, const float& a) { void CHyprBar::renderPass(PHLMONITOR pMonitor, const float& a) { const auto PWINDOW = m_pWindow.lock(); - static auto* const PCOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_color")->getDataStaticPtr(); - static auto* const PHEIGHT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->getDataStaticPtr(); - static auto* const PPRECEDENCE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_precedence_over_border")->getDataStaticPtr(); - static auto* const PALIGNBUTTONS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment")->getDataStaticPtr(); - static auto* const PENABLETITLE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_title_enabled")->getDataStaticPtr(); - static auto* const PENABLEBLUR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_blur")->getDataStaticPtr(); - static auto* const PENABLEBLURGLOBAL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "decoration:blur:enabled")->getDataStaticPtr(); - static auto* const PINACTIVECOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:inactive_button_color")->getDataStaticPtr(); + static auto* const PCOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_color")->getDataStaticPtr(); + static auto* const PCOLORINACTIVE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_color_inactive")->getDataStaticPtr(); + static auto* const PHEIGHT = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->getDataStaticPtr(); + static auto* const PPRECEDENCE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_precedence_over_border")->getDataStaticPtr(); + static auto* const PALIGNBUTTONS = (Hyprlang::STRING const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_buttons_alignment")->getDataStaticPtr(); + static auto* const PENABLETITLE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_title_enabled")->getDataStaticPtr(); + static auto* const PENABLEBLUR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_blur")->getDataStaticPtr(); + static auto* const PENABLEBLURGLOBAL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "decoration:blur:enabled")->getDataStaticPtr(); + static auto* const PINACTIVECOLOR = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:inactive_button_color")->getDataStaticPtr(); - if (**PINACTIVECOLOR > 0) { - bool currentWindowFocus = PWINDOW == Desktop::focusState()->window(); - if (currentWindowFocus != m_bWindowHasFocus) { - m_bWindowHasFocus = currentWindowFocus; - m_bButtonsDirty = true; - } + // Check window focus state + bool currentWindowFocus = PWINDOW == Desktop::focusState()->window(); + if (currentWindowFocus != m_bWindowHasFocus) { + m_bWindowHasFocus = currentWindowFocus; + if (**PINACTIVECOLOR > 0) + m_bButtonsDirty = true; } - const CHyprColor DEST_COLOR = m_bForcedBarColor.value_or(**PCOLOR); + // Choose bar color based on focus state + CHyprColor baseBarColor = **PCOLOR; + if (!m_bWindowHasFocus && **PCOLORINACTIVE > 0) { + baseBarColor = **PCOLORINACTIVE; + } + + const CHyprColor DEST_COLOR = m_bForcedBarColor.value_or(baseBarColor); if (DEST_COLOR != m_cRealBarColor->goal()) *m_cRealBarColor = DEST_COLOR; diff --git a/hyprbars/main.cpp b/hyprbars/main.cpp index 22c9d1f..1f90e9c 100644 --- a/hyprbars/main.cpp +++ b/hyprbars/main.cpp @@ -133,6 +133,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { [&](void* self, SCallbackInfo& info, std::any data) { onUpdateWindowRules(std::any_cast(data)); }); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_color", Hyprlang::INT{*configStringToInt("rgba(33333388)")}); + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_color_inactive", Hyprlang::INT{0}); // unset by default, uses bar_color if not set HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_height", Hyprlang::INT{15}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:col.text", Hyprlang::INT{*configStringToInt("rgba(ffffffff)")}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_text_size", Hyprlang::INT{10}); @@ -148,6 +149,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:enabled", Hyprlang::INT{1}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:icon_on_hover", Hyprlang::INT{0}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:inactive_button_color", Hyprlang::INT{0}); // unset + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:button_hover_bg_color", Hyprlang::INT{*configStringToInt("rgba(ffffff11)")}); // subtle white hover HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:on_double_click", Hyprlang::STRING{""}); HyprlandAPI::addConfigKeyword(PHANDLE, "plugin:hyprbars:hyprbars-button", onNewButton, Hyprlang::SHandlerOptions{});