Repository navigation
Expand file tree
/
Copy pathmenu.lua.in
More file actions
370 lines (315 loc) · 11.4 KB
/
Copy pathmenu.lua.in
File metadata and controls
370 lines (315 loc) · 11.4 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
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
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
---------------------------------------------------------------------------
-- @author Damien Leone <damien.leone@gmail.com>
-- @author Julien Danjou <julien@danjou.info>
-- @copyright 2008 Damien Leone, Julien Danjou
-- @release @AWESOME_VERSION@
---------------------------------------------------------------------------
-- Grab environment we need
local pairs = pairs
local table = table
local type = type
local wibox = wibox
local image = image
local widget = widget
local button = require("awful.button")
local capi =
{
screen = screen,
mouse = mouse,
client = client,
keygrabber = keygrabber
}
local util = require("awful.util")
local tags = require("awful.tag")
local awbeautiful = require("beautiful")
local tonumber = tonumber
--- Menu module for awful
module("awful.menu")
local cur_menu
local menu_keys = {}
local function load_theme(custom)
local theme = {}
local beautiful
beautiful = awbeautiful.get()
theme.fg_focus = custom.fg_focus or beautiful.menu_fg_focus or beautiful.fg_focus
theme.bg_focus = custom.bg_focus or beautiful.menu_bg_focus or beautiful.bg_focus
theme.fg_normal = custom.fg_normal or beautiful.menu_fg_normal or beautiful.fg_normal
theme.bg_normal = custom.bg_normal or beautiful.menu_bg_normal or beautiful.bg_normal
theme.submenu_icon = custom.submenu_icon or beautiful.menu_submenu_icon
theme.menu_height = custom.height or beautiful.menu_height or 16
theme.menu_width = custom.width or beautiful.menu_width or 100
theme.border = custom.border_color or beautiful.menu_border_color or beautiful.border_normal
theme.border_width = custom.border_width or beautiful.menu_border_width or beautiful.border_width
return theme
end
local function item_leave(menu, num)
if num > 0 then
menu.items[num].wibox.fg = menu.theme.fg_normal
menu.items[num].wibox.bg = menu.theme.bg_normal
end
end
--- Hide a menu popup.
-- @param menu The menu to hide.
function hide(menu)
-- Remove items from screen
for i = 1, #menu.items do
item_leave(menu, i)
menu.items[i].wibox.screen = nil
end
if menu.active_child then
menu.active_child:hide()
menu.active_child = nil
end
menu.sel = nil
if cur_menu == menu then
cur_menu = cur_menu.parent
end
if not cur_menu and menu.keygrabber then
capi.keygrabber.stop()
end
end
-- Get the elder parent so for example when you kill
-- it, it will destroy the whole family.
local function get_parents(menu)
if menu.parent then
return get_parents(menu.parent)
end
return menu
end
local function exec(menu, num, mouse_event)
local cmd = menu.items[num].cmd
if type(cmd) == "table" then
if #cmd == 0 then
return
end
if not menu.child[num] then
menu.child[num] = new({ items = cmd }, menu, num)
end
if menu.active_child then
menu.active_child:hide()
menu.active_child = nil
end
menu.active_child = menu.child[num]
menu.active_child:show()
elseif type(cmd) == "string" then
get_parents(menu):hide()
util.spawn(cmd)
elseif type(cmd) == "function" then
get_parents(menu):hide()
cmd()
end
end
local function item_enter(menu, num, mouse_event)
if menu.sel == num then
return
elseif menu.sel then
item_leave(menu, menu.sel)
end
menu.items[num].wibox.fg = menu.theme.fg_focus
menu.items[num].wibox.bg = menu.theme.bg_focus
menu.sel = num
cur_menu = menu
if menu.auto_expand and mouse_event then
if menu.active_child then
menu.active_child:hide()
menu.active_child = nil
end
if type(menu.items[num].cmd) == "table" then
exec(menu, num)
end
end
end
local function grabber(mod, key, event)
if event == "release" then
return true
end
local sel = cur_menu.sel or 0
if key == menu_keys.up then
local sel_new = sel-1 < 1 and #cur_menu.items or sel-1
item_enter(cur_menu, sel_new)
elseif key == menu_keys.down then
local sel_new = sel+1 > #cur_menu.items and 1 or sel+1
item_enter(cur_menu, sel_new)
elseif sel > 0 and key == menu_keys.exec then
exec(cur_menu, sel)
elseif key == menu_keys.back then
cur_menu:hide()
elseif key == menu_keys.close then
get_parents(cur_menu):hide()
end
return true
end
local function add_item(data, num, item_info)
local item = wibox({
position = "floating",
fg = data.theme.fg_normal,
bg = data.theme.bg_normal,
border_color = data.theme.border,
border_width = data.theme.border_width
})
-- Create bindings
local bindings = util.table.join(
button({}, 1, function () item_enter(data, num); exec(data, num) end),
button({}, 3, function () hide(data) end)
)
-- Create the item label widget
local label = widget({ type = "textbox", align = "left" })
label.text = item_info[1]
label:margin({ left = data.h + 2 })
-- Set icon if needed
if item_info[3] then
local icon = type(item_info[3]) == "string" and image(item_info[3]) or item_info[3]
if icon.width > tonumber(data.h) or icon.height > tonumber(data.h) then
local width, height
if ((data.h/icon.height) * icon.width) > tonumber(data.h) then
width, height = data.h, (tonumber(data.h) / icon.width) * icon.height
else
width, height = (tonumber(data.h) / icon.height) * icon.width, data.h
end
icon = icon:crop_and_scale(0, 0, icon.width, icon.height, width, height)
end
label.bg_image = icon
end
item:buttons(bindings)
function label.mouse_enter() item_enter(data, num, true) end
function item.mouse_enter() item_enter(data, num, true) end
-- Create the submenu icon widget
local submenu
if type(item_info[2]) == "table" then
submenu = widget({ type = "imagebox", align = "right" })
submenu.image = data.theme.submenu_icon and image(data.theme.submenu_icon)
submenu:buttons(bindings)
function submenu.mouse_enter() item_enter(data, num, true) end
end
-- Add widgets to the wibox
item.widgets = { label, submenu }
item.ontop = true
return { wibox = item, cmd = item_info[2] }
end
--- Build a popup menu with running clients and shows it.
-- @param menu Menu table, see new() function for more informations
-- @return The menu.
function clients(menu)
local cls = capi.client.get()
local cls_t = {}
for k, c in pairs(cls) do
cls_t[#cls_t + 1] = { util.escape(c.name) or "",
function ()
if not c:isvisible() then
tags.viewmore(c:tags(), c.screen)
end
capi.client.focus = c
end,
c.icon }
end
if not menu then
menu = {}
end
menu.items = cls_t
local m = new(menu)
m:show()
return m
end
local function set_coords(menu, screen_idx)
local s_geometry = capi.screen[screen_idx].workarea
local screen_w = s_geometry.x + s_geometry.width
local screen_h = s_geometry.y + s_geometry.height
local i_h = menu.h - menu.theme.border_width
local m_h = (i_h * #menu.items) + menu.theme.border_width
if menu.parent then
menu.w = menu.parent.w
menu.h = menu.parent.h
local p_w = i_h * (menu.num - 1)
local m_w = menu.w - menu.theme.border_width
menu.y = menu.parent.y + p_w + m_h > screen_h and screen_h - m_h or menu.parent.y + p_w
menu.x = menu.parent.x + m_w*2 > screen_w and menu.parent.x - m_w or menu.parent.x + m_w
else
local m_coords = capi.mouse.coords()
local m_w = menu.w
menu.y = m_coords.y+1 < s_geometry.y and s_geometry.y or m_coords.y+1
menu.x = m_coords.x+1 < s_geometry.x and s_geometry.x or m_coords.x+1
menu.y = menu.y + m_h > screen_h and screen_h - m_h or menu.y
menu.x = menu.x + m_w > screen_w and screen_w - m_w or menu.x
end
end
--- Show a menu.
-- @param menu The menu to show.
-- @param keygrabber A boolean enabling or not the keyboard navigation.
function show(menu, keygrabber)
local screen_index = capi.mouse.screen
set_coords(menu, screen_index)
for num, item in pairs(menu.items) do
local wibox = item.wibox
wibox:geometry({
width = menu.w,
height = menu.h,
x = menu.x,
y = menu.y + (num - 1) * (menu.h - menu.theme.border_width)
})
wibox.screen = screen_index
end
if menu.parent then
menu.keygrabber = menu.parent.keygrabber
elseif keygrabber ~= nil then
menu.keygrabber = keygrabber
else
menu.keygrabber = false
end
if not cur_menu and menu.keygrabber then
capi.keygrabber.run(grabber)
end
cur_menu = menu
end
--- Toggle menu visibility.
-- @param menu The menu to show if it's hidden, or to hide if it's shown.
-- @param keygrabber A boolean enabling or not the keyboard navigation.
function toggle(menu, keygrabber)
if menu.items[1] and menu.items[1].wibox.screen then
hide(menu)
else
show(menu, keygrabber)
end
end
--- Set key bindings for menu navigation.
-- @param keys Table containing the following keys: up, down, exec, back, close. If a key is missing the default key binding will be used, defaults are respectively: "Up", "Down", "Return", "Left", "Escape".
function setkeys(keys)
menu_keys.up = keys and keys.up or "Up"
menu_keys.down = keys and keys.down or "Down"
menu_keys.exec = keys and keys.exec or "Return"
menu_keys.back = keys and keys.back or "Left"
menu_keys.close = keys and keys.close or "Escape"
end
--- Open a menu popup.
-- @param menu Table containing the menu informations. Key items: Table containing the displayed items, each element is a tab containing: item name, tiggered action, submenu table or function, item icon (optional). Keys [fg|bg]_[focus|normal], border, border_width, submenu_icon, height and width override the default display for your menu, each of them are optional. Key auto_expand controls the submenu auto expand behaviour by setting it to true (default) or false.
-- @param parent Specify the parent menu if we want to open a submenu, this value should never be set by the user.
-- @param num Specify the parent's clicked item number if we want to open a submenu, this value should never be set by the user.
function new(menu, parent, num)
-- Create a table to store our menu informations
local data = {}
if not menu_keys.up then
setkeys(menu.keys)
end
data.items = {}
data.num = num or 1
data.theme = parent and parent.theme or load_theme(menu)
data.parent = parent
data.child = {}
if parent then
data.auto_expand = parent.auto_expand
elseif menu.auto_expand ~= nil then
data.auto_expand = menu.auto_expand
else
data.auto_expand = true
end
data.h = parent and parent.h or data.theme.menu_height
data.w = parent and parent.w or data.theme.menu_width
-- Create items
for k, v in pairs(menu.items) do
table.insert(data.items, add_item(data, k, v))
end
-- Set methods
data.hide = hide
data.show = show
data.toggle = toggle
return data
end