5d6b415c7f02194cbb30d534b30cd91e7032eaa3
- Author
- outfoxxed <outfoxxed@outfoxxed.me>
- Committer
- outfoxxed <outfoxxed@outfoxxed.me>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/CMakeLists.txt b/CMakeLists.txt
2index 64e3c51fb5d7c87c832a8421f5c9e8679e4e523c..5ad5ce0cfeaecc65293900920107a56ee1928868 100644
3--- a/CMakeLists.txt
4+++ b/CMakeLists.txt
5@@ -11,11 +11,12 @@ if(CMAKE_EXPORT_COMPILE_COMMANDS)
6 endif()
7
8 find_package(PkgConfig REQUIRED)
9-pkg_check_modules(DEPS REQUIRED hyprland pixman-1 libdrm)
10+pkg_check_modules(DEPS REQUIRED hyprland pixman-1 libdrm pango pangocairo)
11
12 add_library(hy3 SHARED
13 src/main.cpp
14 src/Hy3Layout.cpp
15+ src/TabGroup.cpp
16 src/SelectionHook.cpp
17 )
18
19diff --git a/flake.lock b/flake.lock
20index c8699520113bfe5c1d2e89b9afd501fe2fc4c070..2764e8b3b414f521f602bc6b50ca932b84b46451 100644
21--- a/flake.lock
22+++ b/flake.lock
23@@ -8,11 +8,11 @@
24 "xdph": "xdph"
25 },
26 "locked": {
27- "lastModified": 1682603803,
28- "narHash": "sha256-NY9nVAdB7UyInu2vPx/DIUVNZ83t4RdP16QY9DTIn4s=",
29+ "lastModified": 1684167111,
30+ "narHash": "sha256-0JKyr8WOpcXJP5XaLnUSI7e1d7N5Rcpyf72+N4ZEtjo=",
31 "owner": "hyprwm",
32 "repo": "Hyprland",
33- "rev": "f23455e592bca14e0abd9249de467cc71cd2850e",
34+ "rev": "5b84b0fb445bc4485510bba516c84141aaeafd04",
35 "type": "github"
36 },
37 "original": {
38@@ -44,11 +44,11 @@
39 },
40 "nixpkgs": {
41 "locked": {
42- "lastModified": 1682453498,
43- "narHash": "sha256-WoWiAd7KZt5Eh6n+qojcivaVpnXKqBsVgpixpV2L9CE=",
44+ "lastModified": 1683014792,
45+ "narHash": "sha256-6Va9iVtmmsw4raBc3QKvQT2KT/NGRWlvUlJj46zN8B8=",
46 "owner": "NixOS",
47 "repo": "nixpkgs",
48- "rev": "c8018361fa1d1650ee8d4b96294783cf564e8a7f",
49+ "rev": "1a411f23ba299db155a5b45d5e145b85a7aafc42",
50 "type": "github"
51 },
52 "original": {
53diff --git a/flake.nix b/flake.nix
54index 75e1b450c2f93bc549514fae7405d25fe43521bb..af84a4587bd4afeca47eba8dccc85ef47b776931 100644
55--- a/flake.nix
56+++ b/flake.nix
57@@ -15,8 +15,10 @@
58
59 nativeBuildInputs = with pkgs; [ cmake pkg-config ];
60
61- buildInputs = [
62+ buildInputs = with pkgs; [
63 hyprland.packages.${system}.hyprland.dev
64+ pango
65+ cairo
66 ] ++ hyprland.packages.${system}.hyprland.buildInputs;
67
68 # no noticeable impact on performance and greatly assists debugging
69diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
70index 68e04a816e7866d9b91eaad732c60cc63a1ac7a8..6ec06ec83d90838bb409399ffd3ea8d16cf0f825 100644
71--- a/src/Hy3Layout.cpp
72+++ b/src/Hy3Layout.cpp
73@@ -41,18 +41,6 @@ Hy3NodeData::~Hy3NodeData() {
74 }
75 }
76
77-Hy3NodeData::Hy3NodeData(const Hy3NodeData& from): type(from.type) {
78- Debug::log(LOG, "Copy CTor type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group);
79- switch (from.type) {
80- case Hy3NodeData::Window:
81- this->as_window = from.as_window;
82- break;
83- case Hy3NodeData::Group:
84- new(&this->as_group) Hy3GroupData(from.as_group);
85- break;
86- }
87-}
88-
89 Hy3NodeData::Hy3NodeData(Hy3NodeData&& from): type(from.type) {
90 Debug::log(LOG, "Move CTor type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group);
91 switch (from.type) {
92@@ -65,7 +53,7 @@ Hy3NodeData::Hy3NodeData(Hy3NodeData&& from): type(from.type) {
93 }
94 }
95
96-Hy3NodeData& Hy3NodeData::operator=(const Hy3NodeData& from) {
97+Hy3NodeData& Hy3NodeData::operator=(Hy3NodeData&& from) {
98 Debug::log(LOG, "operator= type matches? %d is group? %d", this->type == from.type, this->type == Hy3NodeData::Group);
99 if (this->type == Hy3NodeData::Group) {
100 this->as_group.~Hy3GroupData();
101@@ -78,7 +66,7 @@ Hy3NodeData& Hy3NodeData::operator=(const Hy3NodeData& from) {
102 this->as_window = from.as_window;
103 break;
104 case Hy3NodeData::Group:
105- new(&this->as_group) Hy3GroupData(from.as_group);
106+ new(&this->as_group) Hy3GroupData(std::move(from.as_group));
107 break;
108 }
109
110@@ -121,36 +109,44 @@ void Hy3Node::recalcSizePosRecursive(bool force) {
111 errorNotif();
112 }
113
114- double distortOut;
115- double distortIn;
116+ double distort_out;
117+ double distort_in;
118+ double tab_height_offset;
119
120- const auto* gaps_in = &g_pConfigManager->getConfigValuePtr("general:gaps_in")->intValue;
121- const auto* gaps_out = &g_pConfigManager->getConfigValuePtr("general:gaps_out")->intValue;
122+ static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue;
123+ static const auto* gaps_out = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_out")->intValue;
124+ static const auto* tab_bar_height = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:bar_height")->intValue;
125+
126+ tab_height_offset = *gaps_in * 2 + *tab_bar_height;
127
128 if (gaps_in > gaps_out) {
129- distortOut = *gaps_out - 1.0;
130+ distort_out = *gaps_out - 1.0;
131 } else {
132- distortOut = *gaps_in - 1.0;
133+ distort_out = *gaps_in - 1.0;
134 }
135
136- if (distortOut < 0) distortOut = 0.0;
137+ if (distort_out < 0) distort_out = 0.0;
138
139- distortIn = *gaps_in * 2;
140+ distort_in = *gaps_in * 2;
141
142 switch (group->layout) {
143 case Hy3GroupLayout::SplitH:
144- child->position.x = this->position.x - distortOut;
145- child->size.x = this->size.x - distortIn;
146+ child->position.x = this->position.x - distort_out;
147+ child->size.x = this->size.x - distort_in;
148 child->position.y = this->position.y;
149 child->size.y = this->size.y;
150 break;
151 case Hy3GroupLayout::SplitV:
152- child->position.y = this->position.y - distortOut;
153- child->size.y = this->size.y - distortIn;
154+ child->position.y = this->position.y - distort_out;
155+ child->size.y = this->size.y - distort_in;
156 child->position.x = this->position.x;
157 child->size.x = this->size.x;
158+ break;
159 case Hy3GroupLayout::Tabbed:
160- // TODO
161+ child->position.y = this->position.y + tab_height_offset;
162+ child->size.y = this->size.y - tab_height_offset;
163+ child->position.x = this->position.x;
164+ child->size.x = this->size.x;
165 break;
166 }
167
168@@ -182,6 +178,7 @@ void Hy3Node::recalcSizePosRecursive(bool force) {
169 offset += child->size.x;
170 child->position.y = this->position.y;
171 child->size.y = this->size.y;
172+ //child->setHidden(false);
173diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
174index 7b34ae69b6c1710c08461a545d1a13051b9e114a..8e044d284c56b9dcfe2d2a5c7b9bf76f81a9c009 100644
175--- a/src/Hy3Layout.hpp
176+++ b/src/Hy3Layout.hpp
177@@ -1,5 +1,8 @@
178 #pragma once
179
180+struct Hy3Node;
181+#include "TabGroup.hpp"
182+
183 #include <list>
184 #include <hyprland/src/layout/IHyprLayout.hpp>
185
186@@ -23,6 +26,7 @@ struct Hy3GroupData {
187 Hy3GroupLayout layout = Hy3GroupLayout::SplitH;
188 std::list<Hy3Node*> children;
189 Hy3Node* lastFocusedChild = nullptr;
190+ std::unique_ptr<Hy3TabGroup> tab_bar;
191
192 bool hasChild(Hy3Node* child);
193
194@@ -30,7 +34,6 @@ struct Hy3GroupData {
195
196 private:
197 Hy3GroupData(Hy3GroupData&&) = default;
198- Hy3GroupData(const Hy3GroupData&) = default;
199
200 friend class Hy3NodeData;
201 };
202@@ -54,9 +57,8 @@ public:
203
204 //private: - I give up, C++ wins
205 Hy3NodeData(Hy3GroupData);
206- Hy3NodeData(const Hy3NodeData&);
207 Hy3NodeData(Hy3NodeData&&);
208- Hy3NodeData& operator=(const Hy3NodeData&);
209+ Hy3NodeData& operator=(Hy3NodeData&&);
210 };
211
212 struct Hy3Node {
213@@ -76,6 +78,9 @@ struct Hy3Node {
214 void raiseToTop();
215 Hy3Node* getFocusedNode();
216 void updateDecos();
217+ void setHidden(bool hidden);
218+ bool isUrgent();
219+ std::string getTitle();
220
221 bool operator==(const Hy3Node&) const;
222
223@@ -126,6 +131,8 @@ public:
224 Hy3Node* getWorkspaceRootGroup(const int&);
225 Hy3Node* getWorkspaceFocusedNode(const int&);
226
227+ static void renderHook(void*, std::any);
228+
229 std::list<Hy3Node> nodes;
230 private:
231 struct {
232diff --git a/src/TabGroup.cpp b/src/TabGroup.cpp
233new file mode 100644
234index 0000000000000000000000000000000000000000..d40f63ac93406cfd095acc55282f97460dde909e
235--- /dev/null
236+++ b/src/TabGroup.cpp
237@@ -0,0 +1,198 @@
238+#include "globals.hpp"
239+#include "TabGroup.hpp"
240+#include "Hy3Layout.hpp"
241+
242+#include <hyprland/src/Compositor.hpp>
243+#include <cairo/cairo.h>
244+
245+void Hy3TabBar::updateWithGroupEntries(Hy3Node& group_node) {
246+ if (group_node.data.type != Hy3NodeData::Group) return;
247+ auto& group = group_node.data.as_group;
248+
249+ auto entries_iter = this->entries.begin();
250+ auto group_iter = group.children.begin();
251+
252+ auto* root_node = &group_node;
253+ while (root_node->parent != nullptr) root_node = root_node->parent;
254+ Hy3Node* focused_node = root_node->getFocusedNode();
255+
256+ while (entries_iter != this->entries.end()) {
257+ if (group_iter == group.children.end()) {
258+ needs_redraw = true;
259+
260+ while (entries_iter != this->entries.end()) {
261+ entries_iter = this->entries.erase(entries_iter);
262+ }
263+
264+ return;
265+ };
266+
267+ auto& entry = *entries_iter;
268+ auto& node = **group_iter;
269+
270+ std::string title = node.getTitle();
271+ bool urgent = node.isUrgent();
272+ bool focused = focused_node == &group_node
273+ || focused_node == &node
274+ || (node.data.type == Hy3NodeData::Group && node.data.as_group.hasChild(focused_node));
275+
276+ if (entry.urgent != urgent
277+ || entry.focused != focused
278+ || entry.window_title != title)
279+ this->needs_redraw = true;
280+
281+ entry.window_title = std::move(title);
282+ entry.urgent = urgent;
283+ entry.focused = focused;
284+
285+ entries_iter = std::next(entries_iter);
286+ group_iter = std::next(group_iter);
287+ }
288+
289+ while (group_iter != group.children.end()) {
290+ needs_redraw = true;
291+
292+ auto& node = **group_iter;
293+
294+ this->entries.push_back({
295+ .window_title = node.getTitle(),
296+ .urgent = node.isUrgent(),
297+ .focused = focused_node == &group_node
298+ || focused_node == &node
299+ || (node.data.type == Hy3NodeData::Group && node.data.as_group.hasChild(focused_node)),
300+ });
301+
302+ group_iter = std::next(group_iter);
303+ }
304+}
305+
306+void Hy3TabBar::setPos(Vector2D pos) {
307+ if (pos == this->pos) return;
308+ needs_redraw = true;
309+ this->pos = pos;
310+}
311+
312+void Hy3TabBar::setSize(Vector2D size) {
313+ this->size = size;
314+}
315+
316+void Hy3TabBar::prepareTexture() {
317+ auto bar_width = this->size.x;
318+ auto bar_height = this->size.y;
319+
320+ if (needs_redraw || this->texture.m_iTexID == 0) {
321+ static const auto* rounding_setting = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:rounding")->intValue;
322+ auto rounding = *rounding_setting;
323+
324+ auto cairo_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, this->size.x, this->size.y);
325+ auto cairo = cairo_create(cairo_surface);
326+
327+ // clear pixmap
328+ cairo_save(cairo);
329+ cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
330+ cairo_paint(cairo);
331+ cairo_restore(cairo);
332+
333+ auto swidth = (double) bar_width / (double) this->entries.size();
334+ size_t i = 0;
335+
336+ for (auto& entry: this->entries) {
337diff --git a/src/TabGroup.hpp b/src/TabGroup.hpp
338new file mode 100644
339index 0000000000000000000000000000000000000000..08d0de67542dceb7056895882fb1dbcabdaacd5a
340--- /dev/null
341+++ b/src/TabGroup.hpp
342@@ -0,0 +1,54 @@
343+#pragma once
344+
345+#include <hyprland/src/helpers/AnimatedVariable.hpp>
346+#include <hyprland/src/helpers/Vector2D.hpp>
347+#include <vector>
348+
349+class Hy3TabGroup;
350+
351+#include "Hy3Layout.hpp"
352+#include <hyprland/src/render/Texture.hpp>
353+
354+struct Hy3TabBarEntry {
355+ std::string window_title;
356+ bool urgent = false;
357+ bool focused = false;
358+};
359+
360+class Hy3TabBar {
361+public:
362+ CTexture texture;
363+
364+ void updateWithGroupEntries(Hy3Node&);
365+ void setPos(Vector2D);
366+ void setSize(Vector2D);
367+
368+ // Redraw the texture if necessary, and bind it to GL_TEXTURE_2D
369+ void prepareTexture();
370+private:
371+ bool needs_redraw = true;
372+
373+ std::vector<Hy3TabBarEntry> entries;
374+ // scaled pos/size
375+ Vector2D pos;
376+ Vector2D size;
377+};
378+
379+class Hy3TabGroup {
380+public:
381+ Hy3TabBar bar;
382+ CAnimatedVariable pos;
383+ CAnimatedVariable size;
384+
385+ // initialize a group with the given node. UB if node is not a group.
386+ Hy3TabGroup(Hy3Node&);
387+
388+ // update tab bar with node position and data. UB if node is not a group.
389+ void updateWithGroup(Hy3Node&);
390+ // render the scaled tab bar on the current monitor.
391+ void renderTabBar();
392+
393+private:
394+ // moving a Hy3TabGroup will unregister any active animations
395+ Hy3TabGroup(Hy3TabGroup&&) = delete;
396+};
397diff --git a/src/main.cpp b/src/main.cpp
398index f299fe92a18322710fded7165d77cb1897035d9a..309303b838f6221f7bdccb287417e507dc2df6ca 100644
399--- a/src/main.cpp
400+++ b/src/main.cpp
401@@ -45,6 +45,8 @@ void dispatch_makegroup(std::string arg) {
402 g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitH);
403 } else if (arg == "v") {
404 g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::SplitV);
405+ } else if (arg == "tab") {
406+ g_Hy3Layout->makeGroupOnWorkspace(workspace, Hy3GroupLayout::Tabbed);
407 } else if (arg == "opposite") {
408 g_Hy3Layout->makeOppositeGroupOnWorkspace(workspace);
409 }
410@@ -104,6 +106,8 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
411 selection_hook::init();
412
413 HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:no_gaps_when_only", SConfigValue{.intValue = 0});
414+ HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:bar_height", SConfigValue{.intValue = 15});
415+ HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:tabs:rounding", SConfigValue{.intValue = 3});
416
417 g_Hy3Layout = std::make_unique<Hy3Layout>();
418 HyprlandAPI::addLayout(PHANDLE, "hy3", g_Hy3Layout.get());