Initial work on tab groups

This commit is contained in:
outfoxxed 2023-05-16 03:02:26 -07:00
parent 4c000cc03c
commit 5d6b415c7f
No known key found for this signature in database
GPG key ID: 4C88A185FB89301E
8 changed files with 419 additions and 44 deletions

View file

@ -11,11 +11,12 @@ if(CMAKE_EXPORT_COMPILE_COMMANDS)
endif() endif()
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(DEPS REQUIRED hyprland pixman-1 libdrm) pkg_check_modules(DEPS REQUIRED hyprland pixman-1 libdrm pango pangocairo)
add_library(hy3 SHARED add_library(hy3 SHARED
src/main.cpp src/main.cpp
src/Hy3Layout.cpp src/Hy3Layout.cpp
src/TabGroup.cpp
src/SelectionHook.cpp src/SelectionHook.cpp
) )

12
flake.lock generated
View file

@ -8,11 +8,11 @@
"xdph": "xdph" "xdph": "xdph"
}, },
"locked": { "locked": {
"lastModified": 1682603803, "lastModified": 1684167111,
"narHash": "sha256-NY9nVAdB7UyInu2vPx/DIUVNZ83t4RdP16QY9DTIn4s=", "narHash": "sha256-0JKyr8WOpcXJP5XaLnUSI7e1d7N5Rcpyf72+N4ZEtjo=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "Hyprland", "repo": "Hyprland",
"rev": "f23455e592bca14e0abd9249de467cc71cd2850e", "rev": "5b84b0fb445bc4485510bba516c84141aaeafd04",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -44,11 +44,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1682453498, "lastModified": 1683014792,
"narHash": "sha256-WoWiAd7KZt5Eh6n+qojcivaVpnXKqBsVgpixpV2L9CE=", "narHash": "sha256-6Va9iVtmmsw4raBc3QKvQT2KT/NGRWlvUlJj46zN8B8=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "c8018361fa1d1650ee8d4b96294783cf564e8a7f", "rev": "1a411f23ba299db155a5b45d5e145b85a7aafc42",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -15,8 +15,10 @@
nativeBuildInputs = with pkgs; [ cmake pkg-config ]; nativeBuildInputs = with pkgs; [ cmake pkg-config ];
buildInputs = [ buildInputs = with pkgs; [
hyprland.packages.${system}.hyprland.dev hyprland.packages.${system}.hyprland.dev
pango
cairo
] ++ hyprland.packages.${system}.hyprland.buildInputs; ] ++ hyprland.packages.${system}.hyprland.buildInputs;
# no noticeable impact on performance and greatly assists debugging # no noticeable impact on performance and greatly assists debugging

View file

@ -41,18 +41,6 @@ Hy3NodeData::~Hy3NodeData() {
} }
} }
Hy3NodeData::Hy3NodeData(const Hy3NodeData& from): type(from.type) {
Debug::log(LOG, "Copy CTor type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group);
switch (from.type) {
case Hy3NodeData::Window:
this->as_window = from.as_window;
break;
case Hy3NodeData::Group:
new(&this->as_group) Hy3GroupData(from.as_group);
break;
}
}
Hy3NodeData::Hy3NodeData(Hy3NodeData&& from): type(from.type) { Hy3NodeData::Hy3NodeData(Hy3NodeData&& from): type(from.type) {
Debug::log(LOG, "Move CTor type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group); Debug::log(LOG, "Move CTor type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group);
switch (from.type) { switch (from.type) {
@ -65,7 +53,7 @@ Hy3NodeData::Hy3NodeData(Hy3NodeData&& from): type(from.type) {
} }
} }
Hy3NodeData& Hy3NodeData::operator=(const Hy3NodeData& from) { Hy3NodeData& Hy3NodeData::operator=(Hy3NodeData&& from) {
Debug::log(LOG, "operator= type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group); Debug::log(LOG, "operator= type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group);
if (this->type == Hy3NodeData::Group) { if (this->type == Hy3NodeData::Group) {
this->as_group.~Hy3GroupData(); this->as_group.~Hy3GroupData();
@ -78,7 +66,7 @@ Hy3NodeData& Hy3NodeData::operator=(const Hy3NodeData& from) {
this->as_window = from.as_window; this->as_window = from.as_window;
break; break;
case Hy3NodeData::Group: case Hy3NodeData::Group:
new(&this->as_group) Hy3GroupData(from.as_group); new(&this->as_group) Hy3GroupData(std::move(from.as_group));
break; break;
} }
@ -121,36 +109,44 @@ void Hy3Node::recalcSizePosRecursive(bool force) {
errorNotif(); errorNotif();
} }
double distortOut; double distort_out;
double distortIn; double distort_in;
double tab_height_offset;
const auto* gaps_in = &g_pConfigManager->getConfigValuePtr("general:gaps_in")->intValue; static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue;
const auto* gaps_out = &g_pConfigManager->getConfigValuePtr("general:gaps_out")->intValue; static const auto* gaps_out = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_out")->intValue;
static const auto* tab_bar_height = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:bar_height")->intValue;
tab_height_offset = *gaps_in * 2 + *tab_bar_height;
if (gaps_in > gaps_out) { if (gaps_in > gaps_out) {
distortOut = *gaps_out - 1.0; distort_out = *gaps_out - 1.0;
} else { } else {
distortOut = *gaps_in - 1.0; distort_out = *gaps_in - 1.0;
} }
if (distortOut < 0) distortOut = 0.0; if (distort_out < 0) distort_out = 0.0;
distortIn = *gaps_in * 2; distort_in = *gaps_in * 2;
switch (group->layout) { switch (group->layout) {
case Hy3GroupLayout::SplitH: case Hy3GroupLayout::SplitH:
child->position.x = this->position.x - distortOut; child->position.x = this->position.x - distort_out;
child->size.x = this->size.x - distortIn; child->size.x = this->size.x - distort_in;
child->position.y = this->position.y; child->position.y = this->position.y;
child->size.y = this->size.y; child->size.y = this->size.y;
break; break;
case Hy3GroupLayout::SplitV: case Hy3GroupLayout::SplitV:
child->position.y = this->position.y - distortOut; child->position.y = this->position.y - distort_out;
child->size.y = this->size.y - distortIn; child->size.y = this->size.y - distort_in;
child->position.x = this->position.x; child->position.x = this->position.x;
child->size.x = this->size.x; child->size.x = this->size.x;
break;
case Hy3GroupLayout::Tabbed: case Hy3GroupLayout::Tabbed:
// TODO child->position.y = this->position.y + tab_height_offset;
child->size.y = this->size.y - tab_height_offset;
child->position.x = this->position.x;
child->size.x = this->size.x;
break; break;
} }
@ -182,6 +178,7 @@ void Hy3Node::recalcSizePosRecursive(bool force) {
offset += child->size.x; offset += child->size.x;
child->position.y = this->position.y; child->position.y = this->position.y;
child->size.y = this->size.y; child->size.y = this->size.y;
//child->setHidden(false);
break; break;
case Hy3GroupLayout::SplitV: case Hy3GroupLayout::SplitV:
child->position.y = this->position.y + offset; child->position.y = this->position.y + offset;
@ -189,11 +186,14 @@ void Hy3Node::recalcSizePosRecursive(bool force) {
offset += child->size.y; offset += child->size.y;
child->position.x = this->position.x; child->position.x = this->position.x;
child->size.x = this->size.x; child->size.x = this->size.x;
//child->setHidden(false);
break; break;
case Hy3GroupLayout::Tabbed: case Hy3GroupLayout::Tabbed:
// TODO: tab bars child->position.y = this->position.y + 20;
child->position = this->position; child->size.y = this->size.y - 20;
child->size = this->size; child->position.x = this->position.x;
child->size.x = this->size.x;
//child->setHidden(group->lastFocusedChild != child);
break; break;
} }
@ -201,6 +201,62 @@ void Hy3Node::recalcSizePosRecursive(bool force) {
} }
} }
void Hy3Node::setHidden(bool hidden) {
switch (this->data.type) {
case Hy3NodeData::Window:
this->data.as_window->setHidden(hidden);
break;
case Hy3NodeData::Group:
for (auto* child: this->data.as_group.children) {
child->setHidden(hidden);
}
}
}
bool Hy3Node::isUrgent() {
switch (this->data.type) {
case Hy3NodeData::Window:
return this->data.as_window->m_bIsUrgent;
case Hy3NodeData::Group:
for (auto* child: this->data.as_group.children) {
if (child->isUrgent()) return true;
}
return false;
}
}
std::string Hy3Node::getTitle() {
switch (this->data.type) {
case Hy3NodeData::Window:
return this->data.as_window->m_szTitle;
case Hy3NodeData::Group:
std::string title;
switch (this->data.as_group.layout) {
case Hy3GroupLayout::SplitH:
title = "[H] ";
break;
case Hy3GroupLayout::SplitV:
title = "[V] ";
break;
case Hy3GroupLayout::Tabbed:
title = "[T] ";
break;
}
if (this->data.as_group.lastFocusedChild == nullptr) {
title += "Group";
} else {
title += this->data.as_group.lastFocusedChild->getTitle();
}
return title;
}
return "";
}
void Hy3Node::markFocused() { void Hy3Node::markFocused() {
Hy3Node* node = this; Hy3Node* node = this;
@ -384,8 +440,8 @@ bool Hy3GroupData::hasChild(Hy3Node* node) {
void Hy3Node::swapData(Hy3Node& a, Hy3Node& b) { void Hy3Node::swapData(Hy3Node& a, Hy3Node& b) {
Hy3NodeData aData = std::move(a.data); Hy3NodeData aData = std::move(a.data);
a.data = b.data; a.data = std::move(b.data);
b.data = aData; b.data = std::move(aData);
if (a.data.type == Hy3NodeData::Group) { if (a.data.type == Hy3NodeData::Group) {
for (auto child: a.data.as_group.children) { for (auto child: a.data.as_group.children) {
@ -687,6 +743,10 @@ void Hy3Layout::onWindowFocusChange(CWindow* window) {
auto* node = this->getNodeFromWindow(window); auto* node = this->getNodeFromWindow(window);
if (node == nullptr) return; if (node == nullptr) return;
if (node->parent != nullptr && node->parent->data.as_group.layout == Hy3GroupLayout::Tabbed) {
node->parent->recalcSizePosRecursive();
}
node->markFocused(); node->markFocused();
} }
@ -1066,6 +1126,8 @@ void Hy3Layout::replaceWindowDataWith(CWindow* from, CWindow* to) {
this->applyNodeDataToWindow(node); this->applyNodeDataToWindow(node);
} }
std::unique_ptr<HOOK_CALLBACK_FN> renderHookPtr = std::make_unique<HOOK_CALLBACK_FN>(Hy3Layout::renderHook);
void Hy3Layout::onEnable() { void Hy3Layout::onEnable() {
for (auto &window : g_pCompositor->m_vWindows) { for (auto &window : g_pCompositor->m_vWindows) {
if (window->isHidden() if (window->isHidden()
@ -1077,10 +1139,12 @@ void Hy3Layout::onEnable() {
this->onWindowCreatedTiling(window.get()); this->onWindowCreatedTiling(window.get());
} }
HyprlandAPI::registerCallbackStatic(PHANDLE, "render", renderHookPtr.get());
selection_hook::enable(); selection_hook::enable();
} }
void Hy3Layout::onDisable() { void Hy3Layout::onDisable() {
HyprlandAPI::unregisterCallback(PHANDLE, renderHookPtr.get());
selection_hook::disable(); selection_hook::disable();
this->nodes.clear(); this->nodes.clear();
} }
@ -1198,7 +1262,7 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(Hy3Node& node, ShiftDirection direction, boo
// if we haven't gone up any levels and the group is in the same direction // if we haven't gone up any levels and the group is in the same direction
// there's no reason to wrap the root group. // there's no reason to wrap the root group.
if (shiftMatchesLayout(group.layout, direction)) break; if (group.layout != Hy3GroupLayout::Tabbed && shiftMatchesLayout(group.layout, direction)) break;
if (group.layout != Hy3GroupLayout::Tabbed if (group.layout != Hy3GroupLayout::Tabbed
&& group.children.size() == 2 && group.children.size() == 2
@ -1413,3 +1477,48 @@ std::string Hy3Node::debugNode() {
return buf.str(); return buf.str();
} }
// Recursively render tabs on all tab groups.
void renderTabsRecursive(Hy3Node& node);
// Render tabs for the provided node, blindly assuming it is a tab group.
void renderTabs(Hy3Node& node);
void Hy3Layout::renderHook(void*, std::any data) {
auto render_stage = std::any_cast<eRenderStage>(data);
if (render_stage == RENDER_POST_WINDOWS) {
auto* monitor = g_pHyprOpenGL->m_RenderData.pMonitor;
auto workspace = monitor->activeWorkspace;
auto* root = g_Hy3Layout->getWorkspaceRootGroup(workspace);
if (root != nullptr) renderTabsRecursive(*root);
}
}
void renderTabsRecursive(Hy3Node& node) {
if (node.data.type == Hy3NodeData::Group) {
for (auto* child: node.data.as_group.children) {
if (node.data.as_group.layout != Hy3GroupLayout::Tabbed
|| node.data.as_group.lastFocusedChild == child)
{
renderTabsRecursive(*child);
}
}
if (node.data.as_group.layout == Hy3GroupLayout::Tabbed) {
renderTabs(node);
}
}
}
void renderTabs(Hy3Node& node) {
auto& group = node.data.as_group;
if (!group.tab_bar) {
group.tab_bar = std::unique_ptr<Hy3TabGroup>(new Hy3TabGroup(node));
} else {
group.tab_bar->updateWithGroup(node);
}
group.tab_bar->renderTabBar();
}

View file

@ -1,5 +1,8 @@
#pragma once #pragma once
struct Hy3Node;
#include "TabGroup.hpp"
#include <list> #include <list>
#include <hyprland/src/layout/IHyprLayout.hpp> #include <hyprland/src/layout/IHyprLayout.hpp>
@ -23,6 +26,7 @@ struct Hy3GroupData {
Hy3GroupLayout layout = Hy3GroupLayout::SplitH; Hy3GroupLayout layout = Hy3GroupLayout::SplitH;
std::list<Hy3Node*> children; std::list<Hy3Node*> children;
Hy3Node* lastFocusedChild = nullptr; Hy3Node* lastFocusedChild = nullptr;
std::unique_ptr<Hy3TabGroup> tab_bar;
bool hasChild(Hy3Node* child); bool hasChild(Hy3Node* child);
@ -30,7 +34,6 @@ struct Hy3GroupData {
private: private:
Hy3GroupData(Hy3GroupData&&) = default; Hy3GroupData(Hy3GroupData&&) = default;
Hy3GroupData(const Hy3GroupData&) = default;
friend class Hy3NodeData; friend class Hy3NodeData;
}; };
@ -54,9 +57,8 @@ public:
//private: - I give up, C++ wins //private: - I give up, C++ wins
Hy3NodeData(Hy3GroupData); Hy3NodeData(Hy3GroupData);
Hy3NodeData(const Hy3NodeData&);
Hy3NodeData(Hy3NodeData&&); Hy3NodeData(Hy3NodeData&&);
Hy3NodeData& operator=(const Hy3NodeData&); Hy3NodeData& operator=(Hy3NodeData&&);
}; };
struct Hy3Node { struct Hy3Node {
@ -76,6 +78,9 @@ struct Hy3Node {
void raiseToTop(); void raiseToTop();
Hy3Node* getFocusedNode(); Hy3Node* getFocusedNode();
void updateDecos(); void updateDecos();
void setHidden(bool hidden);
bool isUrgent();
std::string getTitle();
bool operator==(const Hy3Node&) const; bool operator==(const Hy3Node&) const;
@ -126,6 +131,8 @@ public:
Hy3Node* getWorkspaceRootGroup(const int&); Hy3Node* getWorkspaceRootGroup(const int&);
Hy3Node* getWorkspaceFocusedNode(const int&); Hy3Node* getWorkspaceFocusedNode(const int&);
static void renderHook(void*, std::any);
std::list<Hy3Node> nodes; std::list<Hy3Node> nodes;
private: private:
struct { struct {

198
src/TabGroup.cpp Normal file
View file

@ -0,0 +1,198 @@
#include "globals.hpp"
#include "TabGroup.hpp"
#include "Hy3Layout.hpp"
#include <hyprland/src/Compositor.hpp>
#include <cairo/cairo.h>
void Hy3TabBar::updateWithGroupEntries(Hy3Node& group_node) {
if (group_node.data.type != Hy3NodeData::Group) return;
auto& group = group_node.data.as_group;
auto entries_iter = this->entries.begin();
auto group_iter = group.children.begin();
auto* root_node = &group_node;
while (root_node->parent != nullptr) root_node = root_node->parent;
Hy3Node* focused_node = root_node->getFocusedNode();
while (entries_iter != this->entries.end()) {
if (group_iter == group.children.end()) {
needs_redraw = true;
while (entries_iter != this->entries.end()) {
entries_iter = this->entries.erase(entries_iter);
}
return;
};
auto& entry = *entries_iter;
auto& node = **group_iter;
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 (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);
}
}
void Hy3TabBar::setPos(Vector2D pos) {
if (pos == this->pos) return;
needs_redraw = true;
this->pos = pos;
}
void Hy3TabBar::setSize(Vector2D size) {
this->size = size;
}
void Hy3TabBar::prepareTexture() {
auto bar_width = this->size.x;
auto bar_height = this->size.y;
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 cairo_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, this->size.x, this->size.y);
auto cairo = cairo_create(cairo_surface);
// clear pixmap
cairo_save(cairo);
cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
cairo_paint(cairo);
cairo_restore(cairo);
auto swidth = (double) bar_width / (double) this->entries.size();
size_t i = 0;
for (auto& entry: this->entries) {
auto width = swidth;
auto x = i * width;
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++;
}
auto data = cairo_image_surface_get_data(cairo_surface);
this->texture.allocate();
glBindTexture(GL_TEXTURE_2D, this->texture.m_iTexID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
#ifdef GLES32
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, bar_width, bar_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);
}
}
Hy3TabGroup::Hy3TabGroup(Hy3Node& node) {
this->pos.create(AVARTYPE_VECTOR, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), nullptr, AVARDAMAGE_NONE);
this->size.create(AVARTYPE_VECTOR, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), nullptr, AVARDAMAGE_NONE);
Debug::log(LOG, "registered anims");
this->pos.registerVar();
this->size.registerVar();
this->updateWithGroup(node);
this->pos.warp(false);
this->size.warp(false);
}
void Hy3TabGroup::updateWithGroup(Hy3Node& node) {
static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue;
static const auto* bar_height = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:bar_height")->intValue;
auto tpos = node.position + Vector2D(*gaps_in, *gaps_in);
auto tsize = Vector2D(node.size.x - *gaps_in * 2, *bar_height);
if (this->pos.goalv() != tpos) this->pos = tpos;
if (this->size.goalv() != tsize) this->size = tsize;
this->bar.updateWithGroupEntries(node);
}
void Hy3TabGroup::renderTabBar() {
auto* monitor = g_pHyprOpenGL->m_RenderData.pMonitor;
auto scale = monitor->scale;
auto pos = this->pos.vec();
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);
g_pHyprRenderer->damageBox(&box);
}

54
src/TabGroup.hpp Normal file
View file

@ -0,0 +1,54 @@
#pragma once
#include <hyprland/src/helpers/AnimatedVariable.hpp>
#include <hyprland/src/helpers/Vector2D.hpp>
#include <vector>
class Hy3TabGroup;
#include "Hy3Layout.hpp"
#include <hyprland/src/render/Texture.hpp>
struct Hy3TabBarEntry {
std::string window_title;
bool urgent = false;
bool focused = false;
};
class Hy3TabBar {
public:
CTexture texture;
void updateWithGroupEntries(Hy3Node&);
void setPos(Vector2D);
void setSize(Vector2D);
// Redraw the texture if necessary, and bind it to GL_TEXTURE_2D
void prepareTexture();
private:
bool needs_redraw = true;
std::vector<Hy3TabBarEntry> entries;
// scaled pos/size
Vector2D pos;
Vector2D size;
};
class Hy3TabGroup {
public:
Hy3TabBar bar;
CAnimatedVariable pos;
CAnimatedVariable size;
// initialize a group with the given node. UB if node is not a group.
Hy3TabGroup(Hy3Node&);
// update tab bar with node position and data. UB if node is not a group.
void updateWithGroup(Hy3Node&);
// render the scaled tab bar on the current monitor.
void renderTabBar();
private:
// moving a Hy3TabGroup will unregister any active animations
Hy3TabGroup(Hy3TabGroup&&) = delete;
};

View file

@ -45,6 +45,8 @@ void dispatch_makegroup(std::string arg) {
g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitH); g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitH);
} else if (arg == "v") { } else if (arg == "v") {
g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitV); g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitV);
} else if (arg == "tab") {
g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::Tabbed);
} else if (arg == "opposite") { } else if (arg == "opposite") {
g_Hy3Layout->makeOppositeGroupOnWorkspace(workspace); g_Hy3Layout->makeOppositeGroupOnWorkspace(workspace);
} }
@ -104,6 +106,8 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
selection_hook::init(); selection_hook::init();
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:no_gaps_when_only", SConfigValue{.intValue = 0}); HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:no_gaps_when_only", SConfigValue{.intValue = 0});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:bar_height", SConfigValue{.intValue = 15});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:rounding", SConfigValue{.intValue = 3});
g_Hy3Layout = std::make_unique<Hy3Layout>(); g_Hy3Layout = std::make_unique<Hy3Layout>();
HyprlandAPI::addLayout(PHANDLE, "hy3", g_Hy3Layout.get()); HyprlandAPI::addLayout(PHANDLE, "hy3", g_Hy3Layout.get());