311939d4439033cfdeb77dbc1dba0d689cef750b

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

Message

feat: add cursor warping support to `hy3:movefocus`

Diff

This diff is truncated to protect this page.

  1diff --git a/CHANGELOG.md b/CHANGELOG.md
  2index ec064b495b39f1c4494d56f42b631f9613cdafa0..655cedfcfc65df44d039d78fd5b1c43c6afbc634 100644
  3--- a/CHANGELOG.md
  4+++ b/CHANGELOG.md
  5@@ -1,5 +1,9 @@
  6 # Changelog
  7 
  8+## Upcoming
  9+
 10+- Added cursor warping options for `hy3:movefocus`.
 11+
 12 ## hl0.37.1 and before
 13 
 14 - Added `no_gaps_when_only = 2`
 15diff --git a/README.md b/README.md
 16index ef2dda849ab03b3d5d2ebb00570734e4a3ceedb9..4b732346cea41cc78b4495319dff2d8868f15aa6 100644
 17--- a/README.md
 18+++ b/README.md
 19@@ -317,8 +317,10 @@ plugin {
 20    - `toggletab` will untab if group is tabbed, and tab if group is untabbed
 21    - `opposite` will toggle between horizontal and vertical layouts if the group is not tabbed.
 22  - `hy3:setephemeral, <true | false>` - change the ephemerality of the group the node belongs to
 23- - `hy3:movefocus, <l | u | d | r | left | down | up | right>, [visible]` - move the focus left, up, down, or right
 24+ - `hy3:movefocus, <l | u | d | r | left | down | up | right>, [visible], [warp | nowarp]` - move the focus left, up, down, or right
 25    - `visible` - only move between visible nodes, not hidden tabs
 26+   - `warp` - warp the mouse to the selected window, even if `general:no_cursor_warps` is true.
 27+   - `nowarp` - does not warp the mouse to the selected window, even if `general:no_cursor_warps` is false.
 28  - `hy3:movewindow, <l | u | d | r | left | down | up | right>, [once], [visible]` - move a window left, up, down, or right
 29    - `once` - only move directly to the neighboring group, without moving into any of its subgroups
 30    - `visible` - only move between visible nodes, not hidden tabs
 31diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
 32index 348e1fcc08c81dda5ea8925eb089ea79d1f4a120..bc5dc5dbfc64b55818bd1bdcd92f18fdef51b513 100644
 33--- a/src/Hy3Layout.cpp
 34+++ b/src/Hy3Layout.cpp
 35@@ -8,6 +8,7 @@
 36 #include <ranges>
 37 
 38 #include "Hy3Layout.hpp"
 39+#include "Hy3Node.hpp"
 40 #include "SelectionHook.hpp"
 41 #include "globals.hpp"
 42 
 43@@ -878,7 +879,12 @@ void Hy3Layout::shiftWindow(
 44 	this->shiftNode(*node, direction, once, visible);
 45 }
 46 
 47-void Hy3Layout::shiftFocus(const PHLWORKSPACE& workspace, ShiftDirection direction, bool visible) {
 48+void Hy3Layout::shiftFocus(
 49+    const PHLWORKSPACE& workspace,
 50+    ShiftDirection direction,
 51+    bool visible,
 52+    bool warp
 53+) {
 54 	auto current_window = g_pCompositor->m_pLastWindow.lock();
 55 
 56 	if (current_window != nullptr) {
 57@@ -893,7 +899,10 @@ void Hy3Layout::shiftFocus(const PHLWORKSPACE& workspace, ShiftDirection directi
 58 			                                        : 'r'
 59 			);
 60 
 61-			if (next_window != nullptr) g_pCompositor->focusWindow(next_window);
 62+			if (next_window != nullptr) {
 63+				g_pCompositor->focusWindow(next_window);
 64+				if (warp) Hy3Layout::warpCursorToBox(next_window->m_vPosition, next_window->m_vSize);
 65+			}
 66 			return;
 67 		}
 68 	}
 69@@ -904,7 +913,13 @@ void Hy3Layout::shiftFocus(const PHLWORKSPACE& workspace, ShiftDirection directi
 70 	auto* target = this->shiftOrGetFocus(*node, direction, false, false, visible);
 71 
 72 	if (target != nullptr) {
 73-		target->focus();
 74+		if (warp) {
 75+			// don't warp for nodes in the same tab
 76+			warp = node->parent == nullptr || target->parent == nullptr || node->parent != target->parent
 77+			    || node->parent->data.as_group().layout != Hy3GroupLayout::Tabbed;
 78+		}
 79+
 80+		target->focus(warp);
 81 		while (target->parent != nullptr) target = target->parent;
 82 		target->recalcSizePosRecursive();
 83 	}
 84@@ -1007,24 +1022,24 @@ void Hy3Layout::changeFocus(const PHLWORKSPACE& workspace, FocusShift shift) {
 85 			node = node->parent;
 86 		}
 87 
 88-		node->focus();
 89+		node->focus(false);
 90 		return;
 91 	case FocusShift::Raise:
 92 		if (node->parent == nullptr) goto bottom;
 93 		else {
 94-			node->parent->focus();
 95+			node->parent->focus(false);
 96 		}
 97 		return;
 98 	case FocusShift::Lower:
 99 		if (node->data.is_group() && node->data.as_group().focused_child != nullptr)
100-			node->data.as_group().focused_child->focus();
101+			node->data.as_group().focused_child->focus(false);
102 		return;
103 	case FocusShift::Tab:
104 		// make sure we go up at least one level
105 		if (node->parent != nullptr) node = node->parent;
106 		while (node->parent != nullptr) {
107 			if (node->data.as_group().layout == Hy3GroupLayout::Tabbed) {
108-				node->focus();
109+				node->focus(false);
110 				return;
111 			}
112 
113@@ -1036,7 +1051,7 @@ void Hy3Layout::changeFocus(const PHLWORKSPACE& workspace, FocusShift shift) {
114 		if (node->parent != nullptr) node = node->parent;
115 		while (node->parent != nullptr) {
116 			if (node->parent->data.as_group().layout == Hy3GroupLayout::Tabbed) {
117-				node->focus();
118+				node->focus(false);
119 				return;
120 			}
121 
122@@ -1050,7 +1065,7 @@ bottom:
123 		node = node->data.as_group().focused_child;
124 	}
125 
126-	node->focus();
127+	node->focus(false);
128 	return;
129 }
130 
131@@ -1202,7 +1217,7 @@ hastab:
132 	       && focus->data.as_group().focused_child != nullptr)
133 		focus = focus->data.as_group().focused_child;
134 
135diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
136index 79162612d23e378f72100b7bcd66f9067af8875c..d6b118b06dddb4b9e78e10a2b44aebf24565f110 100644
137--- a/src/Hy3Layout.hpp
138+++ b/src/Hy3Layout.hpp
139@@ -112,7 +112,7 @@ public:
140 	void changeGroupEphemeralityOn(Hy3Node&, bool ephemeral);
141 	void shiftNode(Hy3Node&, ShiftDirection, bool once, bool visible);
142 	void shiftWindow(const PHLWORKSPACE& workspace, ShiftDirection, bool once, bool visible);
143-	void shiftFocus(const PHLWORKSPACE& workspace, ShiftDirection, bool visible);
144+	void shiftFocus(const PHLWORKSPACE& workspace, ShiftDirection, bool visible, bool warp);
145 	void moveNodeToWorkspace(const PHLWORKSPACE& origin, std::string wsname, bool follow);
146 	void changeFocus(const PHLWORKSPACE& workspace, FocusShift);
147 	void focusTab(
148@@ -125,6 +125,7 @@ public:
149 	void setNodeSwallow(const PHLWORKSPACE& workspace, SetSwallowOption);
150 	void killFocusedNode(const PHLWORKSPACE& workspace);
151 	void expand(const PHLWORKSPACE& workspace, ExpandOption, ExpandFullscreenOption);
152+	static void warpCursorToBox(const Vector2D& pos, const Vector2D& size);
153 
154 	bool shouldRenderSelected(const PHLWINDOW&);
155 
156diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp
157index 04ef6f54c2ea31884840dbfc88a889cf06a3e2ec..5a9dd3d648499af2e3c4e7a44a6523eec3332627 100644
158--- a/src/Hy3Node.cpp
159+++ b/src/Hy3Node.cpp
160@@ -8,6 +8,7 @@
161 #include <hyprland/src/helpers/Box.hpp>
162 #include <hyprland/src/plugins/PluginAPI.hpp>
163 
164+#include "Hy3Layout.hpp"
165 #include "Hy3Node.hpp"
166 #include "globals.hpp"
167 
168@@ -163,19 +164,25 @@ PHLWINDOW Hy3NodeData::as_window() {
169 
170 bool Hy3Node::operator==(const Hy3Node& rhs) const { return this->data == rhs.data; }
171 
172-void Hy3Node::focus() {
173+void Hy3Node::focus(bool warp) {
174 	this->markFocused();
175 
176 	switch (this->data.type()) {
177-	case Hy3NodeType::Window:
178-		this->data.as_window()->setHidden(false);
179-		g_pCompositor->focusWindow(this->data.as_window());
180+	case Hy3NodeType::Window: {
181+		auto window = this->data.as_window();
182+		window->setHidden(false);
183+		g_pCompositor->focusWindow(window);
184+		if (warp) Hy3Layout::warpCursorToBox(window->m_vPosition, window->m_vSize);
185 		break;
186-	case Hy3NodeType::Group:
187+	}
188+	case Hy3NodeType::Group: {
189 		g_pCompositor->focusWindow(nullptr);
190 		this->raiseToTop();
191+
192+		if (warp) Hy3Layout::warpCursorToBox(this->position, this->size);
193 		break;
194 	}
195+	}
196 }
197 
198 PHLWINDOW Hy3Node::bringToTop() {
199diff --git a/src/Hy3Node.hpp b/src/Hy3Node.hpp
200index 7a52f3c83ac9e2fb58a938dbeb4a1be301057770..2d3969e8e134e6a1824253fb72ae49f2338508e9 100644
201--- a/src/Hy3Node.hpp
202+++ b/src/Hy3Node.hpp
203@@ -95,7 +95,7 @@ struct Hy3Node {
204 
205 	bool operator==(const Hy3Node&) const;
206 
207-	void focus();
208+	void focus(bool warp);
209 	void focusWindow();
210 	PHLWINDOW bringToTop();
211 	void markFocused();
212diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp
213index 843b17a8d40ae9167629f55292c42bb98b411fbe..aae34e8523723b010bdaad2d10fb0941383419b0 100644
214--- a/src/dispatchers.cpp
215+++ b/src/dispatchers.cpp
216@@ -113,9 +113,20 @@ void dispatch_movefocus(std::string value) {
217 
218 	auto args = CVarList(value);
219 
220-	if (auto shift = parseShiftArg(args[0])) {
221-		g_Hy3Layout->shiftFocus(workspace, shift.value(), args[1] == "visible");
222-	}
223+	static const auto no_cursor_warps = ConfigValue<Hyprlang::INT>("general:no_cursor_warps");
224+	auto warp_cursor = !*no_cursor_warps;
225+
226+	int argi = 0;
227+	auto shift = parseShiftArg(args[argi++]);
228+	if (!shift) return;
229+
230+	auto visible = args[argi] == "visible";
231+	if (visible) argi++;
232+
233+	if (args[argi] == "nowarp") warp_cursor = false;
234+	else if (args[argi] == "warp") warp_cursor = true;
235+
236+	g_Hy3Layout->shiftFocus(workspace, shift.value(), visible, warp_cursor);
237 }
238 
239 void dispatch_move_to_workspace(std::string value) {