From 206ac000b98ebab6acef9047f2dbd211d715c5b4 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Mon, 22 May 2023 21:40:32 +0200 Subject: [PATCH] groupbars: add title and gradient rendering --- src/config/ConfigManager.cpp | 2 + .../decorations/CHyprGroupBarDecoration.cpp | 196 +++++++++++++++++- .../decorations/CHyprGroupBarDecoration.hpp | 31 ++- 3 files changed, 219 insertions(+), 10 deletions(-) diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 371234c3..b475bd4d 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -94,6 +94,8 @@ void CConfigManager::setDefaultVars() { configValues["misc:cursor_zoom_factor"].floatValue = 1.f; configValues["misc:cursor_zoom_rigid"].intValue = 0; configValues["misc:allow_session_lock_restore"].intValue = 0; + configValues["misc:render_titles_in_groupbar"].intValue = 1; + configValues["misc:groupbar_titles_font_size"].intValue = 8; configValues["debug:int"].intValue = 0; configValues["debug:log_damage"].intValue = 0; diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp index c3663970..ac093348 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.cpp +++ b/src/render/decorations/CHyprGroupBarDecoration.cpp @@ -1,5 +1,7 @@ #include "CHyprGroupBarDecoration.hpp" #include "../../Compositor.hpp" +#include +#include CHyprGroupBarDecoration::CHyprGroupBarDecoration(CWindow* pWindow) { m_pWindow = pWindow; @@ -17,25 +19,31 @@ eDecorationType CHyprGroupBarDecoration::getDecorationType() { constexpr int BAR_INDICATOR_HEIGHT = 3; constexpr int BAR_PADDING_OUTER_VERT = 2; +constexpr int BAR_TEXT_PAD = 2; // void CHyprGroupBarDecoration::updateWindow(CWindow* pWindow) { damageEntire(); - const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID); + const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID); - const auto WORKSPACEOFFSET = PWORKSPACE && !pWindow->m_bPinned ? PWORKSPACE->m_vRenderOffset.vec() : Vector2D(); + const auto WORKSPACEOFFSET = PWORKSPACE && !pWindow->m_bPinned ? PWORKSPACE->m_vRenderOffset.vec() : Vector2D(); + + static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("misc:render_titles_in_groupbar")->intValue; + static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("misc:groupbar_titles_font_size")->intValue; if (pWindow->m_vRealPosition.vec() + WORKSPACEOFFSET != m_vLastWindowPos || pWindow->m_vRealSize.vec() != m_vLastWindowSize) { // we draw 3px above the window's border with 3px const auto PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue; - m_seExtents.topLeft = Vector2D(0, *PBORDERSIZE + BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT); + m_seExtents.topLeft = Vector2D(0, *PBORDERSIZE + BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PRENDERTITLES ? *PTITLEFONTSIZE : 0)); m_seExtents.bottomRight = Vector2D(); m_vLastWindowPos = pWindow->m_vRealPosition.vec() + WORKSPACEOFFSET; m_vLastWindowSize = pWindow->m_vRealSize.vec(); + + invalidateTextures(); } if (!m_pWindow->m_sGroupData.pNextWindow) { @@ -74,7 +82,10 @@ void CHyprGroupBarDecoration::damageEntire() { void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& offset) { // get how many bars we will draw - int barsToDraw = m_dwGroupMembers.size(); + int barsToDraw = m_dwGroupMembers.size(); + + static auto* const PRENDERTITLES = &g_pConfigManager->getConfigValuePtr("misc:render_titles_in_groupbar")->intValue; + static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("misc:groupbar_titles_font_size")->intValue; if (barsToDraw < 1 || m_pWindow->isHidden() || !g_pCompositor->windowValidMapped(m_pWindow)) return; @@ -90,7 +101,7 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& for (int i = 0; i < barsToDraw; ++i) { wlr_box rect = {m_vLastWindowPos.x + xoff - pMonitor->vecPosition.x + offset.x, - m_vLastWindowPos.y - m_seExtents.topLeft.y + BAR_PADDING_OUTER_VERT - pMonitor->vecPosition.y + offset.y, BARW, BAR_INDICATOR_HEIGHT}; + m_vLastWindowPos.y - BAR_PADDING_OUTER_VERT - BAR_INDICATOR_HEIGHT - pMonitor->vecPosition.y + offset.y, BARW, BAR_INDICATOR_HEIGHT}; if (rect.width <= 0 || rect.height <= 0) break; @@ -105,10 +116,185 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& color.a *= a; g_pHyprOpenGL->renderRect(&rect, color); + // render title if necessary + if (*PRENDERTITLES) { + CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[i]->m_szTitle); + + if (!pTitleTex) + pTitleTex = + m_dpTitleTextures + .emplace_back(std::make_unique(m_dwGroupMembers[i], Vector2D{BARW * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale})) + .get(); + + rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * 0.8 * pMonitor->scale; + + rect.y -= rect.height; + rect.width = BARW * pMonitor->scale; + + refreshGradients(); + + g_pHyprOpenGL->renderTexture((m_dwGroupMembers[i] == g_pCompositor->m_pLastWindow ? m_tGradientActive : m_tGradientInactive), &rect, 1.0); + + rect.y -= (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * 0.2 * pMonitor->scale; + rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale; + + g_pHyprOpenGL->renderTexture(pTitleTex->tex, &rect, 1.f); + } + xoff += PAD + BARW; } + + if (*PRENDERTITLES) + clearUnusedTextures(); } SWindowDecorationExtents CHyprGroupBarDecoration::getWindowDecorationReservedArea() { return SWindowDecorationExtents{{0, BAR_INDICATOR_HEIGHT + BAR_PADDING_OUTER_VERT * 2}, {}}; +} + +CTitleTex* CHyprGroupBarDecoration::textureFromTitle(const std::string& title) { + for (auto& tex : m_dpTitleTextures) { + if (tex->szContent == title) + return tex.get(); + } + + return nullptr; +} + +void CHyprGroupBarDecoration::clearUnusedTextures() { + for (auto& tex : m_dpTitleTextures | std::views::reverse) { + bool found = false; + + for (auto& w : m_dwGroupMembers) { + if (tex->szContent == w->m_szTitle && tex->pWindowOwner == w) { + found = true; + break; + } + } + + if (!found) + std::erase(m_dpTitleTextures, tex); + } +} + +void CHyprGroupBarDecoration::invalidateTextures() { + m_dpTitleTextures.clear(); +} + +CTitleTex::CTitleTex(CWindow* pWindow, const Vector2D& bufferSize) { + szContent = pWindow->m_szTitle; + pWindowOwner = pWindow; + const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y); + const auto CAIRO = cairo_create(CAIROSURFACE); + + static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("misc:groupbar_titles_font_size")->intValue; + + // clear the pixmap + cairo_save(CAIRO); + cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR); + cairo_paint(CAIRO); + cairo_restore(CAIRO); + + // draw title using Pango + PangoLayout* layout = pango_cairo_create_layout(CAIRO); + pango_layout_set_text(layout, szContent.c_str(), -1); + + PangoFontDescription* fontDesc = pango_font_description_from_string("Sans"); + pango_font_description_set_size(fontDesc, *PTITLEFONTSIZE * PANGO_SCALE); + pango_layout_set_font_description(layout, fontDesc); + pango_font_description_free(fontDesc); + + const int maxWidth = bufferSize.x; + + pango_layout_set_width(layout, maxWidth * PANGO_SCALE); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + + cairo_set_source_rgba(CAIRO, 1.f, 1.f, 1.f, 1.f); + + int layoutWidth, layoutHeight; + pango_layout_get_size(layout, &layoutWidth, &layoutHeight); + const int xOffset = std::round((bufferSize.x / 2.0 - layoutWidth / PANGO_SCALE / 2.0)); + const int yOffset = std::round((bufferSize.y / 2.0 - layoutHeight / PANGO_SCALE / 2.0)); + + cairo_move_to(CAIRO, xOffset, yOffset); + pango_cairo_show_layout(CAIRO, layout); + + g_object_unref(layout); + + cairo_surface_flush(CAIROSURFACE); + + // copy the data to an OpenGL texture we have + const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); + tex.allocate(); + glBindTexture(GL_TEXTURE_2D, tex.m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + +#ifndef GLES2 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); +#endif + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize.x, bufferSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); + + // delete cairo + cairo_destroy(CAIRO); + cairo_surface_destroy(CAIROSURFACE); +} + +CTitleTex::~CTitleTex() { + tex.destroyTexture(); +} + +void renderGradientTo(CTexture& tex, const CColor& grad) { + + const Vector2D& bufferSize = g_pCompositor->m_pLastMonitor->vecPixelSize; + + const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y); + const auto CAIRO = cairo_create(CAIROSURFACE); + + // clear the pixmap + cairo_save(CAIRO); + cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR); + cairo_paint(CAIRO); + cairo_restore(CAIRO); + + cairo_pattern_t* pattern; + pattern = cairo_pattern_create_linear(0, 0, 0, bufferSize.y); + cairo_pattern_add_color_stop_rgba(pattern, 1, grad.r, grad.g, grad.b, grad.a); + cairo_pattern_add_color_stop_rgba(pattern, 0, grad.r, grad.g, grad.b, 0); + cairo_rectangle(CAIRO, 0, 0, bufferSize.x, bufferSize.y); + cairo_set_source(CAIRO, pattern); + cairo_fill(CAIRO); + cairo_pattern_destroy(pattern); + + cairo_surface_flush(CAIROSURFACE); + + // copy the data to an OpenGL texture we have + const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); + tex.allocate(); + glBindTexture(GL_TEXTURE_2D, tex.m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + +#ifndef GLES2 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); +#endif + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize.x, bufferSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); + + // delete cairo + cairo_destroy(CAIRO); + cairo_surface_destroy(CAIROSURFACE); +} + +void CHyprGroupBarDecoration::refreshGradients() { + if (m_tGradientActive.m_iTexID > 0) + return; + + static auto* const PGROUPCOLACTIVE = &g_pConfigManager->getConfigValuePtr("general:col.group_border_active")->data; + static auto* const PGROUPCOLINACTIVE = &g_pConfigManager->getConfigValuePtr("general:col.group_border")->data; + renderGradientTo(m_tGradientActive, ((CGradientValueData*)PGROUPCOLACTIVE->get())->m_vColors[0]); + renderGradientTo(m_tGradientInactive, ((CGradientValueData*)PGROUPCOLINACTIVE->get())->m_vColors[0]); } \ No newline at end of file diff --git a/src/render/decorations/CHyprGroupBarDecoration.hpp b/src/render/decorations/CHyprGroupBarDecoration.hpp index 712c0440..3a0fec27 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.hpp +++ b/src/render/decorations/CHyprGroupBarDecoration.hpp @@ -2,6 +2,18 @@ #include "IHyprWindowDecoration.hpp" #include +#include "../Texture.hpp" +#include + +class CTitleTex { + public: + CTitleTex(CWindow* pWindow, const Vector2D& bufferSize); + ~CTitleTex(); + + CTexture tex; + std::string szContent; + CWindow* pWindowOwner = nullptr; +}; class CHyprGroupBarDecoration : public IHyprWindowDecoration { public: @@ -21,12 +33,21 @@ class CHyprGroupBarDecoration : public IHyprWindowDecoration { virtual SWindowDecorationExtents getWindowDecorationReservedArea(); private: - SWindowDecorationExtents m_seExtents; + SWindowDecorationExtents m_seExtents; - CWindow* m_pWindow = nullptr; + CWindow* m_pWindow = nullptr; - Vector2D m_vLastWindowPos; - Vector2D m_vLastWindowSize; + Vector2D m_vLastWindowPos; + Vector2D m_vLastWindowSize; - std::deque m_dwGroupMembers; + std::deque m_dwGroupMembers; + std::deque> m_dpTitleTextures; + + CTitleTex* textureFromTitle(const std::string&); + void clearUnusedTextures(); + void invalidateTextures(); + + void refreshGradients(); + CTexture m_tGradientActive; + CTexture m_tGradientInactive; }; \ No newline at end of file