2fa77b4dbcdbb6921d47da7992770dfe6f03893b

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

Message

Initial work on expand/lens nodes

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 }