8715516d93ec5164a091454b846a158e345da65c

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

Message

Tab removal animation

Requires Hyprland#2389 (tracked in flake)

Diff

This diff is truncated to protect this page.

  1diff --git a/flake.lock b/flake.lock
  2index a52171dd0949b2329b2635d9184de86bf20a9f84..b740f6a253ba089d8600b6cba0e6f917e08495c7 100644
  3--- a/flake.lock
  4+++ b/flake.lock
  5@@ -8,16 +8,17 @@
  6         "xdph": "xdph"
  7       },
  8       "locked": {
  9-        "lastModified": 1685105343,
 10-        "narHash": "sha256-ypXKGzTQWJqbHHrPnSHLo4b2vNtfOFyh6sDdNPi7/WQ=",
 11-        "owner": "hyprwm",
 12+        "lastModified": 1685317265,
 13+        "narHash": "sha256-Bv/ksFWpm2RSRCY40pIJddNEn5G3yjLs9NUHbpVDGy0=",
 14+        "owner": "outfoxxed",
 15         "repo": "Hyprland",
 16-        "rev": "5f4659afef5856c509d53957e62b7f6c38d39f41",
 17+        "rev": "51441d5b9de13335e5723cdbb78e372ebe36490e",
 18         "type": "github"
 19       },
 20       "original": {
 21-        "owner": "hyprwm",
 22+        "owner": "outfoxxed",
 23         "repo": "Hyprland",
 24+        "rev": "51441d5b9de13335e5723cdbb78e372ebe36490e",
 25         "type": "github"
 26       }
 27     },
 28diff --git a/flake.nix b/flake.nix
 29index af84a4587bd4afeca47eba8dccc85ef47b776931..3f70a66f459e7c05395f1d5e41f615378b6f673b 100644
 30--- a/flake.nix
 31+++ b/flake.nix
 32@@ -1,6 +1,7 @@
 33 {
 34   inputs = {
 35-    hyprland.url = "github:hyprwm/Hyprland";
 36+    #hyprland.url = "github:hyprwm/Hyprland";
 37+    hyprland.url = "github:outfoxxed/Hyprland?rev=51441d5b9de13335e5723cdbb78e372ebe36490e";
 38   };
 39 
 40   outputs = { self, hyprland, ... }: let
 41diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
 42index 38f828ab76256a2be461aeda50dd2a5f31ef0e81..e97898e6688d8c7309228267787748a1bb7ec36c 100644
 43--- a/src/Hy3Layout.cpp
 44+++ b/src/Hy3Layout.cpp
 45@@ -1523,7 +1523,7 @@ void renderTabs(Hy3Node& node) {
 46 	auto& group = node.data.as_group;
 47 
 48 	if (!group.tab_bar) {
 49-		group.tab_bar = std::unique_ptr<Hy3TabGroup>(new Hy3TabGroup(node));
 50+		group.tab_bar = Hy3TabGroup::new_(node);
 51 	} else {
 52 		group.tab_bar->updateWithGroup(node);
 53 	}
 54diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
 55index f4377a87a4454846d539b6513aac1d9c148bd20f..1dc286c5d9d582ba6a38827a70351f6116093994 100644
 56--- a/src/Hy3Layout.hpp
 57+++ b/src/Hy3Layout.hpp
 58@@ -27,7 +27,7 @@ struct Hy3GroupData {
 59 	std::list<Hy3Node*> children;
 60 	bool group_focused = true;
 61 	Hy3Node* focused_child = nullptr;
 62-	std::unique_ptr<Hy3TabGroup> tab_bar;
 63+	std::shared_ptr<Hy3TabGroup> tab_bar;
 64 
 65 	bool hasChild(Hy3Node* child);
 66 
 67diff --git a/src/TabGroup.cpp b/src/TabGroup.cpp
 68index 81fcfbbf02aaa38d1e8a93105259519e0ed2be1b..ce664f471055b88b3743825fc99f6d632bd20d25 100644
 69--- a/src/TabGroup.cpp
 70+++ b/src/TabGroup.cpp
 71@@ -5,7 +5,7 @@
 72 #include <hyprland/src/Compositor.hpp>
 73 #include <cairo/cairo.h>
 74 
 75-Hy3TabBarEntry::Hy3TabBarEntry(Hy3TabBar& tab_bar, Hy3Node& node): tab_bar(tab_bar), node(node) {
 76+Hy3TabBarEntry::Hy3TabBarEntry(std::shared_ptr<Hy3TabGroup> tab_group, Hy3Node& node): tab_group(tab_group), node(node) {
 77 	this->offset.create(AVARTYPE_FLOAT, -1.0f, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), nullptr, AVARDAMAGE_NONE);
 78 	this->width.create(AVARTYPE_FLOAT, -1.0f, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), nullptr, AVARDAMAGE_NONE);
 79 
 80@@ -20,6 +20,10 @@ bool Hy3TabBarEntry::operator==(const Hy3Node& node) const {
 81 	return this->node == node;
 82 }
 83 
 84+bool Hy3TabBarEntry::operator==(const Hy3TabBarEntry& entry) const {
 85+	return this->node == entry.node;
 86+}
 87+
 88 void Hy3TabBarEntry::prepareTexture(float scale, Vector2D size) {
 89 	static const auto* rounding_setting = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hy3:tabs:rounding")->intValue;
 90 
 91@@ -91,6 +95,16 @@ void Hy3TabBarEntry::prepareTexture(float scale, Vector2D size) {
 92 	}
 93 }
 94 
 95+void Hy3TabBarEntry::animateRemoval() {
 96+	if (this->width.goalf() == 0.0) return;
 97+	this->width = 0.0;
 98+
 99+	// not removing the callback is required for soundness, as the animation will
100+	// be deleted once the callback ends.
101+	this->width.setCallbackOnEnd([this](void*) {
102+		this->tab_group->bar.entries.remove(*this);
103+	}, false);
104+}
105 
106 Hy3TabBar::Hy3TabBar() {
107 	this->vertical_pos.create(AVARTYPE_FLOAT, 1.0f, g_pConfigManager->getAnimationPropertyConfig("windowsMove"), nullptr, AVARDAMAGE_NONE);
108@@ -123,7 +137,7 @@ void Hy3TabBar::focusNode(Hy3Node* node) {
109 }
110 
111 void Hy3TabBar::updateNodeList(std::list<Hy3Node*>& nodes) {
112-	std::list<Hy3TabBarEntry> removed_entries;
113+	std::list<std::list<Hy3TabBarEntry>::iterator> removed_entries;
114 
115 	auto entry = this->entries.begin();
116 	auto node = nodes.begin();
117@@ -133,7 +147,8 @@ void Hy3TabBar::updateNodeList(std::list<Hy3Node*>& nodes) {
118 		while (true) {
119 			if (entry == this->entries.end()) goto exitloop;
120 			if (*entry == **node) break;
121-			removed_entries.splice(removed_entries.end(), this->entries, entry++);
122+			removed_entries.push_back(entry);
123+			entry = std::next(entry);
124 		}
125 
126 		node = std::next(node);
127@@ -143,7 +158,10 @@ void Hy3TabBar::updateNodeList(std::list<Hy3Node*>& nodes) {
128  exitloop:
129 
130 	// move any extra entries to removed_entries
131-	removed_entries.splice(removed_entries.end(), this->entries, entry, this->entries.end());
132+	while (entry != this->entries.end()) {
133+		removed_entries.push_back(entry);
134+		entry = std::next(entry);
135+	}
136 
137 	entry = this->entries.begin();
138 	node = nodes.begin();
139@@ -151,15 +169,26 @@ void Hy3TabBar::updateNodeList(std::list<Hy3Node*>& nodes) {
140 	// add missing entries, taking first from removed_entries
141 	while (node != nodes.end()) {
142 		if (entry == this->entries.end() || *entry != **node) {
143-			auto moved = std::find(removed_entries.begin(), removed_entries.end(), **node);
144+			if (std::find(removed_entries.begin(), removed_entries.end(), entry) != removed_entries.end()) {
145+				entry = std::next(entry);
146+				continue;
147+			}
148+
149+			auto moved = std::find_if(removed_entries.begin(), removed_entries.end(), [&node](auto entry) { return **node == *entry; });
150 			if (moved != removed_entries.end()) {
151-				this->entries.splice(entry, removed_entries, moved);
152-				entry = moved;
153+				this->entries.splice(entry, this->entries, *moved);
154+				entry = *moved;
155+				removed_entries.erase(moved);
156 			} else {
157-				entry = this->entries.emplace(entry, *this, **node);
158+				entry = this->entries.emplace(entry, this->group.lock(), **node);
159 			}
160 		}
161 
162+		if (entry->width.goalf() == 0.0) {
163+			entry->width.setCallbackOnEnd(nullptr);
164+			entry->width = -1.0;
165+		}
166+
167 		// set stats from node data
168 		auto* root = *node;
169 		while (root->parent != nullptr) root = root->parent;
170@@ -173,8 +202,7 @@ void Hy3TabBar::updateNodeList(std::list<Hy3Node*>& nodes) {
171diff --git a/src/TabGroup.hpp b/src/TabGroup.hpp
172index 29309993b7185764ac584baad2d146c1c7c63a03..7fa4ebd01b10efc445d74aa6ffc7c1df17b65e6c 100644
173--- a/src/TabGroup.hpp
174+++ b/src/TabGroup.hpp
175@@ -3,6 +3,7 @@
176 #include <hyprland/src/helpers/AnimatedVariable.hpp>
177 #include <hyprland/src/helpers/Vector2D.hpp>
178 #include <list>
179+#include <memory>
180 #include <vector>
181 
182 class Hy3TabGroup;
183@@ -19,16 +20,19 @@ struct Hy3TabBarEntry {
184 	CTexture texture;
185 	CAnimatedVariable offset; // offset 0, 0.0-1.0 of total bar
186 	CAnimatedVariable width; // 0.0-1.0 of total bar
187-	Hy3TabBar& tab_bar;
188+	std::shared_ptr<Hy3TabGroup> tab_group;
189 	Hy3Node& node; // only used for comparioson. do not deref.
190 	Vector2D last_render_size;
191 	float last_render_scale = 0.0;
192 	float last_render_rounding = 0.0;
193 
194-	Hy3TabBarEntry(Hy3TabBar&, Hy3Node&);
195-	bool operator==(const Hy3Node& node) const;
196+	Hy3TabBarEntry(std::shared_ptr<Hy3TabGroup>, Hy3Node&);
197+	bool operator==(const Hy3Node&) const;
198+	bool operator==(const Hy3TabBarEntry&) const;
199 
200-	void prepareTexture(float scale, Vector2D size);
201+	void prepareTexture(float, Vector2D);
202+
203+	void animateRemoval();
204 };
205 
206 class Hy3TabBar {
207@@ -44,6 +48,7 @@ public:
208 	void setSize(Vector2D);
209 
210 	std::list<Hy3TabBarEntry> entries;
211+	std::weak_ptr<Hy3TabGroup> group;
212 private:
213 	Hy3Node* focused_node = nullptr;
214 	CAnimatedVariable focus_opacity;
215@@ -60,7 +65,7 @@ public:
216 	CAnimatedVariable size;
217 
218 	// initialize a group with the given node. UB if node is not a group.
219-	Hy3TabGroup(Hy3Node&);
220+	static std::shared_ptr<Hy3TabGroup> new_(Hy3Node&);
221 
222 	// update tab bar with node position and data. UB if node is not a group.
223 	void updateWithGroup(Hy3Node&);
224@@ -68,8 +73,11 @@ public:
225 	void renderTabBar();
226 
227 private:
228+	std::weak_ptr<Hy3TabGroup> self;
229 	std::vector<CWindow*> stencil_windows;
230 
231+	Hy3TabGroup();
232+
233 	// moving a Hy3TabGroup will unregister any active animations
234 	Hy3TabGroup(Hy3TabGroup&&) = delete;
235