Diff
1diff --git a/README.md b/README.md
2index 17e2f587df413e04a56a9cc3353117085358d472..18948dfc8911d56b0e8f586b718c5e756fb6c541 100644
3--- a/README.md
4+++ b/README.md
5@@ -98,6 +98,7 @@ plugin {
6 - `hy3:movewindow, <l | u | d | r | left | down | up | right> [, once]` - move a window left, up, down, or right
7 - `once` - only move directly to the neighboring group, without moving into any of its subgroups
8 - `hy3:raisefocus` - raise the active focus one level
9+ - `hy3:focustab` - focus a tab under the mouse (bind to `mouse:272` for left click)
10 - `hy3:debugnodes` - print the node tree into the hyprland log
11
12 ## Installing
13diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
14index 315257fd573f9b1f2fae0d15552c19b57b361445..5abdac295ac39be679252b343af056841ed37589 100644
15--- a/src/Hy3Layout.cpp
16+++ b/src/Hy3Layout.cpp
17@@ -1544,6 +1544,57 @@ void Hy3Layout::raiseFocus(int workspace) {
18 }
19 }
20
21+Hy3Node* Hy3Node::findNodeForTabGroup(Hy3TabGroup& tab_group) {
22+ if (this->data.type == Hy3NodeData::Group) {
23+ auto& group = this->data.as_group;
24+
25+ if (group.layout == Hy3GroupLayout::Tabbed && group.tab_bar == &tab_group) {
26+ return this;
27+ }
28+
29+ for (auto& node: group.children) {
30+ auto* r = node->findNodeForTabGroup(tab_group);
31+ if (r != nullptr) return r;
32+ }
33+ } else return nullptr;
34+
35+ return nullptr;
36+}
37+
38+void Hy3Layout::focusTab(int workspace) {
39+ auto* node = this->getWorkspaceRootGroup(workspace);
40+ if (node == nullptr) return;
41+
42+ auto mouse_pos = g_pInputManager->getMouseCoordsInternal();
43+
44+ for (auto& tab_group: this->tab_groups) {
45+ auto pos = tab_group.pos.vec();
46+ if (pos.x > mouse_pos.x || pos.y > mouse_pos.y) continue;
47+ auto size = tab_group.size.vec();
48+ if (pos.x + size.x < mouse_pos.x || pos.y + size.y < mouse_pos.y) continue;
49+
50+ Debug::log(LOG, "!!! tab group clicked: %f %f, %f %f [%f %f]", pos.x, pos.y, size.x, size.y, mouse_pos.x, mouse_pos.y);
51+ auto* group = node->findNodeForTabGroup(tab_group);
52+ if (group == nullptr) continue;
53+
54+ auto delta = mouse_pos - pos;
55+
56+ auto& node_list = group->data.as_group.children;
57+ auto node_iter = node_list.begin();
58+
59+ for (auto& tab: tab_group.bar.entries) {
60+ if (node_iter == node_list.end()) break;
61+
62+ if (delta.x > tab.offset.fl() * size.x && delta.x < (tab.offset.fl() + tab.width.fl()) * size.x) {
63+ (*node_iter)->focus();
64+ break;
65+ }
66+
67+ node_iter = std::next(node_iter);
68+ }
69+ }
70+}
71+
72 bool Hy3Layout::shouldRenderSelected(CWindow* window) {
73 if (window == nullptr) return false;
74 auto* root = this->getWorkspaceRootGroup(window->m_iWorkspaceID);
75diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
76index 0a75f065713070c6bfc4ea1e85524092b5eb1ad2..112e50c400fd406f94c008dc2b3a13f26779ba4c 100644
77--- a/src/Hy3Layout.hpp
78+++ b/src/Hy3Layout.hpp
79@@ -88,6 +88,7 @@ struct Hy3Node {
80 void updateTabBarRecursive();
81 bool isUrgent();
82 bool isIndirectlyFocused();
83+ Hy3Node* findNodeForTabGroup(Hy3TabGroup&);
84 std::string getTitle();
85
86 bool operator==(const Hy3Node&) const;
87@@ -133,6 +134,7 @@ public:
88 void shiftWindow(int, ShiftDirection, bool);
89 void shiftFocus(int, ShiftDirection);
90 void raiseFocus(int);
91+ void focusTab(int);
92
93 bool shouldRenderSelected(CWindow*);
94
95diff --git a/src/main.cpp b/src/main.cpp
96index a572c6c8e69c980baafb558a5fedec4a63550778..8830fef2f2654704d920b16780c253b89784e41e 100644
97--- a/src/main.cpp
98+++ b/src/main.cpp
99@@ -88,6 +88,13 @@ void dispatch_raisefocus(std::string arg) {
100 g_Hy3Layout->raiseFocus(workspace);
101 }
102
103+void dispatch_focustab(std::string arg) {
104+ int workspace = workspace_for_action();
105+ if (workspace == -1) return;
106+
107+ g_Hy3Layout->focusTab(workspace);
108+}
109+
110 void dispatch_debug(std::string arg) {
111 int workspace = workspace_for_action();
112 if (workspace == -1) return;
113@@ -128,6 +135,7 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
114 HyprlandAPI::addDispatcher(PHANDLE, "hy3:movefocus", dispatch_movefocus);
115 HyprlandAPI::addDispatcher(PHANDLE, "hy3:movewindow", dispatch_movewindow);
116 HyprlandAPI::addDispatcher(PHANDLE, "hy3:raisefocus", dispatch_raisefocus);
117+ HyprlandAPI::addDispatcher(PHANDLE, "hy3:focustab", dispatch_focustab);
118 HyprlandAPI::addDispatcher(PHANDLE, "hy3:debugnodes", dispatch_debug);
119
120 return {"hy3", "i3 like layout for hyprland", "outfoxxed", "0.1"};