From 45a97217029823f684753beedf44386d24c10d72 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Thu, 25 May 2023 02:27:02 -0700 Subject: [PATCH] Replace tab entry logic and tab bar renderer (WIP) --- src/TabGroup.cpp | 258 ++++++++++++++++++++++++++++------------------- src/TabGroup.hpp | 41 +++++--- 2 files changed, 181 insertions(+), 118 deletions(-) diff --git a/src/TabGroup.cpp b/src/TabGroup.cpp index d40f63a..979444a 100644 --- a/src/TabGroup.cpp +++ b/src/TabGroup.cpp @@ -5,86 +5,151 @@ #include #include -void Hy3TabBar::updateWithGroupEntries(Hy3Node& group_node) { - if (group_node.data.type != Hy3NodeData::Group) return; - auto& group = group_node.data.as_group; +Hy3TabBarEntry::Hy3TabBarEntry(Hy3TabBar& tab_bar, Hy3Node& node): tab_bar(tab_bar), node(node) { + this->offset.create(AVARTYPE_FLOAT, -1.0f, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), nullptr, AVARDAMAGE_NONE); + this->width.create(AVARTYPE_FLOAT, -1.0f, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), nullptr, AVARDAMAGE_NONE); - auto entries_iter = this->entries.begin(); - auto group_iter = group.children.begin(); + this->offset.registerVar(); + this->width.registerVar(); - auto* root_node = &group_node; - while (root_node->parent != nullptr) root_node = root_node->parent; - Hy3Node* focused_node = root_node->getFocusedNode(); + this->window_title = node.getTitle(); + this->urgent = node.isUrgent(); +} - while (entries_iter != this->entries.end()) { - if (group_iter == group.children.end()) { - needs_redraw = true; +bool Hy3TabBarEntry::operator==(const Hy3Node& node) const { + return this->node == node; +} - while (entries_iter != this->entries.end()) { - entries_iter = this->entries.erase(entries_iter); - } +Hy3TabBar::Hy3TabBar() { + this->vertical_pos.create(AVARTYPE_FLOAT, 20.0f, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), nullptr, AVARDAMAGE_NONE); + this->fade_opacity.create(AVARTYPE_FLOAT, 0.0f, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), nullptr, AVARDAMAGE_NONE); + this->focus_start.create(AVARTYPE_FLOAT, 0.0f, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), nullptr, AVARDAMAGE_NONE); + this->focus_end.create(AVARTYPE_FLOAT, 1.0f, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), nullptr, AVARDAMAGE_NONE); + this->vertical_pos.registerVar(); + this->fade_opacity.registerVar(); + this->focus_start.registerVar(); + this->focus_end.registerVar(); - return; - }; + this->vertical_pos = 0.0; + this->fade_opacity = 1.0; +} - auto& entry = *entries_iter; - auto& node = **group_iter; +void Hy3TabBar::focusNode(Hy3Node* node) { + this->focused_node = node; - std::string title = node.getTitle(); - bool urgent = node.isUrgent(); - bool focused = focused_node == &group_node - || focused_node == &node - || (node.data.type == Hy3NodeData::Group && node.data.as_group.hasChild(focused_node)); + if (this->focused_node == nullptr) { + this->focus_start = 0.0; + this->focus_end = 1.0; + } else { + auto entry = std::find(this->entries.begin(), this->entries.end(), *node); - if (entry.urgent != urgent - || entry.focused != focused - || entry.window_title != title) - this->needs_redraw = true; - - entry.window_title = std::move(title); - entry.urgent = urgent; - entry.focused = focused; - - entries_iter = std::next(entries_iter); - group_iter = std::next(group_iter); - } - - while (group_iter != group.children.end()) { - needs_redraw = true; - - auto& node = **group_iter; - - this->entries.push_back({ - .window_title = node.getTitle(), - .urgent = node.isUrgent(), - .focused = focused_node == &group_node - || focused_node == &node - || (node.data.type == Hy3NodeData::Group && node.data.as_group.hasChild(focused_node)), - }); - - group_iter = std::next(group_iter); + if (entry != this->entries.end()) { + this->focus_start = entry->offset.goalf(); + this->focus_end = entry->offset.goalf() + entry->width.goalf(); + } } } -void Hy3TabBar::setPos(Vector2D pos) { - if (pos == this->pos) return; - needs_redraw = true; - this->pos = pos; +void Hy3TabBar::updateNodeList(std::list& nodes) { + std::list removed_entries; + + auto entry = this->entries.begin(); + auto node = nodes.begin(); + + // move any out of order entries to removed_entries + while (node != nodes.end()) { + while (true) { + if (entry == this->entries.end()) goto exitloop; + if (*entry == **node) break; + removed_entries.splice(removed_entries.end(), this->entries, entry++); + } + + node = std::next(node); + entry = std::next(entry); + } + + exitloop: + + // move any extra entries to removed_entries + removed_entries.splice(removed_entries.end(), this->entries, entry, this->entries.end()); + + entry = this->entries.begin(); + node = nodes.begin(); + + // add missing entries, taking first from removed_entries + while (node != nodes.end()) { + if (entry == this->entries.end() || *entry != **node) { + auto moved = std::find(removed_entries.begin(), removed_entries.end(), **node); + if (moved != removed_entries.end()) { + this->entries.splice(std::next(entry), removed_entries, moved); + entry = moved; + } else { + entry = this->entries.insert(std::next(entry), Hy3TabBarEntry(*this, **node)); + } + } + + node = std::next(node); + if (entry != this->entries.end()) entry = std::next(entry); + } + + // initiate remove animations for any removed entries + for (auto& entry: removed_entries) { + // TODO: working entry remove anim + entry.width = 0.0; + } +} + +void Hy3TabBar::updateAnimations(bool warp) { + int active_entries = 0; + for (auto& entry: this->entries) { + if (entry.width.goalf() != 0.0) active_entries++; + } + + float entry_width = active_entries == 0 ? 0.0 : 1.0 / active_entries; + float offset = 0.0; + + auto entry = this->entries.begin(); + while (entry != this->entries.end()) { + if (warp && entry->width.goalf() == 0.0) { + this->entries.erase(entry++); + continue; + } + + auto warp_init = entry->offset.goalf() == -1.0; + entry->offset = offset; + + if (warp_init) { + entry->offset.warp(); + entry->width.setValueAndWarp(0.0); + } + + entry->width = entry_width; + offset += entry_width; + + entry = std::next(entry); + } } void Hy3TabBar::setSize(Vector2D size) { + if (size == this->size) return; + this->need_mask_redraw = true; this->size = size; } -void Hy3TabBar::prepareTexture() { - auto bar_width = this->size.x; - auto bar_height = this->size.y; +void Hy3TabBar::prepareMask() { + static const auto* rounding_setting = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:rounding")->intValue; + auto rounding = *rounding_setting; - if (needs_redraw || this->texture.m_iTexID == 0) { - static const auto* rounding_setting = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:rounding")->intValue; - auto rounding = *rounding_setting; + auto width = this->size.x; + auto height = this->size.y; - auto cairo_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, this->size.x, this->size.y); + if (this->need_mask_redraw + || this->last_mask_rounding != rounding + || this->mask_texture.m_iTexID == 0 + ) { + this->last_mask_rounding = rounding; + + auto cairo_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); auto cairo = cairo_create(cairo_surface); // clear pixmap @@ -93,48 +158,28 @@ void Hy3TabBar::prepareTexture() { cairo_paint(cairo); cairo_restore(cairo); - auto swidth = (double) bar_width / (double) this->entries.size(); - size_t i = 0; + // set brush + cairo_set_source_rgba(cairo, 0.2, 0.7, 1.0, 0.5); + cairo_set_line_width(cairo, 2.0); - for (auto& entry: this->entries) { - auto width = swidth; - auto x = i * width; + // outline bar shape + cairo_move_to(cairo, 0, rounding); + cairo_arc(cairo, rounding, rounding, rounding, -180.0 * (M_PI / 180.0), -90.0 * (M_PI / 180.0)); + cairo_line_to(cairo, width - rounding, 0); + cairo_arc(cairo, width - rounding, rounding, rounding, -90.0 * (M_PI / 180.0), 0.0); + cairo_line_to(cairo, width, height - rounding); + cairo_arc(cairo, width - rounding, height - rounding, rounding, 0.0, 90.0 * (M_PI / 180.0)); + cairo_line_to(cairo, rounding, height); + cairo_arc(cairo, rounding, height - rounding, rounding, -270.0 * (M_PI / 180.0), -180.0 * (M_PI / 180.0)); + cairo_close_path(cairo); - if (entry.focused) { - cairo_set_source_rgba(cairo, 0.0, 1.0, 0.7, 0.5); - } else { - cairo_set_source_rgba(cairo, 0.2, 0.7, 1.0, 0.5); - } - - if (i == this->entries.size() - 1) { - cairo_move_to(cairo, x + width - rounding, rounding); - cairo_arc(cairo, x + width - rounding, rounding, rounding, -90.0 * (M_PI / 180.0), 0.0); - cairo_rectangle(cairo, x + width - rounding, rounding, rounding, bar_height - rounding * 2); - cairo_move_to(cairo, x + width - rounding, bar_height - rounding); - cairo_arc(cairo, x + width - rounding, bar_height - rounding, rounding, 0.0, 90.0 * (M_PI / 180.0)); - width -= rounding; - } - - if (i == 0) { - cairo_move_to(cairo, x + rounding, rounding); - cairo_arc(cairo, x + rounding, rounding, rounding, -180.0 * (M_PI / 180.0), -90.0 * (M_PI / 180.0)); - cairo_rectangle(cairo, x, rounding, rounding, bar_height - rounding * 2); - cairo_move_to(cairo, x + rounding, bar_height - rounding); - cairo_arc(cairo, x + rounding, bar_height - rounding, rounding, -270.0 * (M_PI / 180.0), -180.0 * (M_PI / 180.0)); - x += rounding; - width -= rounding; - } - - cairo_rectangle(cairo, x, 0, width, bar_height); - cairo_fill(cairo); - - i++; - } + // draw + cairo_fill(cairo); auto data = cairo_image_surface_get_data(cairo_surface); - this->texture.allocate(); + this->mask_texture.allocate(); - glBindTexture(GL_TEXTURE_2D, this->texture.m_iTexID); + glBindTexture(GL_TEXTURE_2D, this->mask_texture.m_iTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -143,12 +188,12 @@ void Hy3TabBar::prepareTexture() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); #endif - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bar_width, bar_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); cairo_destroy(cairo); cairo_surface_destroy(cairo_surface); } else { - glBindTexture(GL_TEXTURE_2D, this->texture.m_iTexID); + glBindTexture(GL_TEXTURE_2D, this->mask_texture.m_iTexID); } } @@ -160,8 +205,9 @@ Hy3TabGroup::Hy3TabGroup(Hy3Node& node) { this->size.registerVar(); this->updateWithGroup(node); - this->pos.warp(false); - this->size.warp(false); + this->bar.updateAnimations(true); + this->pos.warp(); + this->size.warp(); } void Hy3TabGroup::updateWithGroup(Hy3Node& node) { @@ -174,7 +220,7 @@ void Hy3TabGroup::updateWithGroup(Hy3Node& node) { if (this->pos.goalv() != tpos) this->pos = tpos; if (this->size.goalv() != tsize) this->size = tsize; - this->bar.updateWithGroupEntries(node); + this->bar.updateNodeList(node.data.as_group.children); } void Hy3TabGroup::renderTabBar() { @@ -183,16 +229,16 @@ void Hy3TabGroup::renderTabBar() { auto scale = monitor->scale; auto pos = this->pos.vec(); + pos.y += this->bar.vertical_pos.fl(); auto size = this->size.vec(); auto scaled_pos = Vector2D(std::round(pos.x * scale), std::round(pos.y * scale)); auto scaled_size = Vector2D(std::round(size.x * scale), std::round(size.y * scale)); auto box = wlr_box { scaled_pos.x, scaled_pos.y, scaled_size.x, scaled_size.y }; - this->bar.setPos(scaled_pos); this->bar.setSize(scaled_size); - this->bar.prepareTexture(); - g_pHyprOpenGL->renderTexture(this->bar.texture, &box, 1.0); + this->bar.prepareMask(); + g_pHyprOpenGL->renderTexture(this->bar.mask_texture, &box, this->bar.fade_opacity.fl()); g_pHyprRenderer->damageBox(&box); } diff --git a/src/TabGroup.hpp b/src/TabGroup.hpp index 08d0de6..eb373c4 100644 --- a/src/TabGroup.hpp +++ b/src/TabGroup.hpp @@ -2,9 +2,10 @@ #include #include -#include +#include class Hy3TabGroup; +class Hy3TabBar; #include "Hy3Layout.hpp" #include @@ -12,25 +13,41 @@ class Hy3TabGroup; struct Hy3TabBarEntry { std::string window_title; bool urgent = false; - bool focused = false; + CAnimatedVariable offset; // offset 0, 0.0-1.0 of total bar + CAnimatedVariable width; // 0.0-1.0 of total bar + Hy3TabBar& tab_bar; + Hy3Node& node; // only used for comparioson. do not deref. + + Hy3TabBarEntry(Hy3TabBar&, Hy3Node&); + bool operator==(const Hy3Node& node) const; }; class Hy3TabBar { public: - CTexture texture; + CTexture mask_texture; + CAnimatedVariable vertical_pos; + CAnimatedVariable fade_opacity; - void updateWithGroupEntries(Hy3Node&); - void setPos(Vector2D); + Hy3TabBar(); + + void focusNode(Hy3Node*); + void updateNodeList(std::list& nodes); + void updateAnimations(bool warp = false); void setSize(Vector2D); - // Redraw the texture if necessary, and bind it to GL_TEXTURE_2D - void prepareTexture(); -private: - bool needs_redraw = true; + // Redraw the mask texture if necessary, and bind it to GL_TEXTURE_2D + void prepareMask(); - std::vector entries; - // scaled pos/size - Vector2D pos; +private: + bool need_mask_redraw = false; + int last_mask_rounding = 0; + + Hy3Node* focused_node = nullptr; + CAnimatedVariable focus_opacity; + CAnimatedVariable focus_start; + CAnimatedVariable focus_end; + + std::list entries; Vector2D size; };