-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathinit.lua
310 lines (275 loc) · 9.65 KB
/
init.lua
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
local gears = require("gears")
local awful = require('awful')
local beautiful = require("beautiful")
local naughty = require("naughty")
local wibox = require("wibox")
local math = require("math")
local dpi = require("beautiful.xresources").apply_dpi
--- Cached objects
local wbox
local title_widget
local layout_widgets
local num_columns = 3
local hidden = {}
local function debug_print(str)
naughty.notify({ preset = naughty.config.presets.normal,
title = "Debug",
text = str })
end
local function print_error(str)
naughty.notify({ preset = naughty.config.presets.critical,
title = "Error",
text = str })
end
local function key_to_string(mod, key)
if key == " " then
key = "space"
end
local mod_copy = {}
for _, modifier in ipairs(mod) do
table.insert(mod_copy, modifier)
end
table.sort(mod_copy)
if #mod_copy > 0 then
key = table.concat(mod_copy, "-") .. "-" .. key
end
-- to lower case
key = key:lower()
return key
end
local function colorize(text, fg, bg)
if fg == nil then
fg = ""
else
fg = " foreground=\"" .. fg .. "\""
end
if bg == nil then
bg = ""
else
bg = " background=\"" .. bg .. "\""
end
return "<span" .. fg .. bg .. ">" .. text .. "</span>"
end
local function bold(text)
return "<b>" .. text .. "</b>"
end
local function escape_markup(text)
return text:gsub("&", "&"):gsub("<", "<"):gsub(">", ">")
end
local function start(args)
local config = args.config
if not config then
error("config not given")
end
local activation_key = args.activation_key
if not activation_key then
error("activation_key not given")
end
local ignored_mod = args.ignored_mod or nil
local hide_first_level = args.hide_first_level or false
local key_fg = args.key_fg or beautiful.fg_normal
local key_bg = args.key_bg or "#eeeeee"
local key_control_fg = args.key_control_fg or "#00bb00"
local key_shift_fg = args.key_shift_fg or "#4488ff"
local key_modifier_fg = args.key_modifier_fg or "#aa4444"
local activation_fg = args.activation_fg or "#44aaff"
local nested_fg = args.nested_fg or "#4488aa"
local nested_bg = args.nested_bg or nil
local focused_fg = args.focused_fg or nil
local focused_bg = args.focused_bg or "#dddddd"
local current_key_table = config
local breadcrumbs = {}
local focused_key = nil
local initial_screen = awful.screen.focused()
local margin_size = dpi(6)
local title_height = dpi(22)
local key_height = dpi(15)
local main_width = dpi(800)
local y_anchor_pos = 0.75
local colorized_control = colorize("C", key_control_fg)
local colorized_shift = colorize("S", key_shift_fg)
local colorized_mod1 = colorize("A", key_modifier_fg)
local colorized_mod4 = colorize("Super", key_modifier_fg)
local colorized_mod3 = colorize("Mod3", key_modifier_fg)
local function key_string_to_label(key_string)
key_string = key_string:gsub("control", colorized_control)
key_string = key_string:gsub("shift", colorized_shift)
key_string = key_string:gsub("mod1", colorized_mod1)
key_string = key_string:gsub("mod4", colorized_mod4)
key_string = key_string:gsub("mod3", colorized_mod3)
return colorize(bold(" " .. key_string .. " "), key_fg, key_bg)
end
local function stylize_nested(description)
return colorize(bold(description), nested_fg, nested_bg)
end
local function update_ui()
if hide_first_level and #breadcrumbs == 0 then
return
end
if not wbox then
wbox = wibox({
ontop = true,
type="dock",
border_width = beautiful.border_width,
border_color = beautiful.border_focus,
placement = awful.placement.centered,
})
wbox.screen = initial_screen
wbox:set_fg(beautiful.fg_normal)
wbox:set_bg(beautiful.bg_normal..'e2')
local container_inner = wibox.layout.align.vertical()
local container_layout = wibox.container.margin(
container_inner,
margin_size, margin_size,
margin_size, margin_size)
container_layout = wibox.container.background(container_layout)
wbox:set_widget(container_layout)
layout_widgets = {}
for i = 1, num_columns do
local layout_widget = wibox.layout.fixed.vertical()
table.insert(layout_widgets, layout_widget)
end
title_widget = wibox.widget {
markup = "",
valign = "bottom",
align = "center",
forced_height = title_height,
widget = wibox.widget.textbox,
}
container_inner:set_bottom(title_widget)
local args = {
layout = wibox.layout.flex.horizontal,
forced_width = main_width,
}
for _, layout_widget in ipairs(layout_widgets) do
table.insert(args, layout_widget)
end
container_inner:set_middle(wibox.widget(args))
else
for _, layout_widget in ipairs(layout_widgets) do
layout_widget:reset()
end
end
-- build title widget markup using breadcrumbs
local title_markup = colorize(bold(activation_key), activation_fg)
for i, v in ipairs(breadcrumbs) do
title_markup = title_markup .. " " .. key_string_to_label(escape_markup(v[1]))
end
if #breadcrumbs > 0 then
local v = breadcrumbs[#breadcrumbs]
title_markup = title_markup .. " - " .. stylize_nested(escape_markup(v[2]))
end
title_widget.markup = title_markup
-- Set geometry always, the screen might have changed.
local wa = initial_screen.workarea
local x = math.ceil(wa.x + wa.width / 2 - main_width / 2) -- Center align
wbox:geometry({
x = x,
width = main_width,
})
local wbox_height = title_height
local keys = {}
for k, _ in pairs(current_key_table) do
table.insert(keys, {k, key_string_to_label(escape_markup(k))})
end
table.sort(keys, function(a, b) return a[2] < b[2] end)
-- loop through each key
local current_column = 1
for _, entry in ipairs(keys) do
local k = entry[1]
local label = entry[2]
local v = current_key_table[k]
local description = v[1]
if description == hidden then
-- skip hidden keys
goto continue
end
description = escape_markup(description)
local child = v[2]
local child_is_function = type(child) == "function"
if not child_is_function then
description = stylize_nested(description .. "...")
end
local height = key_height
local markup = label .. " - " .. description
if focused_key == k then
markup = colorize(markup, focused_fg, focused_bg)
end
local key_widget = wibox.widget {
markup = markup,
valign = "center",
align = "left",
forced_height = height,
widget = wibox.widget.textbox,
}
layout_widgets[current_column]:add(key_widget)
if current_column == 1 then
wbox_height = wbox_height + height
end
current_column = current_column + 1
if current_column > num_columns then
current_column = 1
end
::continue::
end
local h = wbox_height + margin_size * 2
wbox.screen = initial_screen
wbox:geometry({
height = h,
y = wa.y + wa.height * y_anchor_pos - h,
})
wbox.visible = true
end
local grabber
local function stop()
if wbox then
wbox.visible = false
end
awful.keygrabber.stop(grabber)
grabber = nil
breadcrumbs = {}
end
update_ui()
grabber = awful.keygrabber.run(function(mod, key, event)
if event == "release" then
if key == activation_key then
stop()
end
return
end
local mod_copy = {}
for _, modifier in ipairs(mod) do
if modifier ~= ignored_mod and modifier ~= "Mod2" then
table.insert(mod_copy, modifier)
end
end
mod = mod_copy
local key_string = key_to_string(mod, key)
-- debug_print("grabber: mod: " .. table.concat(mod, ',')
-- .. ", key: " .. tostring(key)
-- .. ", event: " .. tostring(event)
-- .. ", key_string: " .. key_string)
local child = current_key_table[key_string]
if child then
local description = child[1]
child = child[2]
if type(child) == "function" then
local status, err = pcall(child)
if not status then
print_error("Error: " .. err)
end
focused_key = key_string
update_ui()
else
table.insert(breadcrumbs, {key, description})
current_key_table = child
focused_key = nil
update_ui()
end
end
end)
end
return {
start = start,
hidden = hidden,
}