mirror of
https://github.com/hyprwm/hyprland-plugins.git
synced 2024-12-22 06:59:48 +01:00
hyprbars: add icon support + general improvements
This commit is contained in:
parent
7ddc207857
commit
f46771dd4f
5 changed files with 123 additions and 13 deletions
|
@ -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
|
||||
```
|
|
@ -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);
|
||||
|
||||
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->scissor((wlr_box*)nullptr);
|
||||
|
||||
renderBarButtonsText(&textBox, pMonitor->scale);
|
||||
|
||||
m_bWindowSizeChanged = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue