mirror of
https://github.com/Trensa-Organization/hy3.git
synced 2025-03-15 18:53:40 +01:00
Initial work on tab groups
This commit is contained in:
parent
4c000cc03c
commit
5d6b415c7f
8 changed files with 419 additions and 44 deletions
|
@ -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
12
flake.lock
generated
|
@ -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": {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
|
@ -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
198
src/TabGroup.cpp
Normal 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
54
src/TabGroup.hpp
Normal 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;
|
||||||
|
};
|
|
@ -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());
|
||||||
|
|
Loading…
Add table
Reference in a new issue