├── README.md
└── lib
└── dmenu.lua
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Native dmenu for the Awesome window manager
3 | ===========================================
4 |
5 | The dmenu tool that comes with Linux is great, but using in within the Awesome window manager is cumbersome.
6 | This is where _dmenu.lua_ comes handy. It does 99% of what dmenu does, but it plays well with Awesome's rc.lua. In other words, you can select an item from a predefined list of items, then spawn a process or run a callback function.
7 |
8 | Usage
9 | -----
10 |
11 | In rc.lua:
12 |
13 | local dmenu = require("lib/dmenu")
14 |
15 | ...
16 |
17 | mydmenu = dmenu({
18 | chromium = "chromium",
19 | vifm = "vifm",
20 | vim = terminal .. " -e vim",
21 | urxvt = function()
22 | local matcher = function (c)
23 | return awful.rules.match(c, {class = 'URxvt'})
24 | end
25 | awful.client.run_or_raise(exec, matcher)
26 | end
27 | })
28 |
29 | ...
30 |
31 | mywibox:set_widget(mydmenu.textbox)
32 |
33 | ...
34 |
35 | -- Execute
36 | awful.key({ modkey }, "r", function ()
37 | mydmenu:show()
38 | end)
39 |
40 | Keys
41 | ----
42 |
43 | dmenu.lua handles the same keyboard shortcuts as the Linux dmenu. Once activated, it will show a list of the table keys. In the above example that would be _chromium | vifm | vim | urxvt_.
44 | Left and right keys will move the selection. Typing will reduce the list to the items matching the typed string. Return will execute and Escape will exit.
45 |
46 | TODO
47 | ----
48 |
49 | * If the list of items is too long, it gets cut off and navigation stops at the last visible item (as opposed to shifting the items by one and showing the next one).
50 | * A custom key handler could be installed in case someone requires more complicated keyboard shortcuts.
51 |
52 |
--------------------------------------------------------------------------------
/lib/dmenu.lua:
--------------------------------------------------------------------------------
1 | ---------------------------------------------------------------------------
2 | -- Native dmenu for the Awesome window manager.
3 | -- Written by Stefan G. (naturalist@github)
4 | -- License: BSD
5 | ---------------------------------------------------------------------------
6 |
7 | local awful = require("awful")
8 | local textbox = require("wibox.widget.textbox")
9 | local keygrabber = require("awful.keygrabber")
10 | local beautiful = require("beautiful")
11 |
12 | local index = 0
13 | local typed = ""
14 |
15 | local _dmenu = {
16 | nor_bg_color = beautiful.bg_normal or "black",
17 | nor_fg_color = beautiful.fg_normal or "white",
18 | sel_bg_color = beautiful.bg_focus or "white",
19 | sel_fg_color = beautiful.fg_focus or "black",
20 | separator = " | ",
21 | prompt = "Run:"
22 | }
23 | local dmenu = {}
24 | local mt = {}
25 | setmetatable(dmenu, mt)
26 |
27 | local function get_names(str)
28 | local names = {}
29 | for k, _ in pairs(dmenu.items) do
30 | if str == nil or str == "" then
31 | table.insert(names, k)
32 | else
33 | if string.find(k, str) then
34 | table.insert(names, k)
35 | end
36 | end
37 | end
38 | table.sort(names)
39 | return names
40 | end
41 |
42 | local function draw()
43 | local formatted = {}
44 | for i, k in ipairs(dmenu.names) do
45 | local val = k
46 | if index % #dmenu.names + 1 == i then
47 | val = "" ..
49 | k .. ""
50 | end
51 | table.insert(formatted, val)
52 | end
53 |
54 | local prompt = string.format(
55 | '%s',
56 | dmenu.sel_fg_color, dmenu.sel_bg_color, dmenu.prompt
57 | )
58 |
59 | local items = string.format(
60 | '%s',
61 | dmenu.nor_fg_color, dmenu.nor_bg_color,
62 | table.concat(formatted, dmenu.separator)
63 | )
64 |
65 | local markup = string.format(
66 | '%s (%s) %s',
67 | prompt, typed, items
68 | )
69 |
70 | dmenu.textbox:set_markup( markup )
71 | end
72 |
73 | function dmenu.new(items, args)
74 | local args = args or {}
75 | for k, _ in pairs(_dmenu) do
76 | if args[k] then _dmenu[k] = args[k] end
77 | end
78 | dmenu.textbox = textbox()
79 | if args.font then dmenu.textbox:set_font(args.font) end
80 | dmenu.items = items
81 | return dmenu
82 | end
83 |
84 | function dmenu:show()
85 | dmenu.names = get_names(typed)
86 | local grabber
87 | grabber = keygrabber.run(function(mod, key, event)
88 | if event == "release" then return end
89 |
90 | local _typed = typed
91 |
92 | if key == "Right" then
93 | index = index + 1
94 | elseif key == "Left" then
95 | index = index - 1
96 | elseif #key == 1 and (string.lower(key) >= "a" or string.lower(key) <= "z") then
97 | typed = typed .. key
98 | elseif key == "BackSpace" then
99 | if typed ~= "" then
100 | typed = string.sub(typed, 1, #typed - 1)
101 | end
102 | elseif key == "Return" or key == "Escape" then
103 | if key == "Return" then
104 | local callback = dmenu.items[self.names[index + 1]]
105 | if type(callback) == "function" then
106 | callback()
107 | elseif type(callback) == "string" then
108 | awful.util.spawn(callback)
109 | end
110 | end
111 | keygrabber.stop(grabber)
112 | self:hide()
113 | return
114 | end
115 |
116 | if typed ~= _typed then
117 | dmenu.names = get_names(typed)
118 | end
119 |
120 | draw()
121 | end)
122 | end
123 |
124 | function dmenu:hide()
125 | self.textbox:set_markup("")
126 | index = 0
127 | typed = ""
128 | end
129 |
130 | function mt:__call(...)
131 | return dmenu.new(...)
132 | end
133 |
134 | function mt.__index(t, k)
135 | return _dmenu[k]
136 | end
137 |
138 | function mt.__newindex(t, k, v)
139 | _dmenu[k] = v
140 | if k == "names" then
141 | index = 0
142 | draw()
143 | end
144 | end
145 |
146 | return dmenu
147 |
--------------------------------------------------------------------------------