diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp index b93c181..7c3956f 100644 --- a/src/Hy3Layout.cpp +++ b/src/Hy3Layout.cpp @@ -496,6 +496,7 @@ Hy3Node* Hy3Node::intoGroup(Hy3GroupLayout layout) { this->data.as_group.group_focused = false; this->data.as_group.focused_child = node; this->recalcSizePosRecursive(); + this->updateTabBarRecursive(); return node; } @@ -583,6 +584,15 @@ void Hy3Node::updateTabBar() { } } +void Hy3Node::updateTabBarRecursive() { + auto* node = this; + + while (node != nullptr) { + node->updateTabBar(); + node = node->parent; + } +} + void Hy3Node::updateDecos() { switch (this->data.type) { case Hy3NodeData::Window: @@ -1243,6 +1253,7 @@ void Hy3Layout::replaceWindowDataWith(CWindow* from, CWindow* to) { } std::unique_ptr renderHookPtr = std::make_unique(Hy3Layout::renderHook); +std::unique_ptr windowTitleHookPtr = std::make_unique(Hy3Layout::windowTitleHook); std::unique_ptr tickHookPtr = std::make_unique(Hy3Layout::tickHook); void Hy3Layout::onEnable() { @@ -1257,12 +1268,14 @@ void Hy3Layout::onEnable() { } HyprlandAPI::registerCallbackStatic(PHANDLE, "render", renderHookPtr.get()); + HyprlandAPI::registerCallbackStatic(PHANDLE, "windowTitle", windowTitleHookPtr.get()); HyprlandAPI::registerCallbackStatic(PHANDLE, "tick", tickHookPtr.get()); selection_hook::enable(); } void Hy3Layout::onDisable() { HyprlandAPI::unregisterCallback(PHANDLE, renderHookPtr.get()); + HyprlandAPI::unregisterCallback(PHANDLE, windowTitleHookPtr.get()); HyprlandAPI::unregisterCallback(PHANDLE, tickHookPtr.get()); selection_hook::disable(); this->nodes.clear(); @@ -1594,6 +1607,14 @@ void Hy3Layout::renderHook(void*, std::any data) { } } +void Hy3Layout::windowTitleHook(void*, std::any data) { + CWindow* window = std::any_cast(data); + if (window == nullptr) return; + auto* node = g_Hy3Layout->getNodeFromWindow(window); + + node->updateTabBarRecursive(); +} + void Hy3Layout::tickHook(void*, std::any) { auto& tab_groups = g_Hy3Layout->tab_groups; auto entry = tab_groups.begin(); diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp index a373fab..0a75f06 100644 --- a/src/Hy3Layout.hpp +++ b/src/Hy3Layout.hpp @@ -84,6 +84,8 @@ struct Hy3Node { Hy3Node* getFocusedNode(); void updateDecos(); void setHidden(bool hidden); + void updateTabBar(); + void updateTabBarRecursive(); bool isUrgent(); bool isIndirectlyFocused(); std::string getTitle(); @@ -100,9 +102,6 @@ struct Hy3Node { Hy3Node* intoGroup(Hy3GroupLayout); static void swapData(Hy3Node&, Hy3Node&); - -private: - void updateTabBar(); }; class Hy3Layout: public IHyprLayout { @@ -141,6 +140,7 @@ public: Hy3Node* getWorkspaceFocusedNode(const int&); static void renderHook(void*, std::any); + static void windowTitleHook(void*, std::any); static void tickHook(void*, std::any); std::list nodes; diff --git a/src/TabGroup.cpp b/src/TabGroup.cpp index 3c002a0..90cc744 100644 --- a/src/TabGroup.cpp +++ b/src/TabGroup.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -47,27 +48,63 @@ void Hy3TabBarEntry::setUrgent(bool urgent) { } } +void Hy3TabBarEntry::setWindowTitle(std::string title) { + if (this->window_title != title) { + this->window_title = title; + this->tab_bar.dirty = true; + } +} + void Hy3TabBarEntry::prepareTexture(float scale, wlr_box& box) { - static const auto* rounding_setting = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:rounding")->intValue; + static const auto* s_rounding = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:rounding")->intValue; + static const auto* render_text = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:render_text")->intValue; + static const auto* text_font = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:text_font")->strValue; + static const auto* text_height = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:text_height")->intValue; + static const auto* text_padding = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:text_padding")->intValue; static const auto* col_active = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.active")->intValue; static const auto* col_urgent = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.urgent")->intValue; static const auto* col_inactive = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.inactive")->intValue; + static const auto* col_text_active = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.active")->intValue; + static const auto* col_text_urgent = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.urgent")->intValue; + static const auto* col_text_inactive = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.inactive")->intValue; auto width = box.width; auto height = box.height; - auto rounding = std::min((double) *rounding_setting, std::min(width * 0.5, height * 0.5)); + auto rounding = std::min((double) *s_rounding, std::min(width * 0.5, height * 0.5)); if (this->texture.m_iTexID == 0 - || this->last_render_rounding != rounding - || this->last_render_focused != focused - || this->last_render_urgent != urgent - || !wlr_box_equal(&this->last_render_box, &box) + || this->last_render.x != box.x + || this->last_render.y != box.y + || this->last_render.focused != this->focused + || this->last_render.urgent != this->urgent + || this->last_render.window_title != this->window_title + || this->last_render.rounding != rounding + || this->last_render.text_font != *text_font + || this->last_render.text_height != *text_height + || this->last_render.text_padding != *text_padding + || this->last_render.col_active != *col_active + || this->last_render.col_urgent != *col_urgent + || this->last_render.col_inactive != *col_inactive + || this->last_render.col_text_active != *col_text_active + || this->last_render.col_text_urgent != *col_text_urgent + || this->last_render.col_text_inactive != *col_text_inactive ) { - this->last_render_rounding = rounding; - this->last_render_focused = this->focused; - this->last_render_urgent = this->urgent; - this->last_render_box = box; + this->last_render.x = box.x; + this->last_render.y = box.y; + this->last_render.focused = this->focused; + this->last_render.urgent = this->urgent; + this->last_render.window_title = this->window_title; + this->last_render.rounding = rounding; + this->last_render.text_font = *text_font; + this->last_render.text_height = *text_height; + this->last_render.text_padding = *text_padding; + this->last_render.col_active = *col_active; + this->last_render.col_urgent = *col_urgent; + this->last_render.col_inactive = *col_inactive; + this->last_render.col_text_active = *col_text_active; + this->last_render.col_text_urgent = *col_text_urgent; + this->last_render.col_text_inactive = *col_text_inactive; auto cairo_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); auto cairo = cairo_create(cairo_surface); @@ -104,6 +141,45 @@ void Hy3TabBarEntry::prepareTexture(float scale, wlr_box& box) { // draw cairo_fill(cairo); + // render window title + if (*render_text) { + PangoLayout* layout = pango_cairo_create_layout(cairo); + pango_layout_set_text(layout, this->window_title.c_str(), -1); + + PangoFontDescription* font_desc = pango_font_description_from_string(text_font->c_str()); + pango_font_description_set_size(font_desc, *text_height * scale * PANGO_SCALE); + pango_layout_set_font_description(layout, font_desc); + pango_font_description_free(font_desc); + + int padding = *text_padding * scale; + int width = box.width - padding * 2; + + pango_layout_set_width(layout, width * PANGO_SCALE); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + + CColor c; + if (this->focused) { + c = CColor(*col_text_active); + } else if (this->urgent) { + c = CColor(*col_text_urgent); + } else { + c = CColor(*col_text_inactive); + } + + cairo_set_source_rgba(cairo, c.r, c.g, c.b, c.a); + + int layout_width, layout_height; + pango_layout_get_size(layout, &layout_width, &layout_height); + + auto y_offset = (height / 2.0) - (((double)layout_height / PANGO_SCALE) / 2.0); + cairo_move_to(cairo, padding, y_offset); + pango_cairo_show_layout(cairo, layout); + g_object_unref(layout); + } + + // flush cairo + cairo_surface_flush(cairo_surface); + auto data = cairo_image_surface_get_data(cairo_surface); this->texture.allocate(); @@ -205,6 +281,7 @@ void Hy3TabBar::updateNodeList(std::list& nodes) { auto& parent_group = parent->data.as_group; entry->setFocused(parent_group.focused_child == *node || (parent_group.group_focused && parent->isIndirectlyFocused())); entry->setUrgent((*node)->isUrgent()); + entry->setWindowTitle((*node)->getTitle()); node = std::next(node); if (entry != this->entries.end()) entry = std::next(entry); diff --git a/src/TabGroup.hpp b/src/TabGroup.hpp index 7e43e95..e463f92 100644 --- a/src/TabGroup.hpp +++ b/src/TabGroup.hpp @@ -21,10 +21,25 @@ struct Hy3TabBarEntry { CAnimatedVariable width; // 0.0-1.0 of total bar Hy3TabBar& tab_bar; Hy3Node& node; // only used for comparioson. do not deref. - wlr_box last_render_box; - float last_render_rounding = 0.0; - bool last_render_focused = false; - bool last_render_urgent = false; + + struct { + int x, y; + float rounding = 0.0; + float scale = 0.0; + bool focused = false; + bool urgent = false; + std::string window_title; + + std::string text_font; + int text_height = 0; + int text_padding = 0; + int col_active = 0; + int col_urgent = 0; + int col_inactive = 0; + int col_text_active = 0; + int col_text_urgent = 0; + int col_text_inactive = 0; + } last_render; Hy3TabBarEntry(Hy3TabBar&, Hy3Node&); bool operator==(const Hy3Node&) const; @@ -32,6 +47,7 @@ struct Hy3TabBarEntry { void setFocused(bool); void setUrgent(bool); + void setWindowTitle(std::string); void prepareTexture(float, wlr_box&); }; diff --git a/src/main.cpp b/src/main.cpp index e2cc10a..a572c6c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -108,11 +108,18 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:no_gaps_when_only", SConfigValue{.intValue = 0}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:height", SConfigValue{.intValue = 15}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:padding", SConfigValue{.intValue = 5}); + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:from_top", SConfigValue{.intValue = 0}); + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:rounding", SConfigValue{.intValue = 3}); + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:render_text", SConfigValue{.intValue = 1}); + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:text_font", SConfigValue{.strValue = "Sans"}); + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:text_height", SConfigValue{.intValue = 8}); + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:text_padding", SConfigValue{.intValue = 3}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:col.active", SConfigValue{.intValue = 0xff32b4ff}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:col.urgent", SConfigValue{.intValue = 0xffff4f4f}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:col.inactive", SConfigValue{.intValue = 0x80808080}); - HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:from_top", SConfigValue{.intValue = 0}); - HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:rounding", SConfigValue{.intValue = 3}); + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.active", SConfigValue{.intValue = 0xff000000}); + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.urgent", SConfigValue{.intValue = 0xff000000}); + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:col.text.inactive", SConfigValue{.intValue = 0xff000000}); g_Hy3Layout = std::make_unique(); HyprlandAPI::addLayout(PHANDLE, "hy3", g_Hy3Layout.get());