hyprbars: add icon support + general improvements

This commit is contained in:
Vaxry 2023-10-29 22:31:11 +00:00
parent 7ddc207857
commit f46771dd4f
5 changed files with 123 additions and 13 deletions

View file

@ -2,7 +2,7 @@
Adds simple title bars to windows.
![preview](https://i.ibb.co/GkDTL4Q/20230228-23h20m36s-grim.png)
![preview](https://i.ibb.co/hLDRCpT/20231029-22h30m05s.png)
## Config
@ -16,8 +16,8 @@ plugin {
# example buttons (R -> L)
# hyprbars-button = color, size, on-click
hyprbars-button = rgb(ff4040), 10, hyprctl dispatch killactive
hyprbars-button = rgb(eeee11), 10, hyprctl dispatch fullscreen 1
hyprbars-button = rgb(ff4040), 10, 󰖭, hyprctl dispatch killactive
hyprbars-button = rgb(eeee11), 10, , hyprctl dispatch fullscreen 1
}
}
```
@ -37,5 +37,5 @@ plugin {
Use the `hyprbars-button` keyword.
```ini
hyprbars-button = color, size, on-click
hyprbars-button = color, size, icon, on-click
```

View file

@ -28,6 +28,7 @@ CHyprBar::~CHyprBar() {
damageEntire();
HyprlandAPI::unregisterCallback(PHANDLE, m_pMouseButtonCallback);
HyprlandAPI::unregisterCallback(PHANDLE, m_pMouseMoveCallback);
std::erase(g_pGlobalState->bars, this);
}
bool CHyprBar::allowsInput() {
@ -113,6 +114,63 @@ void CHyprBar::onMouseMove(Vector2D coords) {
}
}
void CHyprBar::renderText(CTexture& out, const std::string& text, const CColor& color, const Vector2D& bufferSize, const float scale, const int fontSize) {
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);
// draw title using Pango
PangoLayout* layout = pango_cairo_create_layout(CAIRO);
pango_layout_set_text(layout, text.c_str(), -1);
PangoFontDescription* fontDesc = pango_font_description_from_string("sans");
pango_font_description_set_size(fontDesc, fontSize * scale * 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_NONE);
cairo_set_source_rgba(CAIRO, color.r, color.g, color.b, color.a);
int layoutWidth, layoutHeight;
pango_layout_get_size(layout, &layoutWidth, &layoutHeight);
const double xOffset = (bufferSize.x / 2.0 - layoutWidth / PANGO_SCALE / 2.0);
const double yOffset = (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);
out.allocate();
glBindTexture(GL_TEXTURE_2D, out.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 CHyprBar::renderBarTitle(const Vector2D& bufferSize, const float scale) {
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;
@ -206,8 +264,8 @@ void CHyprBar::renderBarButtons(const Vector2D& bufferSize, const float scale) {
// draw buttons
int offset = scaledButtonsPad;
auto drawButton = [&](SHyprButton& pButton) -> void {
const auto scaledButtonSize = pButton.size * scale;
auto drawButton = [&](SHyprButton& button) -> void {
const auto scaledButtonSize = button.size * scale;
Vector2D currentPos = Vector2D{bufferSize.x - offset - scaledButtonSize, (bufferSize.y - scaledButtonSize) / 2.0}.floor();
@ -215,7 +273,7 @@ void CHyprBar::renderBarButtons(const Vector2D& bufferSize, const float scale) {
const int Y = currentPos.y;
const int RADIUS = static_cast<int>(std::ceil(scaledButtonSize / 2.0));
cairo_set_source_rgba(CAIRO, pButton.col.r, pButton.col.g, pButton.col.b, pButton.col.a);
cairo_set_source_rgba(CAIRO, button.col.r, button.col.g, button.col.b, button.col.a);
cairo_arc(CAIRO, X, Y + RADIUS, RADIUS, 0, 2 * M_PI);
cairo_fill(CAIRO);
@ -245,6 +303,37 @@ void CHyprBar::renderBarButtons(const Vector2D& bufferSize, const float scale) {
cairo_surface_destroy(CAIROSURFACE);
}
void CHyprBar::renderBarButtonsText(wlr_box* barBox, const float scale) {
const auto scaledButtonsPad = BUTTONS_PAD * scale;
int offset = scaledButtonsPad;
auto drawButton = [&](SHyprButton& button) -> void {
const auto scaledButtonSize = button.size * scale;
if (button.iconTex.m_iTexID == 0 /* icon is not rendered */ && !button.icon.empty()) {
// render icon
const Vector2D BUFSIZE = {scaledButtonSize, scaledButtonSize};
const bool LIGHT = button.col.r + button.col.g + button.col.b < 1;
renderText(button.iconTex, button.icon, LIGHT ? CColor(0xFFFFFFFF) : CColor(0xFF000000), BUFSIZE, scale, scaledButtonSize * 0.62);
}
if (button.iconTex.m_iTexID == 0)
return;
wlr_box pos = {barBox->x + barBox->width - offset - scaledButtonSize * 1.5, barBox->y + (barBox->height - scaledButtonSize) / 2.0, scaledButtonSize, scaledButtonSize};
g_pHyprOpenGL->renderTexture(button.iconTex, &pos, 1);
offset += scaledButtonsPad + scaledButtonSize;
};
for (auto& b : g_pGlobalState->buttons) {
drawButton(b);
}
}
void CHyprBar::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
if (!g_pCompositor->windowValidMapped(m_pWindow))
return;
@ -322,12 +411,17 @@ void CHyprBar::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
wlr_box textBox = {titleBarBox.x, titleBarBox.y, (int)BARBUF.x, (int)BARBUF.y};
g_pHyprOpenGL->renderTexture(m_tTextTex, &textBox, a);
if (m_bButtonsDirty || m_bWindowSizeChanged) {
renderBarButtons(BARBUF, pMonitor->scale);
m_bButtonsDirty = false;
}
g_pHyprOpenGL->renderTexture(m_tButtonsTex, &textBox, a);
g_pHyprOpenGL->scissor((wlr_box*)nullptr);
renderBarButtonsText(&textBox, pMonitor->scale);
m_bWindowSizeChanged = false;
}

View file

@ -25,6 +25,8 @@ class CHyprBar : public IHyprWindowDecoration {
virtual bool allowsInput();
bool m_bButtonsDirty = true;
private:
SWindowDecorationExtents m_seExtents;
@ -41,7 +43,9 @@ class CHyprBar : public IHyprWindowDecoration {
Vector2D cursorRelativeToBar();
void renderBarTitle(const Vector2D& bufferSize, const float scale);
void renderText(CTexture& out, const std::string& text, const CColor& color, const Vector2D& bufferSize, const float scale, const int fontSize);
void renderBarButtons(const Vector2D& bufferSize, const float scale);
void renderBarButtonsText(wlr_box* barBox, const float scale);
void onMouseDown(SCallbackInfo& info, wlr_pointer_button_event* e);
void onMouseMove(Vector2D coords);

View file

@ -8,10 +8,15 @@ struct SHyprButton {
std::string cmd = "";
CColor col = CColor(0, 0, 0, 0);
float size = 10;
std::string icon = "";
CTexture iconTex;
};
class CHyprBar;
struct SGlobalState {
std::vector<SHyprButton> buttons;
std::vector<CHyprBar*> bars;
};
inline std::unique_ptr<SGlobalState> g_pGlobalState;

View file

@ -19,8 +19,11 @@ void onNewWindow(void* self, std::any data) {
// data is guaranteed
auto* const PWINDOW = std::any_cast<CWindow*>(data);
if (!PWINDOW->m_bX11DoesntWantBorders)
HyprlandAPI::addWindowDecoration(PHANDLE, PWINDOW, new CHyprBar(PWINDOW));
if (!PWINDOW->m_bX11DoesntWantBorders) {
CHyprBar* bar = new CHyprBar(PWINDOW);
HyprlandAPI::addWindowDecoration(PHANDLE, PWINDOW, static_cast<IHyprWindowDecoration*>(bar));
g_pGlobalState->bars.push_back(bar);
}
}
void onPreConfigReload() {
@ -30,7 +33,7 @@ void onPreConfigReload() {
void onNewButton(const std::string& k, const std::string& v) {
CVarList vars(v);
// hyprbars-button = color, size, action
// hyprbars-button = color, size, icon, action
if (vars[0].empty() || vars[1].empty())
return;
@ -40,7 +43,11 @@ void onNewButton(const std::string& k, const std::string& v) {
size = std::stof(vars[1]);
} catch (std::exception& e) { return; }
g_pGlobalState->buttons.push_back(SHyprButton{vars[2], configStringToInt(vars[0]), size});
g_pGlobalState->buttons.push_back(SHyprButton{vars[3], configStringToInt(vars[0]), size, vars[2]});
for (auto& b : g_pGlobalState->bars) {
b->m_bButtonsDirty = true;
}
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
@ -72,7 +79,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
if (w->isHidden() || !w->m_bIsMapped)
continue;
HyprlandAPI::addWindowDecoration(PHANDLE, w.get(), new CHyprBar(w.get()));
onNewWindow(nullptr /* unused */, std::any(w.get()));
}
HyprlandAPI::reloadConfig();