1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
local state = {
order = {},
split = {},
default_split = "h",
}
local function target_id(target)
local window = target.window
return window and tostring(window.stable_id) or tostring(target.index)
end
local function active_id(ctx)
for _, target in ipairs(ctx.targets) do
local window = target.window
if window and window.active then
return target_id(target)
end
end
return state.order[#state.order]
end
local function index_of(tbl, value)
for i, v in ipairs(tbl) do
if v == value then
return i
end
end
end
local function sync_order(ctx)
local present = {}
local targets = {}
for _, target in ipairs(ctx.targets) do
local id = target_id(target)
present[id] = true
targets[id] = target
end
local old_order = state.order
state.order = {}
for _, id in ipairs(old_order) do
if present[id] then
table.insert(state.order, id)
else
state.split[id] = nil
end
end
local focused = active_id(ctx)
for _, target in ipairs(ctx.targets) do
local id = target_id(target)
if not index_of(state.order, id) then
local after = focused and index_of(state.order, focused)
table.insert(state.order, after and (after + 1) or (#state.order + 1), id)
end
end
return targets
end
local function place_chain(ctx, targets, ids, area, i)
if i > #ids then
return
end
local target = targets[ids[i]]
if not target then
return
end
if i == #ids then
target:place(area)
return
end
local split = state.split[ids[i]] or state.default_split
if split == "v" then
target:place(ctx:split(area, "top", 0.5))
place_chain(ctx, targets, ids, ctx:split(area, "bottom", 0.5), i + 1)
else
target:place(ctx:split(area, "left", 0.5))
place_chain(ctx, targets, ids, ctx:split(area, "right", 0.5), i + 1)
end
end
local function move_active(ctx, delta)
local id = active_id(ctx)
local i = id and index_of(state.order, id)
local j = i and (i + delta)
if not i or j < 1 or j > #state.order then
return
end
state.order[i], state.order[j] = state.order[j], state.order[i]
end
hl.layout.register("manual", {
recalculate = function(ctx)
local targets = sync_order(ctx)
place_chain(ctx, targets, state.order, ctx.area, 1)
end,
layout_msg = function(ctx, msg)
local id = active_id(ctx)
local command = msg:match("^(%S+)")
if command == "splith" or command == "h" then
if id then
state.split[id] = "h"
end
elseif command == "splitv" or command == "v" then
if id then
state.split[id] = "v"
end
elseif command == "splittoggle" or command == "toggle" then
if id then
state.split[id] = state.split[id] == "v" and "h" or "v"
end
elseif command == "promote" then
local i = id and index_of(state.order, id)
if i then
table.remove(state.order, i)
table.insert(state.order, 1, id)
end
elseif command == "swapnext" then
move_active(ctx, 1)
elseif command == "swapprev" then
move_active(ctx, -1)
elseif command == "rotate" then
for k, v in pairs(state.split) do
state.split[k] = v == "v" and "h" or "v"
end
state.default_split = state.default_split == "v" and "h" or "v"
else
return "manual: expected splith, splitv, splittoggle, promote, swapnext, swapprev, or rotate"
end
return true
end,
})
|