forked from worron/redflat
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkeychain.lua
More file actions
200 lines (169 loc) · 6.36 KB
/
keychain.lua
File metadata and controls
200 lines (169 loc) · 6.36 KB
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
-----------------------------------------------------------------------------------------------------------------------
-- RedFlat prefix hotkey manager --
-----------------------------------------------------------------------------------------------------------------------
-- Emacs like key key sequences
-----------------------------------------------------------------------------------------------------------------------
-- Grab environment
-----------------------------------------------------------------------------------------------------------------------
local string = string
local table = table
local wibox = require("wibox")
local awful = require("awful")
local beautiful = require("beautiful")
local redflat = require("redflat")
local redutil = require("redflat.util")
local redtip = require("redflat.float.hotkeys")
-- Initialize tables and vars for module
-----------------------------------------------------------------------------------------------------------------------
local keychain = {}
local tip_cache = {}
local label_pattern = { Mod1 = "A", Mod4 = "M", Control = "C", Shift = "S" }
-- key bindings
keychain.service = { close = { "Escape" }, help = { "F1" }, stepback = { "BackSpace" } }
-- Generate default theme vars
-----------------------------------------------------------------------------------------------------------------------
local function default_style()
local style = {
geometry = { width = 220, height = 60 },
font = "Sans 14 bold",
border_width = 2,
keytip = { geometry = { width = 500, height = 600 }, exit = false },
color = { border = "#575757", wibox = "#202020" }
}
return redflat.util.table.merge(style, redflat.util.table.check(beautiful, "float.keychain") or {})
end
-- Support functions
-----------------------------------------------------------------------------------------------------------------------
local function build_label(item)
if #item[1] == 0 then return item[2] end
local mods = {}
for _, m in ipairs(item[1]) do mods[#mods + 1] = label_pattern[m] end
return string.format("%s-%s", table.concat(mods, '-'), item[2])
end
local function build_tip(store, item, prefix)
local prefix = prefix or build_label(item)
for _, k in ipairs(item[3]) do
local p = prefix .. " " .. build_label(k)
if type(k[3]) == "table" then
build_tip(store, k, p)
else
table.insert(store, { {}, p, nil, k[#k] })
end
end
return store
end
local function build_fake_keys(keys)
local res = {}
for _, keygroup in ipairs({
{ description = "Undo last key", group = "Action", keyname = "stepback" },
{ description = "Undo sequence", group = "Action", keyname = "close" },
{ description = "Show hotkeys helper", group = "Action", keyname = "help" },
})
do
for _, k in ipairs(keys[keygroup.keyname]) do
table.insert(res, {
{}, k, nil,
{ description = keygroup.description, group = keygroup.group }
})
end
end
return res
end
-- Main widget
-----------------------------------------------------------------------------------------------------------------------
-- Initialize keychain widget
--------------------------------------------------------------------------------
function keychain:init(style)
-- Init vars
------------------------------------------------------------
self.active = nil
self.parents = {}
self.sequence = ""
local style = redflat.util.table.merge(default_style(), style or {})
self.style = style
-- Wibox
------------------------------------------------------------
self.wibox = wibox({
ontop = true,
bg = style.color.wibox,
border_width = style.border_width,
border_color = style.color.border
})
self.wibox:geometry(style.geometry)
self.label = wibox.widget.textbox()
self.label:set_align("center")
self.wibox:set_widget(self.label)
self.label:set_font(style.font)
-- Keygrabber
------------------------------------------------------------
self.keygrabber = function(mod, key, event)
if event == "press" then return false end
-- dirty fix for first key release
if self.actkey == key and #mod == 0 then self.actkey = nil; return end
if awful.util.table.hasitem(self.service.close, key) then self:hide()
elseif awful.util.table.hasitem(self.service.stepback, key) then self:undo()
elseif awful.util.table.hasitem(self.service.help, key) then redtip:show()
else
for _, item in ipairs(self.active[3]) do
if redutil.key.match_grabber(item, mod, key) then self:activate(item); return end
end
end
end
end
-- Set current key item
--------------------------------------------------------------------------------
function keychain:activate(item, keytip)
if not self.wibox then self:init() end
self.actkey = keytip and item[2]
if type(item[3]) == "function" then
item[3]()
self:hide()
else
if not self.active then
redutil.placement.centered(self.wibox, nil, mouse.screen.workarea)
self.wibox.visible = true
awful.keygrabber.run(self.keygrabber)
else
self.parents[#self.parents + 1] = self.active
end
self.active = item
local label = build_label(self.active)
self.sequence = self.sequence == "" and label or self.sequence .. " " .. label
self.label:set_text(self.sequence)
end
-- build keys helper tip
if keytip then
if tip_cache[keytip] then
self.tip = tip_cache[keytip]
else
self.tip = awful.util.table.join(build_tip({}, item), build_fake_keys(self.service))
tip_cache[keytip] = self.tip
end
redtip:set_pack(keytip .. " keychain", self.tip, self.style.keytip.column, self.style.keytip.geometry)
end
end
-- Deactivate last key item
--------------------------------------------------------------------------------
function keychain:undo()
if #self.parents > 0 then
self.sequence = self.sequence:sub(1, - (#build_label(self.active) + 2))
self.label:set_text(self.sequence)
self.active = self.parents[#self.parents]
self.parents[#self.parents] = nil
else
self:hide()
end
end
-- Hide widget
--------------------------------------------------------------------------------
function keychain:hide()
self.wibox.visible = false
awful.keygrabber.stop(self.keygrabber)
self.active = nil
self.parents = {}
self.sequence = ""
redtip:remove_pack()
end
-- End
-----------------------------------------------------------------------------------------------------------------------
return keychain