mirror of
https://github.com/hyprwm/hyprland-plugins.git
synced 2024-11-22 10:35:58 +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.
|
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
|
||||||
```
|
```
|
|
@ -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);
|
||||||
|
|
||||||
|
if (m_bButtonsDirty || m_bWindowSizeChanged) {
|
||||||
renderBarButtons(BARBUF, pMonitor->scale);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue