Hyprbars: Improve Title Rendering (#16)

* use pangocairo to draw title

* add padding and ellipsis

* add max and close colors to config

* remove snake case, better config variable names

* add pangocairo as a dependency to meson build

* add `buttons:button_size`, adjust for bordersize

* add button_size doc

---------

Co-authored-by: nehrbash <stephen.nehrbass@infinitetactics.com>
This commit is contained in:
snehrbass 2023-05-01 15:02:05 -04:00 committed by GitHub
parent 274b46c1f7
commit e80bf4a44d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 99 additions and 55 deletions

View file

@ -1,4 +1,21 @@
all: CXX = g++
g++ -shared -fPIC --no-gnu-unique main.cpp barDeco.cpp -o hyprbars.so -g -I "/usr/include/pixman-1" -I "/usr/include/libdrm" $(shell pkg-config --cflags hyprland) -std=c++23 CXXFLAGS = -shared -fPIC --no-gnu-unique -g -std=c++23
INCLUDES = -I "/usr/include/pixman-1" -I "/usr/include/libdrm" -I $(shell pkg-config --cflags hyprland pangocairo)
LIBS = $(shell pkg-config --libs pangocairo)
SRC = main.cpp barDeco.cpp
TARGET = hyprbars.so
all: $(TARGET)
$(TARGET): $(SRC)
$(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $@ $(LIBS)
clean: clean:
rm ./hyprbars.so rm ./$(TARGET)
meson-build:
mkdir -p build
cd build && meson .. && ninja
.PHONY: all meson-build clean

View file

@ -12,6 +12,9 @@ All config options are in `plugin:hyprbars`:
plugin { plugin {
hyprbars { hyprbars {
# config # config
buttons {
# button config
}
} }
} }
``` ```
@ -20,8 +23,16 @@ plugin {
`bar_height` -> (int) bar's height (default 15) `bar_height` -> (int) bar's height (default 15)
`bar_text_color` -> (col) bar's title text color `col.text` -> (col) bar's title text color
`bar_text_size` -> (int) bar's title text font size (default 10) `bar_text_size` -> (int) bar's title text font size (default 10)
`bar_text_font` -> (str) bar's title text font (default "Sans") `bar_text_font` -> (str) bar's title text font (default "Sans")
## Buttons Config
`button_size` -> (int) the size of the buttons.
`col.maximize` -> (col) maximize button color
`col.close` -> (col) close button color

View file

@ -2,12 +2,11 @@
#include <hyprland/src/Compositor.hpp> #include <hyprland/src/Compositor.hpp>
#include <hyprland/src/Window.hpp> #include <hyprland/src/Window.hpp>
#include <pango/pangocairo.h>
#include "globals.hpp" #include "globals.hpp"
constexpr int BUTTONS_PAD = 5; constexpr int BUTTONS_PAD = 5;
constexpr int BUTTONS_SIZE = 10;
constexpr int BUTTONS_ROUNDING = 5;
CHyprBar::CHyprBar(CWindow* pWindow) { CHyprBar::CHyprBar(CWindow* pWindow) {
m_pWindow = pWindow; m_pWindow = pWindow;
@ -43,9 +42,10 @@ void CHyprBar::onMouseDown(wlr_pointer_button_event* e) {
const auto COORDS = cursorRelativeToBar(); const auto COORDS = cursorRelativeToBar();
static auto* const PHEIGHT = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->intValue; static auto* const PHEIGHT = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->intValue;
static auto* const PBORDERSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "general:border_size")->intValue;
if (!VECINRECT(COORDS, 0, 0, m_vLastWindowSize.x, *PHEIGHT)) { if (!VECINRECT(COORDS, 0, 0, m_vLastWindowSize.x + *PBORDERSIZE * 2, *PHEIGHT)) {
if (m_bDraggingThis) { if (m_bDraggingThis) {
g_pKeybindManager->m_mDispatchers["mouse"]("0movewindow"); g_pKeybindManager->m_mDispatchers["mouse"]("0movewindow");
@ -69,22 +69,22 @@ void CHyprBar::onMouseDown(wlr_pointer_button_event* e) {
} }
// check if on a button // check if on a button
static auto* const PBORDERSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "general:border_size")->intValue; static auto* const PBUTTONSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:buttons:button_size")->intValue;
const auto BARBUF = Vector2D{(int)m_vLastWindowSize.x + 2 * *PBORDERSIZE, *PHEIGHT}; const auto BARBUF = Vector2D{(int)m_vLastWindowSize.x + 2 * *PBORDERSIZE, *PHEIGHT};
Vector2D currentPos = Vector2D{BARBUF.x - BUTTONS_PAD - BUTTONS_SIZE, BARBUF.y / 2.0 - BUTTONS_SIZE / 2.0}.floor(); Vector2D currentPos = Vector2D{BARBUF.x - BUTTONS_PAD - *PBUTTONSIZE, BARBUF.y / 2.0 - *PBUTTONSIZE / 2.0}.floor();
currentPos.y -= BUTTONS_PAD / 2.0; currentPos.y -= BUTTONS_PAD / 2.0;
currentPos.x -= BUTTONS_PAD / 2.0; currentPos.x -= BUTTONS_PAD / 2.0;
if (VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + BUTTONS_SIZE + BUTTONS_PAD, currentPos.y + BUTTONS_SIZE + BUTTONS_PAD)) { if (VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + *PBUTTONSIZE + BUTTONS_PAD, currentPos.y + *PBUTTONSIZE + BUTTONS_PAD)) {
// hit on close // hit on close
g_pCompositor->closeWindow(m_pWindow); g_pCompositor->closeWindow(m_pWindow);
return; return;
} }
currentPos.x -= BUTTONS_PAD + BUTTONS_SIZE; currentPos.x -= BUTTONS_PAD + *PBUTTONSIZE;
if (VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + BUTTONS_SIZE + BUTTONS_PAD, currentPos.y + BUTTONS_SIZE + BUTTONS_PAD)) { if (VECINRECT(COORDS, currentPos.x, currentPos.y, currentPos.x + *PBUTTONSIZE + BUTTONS_PAD, currentPos.y + *PBUTTONSIZE + BUTTONS_PAD)) {
// hit on maximize // hit on maximize
g_pKeybindManager->m_mDispatchers["fullscreen"]("1"); g_pKeybindManager->m_mDispatchers["fullscreen"]("1");
return; return;
@ -94,8 +94,6 @@ void CHyprBar::onMouseDown(wlr_pointer_button_event* e) {
} }
void CHyprBar::onMouseMove(Vector2D coords) { void CHyprBar::onMouseMove(Vector2D coords) {
static auto* const PHEIGHT = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->intValue;
if (m_bDragPending) { if (m_bDragPending) {
m_bDragPending = false; m_bDragPending = false;
g_pKeybindManager->m_mDispatchers["mouse"]("1movewindow"); g_pKeybindManager->m_mDispatchers["mouse"]("1movewindow");
@ -108,15 +106,18 @@ void CHyprBar::onMouseMove(Vector2D coords) {
} }
void CHyprBar::renderBarTitle(const Vector2D& bufferSize) { void CHyprBar::renderBarTitle(const Vector2D& bufferSize) {
static auto* const PCOLOR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_text_color")->intValue; static auto* const PCOLOR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:col.text")->intValue;
static auto* const PSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_text_size")->intValue; static auto* const PSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_text_size")->intValue;
static auto* const PFONT = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_text_font")->strValue; static auto* const PFONT = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_text_font")->strValue;
static auto* const PBUTTONSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:buttons:button_size")->intValue;
static auto* const PBORDERSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "general:border_size")->intValue;
const CColor COLOR = *PCOLOR; const CColor COLOR = *PCOLOR;
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y); const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y);
const auto CAIRO = cairo_create(CAIROSURFACE);
const auto CAIRO = cairo_create(CAIROSURFACE); const int BARPADDING = 10;
// clear the pixmap // clear the pixmap
cairo_save(CAIRO); cairo_save(CAIRO);
@ -124,16 +125,33 @@ void CHyprBar::renderBarTitle(const Vector2D& bufferSize) {
cairo_paint(CAIRO); cairo_paint(CAIRO);
cairo_restore(CAIRO); cairo_restore(CAIRO);
// draw title // draw title using Pango
cairo_select_font_face(CAIRO, PFONT->c_str(), CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); PangoLayout* layout = pango_cairo_create_layout(CAIRO);
cairo_set_font_size(CAIRO, *PSIZE); pango_layout_set_text(layout, m_szLastTitle.c_str(), -1);
PangoFontDescription* fontDesc = pango_font_description_from_string(PFONT->c_str());
pango_font_description_set_size(fontDesc, *PSIZE * PANGO_SCALE);
pango_layout_set_font_description(layout, fontDesc);
pango_font_description_free(fontDesc);
const int leftPadding = *PBORDERSIZE + BARPADDING;
const int rightPadding = (*PBUTTONSIZE * 2) + (BUTTONS_PAD * 3) + *PBORDERSIZE + BARPADDING;
const int maxWidth = bufferSize.x - leftPadding - rightPadding;
pango_layout_set_width(layout, maxWidth * PANGO_SCALE);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
cairo_set_source_rgba(CAIRO, COLOR.r, COLOR.g, COLOR.b, COLOR.a); cairo_set_source_rgba(CAIRO, COLOR.r, COLOR.g, COLOR.b, COLOR.a);
cairo_text_extents_t cairoExtents; int layoutWidth, layoutHeight;
cairo_text_extents(CAIRO, m_szLastTitle.c_str(), &cairoExtents); pango_layout_get_size(layout, &layoutWidth, &layoutHeight);
const int xOffset = std::round(((bufferSize.x - *PBORDERSIZE) / 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, std::round(bufferSize.x / 2.0 - cairoExtents.width / 2.0), *PSIZE + std::round(bufferSize.y / 2.0 - cairoExtents.height / 2.0)); cairo_move_to(CAIRO, xOffset, yOffset);
cairo_show_text(CAIRO, m_szLastTitle.c_str()); pango_cairo_show_layout(CAIRO, layout);
g_object_unref(layout);
cairo_surface_flush(CAIROSURFACE); cairo_surface_flush(CAIROSURFACE);
@ -157,9 +175,12 @@ void CHyprBar::renderBarTitle(const Vector2D& bufferSize) {
} }
void CHyprBar::renderBarButtons(const Vector2D& bufferSize) { void CHyprBar::renderBarButtons(const Vector2D& bufferSize) {
static auto* const PCLOSECOLOR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:buttons:col.close")->intValue;
static auto* const PMAXCOLOR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:buttons:col.maximize")->intValue;
static auto* const PBUTTONSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:buttons:button_size")->intValue;
const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y); const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y);
const auto CAIRO = cairo_create(CAIROSURFACE); const auto CAIRO = cairo_create(CAIROSURFACE);
// clear the pixmap // clear the pixmap
cairo_save(CAIRO); cairo_save(CAIRO);
@ -170,32 +191,22 @@ void CHyprBar::renderBarButtons(const Vector2D& bufferSize) {
// draw buttons for close and max // draw buttons for close and max
auto drawButton = [&](Vector2D pos, CColor col) -> void { auto drawButton = [&](Vector2D pos, CColor col) -> void {
const int X = pos.x; const int X = pos.x;
const int Y = pos.y; const int Y = pos.y;
const int WIDTH = BUTTONS_SIZE; const int RADIUS = static_cast<int>(std::ceil(*PBUTTONSIZE / 2.0));
const int HEIGHT = BUTTONS_SIZE;
const int RADIUS = BUTTONS_ROUNDING;
constexpr double DEGREES = M_PI / 180.0;
cairo_set_source_rgba(CAIRO, col.r, col.g, col.b, col.a); cairo_set_source_rgba(CAIRO, col.r, col.g, col.b, col.a);
cairo_arc(CAIRO, X, Y + RADIUS, RADIUS, 0, 2 * M_PI);
cairo_new_sub_path(CAIRO); cairo_fill(CAIRO);
cairo_arc(CAIRO, X + WIDTH - RADIUS, Y + RADIUS, RADIUS, -90 * DEGREES, 0 * DEGREES);
cairo_arc(CAIRO, X + WIDTH - RADIUS, Y + HEIGHT - RADIUS, RADIUS, 0 * DEGREES, 90 * DEGREES);
cairo_arc(CAIRO, X + RADIUS, Y + HEIGHT - RADIUS, RADIUS, 90 * DEGREES, 180 * DEGREES);
cairo_arc(CAIRO, X + RADIUS, Y + RADIUS, RADIUS, 180 * DEGREES, 270 * DEGREES);
cairo_close_path(CAIRO);
cairo_fill_preserve(CAIRO);
}; };
Vector2D currentPos = Vector2D{bufferSize.x - BUTTONS_PAD - BUTTONS_SIZE, bufferSize.y / 2.0 - BUTTONS_SIZE / 2.0}.floor(); Vector2D currentPos = Vector2D{bufferSize.x - BUTTONS_PAD - *PBUTTONSIZE, bufferSize.y / 2.0 - *PBUTTONSIZE / 2.0}.floor();
drawButton(currentPos, CColor(1.0, 0.0, 0.0, 0.8)); drawButton(currentPos, CColor(*PCLOSECOLOR));
currentPos.x -= BUTTONS_PAD + BUTTONS_SIZE; currentPos.x -= BUTTONS_PAD + *PBUTTONSIZE;
drawButton(currentPos, CColor(0.9, 0.9, 0.1, 0.8)); drawButton(currentPos, CColor(*PMAXCOLOR));
// copy the data to an OpenGL texture we have // copy the data to an OpenGL texture we have
const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); const auto DATA = cairo_image_surface_get_data(CAIROSURFACE);
@ -239,8 +250,8 @@ void CHyprBar::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
const auto BARBUF = Vector2D{(int)m_vLastWindowSize.x + 2 * *PBORDERSIZE, *PHEIGHT} * pMonitor->scale; const auto BARBUF = Vector2D{(int)m_vLastWindowSize.x + 2 * *PBORDERSIZE, *PHEIGHT} * pMonitor->scale;
wlr_box titleBarBox = {(int)m_vLastWindowPos.x - *PBORDERSIZE - pMonitor->vecPosition.x, (int)m_vLastWindowPos.y - *PHEIGHT - pMonitor->vecPosition.y, wlr_box titleBarBox = {(int)m_vLastWindowPos.x - *PBORDERSIZE - pMonitor->vecPosition.x, (int)m_vLastWindowPos.y - *PBORDERSIZE - *PHEIGHT - pMonitor->vecPosition.y,
(int)m_vLastWindowSize.x + 2 * *PBORDERSIZE, *PHEIGHT + *PROUNDING * 2 /* to fill the bottom cuz we can't disable rounding there */}; (int)m_vLastWindowSize.x + 2 * *PBORDERSIZE, *PHEIGHT + *PROUNDING * 3 /* to fill the bottom cuz we can't disable rounding there */};
titleBarBox.x += offset.x; titleBarBox.x += offset.x;
titleBarBox.y += offset.y; titleBarBox.y += offset.y;
@ -329,5 +340,6 @@ SWindowDecorationExtents CHyprBar::getWindowDecorationReservedArea() {
Vector2D CHyprBar::cursorRelativeToBar() { Vector2D CHyprBar::cursorRelativeToBar() {
static auto* const PHEIGHT = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->intValue; static auto* const PHEIGHT = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:bar_height")->intValue;
return g_pInputManager->getMouseCoordsInternal() - m_pWindow->m_vRealPosition.vec() + Vector2D{0, *PHEIGHT}; static auto* const PBORDER = &HyprlandAPI::getConfigValue(PHANDLE, "general:border_size")->intValue;
return g_pInputManager->getMouseCoordsInternal() - m_pWindow->m_vRealPosition.vec() + Vector2D{*PBORDER, *PHEIGHT + *PBORDER};
} }

View file

@ -29,9 +29,12 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_color", SConfigValue{.intValue = configStringToInt("rgba(33333388)")}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_color", SConfigValue{.intValue = configStringToInt("rgba(33333388)")});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_height", SConfigValue{.intValue = 15}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_height", SConfigValue{.intValue = 15});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_text_color", SConfigValue{.intValue = configStringToInt("rgba(ffffffff)")}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:col.text", SConfigValue{.intValue = configStringToInt("rgba(ffffffff)")});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_text_size", SConfigValue{.intValue = 10}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_text_size", SConfigValue{.intValue = 10});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_text_font", SConfigValue{.strValue = "Sans"}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:bar_text_font", SConfigValue{.strValue = "Sans"});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:buttons:col.close", SConfigValue{.intValue = configStringToInt("rgba(cc0000cc)")});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:buttons:col.maximize", SConfigValue{.intValue = configStringToInt("rgba(ffff33cc)")});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprbars:buttons:button_size", SConfigValue{.intValue = 10});
// add deco to existing windows // add deco to existing windows
for (auto& w : g_pCompositor->m_vWindows) { for (auto& w : g_pCompositor->m_vWindows) {

View file

@ -22,6 +22,7 @@ shared_module(meson.project_name(), src,
dependency('hyprland'), dependency('hyprland'),
dependency('pixman-1'), dependency('pixman-1'),
dependency('libdrm'), dependency('libdrm'),
dependency('pangocairo')
], ],
install: true, install: true,
) )