5763c8fe624e6d54653764cb476aa9e10d4030b0

Author
outfoxxed <outfoxxed@outfoxxed.me>
Committer
outfoxxed <outfoxxed@outfoxxed.me>
Date

Message

Fix and document focustab

Diff

This diff is truncated to protect this page.

  1diff --git a/README.md b/README.md
  2index f698cb07eb0331b9a2f7703a72223bb44941ea9e..d53e4e2c8b44d8cb4166e39bf00d43b80109bbae 100644
  3--- a/README.md
  4+++ b/README.md
  5@@ -91,9 +91,9 @@ plugin {
  6 
  7 ### Dispatcher list
  8  - `hy3:makegroup, <h | v | opposite | tab>` - make a vertical / horizontal split or tab group
  9- - `hy3:movefocus, <l | u | d | r | left | down | up | right> [, visible]` - move the focus left, up, down, or right
 10+ - `hy3:movefocus, <l | u | d | r | left | down | up | right>, [visible]` - move the focus left, up, down, or right
 11    - `visible` - only move between visible nodes, not hidden tabs
 12- - `hy3:movewindow, <l | u | d | r | left | down | up | right> [, once]` - move a window left, up, down, or right
 13+ - `hy3:movewindow, <l | u | d | r | left | down | up | right>, [once]` - move a window left, up, down, or right
 14    - `once` - only move directly to the neighboring group, without moving into any of its subgroups
 15  - `hy3:changefocus, <top | bottom | raise | lower | tab | tabnode>`
 16    - `top` - focus all nodes in the workspace
 17@@ -102,6 +102,16 @@ plugin {
 18    - `lower` - lower focus one level
 19    - `tab` - raise focus to the nearest tab
 20    - `tabnode` - raise focus to the nearest node under the tab
 21+ - `hy3:focustab <mouse | [l | r | left | right], [prioritize_hovered | require_hovered], [wrap]>`
 22+   - `mouse` - focus the tab under the mouse, works well with a non consuming bind, e.g.
 23+     ```conf
 24+     # binds hy3:focustab to lmb and still allows windows to receive clicks
 25+     bindn = , mouse:272, hy3:focustab, mouse
 26+     ```
 27+   - `l | r | left | right` - direction to change tabs in
 28+   - `prioritize_hovered` - prioritize the tab group under the mouse when multiple are stacked. use the lowest group if none is under the mouse.
 29+   - `require_hovered` - affect the tab group under the mouse. do nothing if none are hovered.
 30+   - `wrap` - wrap to the opposite size of the tab bar if moving off the end
 31  - `hy3:debugnodes` - print the node tree into the hyprland log
 32 
 33 ## Installing
 34diff --git a/flake.nix b/flake.nix
 35index 4eac23c38d13bc99fd9fea1c42bad31a0b911c01..34c543b4901f7712e763ac8783e25739884b6b46 100644
 36--- a/flake.nix
 37+++ b/flake.nix
 38@@ -41,7 +41,7 @@
 39         name = "hy3";
 40 
 41         nativeBuildInputs = with pkgs; [
 42-          clang-tools
 43+          clang-tools_16
 44           bear
 45         ];
 46 
 47diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
 48index 6d2bcf288cde74664c4ffbad692a2b9d166590e3..4490844d6365ce65cc819495c326842b56951c7f 100644
 49--- a/src/Hy3Layout.cpp
 50+++ b/src/Hy3Layout.cpp
 51@@ -196,10 +196,12 @@ void Hy3Node::recalcSizePosRecursive(bool force) {
 52 
 53 	double offset = 0;
 54 
 55-	if (group->layout == Hy3GroupLayout::Tabbed && group->focused_child != nullptr && !group->focused_child->hidden) {
 56+	if (group->layout == Hy3GroupLayout::Tabbed && group->focused_child != nullptr
 57+	    && !group->focused_child->hidden)
 58+	{
 59 		group->focused_child->setHidden(false);
 60 
 61-		auto box = wlr_box { tpos.x, tpos.y, tsize.x, tsize.y };
 62+		auto box = wlr_box {tpos.x, tpos.y, tsize.x, tsize.y};
 63 		g_pHyprRenderer->damageBox(&box);
 64 	}
 65 
 66@@ -1684,6 +1686,8 @@ bottom:
 67 
 68 Hy3Node* Hy3Node::findNodeForTabGroup(Hy3TabGroup& tab_group) {
 69 	if (this->data.type == Hy3NodeData::Group) {
 70+		if (this->hidden) return nullptr;
 71+
 72 		auto& group = this->data.as_group;
 73 
 74 		if (group.layout == Hy3GroupLayout::Tabbed && group.tab_bar == &tab_group) {
 75@@ -1699,50 +1703,105 @@ Hy3Node* Hy3Node::findNodeForTabGroup(Hy3TabGroup& tab_group) {
 76 	return nullptr;
 77 }
 78 
 79-void Hy3Layout::focusTab(int workspace) {
 80+void Hy3Layout::focusTab(
 81+    int workspace,
 82+    TabFocus target,
 83+    TabFocusMousePriority mouse,
 84+    bool wrap_scroll
 85+) {
 86+	static const auto* tab_bar_padding
 87+	    = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:padding")->intValue;
 88+
 89 	auto* node = this->getWorkspaceRootGroup(workspace);
 90 	if (node == nullptr) return;
 91 
 92-	auto mouse_pos = g_pInputManager->getMouseCoordsInternal();
 93+	Hy3Node* tab_node = nullptr;
 94+	Hy3Node* tab_focused_node;
 95 
 96-	for (auto& tab_group: this->tab_groups) {
 97-		auto pos = tab_group.pos.vec();
 98-		if (pos.x > mouse_pos.x || pos.y > mouse_pos.y) continue;
 99-		auto size = tab_group.size.vec();
100-		if (pos.x + size.x < mouse_pos.x || pos.y + size.y < mouse_pos.y) continue;
101+	if (target == TabFocus::MouseLocation || mouse != TabFocusMousePriority::Ignore) {
102+		auto mouse_pos = g_pInputManager->getMouseCoordsInternal();
103 
104-		Debug::log(
105-		    LOG,
106-		    "!!! tab group clicked: %f %f, %f %f [%f %f]",
107-		    pos.x,
108-		    pos.y,
109-		    size.x,
110-		    size.y,
111-		    mouse_pos.x,
112-		    mouse_pos.y
113-		);
114+		for (auto& tab_group: this->tab_groups) {
115+			auto pos = tab_group.pos.vec();
116+			if (pos.x > mouse_pos.x || pos.y > mouse_pos.y) continue;
117+			auto size = tab_group.size.vec();
118+			if (pos.x + size.x < mouse_pos.x || pos.y + size.y + *tab_bar_padding < mouse_pos.y) continue;
119 
120-		auto* group = node->findNodeForTabGroup(tab_group);
121-		if (group == nullptr) continue;
122+			tab_node = node->findNodeForTabGroup(tab_group);
123+			if (tab_node == nullptr) continue;
124+			if (target != TabFocus::MouseLocation) goto hastab;
125 
126-		auto delta = mouse_pos - pos;
127+			auto delta = mouse_pos - pos;
128 
129-		auto& node_list = group->data.as_group.children;
130-		auto node_iter = node_list.begin();
131+			auto& node_list = tab_node->data.as_group.children;
132+			auto node_iter = node_list.begin();
133 
134-		for (auto& tab: tab_group.bar.entries) {
135-			if (node_iter == node_list.end()) break;
136+			for (auto& tab: tab_group.bar.entries) {
137+				if (node_iter == node_list.end()) break;
138 
139-			if (delta.x > tab.offset.fl() * size.x
140-			    && delta.x < (tab.offset.fl() + tab.width.fl()) * size.x)
141-			{
142-				(*node_iter)->focus();
143-				break;
144+				if (delta.x > tab.offset.fl() * size.x
145+				    && delta.x < (tab.offset.fl() + tab.width.fl()) * size.x)
146+				{
147+					tab_focused_node = *node_iter;
148+					goto hastab;
149+				}
150+
151diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
152index c534e5adb6aee33152e139355033b4fbfff96229..39f6b6ff5a28021153a4aef3ce9795ed0dae06ac 100644
153--- a/src/Hy3Layout.hpp
154+++ b/src/Hy3Layout.hpp
155@@ -31,6 +31,18 @@ enum class FocusShift {
156 	TabNode,
157 };
158 
159+enum class TabFocus {
160+	MouseLocation,
161+	Left,
162+	Right,
163+};
164+
165+enum class TabFocusMousePriority {
166+	Ignore,
167+	Prioritize,
168+	Require,
169+};
170+
171 struct Hy3GroupData {
172 	Hy3GroupLayout layout = Hy3GroupLayout::SplitH;
173 	std::list<Hy3Node*> children;
174@@ -146,7 +158,7 @@ public:
175 	void shiftWindow(int, ShiftDirection, bool);
176 	void shiftFocus(int, ShiftDirection, bool);
177 	void changeFocus(int, FocusShift);
178-	void focusTab(int);
179+	void focusTab(int, TabFocus, TabFocusMousePriority, bool);
180 
181 	bool shouldRenderSelected(CWindow*);
182 
183diff --git a/src/main.cpp b/src/main.cpp
184index 05daae311d93066b22256118d3fb539001b47c9c..efc00403e8e932910c33b9eaa7aad13dea0a4e01 100644
185--- a/src/main.cpp
186+++ b/src/main.cpp
187@@ -93,11 +93,37 @@ void dispatch_changefocus(std::string arg) {
188 	else if (arg == "tabnode") g_Hy3Layout->changeFocus(workspace, FocusShift::TabNode);
189 }
190 
191-void dispatch_focustab(std::string arg) {
192+void dispatch_focustab(std::string value) {
193 	int workspace = workspace_for_action();
194 	if (workspace == -1) return;
195 
196-	g_Hy3Layout->focusTab(workspace);
197+	auto i = 0;
198+	auto args = CVarList(value);
199+
200+	TabFocus focus;
201+	auto mouse = TabFocusMousePriority::Ignore;
202+	bool wrap_scroll = false;
203+
204+	if (args[i] == "l" || args[i] == "left") focus = TabFocus::Left;
205+	else if (args[i] == "r" || args[i] == "right") focus = TabFocus::Right;
206+	else if (args[i] == "mouse") {
207+		g_Hy3Layout->focusTab(workspace, TabFocus::MouseLocation, mouse, false);
208+		return;
209+	} else return;
210+
211+	i++;
212+
213+	if (args[i] == "prioritize_hovered") {
214+		mouse = TabFocusMousePriority::Prioritize;
215+		i++;
216+	} else if (args[i] == "require_hovered") {
217+		mouse = TabFocusMousePriority::Require;
218+		i++;
219+	}
220+
221+	if (args[i++] == "wrap") wrap_scroll = true;
222+
223+	g_Hy3Layout->focusTab(workspace, focus, mouse, wrap_scroll);
224 }
225 
226 void dispatch_debug(std::string arg) {