a1f892fa218f6def606ccb1f81ddc2e57d0551b8

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

Message

Add `hy3:locktab`

Diff

This diff is truncated to protect this page.

  1diff --git a/CHANGELOG.md b/CHANGELOG.md
  2index 4354d4ab25254307cff0f168be95c72edeba4b2d..924faa9e54555096727dd9e19f04f31719e6c835 100644
  3--- a/CHANGELOG.md
  4+++ b/CHANGELOG.md
  5@@ -2,6 +2,7 @@
  6 
  7 # Upcoming
  8 - Added `toggle` to `hy3:makegroup`
  9+- Added `hy3:locktab`
 10 
 11 # hl0.47.0-1 and before
 12 - Added focused tab color, to mark selected tabs inside an unfocused container.
 13diff --git a/README.md b/README.md
 14index 5ce1d224013307ed6bf887b6776bbd78c6838d4e..ac70553d9125d6e6f2f6d164929f824829e9457f 100644
 15--- a/README.md
 16+++ b/README.md
 17@@ -367,12 +367,13 @@ plugin {
 18    - `tabnode` - raise focus to the nearest node under the tab
 19  - `hy3:togglefocuslayer, [nowarp]` - toggle focus between tiled and floating layers
 20    - `nowarp` - do not warp the mouse to the newly focused window
 21- - `hy3:focustab [l | r | left | right | index, <index>], [prioritize_hovered | require_hovered], [wrap]`
 22+ - `hy3:focustab, [l | r | left | right | index, <index>], [prioritize_hovered | require_hovered], [wrap]`
 23    - `l | r | left | right` - direction to change focus towards
 24    - `index, <index>` - select the `index`th tab
 25    - `prioritize_hovered` - prioritize the tab group under the mouse when multiple are stacked. use the lowest group if none is under the mouse.
 26    - `require_hovered` - affect the tab group under the mouse. do nothing if none are hovered.
 27    - `wrap` - wrap to the opposite size of the tab bar if moving off the end
 28+ - `hy3:locktab, [lock | unlock]` - lock the current tab, makingg it behave like a node
 29  - `hy3:debugnodes` - print the node tree into the hyprland log
 30  - :warning: **ALPHA QUALITY** `hy3:setswallow, <true | false | toggle>` - set the containing node's window swallow state
 31  - :warning: **ALPHA QUALITY** `hy3:expand, <expand | shrink | base>` - expand the current node to cover other nodes
 32diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
 33index 8e890090118715e4e7877dcaed745a9da6c05016..227a04910a0f7d8adeba6e4b32ed3ced7914c136 100644
 34--- a/src/Hy3Layout.cpp
 35+++ b/src/Hy3Layout.cpp
 36@@ -130,6 +130,7 @@ void Hy3Layout::insertNode(Hy3Node& node) {
 37 
 38 	if (root != nullptr) {
 39 		opening_after = root->getFocusedNode();
 40+		if (opening_after) opening_after = &opening_after->getPlacementActor();
 41 
 42 		// opening_after->parent cannot be nullptr
 43 		if (opening_after == root) {
 44@@ -156,6 +157,8 @@ void Hy3Layout::insertNode(Hy3Node& node) {
 45 				opening_after = this->getNodeFromWindow(mouse_window.get());
 46 			}
 47 		}
 48+
 49+		if (opening_after) opening_after = &opening_after->getPlacementActor();
 50 	}
 51 
 52 	if (opening_after != nullptr
 53@@ -713,6 +716,7 @@ void Hy3Layout::makeGroupOnWorkspace(
 54     bool toggle
 55 ) {
 56 	auto* node = this->getWorkspaceFocusedNode(workspace);
 57+	if (node) node = &node->getPlacementActor();
 58 
 59 	if (node && toggle) {
 60 		auto* parent = node->parent;
 61@@ -740,12 +744,14 @@ void Hy3Layout::makeOppositeGroupOnWorkspace(
 62     GroupEphemeralityOption ephemeral
 63 ) {
 64 	auto* node = this->getWorkspaceFocusedNode(workspace);
 65+	if (node) node = &node->getPlacementActor();
 66 	this->makeOppositeGroupOn(node, ephemeral);
 67 }
 68 
 69 void Hy3Layout::changeGroupOnWorkspace(const CWorkspace* workspace, Hy3GroupLayout layout) {
 70 	auto* node = this->getWorkspaceFocusedNode(workspace);
 71 	if (node == nullptr) return;
 72+	node = &node->getPlacementActor();
 73 
 74 	this->changeGroupOn(*node, layout);
 75 }
 76@@ -753,6 +759,7 @@ void Hy3Layout::changeGroupOnWorkspace(const CWorkspace* workspace, Hy3GroupLayo
 77 void Hy3Layout::untabGroupOnWorkspace(const CWorkspace* workspace) {
 78 	auto* node = this->getWorkspaceFocusedNode(workspace);
 79 	if (node == nullptr) return;
 80+	node = &node->getPlacementActor();
 81 
 82 	this->untabGroupOn(*node);
 83 }
 84@@ -760,6 +767,7 @@ void Hy3Layout::untabGroupOnWorkspace(const CWorkspace* workspace) {
 85 void Hy3Layout::toggleTabGroupOnWorkspace(const CWorkspace* workspace) {
 86 	auto* node = this->getWorkspaceFocusedNode(workspace);
 87 	if (node == nullptr) return;
 88+	node = &node->getPlacementActor();
 89 
 90 	this->toggleTabGroupOn(*node);
 91 }
 92@@ -767,6 +775,7 @@ void Hy3Layout::toggleTabGroupOnWorkspace(const CWorkspace* workspace) {
 93 void Hy3Layout::changeGroupToOppositeOnWorkspace(const CWorkspace* workspace) {
 94 	auto* node = this->getWorkspaceFocusedNode(workspace);
 95 	if (node == nullptr) return;
 96+	node = &node->getPlacementActor();
 97 
 98 	this->changeGroupToOppositeOn(*node);
 99 }
100@@ -774,6 +783,7 @@ void Hy3Layout::changeGroupToOppositeOnWorkspace(const CWorkspace* workspace) {
101 void Hy3Layout::changeGroupEphemeralityOnWorkspace(const CWorkspace* workspace, bool ephemeral) {
102 	auto* node = this->getWorkspaceFocusedNode(workspace);
103 	if (node == nullptr) return;
104+	node = &node->getPlacementActor();
105 
106 	this->changeGroupEphemeralityOn(*node, ephemeral);
107 }
108@@ -876,20 +886,25 @@ void Hy3Layout::changeGroupEphemeralityOn(Hy3Node& node, bool ephemeral) {
109 }
110 
111 void Hy3Layout::shiftNode(Hy3Node& node, ShiftDirection direction, bool once, bool visible) {
112-	if (once && node.parent != nullptr && node.parent->data.as_group().children.size() == 1) {
113-		if (node.parent->parent == nullptr) {
114-			node.parent->data.as_group().setLayout(Hy3GroupLayout::SplitH);
115-			node.parent->recalcSizePosRecursive();
116-		} else {
117-			auto* node2 = node.parent;
118-			Hy3Node::swapData(node, *node2);
119-			node2->layout->nodes.remove(node);
120-			node2->updateTabBarRecursive();
121-			node2->recalcSizePosRecursive();
122+	if (once) {
123+		auto& n = node.getPlacementActor();
124+		if (n.parent != nullptr && n.parent->data.as_group().children.size() == 1) {
125+			if (n.parent->parent == nullptr) {
126+				n.parent->data.as_group().setLayout(Hy3GroupLayout::SplitH);
127+				n.parent->recalcSizePosRecursive();
128+			} else {
129+				auto* n2 = n.parent;
130+				Hy3Node::swapData(n, *n2);
131+				n2->layout->nodes.remove(n);
132+				n2->updateTabBarRecursive();
133+				n2->recalcSizePosRecursive();
134+			}
135+
136diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
137index 78316353c8bbb11551a014eba6ab74fea64327a3..784c0ac51f832769ff742f0e50d910974d53f603 100644
138--- a/src/Hy3Layout.hpp
139+++ b/src/Hy3Layout.hpp
140@@ -54,6 +54,12 @@ enum class TabFocusMousePriority {
141 	Require,
142 };
143 
144+enum class TabLockMode {
145+	Lock,
146+	Unlock,
147+	Toggle,
148+};
149+
150 enum class SetSwallowOption {
151 	NoSwallow,
152 	Swallow,
153@@ -105,7 +111,12 @@ public:
154 	void onDisable() override;
155 
156 	void insertNode(Hy3Node& node);
157-	void makeGroupOnWorkspace(const CWorkspace* workspace, Hy3GroupLayout, GroupEphemeralityOption, bool toggle);
158+	void makeGroupOnWorkspace(
159+	    const CWorkspace* workspace,
160+	    Hy3GroupLayout,
161+	    GroupEphemeralityOption,
162+	    bool toggle
163+	);
164 	void makeOppositeGroupOnWorkspace(const CWorkspace* workspace, GroupEphemeralityOption);
165 	void changeGroupOnWorkspace(const CWorkspace* workspace, Hy3GroupLayout);
166 	void untabGroupOnWorkspace(const CWorkspace* workspace);
167@@ -139,6 +150,7 @@ public:
168 	void setNodeSwallow(const CWorkspace* workspace, SetSwallowOption);
169 	void killFocusedNode(const CWorkspace* workspace);
170 	void expand(const CWorkspace* workspace, ExpandOption, ExpandFullscreenOption);
171+	void setTabLock(const CWorkspace* workspace, TabLockMode);
172 	static void warpCursorToBox(const Vector2D& pos, const Vector2D& size);
173 	static void warpCursorWithFocus(const Vector2D& pos, bool force = false);
174 
175@@ -169,7 +181,7 @@ private:
176 	// if shift is true, shift the window in the given direction, returning
177 	// nullptr, if shift is false, return the window in the given direction or
178 	// nullptr. if once is true, only one group will be broken out of / into
179-	Hy3Node* shiftOrGetFocus(Hy3Node&, ShiftDirection, bool shift, bool once, bool visible);
180+	Hy3Node* shiftOrGetFocus(Hy3Node*, ShiftDirection, bool shift, bool once, bool visible);
181 
182 	void updateAutotileWorkspaces();
183 	bool shouldAutotileWorkspace(const CWorkspace* workspace);
184diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp
185index 6e5d7986f1ee43be79d09feac8eff09a41f6ed0b..20b687fd00ecbc0793c48cff799497013008e1f5 100644
186--- a/src/Hy3Node.cpp
187+++ b/src/Hy3Node.cpp
188@@ -26,6 +26,7 @@ Hy3GroupData::Hy3GroupData(Hy3GroupLayout layout): layout(layout) {
189 
190 Hy3GroupData::Hy3GroupData(Hy3GroupData&& from) {
191 	this->layout = from.layout;
192+	this->locked = from.locked;
193 	this->previous_nontab_layout = from.previous_nontab_layout;
194 	this->children = std::move(from.children);
195 	this->group_focused = from.group_focused;
196@@ -301,6 +302,12 @@ Hy3Node& Hy3Node::getExpandActor() {
197 	return *node;
198 }
199 
200+Hy3Node& Hy3Node::getPlacementActor() {
201+	Hy3Node* node = &this->getExpandActor();
202+	while (node->parent && node->parent->data.as_group().locked) node = node->parent;
203+	return *node;
204+}
205+
206 void Hy3Node::recalcSizePosRecursive(bool no_animation) {
207 	// clang-format off
208 	static const auto p_gaps_in = ConfigValue<Hyprlang::CUSTOMTYPE, CCssGapData>("general:gaps_in");
209diff --git a/src/Hy3Node.hpp b/src/Hy3Node.hpp
210index ac4ca50a70ae5b4a661facc29179413a3c168df2..af5c1788de4c186fe2415d2b45fe685b954b1e57 100644
211--- a/src/Hy3Node.hpp
212+++ b/src/Hy3Node.hpp
213@@ -37,6 +37,7 @@ struct Hy3GroupData {
214 	Hy3Node* focused_child = nullptr;
215 	ExpandFocusType expand_focused = ExpandFocusType::NotExpanded;
216 	bool ephemeral = false;
217+	bool locked = false;
218 	bool containment = false;
219 	Hy3TabGroup* tab_bar = nullptr;
220 
221@@ -106,6 +107,7 @@ struct Hy3Node {
222 	void resize(ShiftDirection, double, bool no_animation = false);
223 	bool isIndirectlyFocused();
224 	Hy3Node& getExpandActor();
225+	Hy3Node& getPlacementActor();
226 
227 	void recalcSizePosRecursive(bool no_animation = false);
228 	void updateTabBar(bool no_animation = false);
229diff --git a/src/TabGroup.cpp b/src/TabGroup.cpp
230index 8fac8da8d8f6cb8d4463e1dc17f74f98d3a2651a..5d410a1636f4de53d001eed0b8980e9db181e943 100644
231--- a/src/TabGroup.cpp
232+++ b/src/TabGroup.cpp
233@@ -99,6 +99,7 @@ Hy3TabBarEntry::Hy3TabBarEntry(Hy3TabBar& tab_bar, Hy3Node& node): tab_bar(tab_b
234 	this->active->setUpdateCallback(update_callback);
235 	this->focused->setUpdateCallback(update_callback);
236 	this->urgent->setUpdateCallback(update_callback);
237+	this->focused->setUpdateCallback(update_callback);
238 	this->offset->setUpdateCallback(update_callback);
239 	this->width->setUpdateCallback(update_callback);
240 	this->vertical_pos->setUpdateCallback(update_callback);
241@@ -173,18 +174,22 @@ void Hy3TabBarEntry::render(float scale, CBox& box, float opacity_mul) {
242 	static const auto col_border_focused = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.focused.border");
243 	static const auto col_urgent = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.urgent");
244 	static const auto col_border_urgent = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.urgent.border");
245+	static const auto col_locked = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.locked");
246+	static const auto col_border_locked = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.locked.border");
247 	static const auto col_inactive = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.inactive");
248 	static const auto col_border_inactive = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.inactive.border");
249 	// clang-format on
250 
251 	auto radius = std::min((double) *s_radius * scale, std::min(box.width * 0.5, box.height * 0.5));
252 
253-	auto color = this->mergeColors(*col_active, *col_focused, *col_urgent, *col_inactive);
254+	auto color =
255+	    this->mergeColors(*col_active, *col_focused, *col_urgent, *col_locked, *col_inactive);
256 
257 	auto border_color = this->mergeColors(
258 	    *col_border_active,
259 	    *col_border_focused,
260 	    *col_border_urgent,
261+	    *col_border_locked,
262 	    *col_border_inactive
263 	);
264 
265@@ -216,6 +221,7 @@ void Hy3TabBarEntry::renderText(float scale, CBox& box, float opacity) {
266 	static const auto col_text_active = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.active.text");
267 	static const auto col_text_focused = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.active.text");
268 	static const auto col_text_urgent = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.urgent.text");
269+	static const auto col_text_locked = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.locked.text");
270 	static const auto col_text_inactive = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.inactive.text");
271 	// clang-format on
272 
273@@ -343,7 +349,13 @@ void Hy3TabBarEntry::renderText(float scale, CBox& box, float opacity) {
274 
275 	texture_box.round();
276 
277-	auto c = mergeColors(*col_text_active, *col_text_focused, *col_text_urgent, *col_text_inactive);
278+	auto c = mergeColors(
279+	    *col_text_active,
280+	    *col_text_focused,
281+	    *col_text_urgent,
282+	    *col_text_locked,
283+	    *col_text_inactive
284+	);
285 
286 	glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA);
287 	glBlendColor(c.r, c.g, c.b, c.a);
288@@ -358,18 +370,20 @@ CHyprColor Hy3TabBarEntry::mergeColors(
289     const CHyprColor& active,
290     const CHyprColor& focused,
291     const CHyprColor& urgent,
292+    const CHyprColor& locked,
293     const CHyprColor& inactive
294 ) {
295-	auto focused_av = this->focused->value();
296 	auto active_v = this->active->value();
297-	auto focused_v = focused_av - active_v;
298-	auto urgent_v = this->urgent->value();
299-	auto inactive_v = 1.0f - (focused_av + urgent_v);
300+	auto urgent_v = std::max(0.0f, this->urgent->value() - active_v);
301+	auto focused_v = std::max(0.0f, this->focused->value() - active_v - urgent_v);
302+	auto locked_v = std::max(0.0f, this->tab_bar.locked->value() - active_v - urgent_v - focused_v);
303+	auto inactive_v = 1.0f - (active_v + urgent_v + focused_v + locked_v);
304 
305 	return merge_colors(
306 	    std::make_pair(active_v, active),
307-	    std::make_pair(focused_v, focused),
308 	    std::make_pair(urgent_v, urgent),
309+	    std::make_pair(focused_v, focused),
310+	    std::make_pair(locked_v, locked),
311 	    std::make_pair(inactive_v, inactive)
312 	);
313 }
314@@ -382,7 +396,15 @@ Hy3TabBar::Hy3TabBar() {
315 	    AVARDAMAGE_NONE
316 	);
317 
318+	g_pAnimationManager->createAnimation(
319+	    0.0F,
320+	    this->locked,
321+	    g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"),
322+	    AVARDAMAGE_NONE
323+	);
324+
325 	this->fade_opacity->setUpdateCallback([this](auto) { this->dirty = true; });
326+	this->locked->setUpdateCallback([this](auto) { this->dirty = true; });
327 }
328 
329 void Hy3TabBar::beginDestroy() {
330@@ -572,6 +594,9 @@ void Hy3TabGroup::updateWithGroup(Hy3Node& node, bool warp) {
331 	this->bar.updateNodeList(node.data.as_group().children);
332 	this->bar.updateAnimations(warp);
333diff --git a/src/TabGroup.hpp b/src/TabGroup.hpp
334index dbc41299e4b0a5fe99818763b3ddede25bfe6d0d..ec254e92419defc07c5a6ac39f9467dfdaa7705c 100644
335--- a/src/TabGroup.hpp
336+++ b/src/TabGroup.hpp
337@@ -65,6 +65,7 @@ private:
338 	    const CHyprColor& active,
339 	    const CHyprColor& focused,
340 	    const CHyprColor& urgent,
341+	    const CHyprColor& locked,
342 	    const CHyprColor& inactive
343 	);
344 };
345@@ -75,6 +76,7 @@ public:
346 	bool dirty = true;
347 	bool damaged = true;
348 	PHLANIMVAR<float> fade_opacity;
349+	PHLANIMVAR<float> locked;
350 
351 	Hy3TabBar();
352 	void beginDestroy();
353diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp
354index 3344e67947068ec5ff14d9bee089b7e4be3fac59..cb2fe383a5fb45161091412455b9caaa8f3824c3 100644
355--- a/src/dispatchers.cpp
356+++ b/src/dispatchers.cpp
357@@ -252,6 +252,17 @@ void dispatch_expand(std::string value) {
358 	g_Hy3Layout->expand(workspace.get(), expand, fs_expand);
359 }
360 
361+void dispatch_locktab(std::string arg) {
362+	auto workspace = workspace_for_action();
363+	if (!valid(workspace)) return;
364+
365+	auto mode = TabLockMode::Toggle;
366+	if (arg == "lock") mode = TabLockMode::Lock;
367+	else if (arg == "unlock") mode = TabLockMode::Unlock;
368+
369+	g_Hy3Layout->setTabLock(workspace.get(), mode);
370+}
371+
372 void dispatch_debug(std::string arg) {
373 	auto workspace = workspace_for_action();
374 
375@@ -277,5 +288,6 @@ void registerDispatchers() {
376 	HyprlandAPI::addDispatcher(PHANDLE, "hy3:setswallow", dispatch_setswallow);
377 	HyprlandAPI::addDispatcher(PHANDLE, "hy3:killactive", dispatch_killactive);
378 	HyprlandAPI::addDispatcher(PHANDLE, "hy3:expand", dispatch_expand);
379+	HyprlandAPI::addDispatcher(PHANDLE, "hy3:locktab", dispatch_locktab);
380 	HyprlandAPI::addDispatcher(PHANDLE, "hy3:debugnodes", dispatch_debug);
381 }
382diff --git a/src/main.cpp b/src/main.cpp
383index 4fa2de344482a686156ca9c2ca1e07f682cc2260..f74cca89cc5a0200ca64e25e5670f16cf3fc08ab 100644
384--- a/src/main.cpp
385+++ b/src/main.cpp
386@@ -66,6 +66,9 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
387 	CONF("tabs:col.urgent", INT, 0x40ff2233);
388 	CONF("tabs:col.urgent.border", INT, 0xeeff2233);
389 	CONF("tabs:col.urgent.text", INT, 0xffffffff);
390+	CONF("tabs:col.locked", INT, 0x40909033);
391+	CONF("tabs:col.locked.border", INT, 0xee909033);
392+	CONF("tabs:col.locked.text", INT, 0xffffffff);
393 
394 	// autotiling
395 	CONF("autotile:enable", INT, 0);
396diff --git a/src/render.cpp b/src/render.cpp
397index 74c5ddf8996bfd013286e4131579fd68dffd1900..bbd02b4575f6751759c70ef79807524123d29b1a 100644
398--- a/src/render.cpp
399+++ b/src/render.cpp
400@@ -31,7 +31,7 @@ void Hy3Render::renderTab(
401 	auto monitorBox = CBox {Vector2D(), monitorSize};
402 
403 	auto matrix =
404-			rdata.monitorProjection.projectBox(monitorBox, HYPRUTILS_TRANSFORM_NORMAL, monitorBox.rot);
405+	    rdata.monitorProjection.projectBox(monitorBox, HYPRUTILS_TRANSFORM_NORMAL, monitorBox.rot);
406 
407 	auto glMatrix = rdata.projection.copy().multiply(matrix);
408