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. 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 ## Config
@ -16,8 +16,8 @@ plugin {
# example buttons (R -> L) # example buttons (R -> L)
# hyprbars-button = color, size, on-click # hyprbars-button = color, size, on-click
hyprbars-button = rgb(ff4040), 10, hyprctl dispatch killactive hyprbars-button = rgb(ff4040), 10, 󰖭, hyprctl dispatch killactive
hyprbars-button = rgb(eeee11), 10, hyprctl dispatch fullscreen 1 hyprbars-button = rgb(eeee11), 10, , hyprctl dispatch fullscreen 1
} }
} }
``` ```
@ -37,5 +37,5 @@ plugin {
Use the `hyprbars-button` keyword. Use the `hyprbars-button` keyword.
```ini ```ini
hyprbars-button = color, size, on-click hyprbars-button = color, size, icon, on-click
``` ```

View File

@ -28,6 +28,7 @@ CHyprBar::~CHyprBar() {
damageEntire(); damageEntire();
HyprlandAPI::unregisterCallback(PHANDLE, m_pMouseButtonCallback); HyprlandAPI::unregisterCallback(PHANDLE, m_pMouseButtonCallback);
HyprlandAPI::unregisterCallback(PHANDLE, m_pMouseMoveCallback); HyprlandAPI::unregisterCallback(PHANDLE, m_pMouseMoveCallback);
std::erase(g_pGlobalState->bars, this);
} }
bool CHyprBar::allowsInput() { 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) { void CHyprBar::renderBarTitle(const Vector2D& bufferSize, const float scale) {
static auto* const PCOLOR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprbars:col.text")->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;
@ -206,8 +264,8 @@ void CHyprBar::renderBarButtons(const Vector2D& bufferSize, const float scale) {
// draw buttons // draw buttons
int offset = scaledButtonsPad; int offset = scaledButtonsPad;
auto drawButton = [&](SHyprButton& pButton) -> void { auto drawButton = [&](SHyprButton& button) -> void {
const auto scaledButtonSize = pButton.size * scale; const auto scaledButtonSize = button.size * scale;
Vector2D currentPos = Vector2D{bufferSize.x - offset - scaledButtonSize, (bufferSize.y - scaledButtonSize) / 2.0}.floor(); 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 Y = currentPos.y;
const int RADIUS = static_cast<int>(std::ceil(scaledButtonSize / 2.0)); 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_arc(CAIRO, X, Y + RADIUS, RADIUS, 0, 2 * M_PI);
cairo_fill(CAIRO); cairo_fill(CAIRO);
@ -245,6 +303,37 @@ void CHyprBar::renderBarButtons(const Vector2D& bufferSize, const float scale) {
cairo_surface_destroy(CAIROSURFACE); 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) { void CHyprBar::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
if (!g_pCompositor->windowValidMapped(m_pWindow)) if (!g_pCompositor->windowValidMapped(m_pWindow))
return; 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}; wlr_box textBox = {titleBarBox.x, titleBarBox.y, (int)BARBUF.x, (int)BARBUF.y};
g_pHyprOpenGL->renderTexture(m_tTextTex, &textBox, a); g_pHyprOpenGL->renderTexture(m_tTextTex, &textBox, a);
renderBarButtons(BARBUF, pMonitor->scale); if (m_bButtonsDirty || m_bWindowSizeChanged) {
renderBarButtons(BARBUF, pMonitor->scale);
m_bButtonsDirty = false;
}
g_pHyprOpenGL->renderTexture(m_tButtonsTex, &textBox, a); g_pHyprOpenGL->renderTexture(m_tButtonsTex, &textBox, a);
g_pHyprOpenGL->scissor((wlr_box*)nullptr); g_pHyprOpenGL->scissor((wlr_box*)nullptr);
renderBarButtonsText(&textBox, pMonitor->scale);
m_bWindowSizeChanged = false; m_bWindowSizeChanged = false;
} }

View File

@ -25,6 +25,8 @@ class CHyprBar : public IHyprWindowDecoration {
virtual bool allowsInput(); virtual bool allowsInput();
bool m_bButtonsDirty = true;
private: private:
SWindowDecorationExtents m_seExtents; SWindowDecorationExtents m_seExtents;
@ -41,7 +43,9 @@ class CHyprBar : public IHyprWindowDecoration {
Vector2D cursorRelativeToBar(); Vector2D cursorRelativeToBar();
void renderBarTitle(const Vector2D& bufferSize, const float scale); 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 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 onMouseDown(SCallbackInfo& info, wlr_pointer_button_event* e);
void onMouseMove(Vector2D coords); void onMouseMove(Vector2D coords);

View File

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

View File

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