2fa77b4dbcdbb6921d47da7992770dfe6f03893b
- Author
- outfoxxed <outfoxxed@outfoxxed.me>
- Committer
- outfoxxed <outfoxxed@outfoxxed.me>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/README.md b/README.md
2index 7ab51b47dec5bafa86c5c576ecbaafe26c97d973..757bba2eeeeb1d8ede8d5bf58ca47aec025269b9 100644
3--- a/README.md
4+++ b/README.md
5@@ -238,5 +238,9 @@ plugin {
6 - `prioritize_hovered` - prioritize the tab group under the mouse when multiple are stacked. use the lowest group if none is under the mouse.
7 - `require_hovered` - affect the tab group under the mouse. do nothing if none are hovered.
8 - `wrap` - wrap to the opposite size of the tab bar if moving off the end
9- - `hy3:setswallow, <true | false | toggle>` - set the containing node's window swallow state
10 - `hy3:debugnodes` - print the node tree into the hyprland log
11+ - :warning: **ALPHA QUALITY** `hy3:setswallow, <true | false | toggle>` - set the containing node's window swallow state
12+ - :warning: **ALPHA QUALITY** `hy3:expand, <expand | shrink | base>` - expand the current node to cover other nodes
13+ - `expand` - expand by one node
14+ - `shrink` - shrink by one node
15+ - `base` - undo all expansions
16diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
17index 57a6c3e81e5174df9404badb4e3126a7202ba917..1c3ed1c31080faf8c65e4fdc8fdc49f3ceaf11f9 100644
18--- a/src/Hy3Layout.cpp
19+++ b/src/Hy3Layout.cpp
20@@ -1059,6 +1059,91 @@ void Hy3Layout::killFocusedNode(int workspace) {
21 }
22 }
23
24+void Hy3Layout::expand(int workspace_id, ExpandOption option, ExpandFullscreenOption fs_option) {
25+ auto* node = this->getWorkspaceFocusedNode(workspace_id, false, true);
26+ if (node == nullptr) return;
27+
28+ const auto workspace = g_pCompositor->getWorkspaceByID(workspace_id);
29+ const auto monitor = g_pCompositor->getMonitorFromID(workspace->m_iMonitorID);
30+
31+ switch (option) {
32+ case ExpandOption::Expand: {
33+ if (node->parent == nullptr) {
34+ switch (fs_option) {
35+ case ExpandFullscreenOption::MaximizeAsFullscreen:
36+ case ExpandFullscreenOption::MaximizeIntermediate: goto fullscreen;
37+ case ExpandFullscreenOption::MaximizeOnly: return;
38+ }
39+ }
40+
41+ if (node->data.type == Hy3NodeType::Group)
42+ node->data.as_group.expand_focused = ExpandFocusType::Stack;
43+
44+ auto& group = node->parent->data.as_group;
45+ group.focused_child = node;
46+ group.expand_focused = ExpandFocusType::Latch;
47+
48+ node->parent->recalcSizePosRecursive();
49+
50+ if (node->parent->parent == nullptr) {
51+ switch (fs_option) {
52+ case ExpandFullscreenOption::MaximizeAsFullscreen: goto fullscreen;
53+ case ExpandFullscreenOption::MaximizeIntermediate:
54+ case ExpandFullscreenOption::MaximizeOnly: return;
55+ }
56+ }
57+ } break;
58+ case ExpandOption::Shrink:
59+ if (node->data.type == Hy3NodeType::Group) {
60+ auto& group = node->data.as_group;
61+
62+ group.expand_focused = ExpandFocusType::NotExpanded;
63+ if (group.focused_child->data.type == Hy3NodeType::Group)
64+ group.focused_child->data.as_group.expand_focused = ExpandFocusType::Latch;
65+
66+ node->recalcSizePosRecursive();
67+ }
68+ break;
69+ case ExpandOption::Base: {
70+ if (node->data.type == Hy3NodeType::Group) {
71+ node->data.as_group.collapseExpansions();
72+ node->recalcSizePosRecursive();
73+ }
74+ break;
75+ }
76+ case ExpandOption::Maximize: break;
77+ case ExpandOption::Fullscreen: break;
78+ }
79+
80+ return;
81+
82+ CWindow* window;
83+fullscreen:
84+ if (node->data.type != Hy3NodeType::Window) return;
85+ window = node->data.as_window;
86+ if (!window->m_bIsFullscreen || g_pCompositor->isWorkspaceSpecial(window->m_iWorkspaceID)) return;
87+
88+ if (workspace->m_bHasFullscreenWindow) return;
89+
90+ window->m_bIsFullscreen = true;
91+ workspace->m_bHasFullscreenWindow = true;
92+ workspace->m_efFullscreenMode = FULLSCREEN_FULL;
93+ window->m_vRealPosition = monitor->vecPosition;
94+ window->m_vRealSize = monitor->vecSize;
95+ goto fsupdate;
96+unfullscreen:
97+ if (node->data.type != Hy3NodeType::Window) return;
98+ window = node->data.as_window;
99+ window->m_bIsFullscreen = false;
100+ workspace->m_bHasFullscreenWindow = false;
101+ goto fsupdate;
102+fsupdate:
103+ g_pCompositor->updateWindowAnimatedDecorationValues(window);
104+ g_pXWaylandManager->setWindowSize(window, window->m_vRealSize.goalv());
105+ g_pCompositor->moveWindowToTop(window);
106+ this->recalculateMonitor(monitor->ID);
107+}
108+
109 bool Hy3Layout::shouldRenderSelected(CWindow* window) {
110 if (window == nullptr) return false;
111 auto* root = this->getWorkspaceRootGroup(window->m_iWorkspaceID);
112@@ -1090,10 +1175,14 @@ Hy3Node* Hy3Layout::getWorkspaceRootGroup(const int& workspace) {
113 return nullptr;
114 }
115
116-Hy3Node* Hy3Layout::getWorkspaceFocusedNode(const int& workspace, bool ignore_group_focus) {
117+Hy3Node* Hy3Layout::getWorkspaceFocusedNode(
118+ const int& workspace,
119+ bool ignore_group_focus,
120diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
121index 62851fcf1f2e61ac80465897be18f8dbe5234ab7..ee91b23f4014df828862ea0280654856ae3a1974 100644
122--- a/src/Hy3Layout.hpp
123+++ b/src/Hy3Layout.hpp
124@@ -50,6 +50,20 @@ enum class SetSwallowOption {
125 Toggle,
126 };
127
128+enum class ExpandOption {
129+ Expand,
130+ Shrink,
131+ Base,
132+ Maximize,
133+ Fullscreen,
134+};
135+
136+enum class ExpandFullscreenOption {
137+ MaximizeOnly,
138+ MaximizeIntermediate,
139+ MaximizeAsFullscreen,
140+};
141+
142 class Hy3Layout: public IHyprLayout {
143 public:
144 virtual void onWindowCreated(CWindow*);
145@@ -84,11 +98,16 @@ public:
146 void focusTab(int workspace, TabFocus target, TabFocusMousePriority, bool wrap_scroll, int index);
147 void setNodeSwallow(int workspace, SetSwallowOption);
148 void killFocusedNode(int workspace);
149+ void expand(int workspace, ExpandOption, ExpandFullscreenOption);
150
151 bool shouldRenderSelected(CWindow*);
152
153 Hy3Node* getWorkspaceRootGroup(const int& workspace);
154- Hy3Node* getWorkspaceFocusedNode(const int& workspace, bool ignore_group_focus = false);
155+ Hy3Node* getWorkspaceFocusedNode(
156+ const int& workspace,
157+ bool ignore_group_focus = false,
158+ bool stop_at_expanded = false
159+ );
160
161 static void renderHook(void*, std::any);
162 static void windowGroupUrgentHook(void*, std::any);
163diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp
164index 1b2c34bbc69287f6c8d4b36e9d4afd0b448048a8..0ce830aed769e1fc09180ee44d5541438a20e964 100644
165--- a/src/Hy3Node.cpp
166+++ b/src/Hy3Node.cpp
167@@ -12,6 +12,7 @@ Hy3GroupData::Hy3GroupData(Hy3GroupData&& from) {
168 this->layout = from.layout;
169 this->children = std::move(from.children);
170 this->group_focused = from.group_focused;
171+ this->expand_focused = from.expand_focused;
172 this->focused_child = from.focused_child;
173 from.focused_child = nullptr;
174 this->tab_bar = from.tab_bar;
175@@ -35,6 +36,20 @@ bool Hy3GroupData::hasChild(Hy3Node* node) {
176 return false;
177 }
178
179+void Hy3GroupData::collapseExpansions() {
180+ if (this->expand_focused == ExpandFocusType::NotExpanded) return;
181+ this->expand_focused = ExpandFocusType::NotExpanded;
182+
183+ Hy3Node* node = this->focused_child;
184+
185+ while (node->data.type == Hy3NodeType::Group
186+ && node->data.as_group.expand_focused == ExpandFocusType::Stack)
187+ {
188+ node->data.as_group.expand_focused = ExpandFocusType::NotExpanded;
189+ node = node->data.as_group.focused_child;
190+ }
191+}
192+
193 // Hy3NodeData //
194
195 Hy3NodeData::Hy3NodeData(): Hy3NodeData((CWindow*) nullptr) {}
196@@ -191,16 +206,29 @@ void Hy3Node::raiseToTop() {
197 }
198 }
199
200-Hy3Node* Hy3Node::getFocusedNode(bool ignore_group_focus) {
201+Hy3Node* Hy3Node::getFocusedNode(bool ignore_group_focus, bool stop_at_expanded) {
202 switch (this->data.type) {
203 case Hy3NodeType::Window: return this;
204 case Hy3NodeType::Group:
205+ Debug::log(
206+ LOG,
207+ "focusing %p, gf: %d, ef: %d (stop: %d)",
208+ this,
209+ this->data.as_group.group_focused,
210+ this->data.as_group.expand_focused,
211+ stop_at_expanded
212+ );
213+
214 if (this->data.as_group.focused_child == nullptr
215- || (!ignore_group_focus && this->data.as_group.group_focused))
216+ || (!ignore_group_focus && this->data.as_group.group_focused)
217+ || (stop_at_expanded && this->data.as_group.expand_focused != ExpandFocusType::NotExpanded))
218 {
219 return this;
220 } else {
221- return this->data.as_group.focused_child->getFocusedNode(ignore_group_focus);
222+ return this->data.as_group.focused_child->getFocusedNode(
223+ ignore_group_focus,
224+ stop_at_expanded
225+ );
226 }
227 }
228 }
229@@ -218,6 +246,17 @@ bool Hy3Node::isIndirectlyFocused() {
230 return true;
231 }
232
233+// note: assumes this node is the expanded one without checking
234+Hy3Node& Hy3Node::getExpandActor() {
235+ Hy3Node* node = this;
236+
237+ while (node->parent != nullptr
238+ && node->parent->data.as_group.expand_focused != ExpandFocusType::NotExpanded)
239+ node = node->parent;
240+
241+ return *node;
242+}
243+
244 void Hy3Node::recalcSizePosRecursive(bool no_animation) {
245 // clang-format off
246 static const auto* gaps_in = &HyprlandAPI::getConfigValue(PHANDLE, "general:gaps_in")->intValue;
247@@ -260,8 +299,15 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
248 case Hy3GroupLayout::Tabbed: break;
249 }
250
251+ auto expand_focused = group->expand_focused != ExpandFocusType::NotExpanded;
252+ bool directly_contains_expanded
253+ = expand_focused
254+ && (group->focused_child->data.type == Hy3NodeType::Window
255+ || group->focused_child->data.as_group.expand_focused == ExpandFocusType::NotExpanded);
256+
257+ auto child_count = group->children.size() - (directly_contains_expanded ? 1 : 0);
258 double ratio_mul = group->layout != Hy3GroupLayout::Tabbed
259- ? group->children.empty() ? 0 : constraint / group->children.size()
260+ ? child_count <= 0 ? 0 : constraint / child_count
261 : 0;
262
263 double offset = 0;
264@@ -275,7 +321,27 @@ void Hy3Node::recalcSizePosRecursive(bool no_animation) {
265 g_pHyprRenderer->damageBox(&box);
266 }
267diff --git a/src/Hy3Node.hpp b/src/Hy3Node.hpp
268index 0b29099d51f63de4b3e0502655963fca135d6559..29d84f024d7091fb51fc89b3c697d9b95e9fa512 100644
269--- a/src/Hy3Node.hpp
270+++ b/src/Hy3Node.hpp
271@@ -21,11 +21,18 @@ enum class Hy3NodeType {
272 Group,
273 };
274
275+enum class ExpandFocusType {
276+ NotExpanded,
277+ Latch,
278+ Stack,
279+};
280+
281 struct Hy3GroupData {
282 Hy3GroupLayout layout = Hy3GroupLayout::SplitH;
283 std::list<Hy3Node*> children;
284 bool group_focused = true;
285 Hy3Node* focused_child = nullptr;
286+ ExpandFocusType expand_focused = ExpandFocusType::NotExpanded;
287 bool ephemeral = false;
288 bool containment = false;
289 Hy3TabGroup* tab_bar = nullptr;
290@@ -34,6 +41,7 @@ struct Hy3GroupData {
291 ~Hy3GroupData();
292
293 bool hasChild(Hy3Node* child);
294+ void collapseExpansions();
295
296 private:
297 Hy3GroupData(Hy3GroupData&&);
298@@ -84,8 +92,9 @@ struct Hy3Node {
299 bool focusWindow();
300 void markFocused();
301 void raiseToTop();
302- Hy3Node* getFocusedNode(bool ignore_group_focus = false);
303+ Hy3Node* getFocusedNode(bool ignore_group_focus = false, bool stop_at_expanded = false);
304 bool isIndirectlyFocused();
305+ Hy3Node& getExpandActor();
306
307 void recalcSizePosRecursive(bool no_animation = false);
308 void updateTabBar(bool no_animation = false);
309diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp
310index 6abb0ffb117d6c6f842905fc7c0a6a9529bb31a4..d6568e4412b89898f04f9b682f6d0d3fda1a5813 100644
311--- a/src/dispatchers.cpp
312+++ b/src/dispatchers.cpp
313@@ -149,6 +149,31 @@ void dispatch_killactive(std::string value) {
314 g_Hy3Layout->killFocusedNode(workspace);
315 }
316
317+void dispatch_expand(std::string value) {
318+ int workspace = workspace_for_action();
319+ if (workspace == -1) return;
320+
321+ auto args = CVarList(value);
322+
323+ ExpandOption expand;
324+ ExpandFullscreenOption fs_expand = ExpandFullscreenOption::MaximizeIntermediate;
325+
326+ if (args[0] == "expand") expand = ExpandOption::Expand;
327+ else if (args[0] == "shrink") expand = ExpandOption::Shrink;
328+ else if (args[0] == "base") expand = ExpandOption::Base;
329+ else if (args[0] == "maximize") expand = ExpandOption::Maximize;
330+ else if (args[0] == "fullscreen") expand = ExpandOption::Fullscreen;
331+ else return;
332+
333+ if (args[1] == "intermediate_maximize") fs_expand = ExpandFullscreenOption::MaximizeIntermediate;
334+ else if (args[1] == "fullscreen_maximize")
335+ fs_expand = ExpandFullscreenOption::MaximizeAsFullscreen;
336+ else if (args[1] == "maximize_only") fs_expand = ExpandFullscreenOption::MaximizeOnly;
337+ else if (args[1] != "") return;
338+
339+ g_Hy3Layout->expand(workspace, expand, fs_expand);
340+}
341+
342 void dispatch_debug(std::string arg) {
343 int workspace = workspace_for_action();
344 if (workspace == -1) return;
345@@ -169,5 +194,6 @@ void registerDispatchers() {
346 HyprlandAPI::addDispatcher(PHANDLE, "hy3:focustab", dispatch_focustab);
347 HyprlandAPI::addDispatcher(PHANDLE, "hy3:setswallow", dispatch_setswallow);
348 HyprlandAPI::addDispatcher(PHANDLE, "hy3:killactive", dispatch_killactive);
349+ HyprlandAPI::addDispatcher(PHANDLE, "hy3:expand", dispatch_expand);
350 HyprlandAPI::addDispatcher(PHANDLE, "hy3:debugnodes", dispatch_debug);
351 }