Diff
1diff --git a/README.md b/README.md
2index 802f9442020d73473bb0526542a7a70fe130f73d..c5cfe5410393dc68fdd993481b53a83e5f26a568 100644
3--- a/README.md
4+++ b/README.md
5@@ -116,6 +116,7 @@ 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
12 ## Installing
13diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
14index 525379ea9cb6eb2d6c634a2ef216906dbb757bf8..59c85df68d795466bfa212220a17d2a068427b26 100644
15--- a/src/Hy3Layout.cpp
16+++ b/src/Hy3Layout.cpp
17@@ -16,6 +16,59 @@ std::unique_ptr<HOOK_CALLBACK_FN> urgentHookPtr
18 std::unique_ptr<HOOK_CALLBACK_FN> tickHookPtr
19 = std::make_unique<HOOK_CALLBACK_FN>(Hy3Layout::tickHook);
20
21+bool performContainment(Hy3Node& node, bool contained, CWindow* window) {
22+ if (node.data.type == Hy3NodeType::Group) {
23+ auto& group = node.data.as_group;
24+ contained |= group.containment;
25+
26+ auto iter = node.data.as_group.children.begin();
27+ while (iter != node.data.as_group.children.end()) {
28+ switch ((*iter)->data.type) {
29+ case Hy3NodeType::Group: return performContainment(**iter, contained, window);
30+ case Hy3NodeType::Window:
31+ if (contained) {
32+ auto wpid = (*iter)->data.as_window->getPID();
33+ auto ppid = getPPIDof(window->getPID());
34+ while (ppid > 10) { // `> 10` yoinked from HL swallow
35+ if (ppid == wpid) {
36+ node.layout->nodes.push_back({
37+ .parent = &node,
38+ .data = window,
39+ .workspace_id = node.workspace_id,
40+ .layout = node.layout,
41+ });
42+
43+ auto& child_node = node.layout->nodes.back();
44+
45+ group.children.insert(std::next(iter), &child_node);
46+ child_node.markFocused();
47+ node.recalcSizePosRecursive();
48+
49+ return true;
50+ }
51+
52+ ppid = getPPIDof(ppid);
53+ }
54+ }
55+ }
56+
57+ iter = std::next(iter);
58+ }
59+ }
60+
61+ return false;
62+}
63+
64+void Hy3Layout::onWindowCreated(CWindow* window) {
65+ for (auto& node: this->nodes) {
66+ if (node.parent == nullptr && performContainment(node, false, window)) {
67+ return;
68+ }
69+ }
70+
71+ IHyprLayout::onWindowCreated(window);
72+}
73+
74 void Hy3Layout::onWindowCreatedTiling(CWindow* window) {
75 if (window->m_bIsFloating) return;
76
77@@ -938,6 +991,18 @@ hastab:
78 tab_node->recalcSizePosRecursive();
79 }
80
81+void Hy3Layout::setNodeSwallow(int workspace, SetSwallowOption option) {
82+ auto* node = this->getWorkspaceFocusedNode(workspace);
83+ if (node == nullptr || node->parent == nullptr) return;
84+
85+ auto* containment = &node->parent->data.as_group.containment;
86+ switch (option) {
87+ case SetSwallowOption::NoSwallow: *containment = false;
88+ case SetSwallowOption::Swallow: *containment = true;
89+ case SetSwallowOption::Toggle: *containment = !*containment;
90+ }
91+}
92+
93 void Hy3Layout::killFocusedNode(int workspace) {
94 if (g_pCompositor->m_pLastWindow != nullptr && g_pCompositor->m_pLastWindow->m_bIsFloating) {
95 g_pCompositor->closeWindow(g_pCompositor->m_pLastWindow);
96diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
97index 1a352493531d3a45aef8ed64ca5ca5c77c24144b..b77ed7a0813c83d0ed81b385cd69f6d3a3c1c419 100644
98--- a/src/Hy3Layout.hpp
99+++ b/src/Hy3Layout.hpp
100@@ -44,8 +44,15 @@ enum class TabFocusMousePriority {
101 Require,
102 };
103
104+enum class SetSwallowOption {
105+ NoSwallow,
106+ Swallow,
107+ Toggle,
108+};
109+
110 class Hy3Layout: public IHyprLayout {
111 public:
112+ virtual void onWindowCreated(CWindow*);
113 virtual void onWindowCreatedTiling(CWindow*);
114 virtual void onWindowRemovedTiling(CWindow*);
115 virtual void onWindowFocusChange(CWindow*);
116@@ -75,6 +82,7 @@ public:
117 void shiftFocus(int workspace, ShiftDirection, bool visible);
118 void changeFocus(int workspace, FocusShift);
119 void focusTab(int workspace, TabFocus target, TabFocusMousePriority, bool wrap_scroll, int index);
120+ void setNodeSwallow(int workspace, SetSwallowOption);
121 void killFocusedNode(int workspace);
122
123 bool shouldRenderSelected(CWindow*);
124diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp
125index 85b105502b0993965c133df2a69e8b207608e0b8..97436e3cd972ea49c053330264db590c7b13e135 100644
126--- a/src/Hy3Node.cpp
127+++ b/src/Hy3Node.cpp
128@@ -525,6 +525,15 @@ std::string Hy3Node::debugNode() {
129
130 buf << "] size ratio: ";
131 buf << this->size_ratio;
132+
133+ if (this->data.as_group.ephemeral) {
134+ buf << ", ephemeral";
135+ }
136+
137+ if (this->data.as_group.containment) {
138+ buf << ", containment";
139+ }
140+
141 for (auto* child: this->data.as_group.children) {
142 buf << "\n|-";
143 if (child == nullptr) {
144diff --git a/src/Hy3Node.hpp b/src/Hy3Node.hpp
145index 59107bb6c17fac56a0e3b1937104689c8e303667..e98f2cb51437a50ee51d4a38d62ff1c87e9f7788 100644
146--- a/src/Hy3Node.hpp
147+++ b/src/Hy3Node.hpp
148@@ -27,6 +27,7 @@ struct Hy3GroupData {
149 bool group_focused = true;
150 Hy3Node* focused_child = nullptr;
151 bool ephemeral = false;
152+ bool containment = false;
153 Hy3TabGroup* tab_bar = nullptr;
154
155 Hy3GroupData(Hy3GroupLayout layout);
156diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp
157index 1480a2432e7869b47bca904617f0fac8412e8514..6abb0ffb117d6c6f842905fc7c0a6a9529bb31a4 100644
158--- a/src/dispatchers.cpp
159+++ b/src/dispatchers.cpp
160@@ -126,6 +126,22 @@ void dispatch_focustab(std::string value) {
161 g_Hy3Layout->focusTab(workspace, focus, mouse, wrap_scroll, index);
162 }
163
164+void dispatch_setswallow(std::string arg) {
165+ int workspace = workspace_for_action();
166+ if (workspace == -1) return;
167+
168+ SetSwallowOption option;
169+ if (arg == "true") {
170+ option = SetSwallowOption::Swallow;
171+ } else if (arg == "false") {
172+ option = SetSwallowOption::NoSwallow;
173+ } else if (arg == "toggle") {
174+ option = SetSwallowOption::Toggle;
175+ } else return;
176+
177+ g_Hy3Layout->setNodeSwallow(workspace, option);
178+}
179+
180 void dispatch_killactive(std::string value) {
181 int workspace = workspace_for_action();
182 if (workspace == -1) return;
183@@ -151,6 +167,7 @@ void registerDispatchers() {
184 HyprlandAPI::addDispatcher(PHANDLE, "hy3:movewindow", dispatch_movewindow);
185 HyprlandAPI::addDispatcher(PHANDLE, "hy3:changefocus", dispatch_changefocus);
186 HyprlandAPI::addDispatcher(PHANDLE, "hy3:focustab", dispatch_focustab);
187+ HyprlandAPI::addDispatcher(PHANDLE, "hy3:setswallow", dispatch_setswallow);
188 HyprlandAPI::addDispatcher(PHANDLE, "hy3:killactive", dispatch_killactive);
189 HyprlandAPI::addDispatcher(PHANDLE, "hy3:debugnodes", dispatch_debug);
190 }