mirror of
https://github.com/hyprwm/hyprland-plugins.git
synced 2026-05-17 16:18:05 +02:00
678 lines
28 KiB
C++
678 lines
28 KiB
C++
#include "barDeco.hpp"
|
|
|
|
#include <hyprland/src/Compositor.hpp>
|
|
#include <hyprland/src/desktop/state/FocusState.hpp>
|
|
#include <hyprland/src/desktop/view/Window.hpp>
|
|
#include <hyprland/src/helpers/MiscFunctions.hpp>
|
|
#include <hyprland/src/managers/SeatManager.hpp>
|
|
#include <hyprland/src/managers/input/InputManager.hpp>
|
|
#include <hyprland/src/render/Renderer.hpp>
|
|
#include <hyprland/src/config/ConfigManager.hpp>
|
|
#include <hyprland/src/config/shared/animation/AnimationTree.hpp>
|
|
#include <hyprland/src/config/shared/parserUtils/ParserUtils.hpp>
|
|
#include <hyprland/src/config/supplementary/executor/Executor.hpp>
|
|
#include <hyprland/src/config/shared/actions/ConfigActions.hpp>
|
|
#include <hyprland/src/managers/animation/AnimationManager.hpp>
|
|
#include <hyprland/src/protocols/LayerShell.hpp>
|
|
#include <hyprland/src/event/EventBus.hpp>
|
|
#include <hyprland/src/layout/LayoutManager.hpp>
|
|
#include <hyprland/src/render/OpenGL.hpp>
|
|
|
|
#include "globals.hpp"
|
|
#include "BarPassElement.hpp"
|
|
|
|
#include <climits>
|
|
|
|
using namespace Render::GL;
|
|
|
|
static CHyprColor configColor(Config::INTEGER color) {
|
|
return CHyprColor{static_cast<uint64_t>(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<ITouch::SDownEvent> 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<std::chrono::milliseconds>(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<int>(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<int>(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<CBarPassElement>(data));
|
|
}
|
|
|
|
void CHyprBar::renderPass(PHLMONITOR pMonitor, const float& a) {
|
|
const auto PWINDOW = m_pWindow.lock();
|
|
|
|
static auto PENABLEBLURGLOBAL = CConfigValue<Config::BOOL>("decoration:blur:enabled");
|
|
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(Config::ParserUtils::parseColor(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(Config::ParserUtils::parseColor(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;
|
|
}
|
|
}
|