Diff
1diff --git a/README.md b/README.md
2index a89f0691db3d27b6f06075779a51d4805a95cd95..66a11467a3388fc82168f7291bdc11c4ffcb2597 100644
3--- a/README.md
4+++ b/README.md
5@@ -33,8 +33,9 @@ You can use `hy3:makegroup` to create a new split.
6
7 ### Dispatcher list
8 - `hy3:makegroup, <h | v | opposite>` - make a vertical or horizontal split
9- - `hy3:movefocus, <l | u | d | r>` - move the focus left, up, down, or right
10- - `hy3:movewindow, <l | u | d | r>` - move a window left, up, down, or right
11+ - `hy3:movefocus, <l | u | d | r | left | down | up | right>` - move the focus left, up, down, or right
12+ - `hy3:movewindow, <l | u | d | r | left | down | up | right> [, once]` - move a window left, up, down, or right
13+ - `once` - only move directly to the neighboring group, without moving into any of its subgroups
14 - `hy3:raisefocus` - raise the active focus one level
15 - `hy3:debugnodes` - print the node tree into the hyprland log
16
17diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
18index 1237db355527eec83803901cdb2bdb06ee05916a..9d8b4bd579286ed5bccde0bab59b59328f29836a 100644
19--- a/src/Hy3Layout.cpp
20+++ b/src/Hy3Layout.cpp
21@@ -1127,26 +1127,24 @@ void Hy3Layout::makeOppositeGroupOn(Hy3Node* node) {
22 }
23 }
24
25-Hy3Node* shiftOrGetFocus(Hy3Node& node, ShiftDirection direction, bool shift);
26-
27 void Hy3Layout::shiftFocus(int workspace, ShiftDirection direction) {
28 auto* node = this->getWorkspaceFocusedNode(workspace);
29 Debug::log(LOG, "ShiftFocus %p %d", node, direction);
30 if (node == nullptr) return;
31
32 Hy3Node* target;
33- if ((target = this->shiftOrGetFocus(*node, direction, false))) {
34+ if ((target = this->shiftOrGetFocus(*node, direction, false, false))) {
35 g_pCompositor->focusWindow(target->data.as_window);
36 }
37 }
38
39-void Hy3Layout::shiftWindow(int workspace, ShiftDirection direction) {
40+void Hy3Layout::shiftWindow(int workspace, ShiftDirection direction, bool once) {
41 auto* node = this->getWorkspaceFocusedNode(workspace);
42 Debug::log(LOG, "ShiftWindow %p %d", node, direction);
43 if (node == nullptr) return;
44
45
46- this->shiftOrGetFocus(*node, direction, true);
47+ this->shiftOrGetFocus(*node, direction, true, once);
48 }
49
50 bool shiftIsForward(ShiftDirection direction) {
51@@ -1162,16 +1160,20 @@ bool shiftMatchesLayout(Hy3GroupLayout layout, ShiftDirection direction) {
52 || (layout != Hy3GroupLayout::SplitV && !shiftIsVertical(direction));
53 }
54
55-Hy3Node* Hy3Layout::shiftOrGetFocus(Hy3Node& node, ShiftDirection direction, bool shift) {
56+Hy3Node* Hy3Layout::shiftOrGetFocus(Hy3Node& node, ShiftDirection direction, bool shift, bool once) {
57 auto* break_origin = &node;
58 auto* break_parent = break_origin->parent;
59
60+ auto has_broken_once = false;
61+
62 // break parents until we hit a container oriented the same way as the shift direction
63 while (true) {
64 if (break_parent == nullptr) return nullptr;
65
66 auto& group = break_parent->data.as_group; // must be a group in order to be a parent
67
68+ if (has_broken_once) break;
69+
70 if (shiftMatchesLayout(group.layout, direction)) {
71 // group has the correct orientation
72
73@@ -1182,6 +1184,8 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(Hy3Node& node, ShiftDirection direction, boo
74 break;
75 }
76
77+ has_broken_once = true;
78+
79 if (break_parent->parent == nullptr) {
80 if (!shift) return nullptr;
81
82@@ -1233,7 +1237,7 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(Hy3Node& node, ShiftDirection direction, boo
83 if (shiftIsForward(direction)) iter = std::next(iter);
84 else iter = std::prev(iter);
85
86- if ((*iter)->data.type == Hy3NodeData::Window) {
87+ if ((*iter)->data.type == Hy3NodeData::Window || (shift && once && has_broken_once)) {
88 if (shift) {
89 if (target_group == node.parent) {
90 if (shiftIsForward(direction)) insert = std::next(iter);
91@@ -1270,6 +1274,12 @@ Hy3Node* Hy3Layout::shiftOrGetFocus(Hy3Node& node, ShiftDirection direction, boo
92 }
93 }
94
95+ if (shift && once) {
96+ if (shift_after) insert = std::next(iter);
97+ else insert = iter;
98+ break;
99+ }
100+
101 if ((*iter)->data.type == Hy3NodeData::Window) {
102 if (shift) {
103 if (shift_after) insert = std::next(iter);
104diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
105index 02637afe8af3b9ad37c0338d502d986876698f6b..7b34ae69b6c1710c08461a545d1a13051b9e114a 100644
106--- a/src/Hy3Layout.hpp
107+++ b/src/Hy3Layout.hpp
108@@ -117,7 +117,7 @@ public:
109 void makeOppositeGroupOnWorkspace(int);
110 void makeGroupOn(Hy3Node*, Hy3GroupLayout);
111 void makeOppositeGroupOn(Hy3Node*);
112- void shiftWindow(int, ShiftDirection);
113+ void shiftWindow(int, ShiftDirection, bool);
114 void shiftFocus(int, ShiftDirection);
115 void raiseFocus(int);
116
117@@ -140,7 +140,8 @@ private:
118
119 // if shift is true, shift the window in the given direction, returning nullptr,
120 // if shift is false, return the window in the given direction or nullptr.
121- Hy3Node* shiftOrGetFocus(Hy3Node&, ShiftDirection, bool);
122+ // if once is true, only one group will be broken out of / into
123+ Hy3Node* shiftOrGetFocus(Hy3Node&, ShiftDirection, bool, bool);
124
125 friend struct Hy3Node;
126 };
127diff --git a/src/main.cpp b/src/main.cpp
128index 6c6c47697344617a257aa2e308be26928bef5e22..f299fe92a18322710fded7165d77cb1897035d9a 100644
129--- a/src/main.cpp
130+++ b/src/main.cpp
131@@ -1,3 +1,5 @@
132+#include <optional>
133+
134 #include <hyprland/src/plugins/PluginAPI.hpp>
135 #include <hyprland/src/Compositor.hpp>
136
137@@ -48,18 +50,23 @@ void dispatch_makegroup(std::string arg) {
138 }
139 }
140
141-void dispatch_movewindow(std::string arg) {
142+std::optional<ShiftDirection> parseShiftArg(std::string arg) {
143+ if (arg == "l" || arg == "left") return ShiftDirection::Left;
144+ else if (arg == "r" || arg == "right") return ShiftDirection::Right;
145+ else if (arg == "u" || arg == "up") return ShiftDirection::Up;
146+ else if (arg == "d" || arg == "down") return ShiftDirection::Down;
147+ else return {};
148+}
149+
150+void dispatch_movewindow(std::string value) {
151 int workspace = workspace_for_action();
152 if (workspace < 0) return;
153
154- if (arg == "l") {
155- g_Hy3Layout->shiftWindow(workspace, ShiftDirection::Left);
156- } else if (arg == "u") {
157- g_Hy3Layout->shiftWindow(workspace, ShiftDirection::Up);
158- } else if (arg == "d") {
159- g_Hy3Layout->shiftWindow(workspace, ShiftDirection::Down);
160- } else if (arg == "r") {
161- g_Hy3Layout->shiftWindow(workspace, ShiftDirection::Right);
162+ auto args = CVarList(value);
163+
164+ if (auto shift = parseShiftArg(args[0])) {
165+ auto once = args[1] == "once";
166+ g_Hy3Layout->shiftWindow(workspace, shift.value(), once);
167 }
168 }
169
170@@ -67,14 +74,8 @@ void dispatch_movefocus(std::string arg) {
171 int workspace = workspace_for_action();
172 if (workspace < 0) return;
173
174- if (arg == "l") {
175- g_Hy3Layout->shiftFocus(workspace, ShiftDirection::Left);
176- } else if (arg == "u") {
177- g_Hy3Layout->shiftFocus(workspace, ShiftDirection::Up);
178- } else if (arg == "d") {
179- g_Hy3Layout->shiftFocus(workspace, ShiftDirection::Down);
180- } else if (arg == "r") {
181- g_Hy3Layout->shiftFocus(workspace, ShiftDirection::Right);
182+ if (auto shift = parseShiftArg(arg)) {
183+ g_Hy3Layout->shiftFocus(workspace, shift.value());
184 }
185 }
186