summaryrefslogtreecommitdiff
path: root/src/render
diff options
context:
space:
mode:
authorvaxerski <43317083+vaxerski@users.noreply.github.com>2023-05-22 21:40:32 +0200
committervaxerski <43317083+vaxerski@users.noreply.github.com>2023-05-22 21:40:32 +0200
commit206ac000b98ebab6acef9047f2dbd211d715c5b4 (patch)
treefb6bacb44be29e9d1ef92ceebd609a3b848edac7 /src/render
parent1eb6cfd45c6e2e42fc1285628974acef40f03b42 (diff)
groupbars: add title and gradient rendering
Diffstat (limited to 'src/render')
-rw-r--r--src/render/decorations/CHyprGroupBarDecoration.cpp196
-rw-r--r--src/render/decorations/CHyprGroupBarDecoration.hpp31
2 files changed, 217 insertions, 10 deletions
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 <ranges>
+#include <pango/pangocairo.h>
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<CTitleTex>(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 <deque>
+#include "../Texture.hpp"
+#include <string>
+
+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;
+
+ Vector2D m_vLastWindowPos;
+ Vector2D m_vLastWindowSize;
- CWindow* m_pWindow = nullptr;
+ std::deque<CWindow*> m_dwGroupMembers;
+ std::deque<std::unique_ptr<CTitleTex>> m_dpTitleTextures;
- Vector2D m_vLastWindowPos;
- Vector2D m_vLastWindowSize;
+ CTitleTex* textureFromTitle(const std::string&);
+ void clearUnusedTextures();
+ void invalidateTextures();
- std::deque<CWindow*> m_dwGroupMembers;
+ void refreshGradients();
+ CTexture m_tGradientActive;
+ CTexture m_tGradientInactive;
}; \ No newline at end of file