mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-02 17:26:00 +01:00
groupbars: add title and gradient rendering
This commit is contained in:
parent
1eb6cfd45c
commit
206ac000b9
3 changed files with 219 additions and 10 deletions
|
@ -94,6 +94,8 @@ void CConfigManager::setDefaultVars() {
|
||||||
configValues["misc:cursor_zoom_factor"].floatValue = 1.f;
|
configValues["misc:cursor_zoom_factor"].floatValue = 1.f;
|
||||||
configValues["misc:cursor_zoom_rigid"].intValue = 0;
|
configValues["misc:cursor_zoom_rigid"].intValue = 0;
|
||||||
configValues["misc:allow_session_lock_restore"].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:int"].intValue = 0;
|
||||||
configValues["debug:log_damage"].intValue = 0;
|
configValues["debug:log_damage"].intValue = 0;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "CHyprGroupBarDecoration.hpp"
|
#include "CHyprGroupBarDecoration.hpp"
|
||||||
#include "../../Compositor.hpp"
|
#include "../../Compositor.hpp"
|
||||||
|
#include <ranges>
|
||||||
|
#include <pango/pangocairo.h>
|
||||||
|
|
||||||
CHyprGroupBarDecoration::CHyprGroupBarDecoration(CWindow* pWindow) {
|
CHyprGroupBarDecoration::CHyprGroupBarDecoration(CWindow* pWindow) {
|
||||||
m_pWindow = pWindow;
|
m_pWindow = pWindow;
|
||||||
|
@ -17,25 +19,31 @@ eDecorationType CHyprGroupBarDecoration::getDecorationType() {
|
||||||
|
|
||||||
constexpr int BAR_INDICATOR_HEIGHT = 3;
|
constexpr int BAR_INDICATOR_HEIGHT = 3;
|
||||||
constexpr int BAR_PADDING_OUTER_VERT = 2;
|
constexpr int BAR_PADDING_OUTER_VERT = 2;
|
||||||
|
constexpr int BAR_TEXT_PAD = 2;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
void CHyprGroupBarDecoration::updateWindow(CWindow* pWindow) {
|
void CHyprGroupBarDecoration::updateWindow(CWindow* pWindow) {
|
||||||
damageEntire();
|
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) {
|
if (pWindow->m_vRealPosition.vec() + WORKSPACEOFFSET != m_vLastWindowPos || pWindow->m_vRealSize.vec() != m_vLastWindowSize) {
|
||||||
// we draw 3px above the window's border with 3px
|
// we draw 3px above the window's border with 3px
|
||||||
const auto PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
|
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_seExtents.bottomRight = Vector2D();
|
||||||
|
|
||||||
m_vLastWindowPos = pWindow->m_vRealPosition.vec() + WORKSPACEOFFSET;
|
m_vLastWindowPos = pWindow->m_vRealPosition.vec() + WORKSPACEOFFSET;
|
||||||
m_vLastWindowSize = pWindow->m_vRealSize.vec();
|
m_vLastWindowSize = pWindow->m_vRealSize.vec();
|
||||||
|
|
||||||
|
invalidateTextures();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_pWindow->m_sGroupData.pNextWindow) {
|
if (!m_pWindow->m_sGroupData.pNextWindow) {
|
||||||
|
@ -74,7 +82,10 @@ void CHyprGroupBarDecoration::damageEntire() {
|
||||||
|
|
||||||
void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
|
void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
|
||||||
// get how many bars we will draw
|
// 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))
|
if (barsToDraw < 1 || m_pWindow->isHidden() || !g_pCompositor->windowValidMapped(m_pWindow))
|
||||||
return;
|
return;
|
||||||
|
@ -90,7 +101,7 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D&
|
||||||
|
|
||||||
for (int i = 0; i < barsToDraw; ++i) {
|
for (int i = 0; i < barsToDraw; ++i) {
|
||||||
wlr_box rect = {m_vLastWindowPos.x + xoff - pMonitor->vecPosition.x + offset.x,
|
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)
|
if (rect.width <= 0 || rect.height <= 0)
|
||||||
break;
|
break;
|
||||||
|
@ -105,10 +116,185 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D&
|
||||||
color.a *= a;
|
color.a *= a;
|
||||||
g_pHyprOpenGL->renderRect(&rect, color);
|
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;
|
xoff += PAD + BARW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (*PRENDERTITLES)
|
||||||
|
clearUnusedTextures();
|
||||||
}
|
}
|
||||||
|
|
||||||
SWindowDecorationExtents CHyprGroupBarDecoration::getWindowDecorationReservedArea() {
|
SWindowDecorationExtents CHyprGroupBarDecoration::getWindowDecorationReservedArea() {
|
||||||
return SWindowDecorationExtents{{0, BAR_INDICATOR_HEIGHT + BAR_PADDING_OUTER_VERT * 2}, {}};
|
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]);
|
||||||
}
|
}
|
|
@ -2,6 +2,18 @@
|
||||||
|
|
||||||
#include "IHyprWindowDecoration.hpp"
|
#include "IHyprWindowDecoration.hpp"
|
||||||
#include <deque>
|
#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 {
|
class CHyprGroupBarDecoration : public IHyprWindowDecoration {
|
||||||
public:
|
public:
|
||||||
|
@ -21,12 +33,21 @@ class CHyprGroupBarDecoration : public IHyprWindowDecoration {
|
||||||
virtual SWindowDecorationExtents getWindowDecorationReservedArea();
|
virtual SWindowDecorationExtents getWindowDecorationReservedArea();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SWindowDecorationExtents m_seExtents;
|
SWindowDecorationExtents m_seExtents;
|
||||||
|
|
||||||
CWindow* m_pWindow = nullptr;
|
CWindow* m_pWindow = nullptr;
|
||||||
|
|
||||||
Vector2D m_vLastWindowPos;
|
Vector2D m_vLastWindowPos;
|
||||||
Vector2D m_vLastWindowSize;
|
Vector2D m_vLastWindowSize;
|
||||||
|
|
||||||
std::deque<CWindow*> m_dwGroupMembers;
|
std::deque<CWindow*> m_dwGroupMembers;
|
||||||
|
std::deque<std::unique_ptr<CTitleTex>> m_dpTitleTextures;
|
||||||
|
|
||||||
|
CTitleTex* textureFromTitle(const std::string&);
|
||||||
|
void clearUnusedTextures();
|
||||||
|
void invalidateTextures();
|
||||||
|
|
||||||
|
void refreshGradients();
|
||||||
|
CTexture m_tGradientActive;
|
||||||
|
CTexture m_tGradientInactive;
|
||||||
};
|
};
|
Loading…
Reference in a new issue