#include "barDeco.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "globals.hpp" #include "BarPassElement.hpp" #include using namespace Render::GL; static CHyprColor configColor(Config::INTEGER color) { return CHyprColor{static_cast(color)}; } CHyprBar::CHyprBar(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow) { m_pWindow = pWindow; const auto PMONITOR = pWindow->m_monitor.lock(); PMONITOR->m_scheduledRecalc = true; // button events m_pMouseButtonCallback = Event::bus()->m_events.input.mouse.button.listen([&](IPointer::SButtonEvent e, Event::SCallbackInfo& info) { onMouseButton(info, e); }); m_pTouchDownCallback = Event::bus()->m_events.input.touch.down.listen([&](ITouch::SDownEvent e, Event::SCallbackInfo& info) { onTouchDown(info, e); }); m_pTouchUpCallback = Event::bus()->m_events.input.touch.up.listen([&](ITouch::SUpEvent e, Event::SCallbackInfo& info) { onTouchUp(info, e); }); // move events m_pTouchMoveCallback = Event::bus()->m_events.input.touch.motion.listen([&](ITouch::SMotionEvent e, Event::SCallbackInfo& info) { onTouchMove(info, e); }); m_pMouseMoveCallback = Event::bus()->m_events.input.mouse.move.listen([&](Vector2D c, Event::SCallbackInfo& info) { onMouseMove(c); }); g_pAnimationManager->createAnimation(configColor(g_pGlobalState->config.barColor->value()), m_cRealBarColor, Config::animationTree()->getAnimationPropertyConfig("border"), pWindow, AVARDAMAGE_NONE); m_cRealBarColor->setUpdateCallback([&](auto) { damageEntire(); }); } CHyprBar::~CHyprBar() { std::erase(g_pGlobalState->bars, m_self); } SDecorationPositioningInfo CHyprBar::getPositioningInfo() { const auto HEIGHT = g_pGlobalState->config.barHeight->value(); const auto ENABLED = g_pGlobalState->config.enabled->value(); const auto PRECEDENCE = g_pGlobalState->config.barPrecedenceOverBorder->value(); SDecorationPositioningInfo info; info.policy = m_hidden ? DECORATION_POSITION_ABSOLUTE : DECORATION_POSITION_STICKY; info.edges = DECORATION_EDGE_TOP; info.priority = PRECEDENCE ? 10005 : 5000; info.reserved = true; info.desiredExtents = {{0, m_hidden || !ENABLED ? 0 : HEIGHT}, {0, 0}}; return info; } void CHyprBar::onPositioningReply(const SDecorationPositioningReply& reply) { if (reply.assignedGeometry.size() != m_bAssignedBox.size()) m_bWindowSizeChanged = true; m_bAssignedBox = reply.assignedGeometry; } std::string CHyprBar::getDisplayName() { return "Hyprbar"; } bool CHyprBar::inputIsValid() { if (!g_pGlobalState->config.enabled->value()) return false; if (!m_pWindow->m_workspace || !m_pWindow->m_workspace->isVisible() || !g_pInputManager->m_exclusiveLSes.empty() || (g_pSeatManager->m_seatGrab && !g_pSeatManager->m_seatGrab->accepts(m_pWindow->wlSurface()->resource()))) return false; const auto WINDOWATCURSOR = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING); auto focusState = Desktop::focusState(); auto window = focusState->window(); auto monitor = focusState->monitor(); if (WINDOWATCURSOR != m_pWindow && m_pWindow != window) return false; // check if input is on top or overlay shell layers auto PMONITOR = monitor; PHLLS foundSurface = nullptr; Vector2D surfaceCoords; // check top layer g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &surfaceCoords, &foundSurface); if (foundSurface) return false; // check overlay layer g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &PMONITOR->m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &surfaceCoords, &foundSurface); if (foundSurface) return false; return true; } void CHyprBar::onMouseButton(Event::SCallbackInfo& info, IPointer::SButtonEvent e) { if (!inputIsValid()) return; if (e.state != WL_POINTER_BUTTON_STATE_PRESSED) { handleUpEvent(info); return; } handleDownEvent(info, std::nullopt); } void CHyprBar::onTouchDown(Event::SCallbackInfo& info, ITouch::SDownEvent e) { // Don't do anything if you're already grabbed a window with another finger if (!inputIsValid() || e.touchID != 0) return; handleDownEvent(info, e); } void CHyprBar::onTouchUp(Event::SCallbackInfo& info, ITouch::SUpEvent e) { if (!m_bDragPending || !m_bTouchEv || e.touchID != m_touchId) return; handleUpEvent(info); } void CHyprBar::onMouseMove(Vector2D coords) { // ensure proper redraws of button icons on hover when using hardware cursors if (g_pGlobalState->config.iconOnHover->value()) damageOnButtonHover(); if (!m_bDragPending || m_bTouchEv || !validMapped(m_pWindow) || m_touchId != 0) return; m_bDragPending = false; handleMovement(); } void CHyprBar::onTouchMove(Event::SCallbackInfo& info, ITouch::SMotionEvent e) { if (!m_bDragPending || !m_bTouchEv || !validMapped(m_pWindow) || e.touchID != m_touchId) return; auto PMONITOR = m_pWindow->m_monitor.lock(); PMONITOR = PMONITOR ? PMONITOR : Desktop::focusState()->monitor(); const auto COORDS = Vector2D(PMONITOR->m_position.x + e.pos.x * PMONITOR->m_size.x, PMONITOR->m_position.y + e.pos.y * PMONITOR->m_size.y); if (!m_bDraggingThis) { // Initial setup for dragging a window. g_pKeybindManager->m_dispatchers["setfloating"]("activewindow"); g_pKeybindManager->m_dispatchers["resizewindowpixel"]("exact 50% 50%,activewindow"); // pin it so you can change workspaces while dragging a window g_pKeybindManager->m_dispatchers["pin"]("activewindow"); } g_pKeybindManager->m_dispatchers["movewindowpixel"](std::format("exact {} {},activewindow", (int)(COORDS.x - (assignedBoxGlobal().w / 2)), (int)COORDS.y)); m_bDraggingThis = true; } void CHyprBar::handleDownEvent(Event::SCallbackInfo& info, std::optional touchEvent) { m_bTouchEv = touchEvent.has_value(); if (m_bTouchEv) m_touchId = touchEvent.value().touchID; const auto PWINDOW = m_pWindow.lock(); auto COORDS = cursorRelativeToBar(); if (m_bTouchEv) { ITouch::SDownEvent e = touchEvent.value(); auto PMONITOR = g_pCompositor->getMonitorFromName(!e.device->m_boundOutput.empty() ? e.device->m_boundOutput : ""); PMONITOR = PMONITOR ? PMONITOR : Desktop::focusState()->monitor(); COORDS = Vector2D(PMONITOR->m_position.x + e.pos.x * PMONITOR->m_size.x, PMONITOR->m_position.y + e.pos.y * PMONITOR->m_size.y) - assignedBoxGlobal().pos(); } const auto HEIGHT = g_pGlobalState->config.barHeight->value(); const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value(); const auto BARPADDING = g_pGlobalState->config.barPadding->value(); const auto ALIGNBUTTONS = g_pGlobalState->config.barButtonsAlignment->value(); const auto ON_DOUBLE_CLICK = g_pGlobalState->config.onDoubleClick->value(); const bool BUTTONSRIGHT = ALIGNBUTTONS != "left"; if (!VECINRECT(COORDS, 0, 0, assignedBoxGlobal().w, HEIGHT - 1)) { if (m_bDraggingThis) { if (m_bTouchEv) g_pKeybindManager->m_dispatchers["settiled"]("activewindow"); g_pKeybindManager->m_dispatchers["mouse"]("0movewindow"); Log::logger->log(Log::DEBUG, "[hyprbars] Dragging ended on {:x}", (uintptr_t)PWINDOW.get()); } m_bDraggingThis = false; m_bDragPending = false; m_bTouchEv = false; return; } if (Desktop::focusState()->window() != PWINDOW) Desktop::focusState()->fullWindowFocus(PWINDOW, Desktop::FOCUS_REASON_CLICK); if (PWINDOW->m_isFloating) g_pCompositor->changeWindowZOrder(PWINDOW, true); info.cancelled = true; m_bCancelledDown = true; if (doButtonPress(BARPADDING, BARBUTTONPADDING, HEIGHT, COORDS, BUTTONSRIGHT)) return; if (!ON_DOUBLE_CLICK.empty() && std::chrono::duration_cast(Time::steadyNow() - m_lastMouseDown).count() < 400 /* Arbitrary delay I found suitable */) { Config::Supplementary::executor()->spawn(ON_DOUBLE_CLICK); m_bDragPending = false; } else { m_lastMouseDown = Time::steadyNow(); m_bDragPending = true; } } void CHyprBar::handleUpEvent(Event::SCallbackInfo& info) { if (m_pWindow.lock() != Desktop::focusState()->window()) return; if (m_bCancelledDown) info.cancelled = true; m_bCancelledDown = false; if (m_bDraggingThis) { g_pKeybindManager->changeMouseBindMode(MBIND_INVALID); m_bDraggingThis = false; if (m_bTouchEv) Config::Actions::floatWindow(Config::Actions::eTogglableAction::TOGGLE_ACTION_DISABLE); Log::logger->log(Log::DEBUG, "[hyprbars] Dragging ended on {:x}", (uintptr_t)m_pWindow.lock().get()); } m_bDragPending = false; m_bTouchEv = false; m_touchId = 0; } void CHyprBar::handleMovement() { g_pKeybindManager->changeMouseBindMode(MBIND_MOVE); m_bDraggingThis = true; Log::logger->log(Log::DEBUG, "[hyprbars] Dragging initiated on {:x}", (uintptr_t)m_pWindow.lock().get()); return; } bool CHyprBar::doButtonPress(Config::INTEGER barPadding, Config::INTEGER barButtonPadding, Config::INTEGER barHeight, Vector2D COORDS, const bool BUTTONSRIGHT) { //check if on a button float offset = barPadding; for (auto& b : g_pGlobalState->buttons) { const auto BARBUF = Vector2D{(int)assignedBoxGlobal().w, barHeight}; Vector2D currentPos = Vector2D{(BUTTONSRIGHT ? BARBUF.x - barButtonPadding - b.size - offset : offset), (BARBUF.y - b.size) / 2.0}.floor(); if (VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + b.size + barButtonPadding, currentPos.y + b.size)) { // hit on close g_pKeybindManager->m_dispatchers["exec"](b.cmd); return true; } offset += barButtonPadding + b.size; } return false; } void CHyprBar::renderBarTitle(const Vector2D& bufferSize, const float scale) { const auto COLORVAL = g_pGlobalState->config.textColor->value(); const auto SIZE = g_pGlobalState->config.barTextSize->value(); const auto FONT = g_pGlobalState->config.barTextFont->value(); const auto ALIGN = g_pGlobalState->config.barTextAlign->value(); const auto BARPADDING = g_pGlobalState->config.barPadding->value(); const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value(); float buttonSizes = BARBUTTONPADDING; for (auto& b : g_pGlobalState->buttons) { buttonSizes += b.size + BARBUTTONPADDING; } const int scaledSize = std::round(SIZE * scale); const auto scaledButtonsSize = buttonSizes * scale; const auto scaledBarPadding = BARPADDING * scale; const int paddingTotal = scaledBarPadding * 2 + scaledButtonsSize + (ALIGN != "left" ? scaledButtonsSize : 0); const int maxWidth = std::clamp(static_cast(bufferSize.x - paddingTotal), 0, INT_MAX); if (m_szLastTitle.empty() || maxWidth < 1) { m_pTextTex = nullptr; return; } const CHyprColor COLOR = m_bForcedTitleColor.value_or(configColor(COLORVAL)); m_pTextTex = g_pHyprRenderer->renderText(m_szLastTitle, COLOR, scaledSize, false, FONT, maxWidth); } size_t CHyprBar::getVisibleButtonCount(Config::INTEGER barButtonPadding, Config::INTEGER barPadding, const Vector2D& bufferSize, const float scale) { float availableSpace = bufferSize.x - barPadding * scale * 2; size_t count = 0; for (const auto& button : g_pGlobalState->buttons) { const float buttonSpace = (button.size + barButtonPadding) * scale; if (availableSpace >= buttonSpace) { count++; availableSpace -= buttonSpace; } else break; } return count; } void CHyprBar::renderBarButtons(CBox* barBox, const float scale, const float a) { const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value(); const auto BARPADDING = g_pGlobalState->config.barPadding->value(); const auto ALIGNBUTTONS = g_pGlobalState->config.barButtonsAlignment->value(); const auto INACTIVECOLOR = g_pGlobalState->config.inactiveButtonColor->value(); const bool BUTTONSRIGHT = ALIGNBUTTONS != "left"; const auto visibleCount = getVisibleButtonCount(BARBUTTONPADDING, BARPADDING, Vector2D{barBox->w, barBox->h}, scale); const bool INVALIDATEICONS = m_bButtonsDirty || m_bWindowSizeChanged; int offset = BARPADDING * scale; for (size_t i = 0; i < visibleCount; ++i) { auto& button = g_pGlobalState->buttons[i]; const auto scaledButtonSize = button.size * scale; const auto scaledButtonsPad = BARBUTTONPADDING * scale; auto color = button.bgcol; if (INACTIVECOLOR > 0) { color = m_bWindowHasFocus ? color : configColor(INACTIVECOLOR); if (INVALIDATEICONS && button.userfg && button.iconTex) button.iconTex = nullptr; } color.a *= a; CBox buttonBox = {barBox->x + (BUTTONSRIGHT ? barBox->w - offset - scaledButtonSize : offset), barBox->y + (barBox->h - scaledButtonSize) / 2.0, scaledButtonSize, scaledButtonSize}; buttonBox.round(); g_pHyprOpenGL->renderRect(buttonBox, color, {.round = static_cast(std::round(scaledButtonSize / 2.0)), .roundingPower = 2.F}); offset += scaledButtonsPad + scaledButtonSize; } } void CHyprBar::renderBarButtonsText(CBox* barBox, const float scale, const float a) { const auto HEIGHT = g_pGlobalState->config.barHeight->value(); const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value(); const auto BARPADDING = g_pGlobalState->config.barPadding->value(); const auto ALIGNBUTTONS = g_pGlobalState->config.barButtonsAlignment->value(); const auto ICONONHOVER = g_pGlobalState->config.iconOnHover->value(); const bool BUTTONSRIGHT = ALIGNBUTTONS != "left"; const auto visibleCount = getVisibleButtonCount(BARBUTTONPADDING, BARPADDING, Vector2D{barBox->w, barBox->h}, scale); const auto COORDS = cursorRelativeToBar(); int offset = BARPADDING * scale; float noScaleOffset = BARPADDING; for (size_t i = 0; i < visibleCount; ++i) { auto& button = g_pGlobalState->buttons[i]; const auto scaledButtonSize = button.size * scale; const auto scaledButtonsPad = BARBUTTONPADDING * scale; // check if hovering here const auto BARBUF = Vector2D{(int)assignedBoxGlobal().w, HEIGHT}; Vector2D currentPos = Vector2D{(BUTTONSRIGHT ? BARBUF.x - BARBUTTONPADDING - button.size - noScaleOffset : noScaleOffset), (BARBUF.y - button.size) / 2.0}.floor(); bool hovering = VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + button.size + BARBUTTONPADDING, currentPos.y + button.size); noScaleOffset += BARBUTTONPADDING + button.size; if ((!button.iconTex || button.iconTex->m_texID == 0) && !button.icon.empty()) { // render icon auto fgcol = button.userfg ? button.fgcol : (button.bgcol.r + button.bgcol.g + button.bgcol.b < 1) ? CHyprColor(0xFFFFFFFF) : CHyprColor(0xFF000000); button.iconTex = g_pHyprRenderer->renderText(button.icon, fgcol, std::round(button.size * 0.62 * scale), false, "sans", scaledButtonSize); } if (!button.iconTex || button.iconTex->m_texID == 0) continue; const auto iconX = barBox->x + (BUTTONSRIGHT ? barBox->width - offset - scaledButtonSize / 2.0 : offset + scaledButtonSize / 2.0) - button.iconTex->m_size.x / 2.0; const auto iconY = barBox->y + barBox->height / 2.0 - button.iconTex->m_size.y / 2.0; CBox pos = {iconX, iconY, button.iconTex->m_size.x, button.iconTex->m_size.y}; if (!ICONONHOVER || (ICONONHOVER && 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" damageEntire(); } } } void CHyprBar::draw(PHLMONITOR pMonitor, const float& a) { const auto ENABLED = g_pGlobalState->config.enabled->value(); if (m_bLastEnabledState != ENABLED) { m_bLastEnabledState = ENABLED; g_pDecorationPositioner->repositionDeco(this); } if (m_hidden || !validMapped(m_pWindow) || !ENABLED) return; const auto PWINDOW = m_pWindow.lock(); if (!PWINDOW->m_ruleApplicator->decorate().valueOrDefault()) return; auto data = CBarPassElement::SBarData{this, a}; g_pHyprRenderer->m_renderPass.add(makeUnique(data)); } void CHyprBar::renderPass(PHLMONITOR pMonitor, const float& a) { const auto PWINDOW = m_pWindow.lock(); static auto* const PENABLEBLURGLOBAL = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "decoration:blur:enabled")->getDataStaticPtr(); const auto BARCOLOR = g_pGlobalState->config.barColor->value(); const auto HEIGHT = g_pGlobalState->config.barHeight->value(); const auto PRECEDENCE = g_pGlobalState->config.barPrecedenceOverBorder->value(); const auto ALIGNBUTTONS = g_pGlobalState->config.barButtonsAlignment->value(); const auto ENABLETITLE = g_pGlobalState->config.barTitleEnabled->value(); const auto ENABLEBLUR = g_pGlobalState->config.barBlur->value(); const auto INACTIVECOLOR = g_pGlobalState->config.inactiveButtonColor->value(); if (INACTIVECOLOR > 0) { bool currentWindowFocus = PWINDOW == Desktop::focusState()->window(); if (currentWindowFocus != m_bWindowHasFocus) { m_bWindowHasFocus = currentWindowFocus; m_bButtonsDirty = true; } } const CHyprColor DEST_COLOR = m_bForcedBarColor.value_or(configColor(BARCOLOR)); if (DEST_COLOR != m_cRealBarColor->goal()) *m_cRealBarColor = DEST_COLOR; CHyprColor color = m_cRealBarColor->value(); color.a *= a; const bool BUTTONSRIGHT = ALIGNBUTTONS != "left"; const bool SHOULDBLUR = ENABLEBLUR && **PENABLEBLURGLOBAL && color.a < 1.F; if (HEIGHT < 1) { m_iLastHeight = HEIGHT; return; } const auto PWORKSPACE = PWINDOW->m_workspace; const auto WORKSPACEOFFSET = PWORKSPACE && !PWINDOW->m_pinned ? PWORKSPACE->m_renderOffset->value() : Vector2D(); const auto ROUNDING = PWINDOW->rounding() + (PRECEDENCE ? 0 : PWINDOW->getRealBorderSize()); const auto scaledRounding = ROUNDING > 0 ? ROUNDING * pMonitor->m_scale - 2 /* idk why but otherwise it looks bad due to the gaps */ : 0; m_seExtents = {{0, HEIGHT}, {}}; const auto DECOBOX = assignedBoxGlobal(); const auto BARBUF = DECOBOX.size() * pMonitor->m_scale; CBox titleBarBox = {DECOBOX.x - pMonitor->m_position.x, DECOBOX.y - pMonitor->m_position.y, DECOBOX.w, DECOBOX.h + ROUNDING * 3 /* to fill the bottom cuz we can't disable rounding there */}; titleBarBox.translate(PWINDOW->m_floatingOffset).scale(pMonitor->m_scale).round(); if (titleBarBox.w < 1 || titleBarBox.h < 1) return; g_pHyprOpenGL->scissor(titleBarBox); if (ROUNDING) { // the +1 is a shit garbage temp fix until renderRect supports an alpha matte CBox windowBox = {PWINDOW->m_realPosition->value().x + PWINDOW->m_floatingOffset.x - pMonitor->m_position.x + 1, PWINDOW->m_realPosition->value().y + PWINDOW->m_floatingOffset.y - pMonitor->m_position.y + 1, PWINDOW->m_realSize->value().x - 2, PWINDOW->m_realSize->value().y - 2}; if (windowBox.w < 1 || windowBox.h < 1) return; glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); g_pHyprOpenGL->setCapStatus(GL_STENCIL_TEST, true); glStencilFunc(GL_ALWAYS, 1, -1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); windowBox.translate(WORKSPACEOFFSET).scale(pMonitor->m_scale).round(); g_pHyprOpenGL->renderRect(windowBox, CHyprColor(0, 0, 0, 0), {.round = scaledRounding, .roundingPower = m_pWindow->roundingPower()}); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glStencilFunc(GL_NOTEQUAL, 1, -1); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); } if (SHOULDBLUR) g_pHyprOpenGL->renderRect(titleBarBox, color, {.round = scaledRounding, .roundingPower = m_pWindow->roundingPower(), .blur = true, .blurA = a}); else g_pHyprOpenGL->renderRect(titleBarBox, color, {.round = scaledRounding, .roundingPower = m_pWindow->roundingPower()}); // render title if (ENABLETITLE && (m_szLastTitle != PWINDOW->m_title || m_bWindowSizeChanged || !m_pTextTex || m_pTextTex->m_texID == 0 || m_bTitleColorChanged)) { m_szLastTitle = PWINDOW->m_title; renderBarTitle(BARBUF, pMonitor->m_scale); } if (ROUNDING) { // cleanup stencil glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); g_pHyprOpenGL->setCapStatus(GL_STENCIL_TEST, false); glStencilMask(-1); glStencilFunc(GL_ALWAYS, 1, 0xFF); } CBox textBox = {titleBarBox.x, titleBarBox.y, (int)BARBUF.x, (int)BARBUF.y}; if (ENABLETITLE && m_pTextTex) { const auto BARPADDING = g_pGlobalState->config.barPadding->value(); const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value(); const auto ALIGN = g_pGlobalState->config.barTextAlign->value(); float buttonSizes = BARBUTTONPADDING; for (auto& b : g_pGlobalState->buttons) { buttonSizes += b.size + BARBUTTONPADDING; } const auto scaledBorderSize = PWINDOW->getRealBorderSize() * pMonitor->m_scale; const auto scaledButtonsSize = buttonSizes * pMonitor->m_scale; const auto scaledBarPadding = BARPADDING * pMonitor->m_scale; const auto xOffset = ALIGN == "left" ? std::round(scaledBarPadding + (BUTTONSRIGHT ? 0 : scaledButtonsSize)) : std::round(((BARBUF.x - scaledBorderSize) / 2.0 - m_pTextTex->m_size.x / 2.0)); const auto yOffset = std::round((BARBUF.y - m_pTextTex->m_size.y) / 2.0); CBox titleBox = {textBox.x + xOffset, textBox.y + yOffset, m_pTextTex->m_size.x, m_pTextTex->m_size.y}; g_pHyprOpenGL->renderTexture(m_pTextTex, titleBox, {.a = a}); } renderBarButtons(&textBox, pMonitor->m_scale, a); m_bButtonsDirty = false; g_pHyprOpenGL->scissor(nullptr); renderBarButtonsText(&textBox, pMonitor->m_scale, a); m_bWindowSizeChanged = false; m_bTitleColorChanged = false; // dynamic updates change the extents if (m_iLastHeight != HEIGHT) { PWINDOW->layoutTarget()->recalc(); m_iLastHeight = HEIGHT; } } eDecorationType CHyprBar::getDecorationType() { return DECORATION_CUSTOM; } void CHyprBar::updateWindow(PHLWINDOW pWindow) { damageEntire(); } void CHyprBar::onConfigReloaded() { m_bButtonsDirty = true; m_bTitleColorChanged = true; m_pTextTex = nullptr; g_pDecorationPositioner->repositionDeco(this); damageEntire(); } void CHyprBar::damageEntire() { g_pHyprRenderer->damageBox(assignedBoxGlobal()); } Vector2D CHyprBar::cursorRelativeToBar() { return g_pInputManager->getMouseCoordsInternal() - assignedBoxGlobal().pos(); } eDecorationLayer CHyprBar::getDecorationLayer() { return DECORATION_LAYER_UNDER; } uint64_t CHyprBar::getDecorationFlags() { return DECORATION_ALLOWS_MOUSE_INPUT | (g_pGlobalState->config.barPartOfWindow->value() ? DECORATION_PART_OF_MAIN_WINDOW : 0); } CBox CHyprBar::assignedBoxGlobal() { if (!validMapped(m_pWindow)) return {}; CBox box = m_bAssignedBox; box.translate(g_pDecorationPositioner->getEdgeDefinedPoint(DECORATION_EDGE_TOP, m_pWindow.lock())); const auto PWORKSPACE = m_pWindow->m_workspace; const auto WORKSPACEOFFSET = PWORKSPACE && !m_pWindow->m_pinned ? PWORKSPACE->m_renderOffset->value() : Vector2D(); return box.translate(WORKSPACEOFFSET); } PHLWINDOW CHyprBar::getOwner() { return m_pWindow.lock(); } void CHyprBar::updateRules() { const auto PWINDOW = m_pWindow.lock(); auto prevHidden = m_hidden; auto prevForcedTitleColor = m_bForcedTitleColor; m_bForcedBarColor = std::nullopt; m_bForcedTitleColor = std::nullopt; m_hidden = false; if (PWINDOW->m_ruleApplicator->m_otherProps.props.contains(g_pGlobalState->nobarRuleIdx)) m_hidden = truthy(PWINDOW->m_ruleApplicator->m_otherProps.props.at(g_pGlobalState->nobarRuleIdx)->effect); if (PWINDOW->m_ruleApplicator->m_otherProps.props.contains(g_pGlobalState->barColorRuleIdx)) m_bForcedBarColor = CHyprColor(configStringToInt(PWINDOW->m_ruleApplicator->m_otherProps.props.at(g_pGlobalState->barColorRuleIdx)->effect).value_or(0)); if (PWINDOW->m_ruleApplicator->m_otherProps.props.contains(g_pGlobalState->titleColorRuleIdx)) m_bForcedTitleColor = CHyprColor(configStringToInt(PWINDOW->m_ruleApplicator->m_otherProps.props.at(g_pGlobalState->titleColorRuleIdx)->effect).value_or(0)); if (prevHidden != m_hidden) g_pDecorationPositioner->repositionDeco(this); if (prevForcedTitleColor != m_bForcedTitleColor) m_bTitleColorChanged = true; } void CHyprBar::damageOnButtonHover() { const auto BARPADDING = g_pGlobalState->config.barPadding->value(); const auto BARBUTTONPADDING = g_pGlobalState->config.barButtonPadding->value(); const auto HEIGHT = g_pGlobalState->config.barHeight->value(); const auto ALIGNBUTTONS = g_pGlobalState->config.barButtonsAlignment->value(); const bool BUTTONSRIGHT = ALIGNBUTTONS != "left"; float offset = BARPADDING; const auto COORDS = cursorRelativeToBar(); for (auto& b : g_pGlobalState->buttons) { const auto BARBUF = Vector2D{(int)assignedBoxGlobal().w, HEIGHT}; Vector2D currentPos = Vector2D{(BUTTONSRIGHT ? BARBUF.x - BARBUTTONPADDING - b.size - offset : offset), (BARBUF.y - b.size) / 2.0}.floor(); bool hover = VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + b.size + BARBUTTONPADDING, currentPos.y + b.size); if (hover != m_bButtonHovered) { m_bButtonHovered = hover; damageEntire(); } offset += BARBUTTONPADDING + b.size; } }