8f56e6ea73db758152b117d507197c46c629afa8
- 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 d22016f103d5dfef0f3634ff8efd9fa7279c8e23..320d21567c3dc7da31ef423f9b79e63fd62a910a 100644
3--- a/README.md
4+++ b/README.md
5@@ -387,3 +387,78 @@ plugin {
6 - `hy3:equalize, [workspace]` - equalize window sizes in group
7 - no argument: equalizes immediate siblings of the focused window
8 - `workspace`: equalizes all windows across the entire workspace tree
9+
10+### Lua dispatchers
11+
12+When using Hyprland's Lua config, hy3 exposes dispatcher factories under `hl.plugin.hy3`.
13+The returned functions can be passed to `hl.bind(...)`.
14+
15+```lua
16+local hy3 = hl.plugin.hy3
17+
18+-- all factories return dispatcher functions and dispatchers return no values
19+-- option tables are optional except for focus_tab
20+
21+hy3.make_group("h" | "v" | "tab" | "opposite", {
22+ toggle = true | false, -- default: false
23+ ephemeral = true | false | "force", -- default: false
24+})
25+
26+hy3.change_group("h" | "v" | "tab" | "untab" | "toggletab" | "opposite")
27+
28+hy3.set_ephemeral(true | false | "true" | "false")
29+
30+hy3.move_focus("l" | "r" | "u" | "d" | "left" | "right" | "up" | "down", {
31+ visible = true | false, -- default: false
32+ warp = true | false, -- default: follows cursor:no_warps
33+})
34+
35+hy3.toggle_focus_layer({
36+ warp = true | false, -- default: true
37+})
38+
39+hy3.warp_cursor()
40+
41+hy3.move_window("l" | "r" | "u" | "d" | "left" | "right" | "up" | "down", {
42+ once = true | false, -- default: false
43+ visible = true | false, -- default: false
44+})
45+
46+hy3.move_to_workspace("<workspace>", {
47+ follow = true | false, -- default: false
48+ warp = true | false, -- default: follows cursor:no_warps when follow = true
49+})
50+
51+hy3.change_focus("top" | "bottom" | "raise" | "lower" | "tab" | "tabnode")
52+
53+-- direction and index are mutually exclusive
54+hy3.focus_tab({
55+ direction = "l" | "r" | "left" | "right",
56+ mouse = "ignore" | "prioritize_hovered" | "require_hovered", -- default: "ignore"
57+ wrap = true | false, -- default: false
58+})
59+
60+hy3.focus_tab({
61+ index = <number>,
62+ mouse = "ignore" | "prioritize_hovered" | "require_hovered", -- default: "ignore"
63+ wrap = true | false, -- default: false
64+})
65+
66+hy3.set_swallow(true | false | "true" | "false" | "toggle")
67+
68+hy3.kill_active()
69+
70+hy3.expand("expand" | "shrink" | "base" | "maximize" | "fullscreen", {
71+ fullscreen = "" | "intermediate_maximize" | "fullscreen_maximize" | "maximize_only",
72+})
73+
74+hy3.lock_tab(nil | "" | "toggle" | "lock" | "unlock")
75+
76+hy3.equalize({
77+ scope = "" | "group" | "workspace", -- default: "group"
78+ workspace = true | false, -- overrides scope if present
79+ recursive = true | false, -- overrides workspace if present
80+})
81+
82+hy3.debug_nodes()
83+```
84diff --git a/flake.lock b/flake.lock
85index 09a4f55cfc35abe0dc596b28ea9e2a4ba17d2da2..09c670f232f96aa0c9879835e049e79366d130f3 100644
86--- a/flake.lock
87+++ b/flake.lock
88@@ -20,11 +20,11 @@
89 ]
90 },
91 "locked": {
92- "lastModified": 1774211390,
93- "narHash": "sha256-sTtAgCCaX8VNNZlQFACd3i1IQ+DB0Wf3COgiFS152ds=",
94+ "lastModified": 1777499565,
95+ "narHash": "sha256-nU55VWk99Pn1QzQDDjFISocC4SgDZ3Xp+zb6ji3JclM=",
96 "owner": "hyprwm",
97 "repo": "aquamarine",
98- "rev": "f62a4dbfa4e5584f14ad4c62afedf6e4b433cf70",
99+ "rev": "813c1e8981893c11e118b19c125d6bc282f51765",
100 "type": "github"
101 },
102 "original": {
103@@ -87,11 +87,11 @@
104 ]
105 },
106 "locked": {
107- "lastModified": 1772461003,
108- "narHash": "sha256-pVICsV7FtcEeVwg5y/LFh3XFUkVJninm/P1j/JHzEbM=",
109+ "lastModified": 1776511930,
110+ "narHash": "sha256-fCpwFiTW0rT7oKJqr3cqHMnkwypSwQKpbtUEtxdkgrM=",
111 "owner": "hyprwm",
112 "repo": "hyprcursor",
113- "rev": "b62396457b9cfe2ebf24fe05404b09d2a40f8ed7",
114+ "rev": "39435900785d0c560c6ae8777d29f28617d031ef",
115 "type": "github"
116 },
117 "original": {
118@@ -116,11 +116,11 @@
119 ]
120 },
121 "locked": {
122- "lastModified": 1772461523,
123- "narHash": "sha256-mI6A51do+hEUzeJKk9YSWfVHdI/SEEIBi2tp5Whq5mI=",
124+ "lastModified": 1776426399,
125+ "narHash": "sha256-RUESLKNikIeEq9ymGJ6nmcDXiSFQpUW1IhJ245nL3xM=",
126 "owner": "hyprwm",
127 "repo": "hyprgraphics",
128- "rev": "7d63c04b4a2dd5e59ef943b4b143f46e713df804",
129+ "rev": "68d064434787cf1ed4a2fe257c03c5f52f33cf84",
130 "type": "github"
131 },
132 "original": {
133@@ -146,17 +146,17 @@
134 "xdph": "xdph"
135 },
136 "locked": {
137- "lastModified": 1775343634,
138- "narHash": "sha256-y5X04LWh/RDH7FE3y1xEDD+RP8mkfGGiXXZ9u0hky2g=",
139+ "lastModified": 1778333727,
140+ "narHash": "sha256-RGYGbW6aH3jUwGVB9BrSYcKsQj7Dl2K32ao5aWbTK40=",
141 "owner": "hyprwm",
142 "repo": "hyprland",
143- "rev": "3a7bd8fea2ca9711da5523dc185c05ea30ec0f35",
144+ "rev": "af923e30d1d24f1f4a4f5cb8308065173c1d9539",
145 "type": "github"
146 },
147 "original": {
148 "owner": "hyprwm",
149 "repo": "hyprland",
150- "rev": "3a7bd8fea2ca9711da5523dc185c05ea30ec0f35",
151+ "rev": "af923e30d1d24f1f4a4f5cb8308065173c1d9539",
152 "type": "github"
153 }
154 },
155@@ -193,11 +193,11 @@
156 ]
157 },
158 "locked": {
159- "lastModified": 1772467975,
160- "narHash": "sha256-kipyuDBxrZq+beYpZqWzGvFWm4QbayW9agAvi94vDXY=",
161+ "lastModified": 1776426575,
162+ "narHash": "sha256-KI6nIfVihn/DPaeB5Et46Xg3dkNHrrEtUd5LBBVomB0=",
163 "owner": "hyprwm",
164 "repo": "hyprland-guiutils",
165- "rev": "5e1c6b9025aaf4d578f3eff7c0eb1f0c197a9507",
166+ "rev": "a968d211048e3ed538e47b84cb3649299578f19d",
167 "type": "github"
168 },
169 "original": {
170@@ -247,11 +247,11 @@
171 ]
172 },
173 "locked": {
174- "lastModified": 1772459629,
175- "narHash": "sha256-/iwvNUYShmmnwmz/czEUh6+0eF5vCMv0xtDW0STPIuM=",
176+ "lastModified": 1777320127,
177+ "narHash": "sha256-Qu+Wf2Bp5qUjyn2YpZNq8a7JyzTGowhT1knrwE38a9U=",
178 "owner": "hyprwm",
179 "repo": "hyprlang",
180- "rev": "7615ee388de18239a4ab1400946f3d0e498a8186",
181+ "rev": "090117506ddc3d7f26e650ff344d378c2ec329cc",
182 "type": "github"
183 },
184 "original": {
185@@ -324,11 +324,11 @@
186 ]
187 },
188diff --git a/flake.nix b/flake.nix
189index d1c3e3ff3b3949188438a79c2f8309db4ec74295..5d0104d5a53a11930f169ca5b3c99d8ef8101dfb 100644
190--- a/flake.nix
191+++ b/flake.nix
192@@ -1,6 +1,6 @@
193 {
194 inputs = {
195- hyprland.url = "github:hyprwm/hyprland/3a7bd8fea2ca9711da5523dc185c05ea30ec0f35";
196+ hyprland.url = "github:hyprwm/hyprland/af923e30d1d24f1f4a4f5cb8308065173c1d9539";
197 };
198
199 outputs = {
200diff --git a/src/Hy3Layout.cpp b/src/Hy3Layout.cpp
201index 6532640a7aad50c35730fac917f5893e9340713b..9dbbadc968f8f2afc6b520fdb28003b40fe14065 100644
202--- a/src/Hy3Layout.cpp
203+++ b/src/Hy3Layout.cpp
204@@ -20,6 +20,7 @@
205 #include <hyprutils/math/Vector2D.hpp>
206 #include <ranges>
207
208+#include "config/shared/ConfigErrors.hpp"
209 #include "config/shared/complex/ComplexDataTypes.hpp"
210 #include "log.hpp"
211 #include "Hy3Layout.hpp"
212@@ -32,7 +33,7 @@ using namespace Desktop::View;
213
214 static CollapsePolicy nodeCollapsePolicy() {
215 static const auto node_collapse_policy =
216- ConfigValue<Hyprlang::INT>("plugin:hy3:node_collapse_policy");
217+ CConfigValue<Config::INTEGER>("plugin:hy3:node_collapse_policy");
218
219 switch (*node_collapse_policy) {
220 case 0: return CollapsePolicy::SingleNodeGroups;
221@@ -238,7 +239,7 @@ void Hy3Layout::insertNode(UP<Hy3Node> node_up, std::optional<Vector2D> focalPoi
222 opening_into = opening_after->parent.get();
223 } else {
224 static const auto tab_first_window =
225- ConfigValue<Hyprlang::INT>("plugin:hy3:tab_first_window");
226+ CConfigValue<Config::INTEGER>("plugin:hy3:tab_first_window");
227
228 // Use space work area if available, fall back to monitor
229 CBox wa_box(monitor->m_position, monitor->m_size);
230@@ -273,10 +274,10 @@ void Hy3Layout::insertNode(UP<Hy3Node> node_up, std::optional<Vector2D> focalPoi
231
232 {
233 // clang-format off
234- static const auto at_enable = ConfigValue<Hyprlang::INT>("plugin:hy3:autotile:enable");
235- static const auto at_ephemeral = ConfigValue<Hyprlang::INT>("plugin:hy3:autotile:ephemeral_groups");
236- static const auto at_trigger_width = ConfigValue<Hyprlang::INT>("plugin:hy3:autotile:trigger_width");
237- static const auto at_trigger_height = ConfigValue<Hyprlang::INT>("plugin:hy3:autotile:trigger_height");
238+ static const auto at_enable = CConfigValue<Config::INTEGER>("plugin:hy3:autotile:enable");
239+ static const auto at_ephemeral = CConfigValue<Config::INTEGER>("plugin:hy3:autotile:ephemeral_groups");
240+ static const auto at_trigger_width = CConfigValue<Config::INTEGER>("plugin:hy3:autotile:trigger_width");
241+ static const auto at_trigger_height = CConfigValue<Config::INTEGER>("plugin:hy3:autotile:trigger_height");
242 // clang-format on
243
244 this->updateAutotileWorkspaces();
245@@ -402,11 +403,12 @@ void Hy3Layout::onWindowFocusChange(PHLWINDOW window) {
246
247 void Hy3Layout::updateGroupBorderColors() {
248 if (!this->root) return;
249- static auto active_color = ConfigValue<Hyprlang::CUSTOMTYPE, Config::CGradientValueData>("general:col.active_border");
250+ static auto active_color = CConfigValue<Config::IComplexConfigValue>("general:col.active_border");
251+ auto* const active_color_data = sc<Config::CGradientValueData*>(active_color.ptr());
252
253 for (auto& w: this->root->windows()) {
254 if (this->shouldRenderSelected(&w)) {
255- w.m_ruleApplicator->inactiveBorderColor().set(*active_color, Desktop::Types::PRIORITY_LAYOUT);
256+ w.m_ruleApplicator->inactiveBorderColor().set(*active_color_data, Desktop::Types::PRIORITY_LAYOUT);
257 } else {
258 w.m_ruleApplicator->inactiveBorderColor().unset(Desktop::Types::PRIORITY_LAYOUT);
259 }
260@@ -415,7 +417,7 @@ void Hy3Layout::updateGroupBorderColors() {
261 }
262 }
263
264-void Hy3Layout::recalculate() { this->recalcGeometry(); }
265+void Hy3Layout::recalculate(Layout::eRecalculateReason) { this->recalcGeometry(); }
266
267 void Hy3Layout::recalcGeometry(bool no_animation) {
268 auto algo = m_parent.lock();
269@@ -506,7 +508,7 @@ void Hy3Layout::resizeTarget(const Vector2D& delta, SP<Layout::ITarget> target,
270 auto horizontal_neighbor = node->findNeighbor(target_edge_x);
271 auto vertical_neighbor = node->findNeighbor(target_edge_y);
272
273- static const auto animate = ConfigValue<Hyprlang::INT>("misc:animate_manual_resizes");
274+ static const auto animate = CConfigValue<Config::INTEGER>("misc:animate_manual_resizes");
275
276 if (horizontal_neighbor) {
277 horizontal_neighbor->resize(reverse(target_edge_x), resize_delta.x, *animate == 0);
278@@ -537,7 +539,7 @@ void Hy3Layout::moveTargetInDirection(SP<Layout::ITarget> t, Math::eDirection di
279 this->shiftNode(*node, shift, false, false);
280 }
281
282-std::expected<void, std::string> Hy3Layout::layoutMsg(const std::string_view& sv) {
283+Config::ErrorResult Hy3Layout::layoutMsg(const std::string_view& sv) {
284 std::string content(sv);
285
286 if (content == "togglesplit") {
287@@ -1057,13 +1059,13 @@ bottom:
288
289 Hy3Node* findTabBarAt(Hy3Node& node, Vector2D pos, Hy3Node** focused_node) {
290 // clang-format off
291- static const auto p_gaps_in = ConfigValue<Hyprlang::CUSTOMTYPE, Config::CCssGapData>("general:gaps_in");
292- static const auto tab_bar_height = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:height");
293- static const auto tab_bar_padding = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:padding");
294+ static const auto p_gaps_in = CConfigValue<Config::IComplexConfigValue>("general:gaps_in");
295+ static const auto tab_bar_height = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:height");
296+ static const auto tab_bar_padding = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:padding");
297 // clang-format on
298
299 auto workspace_rule = Config::workspaceRuleMgr()->getWorkspaceRuleFor(node.layout()->workspace());
300- auto gaps_in = workspace_rule.and_then([](auto r) { return r.m_gapsIn; }).value_or(*p_gaps_in);
301+ auto gaps_in = workspace_rule.and_then([](auto r) { return r.m_gapsIn; }).value_or(*sc<Config::CCssGapData*>(p_gaps_in.ptr()));
302
303 auto inset = *tab_bar_height + *tab_bar_padding + gaps_in.m_top;
304diff --git a/src/Hy3Layout.hpp b/src/Hy3Layout.hpp
305index d4bb5bfd9119518309f685d8f446d49a905b66af..8506995652d31f212f0dd6a08039b2600cc61ab8 100644
306--- a/src/Hy3Layout.hpp
307+++ b/src/Hy3Layout.hpp
308@@ -107,11 +107,11 @@ public:
309 void movedTarget(SP<Layout::ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt) override;
310 void removeTarget(SP<Layout::ITarget> target) override;
311 void resizeTarget(const Vector2D& delta, SP<Layout::ITarget> target, Layout::eRectCorner corner = Layout::CORNER_NONE) override;
312- void recalculate() override;
313+ void recalculate(Layout::eRecalculateReason reason) override;
314 void recalcGeometry(bool no_animation = false);
315 void swapTargets(SP<Layout::ITarget> a, SP<Layout::ITarget> b) override;
316 void moveTargetInDirection(SP<Layout::ITarget> t, Math::eDirection dir, bool silent) override;
317- std::expected<void, std::string> layoutMsg(const std::string_view& sv) override;
318+ Config::ErrorResult layoutMsg(const std::string_view& sv) override;
319 std::optional<Vector2D> predictSizeForNewTarget() override;
320 SP<Layout::ITarget> getNextCandidate(SP<Layout::ITarget> old) override;
321
322diff --git a/src/Hy3Node.cpp b/src/Hy3Node.cpp
323index 260ee45ab5e3af022a720644d56553b402514e0b..7b2c1440786974855c7ffda62d01f50ee8846a3b 100644
324--- a/src/Hy3Node.cpp
325+++ b/src/Hy3Node.cpp
326@@ -326,10 +326,10 @@ Hy3Node& Hy3Node::getPlacementActor() {
327
328 void Hy3Node::recalcSizePosRecursive(CBox offsets, bool no_animation) {
329 // clang-format off
330- static const auto p_gaps_in = ConfigValue<Hyprlang::CUSTOMTYPE, Config::CCssGapData>("general:gaps_in");
331- static const auto tab_bar_height = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:height");
332- static const auto tab_bar_padding = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:padding");
333- static const auto group_inset = ConfigValue<Hyprlang::INT>("plugin:hy3:group_inset");
334+ static const auto p_gaps_in = CConfigValue<Config::IComplexConfigValue>("general:gaps_in");
335+ static const auto tab_bar_height = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:height");
336+ static const auto tab_bar_padding = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:padding");
337+ static const auto group_inset = CConfigValue<Config::INTEGER>("plugin:hy3:group_inset");
338 // clang-format on
339
340 this->logicalBox = CBox(
341@@ -351,7 +351,7 @@ void Hy3Node::recalcSizePosRecursive(CBox offsets, bool no_animation) {
342
343 auto& group = this->as_group();
344 auto workspace_rule = Config::workspaceRuleMgr()->getWorkspaceRuleFor(this->layout()->workspace());
345- auto gaps_in = workspace_rule.and_then([](auto r) { return r.m_gapsIn; }).value_or(*p_gaps_in);
346+ auto gaps_in = workspace_rule.and_then([](auto r) { return r.m_gapsIn; }).value_or(*sc<Config::CCssGapData*>(p_gaps_in.ptr()));
347
348 auto expand_focused = group.expand_focused != ExpandFocusType::NotExpanded;
349 bool directly_contains_expanded =
350diff --git a/src/TabGroup.cpp b/src/TabGroup.cpp
351index 4d04d27960ef02bd1c0e957f799d352943fdbc01..3c39cf20b3dd3390bbfc43c258a967f9fea55f55 100644
352--- a/src/TabGroup.cpp
353+++ b/src/TabGroup.cpp
354@@ -184,22 +184,22 @@ void Hy3TabBarEntry::render(float scale, CBox& box, float opacity_mul) {
355 auto opacity = opacity_mul * this->fade_opacity->value();
356
357 // clang-format off
358- static const auto s_radius = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:radius");
359- static const auto border_width = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:border_width");
360- static const auto s_opacity = ConfigValue<Hyprlang::FLOAT>("plugin:hy3:tabs:opacity");
361- static const auto blur = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:blur");
362- static const auto col_active = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.active");
363- static const auto col_border_active = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.active.border");
364- static const auto col_focused = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.focused");
365- static const auto col_border_focused = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.focused.border");
366- static const auto col_urgent = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.urgent");
367- static const auto col_border_urgent = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.urgent.border");
368- static const auto col_locked = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.locked");
369- static const auto col_border_locked = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.locked.border");
370- static const auto col_active_alt_monitor = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.active_alt_monitor");
371- static const auto col_border_active_alt_monitor = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.active_alt_monitor.border");
372- static const auto col_inactive = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.inactive");
373- static const auto col_border_inactive = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.inactive.border");
374+ static const auto s_radius = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:radius");
375+ static const auto border_width = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:border_width");
376+ static const auto s_opacity = CConfigValue<Config::FLOAT>("plugin:hy3:tabs:opacity");
377+ static const auto blur = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:blur");
378+ static const auto col_active = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.active");
379+ static const auto col_border_active = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.active.border");
380+ static const auto col_focused = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.focused");
381+ static const auto col_border_focused = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.focused.border");
382+ static const auto col_urgent = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.urgent");
383+ static const auto col_border_urgent = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.urgent.border");
384+ static const auto col_locked = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.locked");
385+ static const auto col_border_locked = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.locked.border");
386+ static const auto col_active_alt_monitor = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.active_alt_monitor");
387+ static const auto col_border_active_alt_monitor = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.active_alt_monitor.border");
388+ static const auto col_inactive = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.inactive");
389+ static const auto col_border_inactive = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.inactive.border");
390 // clang-format on
391
392 auto radius = std::min((double) *s_radius * scale, std::min(box.width * 0.5, box.height * 0.5));
393@@ -236,17 +236,17 @@ void Hy3TabBarEntry::render(float scale, CBox& box, float opacity_mul) {
394
395 void Hy3TabBarEntry::renderText(float scale, CBox& box, float opacity) {
396 // clang-format off
397- static const auto render_text = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:render_text");
398- static const auto text_center = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:text_center");
399- static const auto text_font = ConfigValue<Hyprlang::STRING>("plugin:hy3:tabs:text_font");
400- static const auto text_height = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:text_height");
401- static const auto text_padding = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:text_padding");
402- static const auto col_text_active = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.active.text");
403- static const auto col_text_focused = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.focused.text");
404- static const auto col_text_urgent = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.urgent.text");
405- static const auto col_text_locked = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.locked.text");
406- static const auto col_text_active_alt_monitor = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.active_alt_monitor.text");
407- static const auto col_text_inactive = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:col.inactive.text");
408+ static const auto render_text = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:render_text");
409+ static const auto text_center = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:text_center");
410+ static const auto text_font = CConfigValue<Config::STRING>("plugin:hy3:tabs:text_font");
411+ static const auto text_height = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:text_height");
412+ static const auto text_padding = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:text_padding");
413+ static const auto col_text_active = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.active.text");
414+ static const auto col_text_focused = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.focused.text");
415+ static const auto col_text_urgent = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.urgent.text");
416+ static const auto col_text_locked = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.locked.text");
417+ static const auto col_text_active_alt_monitor = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.active_alt_monitor.text");
418+ static const auto col_text_inactive = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:col.inactive.text");
419 // clang-format on
420
421 if (!*render_text) {
422@@ -281,7 +281,7 @@ void Hy3TabBarEntry::renderText(float scale, CBox& box, float opacity) {
423 auto* layout = pango_layout_new(context);
424 pango_layout_set_text(layout, this->window_title.c_str(), -1);
425
426- auto* font_desc = pango_font_description_from_string(*text_font);
427+ auto* font_desc = pango_font_description_from_string((*text_font).c_str());
428 pango_font_description_set_size(font_desc, *text_height * scale * PANGO_SCALE);
429 pango_layout_set_font_description(layout, font_desc);
430 pango_font_description_free(font_desc);
431@@ -606,7 +606,7 @@ Hy3TabGroup::Hy3TabGroup(Hy3Node& node) {
432 }
433
434 void Hy3TabGroup::updateWithGroup(Hy3Node& node, bool warp) {
435- static const auto bar_height = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:height");
436+ static const auto bar_height = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:height");
437
438 auto tpos = node.visualBox.pos();
439 auto tsize = Vector2D(node.visualBox.w, *bar_height);
440@@ -644,9 +644,9 @@ void Hy3TabBar::damageBox(const Vector2D* position, const Vector2D* size) {
441 }
442
443 void Hy3TabGroup::tick() {
444- static const auto enter_from_top = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:from_top");
445- static const auto padding = ConfigValue<Hyprlang::INT>("plugin:hy3:tabs:padding");
446- static const auto no_gaps_when_only = ConfigValue<Hyprlang::INT>("plugin:hy3:no_gaps_when_only");
447+ static const auto enter_from_top = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:from_top");
448+ static const auto padding = CConfigValue<Config::INTEGER>("plugin:hy3:tabs:padding");
449+ static const auto no_gaps_when_only = CConfigValue<Config::INTEGER>("plugin:hy3:no_gaps_when_only");
450
451 this->bar.tick();
452
453@@ -734,9 +734,9 @@ std::pair<CBox, CBox> Hy3TabGroup::getRenderBB() const {
454diff --git a/src/dispatchers.cpp b/src/dispatchers.cpp
455index 7c1f2137590ea9df17635bb0b7982e6edf50f882..1bf65e9b617ce97fc900dea7f1d163365a4da4eb 100644
456--- a/src/dispatchers.cpp
457+++ b/src/dispatchers.cpp
458@@ -1,10 +1,14 @@
459 #include <optional>
460+#include <string>
461+#include <string_view>
462
463 #include <hyprland/src/Compositor.hpp>
464 #include <hyprland/src/desktop/DesktopTypes.hpp>
465 #include <hyprland/src/plugins/PluginAPI.hpp>
466 #include <hyprutils/string/String.hpp>
467 #include <hyprutils/string/VarList.hpp>
468+#include <hyprland/src/config/lua/LuaBindings.hpp>
469+#include <hyprland/src/config/lua/bindings/LuaBindingsInternal.hpp>
470
471 #include "dispatchers.hpp"
472 #include "log.hpp"
473@@ -12,6 +16,7 @@
474 #include "src/SharedDefs.hpp"
475
476 using Hyprutils::String::CVarList;
477+namespace LuaInternal = Config::Lua::Bindings::Internal;
478
479 static Hy3Layout* hy3InstanceForAction(bool allow_fullscreen = false) {
480 auto workspace = workspace_for_action(allow_fullscreen);
481@@ -19,13 +24,162 @@ static Hy3Layout* hy3InstanceForAction(bool allow_fullscreen = false) {
482 return hy3InstanceForWorkspace(workspace);
483 }
484
485-static SDispatchResult dispatch_makegroup(std::string value) {
486+static void luaCheckArgCount(lua_State* L, const char* fn, int min, int max) {
487+ const int count = lua_gettop(L);
488+ if (count < min || count > max) {
489+ if (min == max)
490+ luaL_error(L, "%s: expected %d argument(s), got %d", fn, min, count);
491+ else
492+ luaL_error(L, "%s: expected %d-%d argument(s), got %d", fn, min, max, count);
493+ }
494+}
495+
496+static bool luaHasOptionsTable(lua_State* L, int idx, const char* fn) {
497+ if (lua_isnoneornil(L, idx)) return false;
498+ if (!lua_istable(L, idx))
499+ luaL_error(L, "%s: options must be a table", fn);
500+ return true;
501+}
502+
503+static std::string luaStringArg(lua_State* L, int idx, const char* fn, const char* name) {
504+ if (lua_isnoneornil(L, idx)) {
505+ luaL_error(L, "%s: missing required argument '%s'", fn, name);
506+ return {};
507+ }
508+
509+ if (!lua_isstring(L, idx)) {
510+ luaL_error(L, "%s: argument '%s' must be a string", fn, name);
511+ return {};
512+ }
513+
514+ return lua_tostring(L, idx);
515+}
516+
517+static std::optional<bool> luaBoolLikeArg(lua_State* L, int idx, const char* fn, const char* name) {
518+ if (lua_isboolean(L, idx)) return lua_toboolean(L, idx);
519+
520+ if (lua_isstring(L, idx)) {
521+ std::string value = lua_tostring(L, idx);
522+ if (value == "true") return true;
523+ if (value == "false") return false;
524+ }
525+
526+ luaL_error(L, "%s: argument '%s' must be a boolean", fn, name);
527+ return {};
528+}
529+
530+static int luaOptionalBoolMode(std::optional<bool> value) {
531+ if (!value) return -1;
532+ return *value ? 1 : 0;
533+}
534+
535+static std::optional<bool> luaOptionalBoolFromUpvalue(lua_State* L, int idx) {
536+ auto mode = static_cast<int>(lua_tointeger(L, lua_upvalueindex(idx)));
537+ if (mode < 0) return {};
538+ return mode != 0;
539+}
540+
541+struct SMakeGroupAction {
542+ Hy3GroupLayout layout = Hy3GroupLayout::SplitH;
543+ bool opposite = false;
544+};
545+
546+static std::optional<SMakeGroupAction> parseMakeGroupArg(std::string_view arg) {
547+ if (arg == "h") return SMakeGroupAction { .layout = Hy3GroupLayout::SplitH };
548+ if (arg == "v") return SMakeGroupAction { .layout = Hy3GroupLayout::SplitV };
549+ if (arg == "tab") return SMakeGroupAction { .layout = Hy3GroupLayout::Tabbed };
550+ if (arg == "opposite") return SMakeGroupAction { .opposite = true };
551+ return {};
552+}
553+
554+static SMakeGroupAction luaMakeGroupActionArg(lua_State* L, int idx, const char* fn) {
555+ auto value = luaStringArg(L, idx, fn, "layout");
556+ auto action = parseMakeGroupArg(value);
557+ if (!action)
558diff --git a/src/globals.hpp b/src/globals.hpp
559index eb69328d94e6bb9326f6ca60cd74afa37e65a1cb..6acbe1c14fa388045e9a51e6c98c37c080627474 100644
560--- a/src/globals.hpp
561+++ b/src/globals.hpp
562@@ -4,11 +4,13 @@
563 #include <vector>
564
565 #include <hyprland/src/desktop/Workspace.hpp>
566+#include <hyprland/src/config/ConfigValue.hpp>
567 #include <hyprland/src/plugins/PluginAPI.hpp>
568 #include <hyprlang.hpp>
569
570 #include "Hy3Layout.hpp"
571 #include "TabGroup.hpp"
572+#include "config/shared/complex/ComplexDataType.hpp"
573
574 inline HANDLE PHANDLE = nullptr;
575
576@@ -42,44 +44,3 @@ inline void errorNotif() {
577 }
578 );
579 }
580-
581-class HyprlangUnspecifiedCustomType {};
582-
583-// abandon hope all ye who enter here
584-template <typename T, typename V = HyprlangUnspecifiedCustomType>
585-class ConfigValue {
586-public:
587- ConfigValue(const std::string& option) {
588- this->static_data_ptr = HyprlandAPI::getConfigValue(PHANDLE, option)->getDataStaticPtr();
589- }
590-
591- template <typename U = T>
592- typename std::enable_if<std::is_same<U, Hyprlang::CUSTOMTYPE>::value, const V&>::type
593- operator*() const {
594- return *(V*) ((Hyprlang::CUSTOMTYPE*) *this->static_data_ptr)->getData();
595- }
596-
597- template <typename U = T>
598- typename std::enable_if<std::is_same<U, Hyprlang::CUSTOMTYPE>::value, const V*>::type
599- operator->() const {
600- return &**this;
601- }
602-
603- // Bullshit microptimization case for strings
604- template <typename U = T>
605- typename std::enable_if<std::is_same<U, Hyprlang::STRING>::value, const char*>::type
606- operator*() const {
607- return *(const char**) this->static_data_ptr;
608- }
609-
610- template <typename U = T>
611- typename std::enable_if<
612- !std::is_same<U, Hyprlang::CUSTOMTYPE>::value && !std::is_same<U, Hyprlang::STRING>::value,
613- const T&>::type
614- operator*() const {
615- return *(T*) *this->static_data_ptr;
616- }
617-
618-private:
619- void* const* static_data_ptr;
620-};
621diff --git a/src/main.cpp b/src/main.cpp
622index fa89ef2597df84064e4f38bf250acef8e2f36d3b..d4ef18216b177133de3b384d37ebc2d952cadf6a 100644
623--- a/src/main.cpp
624+++ b/src/main.cpp
625@@ -1,9 +1,9 @@
626 #include <hyprland/src/Compositor.hpp>
627+#include <hyprland/src/config/values/ConfigValues.hpp>
628 #include <hyprland/src/plugins/PluginAPI.hpp>
629 #include <hyprland/src/render/Renderer.hpp>
630 #include <hyprland/src/render/OpenGL.hpp>
631 #include <hyprland/src/version.h>
632-#include <hyprlang.hpp>
633
634 #include "dispatchers.hpp"
635 #include "globals.hpp"
636@@ -31,56 +31,52 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
637 #endif
638
639 #define CONF(NAME, TYPE, VALUE) \
640- HyprlandAPI::addConfigValue(PHANDLE, "plugin:hy3:" NAME, Hyprlang::CConfigValue((TYPE) VALUE))
641-
642- using Hyprlang::FLOAT;
643- using Hyprlang::INT;
644- using Hyprlang::STRING;
645+ HyprlandAPI::addConfigValueV2(PHANDLE, makeShared<Config::Values::TYPE>("plugin:hy3:" NAME, "", VALUE))
646
647 // general
648- CONF("no_gaps_when_only", INT, 0);
649- CONF("node_collapse_policy", INT, 2);
650- CONF("group_inset", INT, 10);
651- CONF("tab_first_window", INT, 0);
652+ CONF("no_gaps_when_only", Int, 0);
653+ CONF("node_collapse_policy", Int, 2);
654+ CONF("group_inset", Int, 10);
655+ CONF("tab_first_window", Int, 0);
656
657 // tabs
658- CONF("tabs:height", INT, 22);
659- CONF("tabs:padding", INT, 5);
660- CONF("tabs:from_top", INT, 0);
661- CONF("tabs:radius", INT, 6);
662- CONF("tabs:border_width", INT, 2);
663- CONF("tabs:render_text", INT, 1);
664- CONF("tabs:text_center", INT, 1);
665- CONF("tabs:text_font", STRING, "Sans");
666- CONF("tabs:text_height", INT, 8);
667- CONF("tabs:text_padding", INT, 3);
668- CONF("tabs:opacity", FLOAT, 1.0);
669- CONF("tabs:blur", INT, 1);
670- CONF("tabs:col.active", INT, 0x4033ccff);
671- CONF("tabs:col.active.border", INT, 0xee33ccff);
672- CONF("tabs:col.active.text", INT, 0xffffffff);
673- CONF("tabs:col.active_alt_monitor", INT, 0x40606060);
674- CONF("tabs:col.active_alt_monitor.border", INT, 0xee808080);
675- CONF("tabs:col.active_alt_monitor.text", INT, 0xffffffff);
676- CONF("tabs:col.focused", INT, 0x40606060);
677- CONF("tabs:col.focused.border", INT, 0xee808080);
678- CONF("tabs:col.focused.text", INT, 0xffffffff);
679- CONF("tabs:col.inactive", INT, 0x20303030);
680- CONF("tabs:col.inactive.border", INT, 0xaa606060);
681- CONF("tabs:col.inactive.text", INT, 0xffffffff);
682- CONF("tabs:col.urgent", INT, 0x40ff2233);
683- CONF("tabs:col.urgent.border", INT, 0xeeff2233);
684- CONF("tabs:col.urgent.text", INT, 0xffffffff);
685- CONF("tabs:col.locked", INT, 0x40909033);
686- CONF("tabs:col.locked.border", INT, 0xee909033);
687- CONF("tabs:col.locked.text", INT, 0xffffffff);
688+ CONF("tabs:height", Int, 22);
689+ CONF("tabs:padding", Int, 5);
690+ CONF("tabs:from_top", Int, 0);
691+ CONF("tabs:radius", Int, 6);
692+ CONF("tabs:border_width", Int, 2);
693+ CONF("tabs:render_text", Int, 1);
694+ CONF("tabs:text_center", Int, 1);
695+ CONF("tabs:text_font", String, "Sans");
696+ CONF("tabs:text_height", Int, 8);
697+ CONF("tabs:text_padding", Int, 3);
698+ CONF("tabs:opacity", Float, 1.0);
699+ CONF("tabs:blur", Int, 1);
700+ CONF("tabs:col.active", Int, 0x4033ccff);
701+ CONF("tabs:col.active.border", Int, 0xee33ccff);
702+ CONF("tabs:col.active.text", Int, 0xffffffff);
703+ CONF("tabs:col.active_alt_monitor", Int, 0x40606060);
704+ CONF("tabs:col.active_alt_monitor.border", Int, 0xee808080);
705+ CONF("tabs:col.active_alt_monitor.text", Int, 0xffffffff);
706+ CONF("tabs:col.focused", Int, 0x40606060);
707+ CONF("tabs:col.focused.border", Int, 0xee808080);
708+ CONF("tabs:col.focused.text", Int, 0xffffffff);
709+ CONF("tabs:col.inactive", Int, 0x20303030);
710+ CONF("tabs:col.inactive.border", Int, 0xaa606060);
711+ CONF("tabs:col.inactive.text", Int, 0xffffffff);
712+ CONF("tabs:col.urgent", Int, 0x40ff2233);
713+ CONF("tabs:col.urgent.border", Int, 0xeeff2233);
714+ CONF("tabs:col.urgent.text", Int, 0xffffffff);
715+ CONF("tabs:col.locked", Int, 0x40909033);
716+ CONF("tabs:col.locked.border", Int, 0xee909033);
717+ CONF("tabs:col.locked.text", Int, 0xffffffff);
718
719 // autotiling
720- CONF("autotile:enable", INT, 0);
721- CONF("autotile:ephemeral_groups", INT, 1);
722- CONF("autotile:trigger_height", INT, 0);
723- CONF("autotile:trigger_width", INT, 0);
724- CONF("autotile:workspaces", STRING, "all");