Merge pull request #377 from sim590/menu-iterator

menu_iterator: naughty.notify based menu utility
This commit is contained in:
Luca CPZ 2018-02-17 12:19:53 +01:00 committed by GitHub
commit af3427c7df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 162 additions and 0 deletions

View File

@ -179,6 +179,29 @@ function helpers.spairs(t)
end
end
-- create trivial partition of a set. The trivial partition set is the simplest
-- partition of a set. For e.g., the trivial partition set of {a, b, c}, is
-- simply {{a}, {b}, {c}}.
function helpers.trivial_partition_set(set)
local ss = {}
for _,e in pairs(set) do
ss[#ss+1] = {e}
end
return ss
end
-- creates the powerset of a given set
function helpers.powerset(s)
if not s then return {} end
local t = {{}}
for i = 1, #s do
for j = 1, #t do
t[#t+1] = {s[i],unpack(t[j])}
end
end
return t
end
-- }}}
return helpers

139
util/menu_iterator.lua Normal file
View File

@ -0,0 +1,139 @@
--[[
Licensed under GNU General Public License v2
* (c) 2017, Simon Désaulniers <sim.desaulniers@gmail.com>
* (c) 2017, Uli Schlachter
* (c) 2017, Jeferson Siqueira <jefersonlsiq@gmail.com>
--]]
-- Menu iterator using naughty.notify
local naughty = require("naughty")
local util = require("lain.util")
local state = { cid = nil }
local function naughty_destroy_callback(reason)
if reason == naughty.notificationClosedReason.expired or
reason == naughty.notificationClosedReason.dismissedByUser then
local actions = state.index and state.menu[state.index - 1][2]
if actions then
for _,action in pairs(actions) do
-- don't try to call nil callbacks
if action then action() end
end
state.index = nil
end
end
end
-- Iterates over a list of pairs {label, {callbacks}}. After timeout, the last
-- visited choice associated callbacks are executed.
-- * menu: a list of pairs {label, {callbacks}
-- * timeout: time to wait before confirming menu selection
-- * icon: icon to display left to the choiced label
local function iterate(menu, timeout, icon)
timeout = timeout or 4 -- default timeout for each menu entry
icon = icon or nil -- icon to display on the menu
-- Build the list of choices
if not state.index then
state.menu = menu
state.index = 1
end
-- Select one and display the appropriate notification
local label, action
local next = state.menu[state.index]
state.index = state.index + 1
if not next then
label = "Cancel"
state.index = nil
else
label, _ = unpack(next)
end
state.cid = naughty.notify({
text = label,
icon = icon,
timeout = timeout,
screen = mouse.screen,
replaces_id = state.cid,
destroy = naughty_destroy_callback
}).id
end
-- Generates a menu compatible with the iterate function argument and suitable
-- for the following cases:
-- * all possible choices individually.
-- * all possible choices are all the possible subsets of the set of individual
-- choices (the powerset)
--
-- The following describes the function arguments:
-- * args: an array containing the following members:
-- * choices: the list of choices from which to generate the menu
-- * name: the displayed name of the menu (in the form "name: choices")
-- * selected_cb: the callback to execute for each selected choice. Takes
-- the choice as a string argument. The function
-- menu_iterator.naughty_destroy_callback will handle nil
-- callbacks. It is then fine to pass nil callbacks.
-- * rejected_cb: the callback to execute for each rejected choice (in the
-- set of possible choices, but not selected). Takes the
-- choice as a string argument. The function
-- menu_iterator.naughty_destroy_callback will handle nil
-- callbacks. It is then fine to pass nil callbacks.
-- * extra_choices: an array of pairs { choice_text, cb } for extra choices to
-- be added to the menu. The function
-- menu_iterator.naughty_destroy_callback will handle nil
-- callbacks. It is then fine to pass nil callbacks.
-- * combination: the combination of choice to generate. Possible choices
-- are "powerset" and "single" (the default).
local function menu(args)
local choices = assert(args.choices or args[1])
local name = assert(args.name or args[2])
local selected_cb = args.selected_cb
local rejected_cb = args.rejected_cb
local extra_choices = args.extra_choices or {}
local ch_combinations = args.combination == "powerset" and helpers.powerset(choices) or helpers.trivial_partition_set(choices)
for _,c in pairs(extra_choices) do
ch_combinations = awful.util.table.join(ch_combinations, {{c[1]}})
end
local m = {}
for _,c in pairs(ch_combinations) do
if #c > 0 then
local cbs = {}
-- selected choices
for _,ch in pairs(c) do
if awful.util.table.hasitem(choices, ch) then
cbs[#cbs + 1] = selected_cb and function() selected_cb(ch) end or nil
end
end
-- rejected choices
for _,ch in pairs(choices) do
if not awful.util.table.hasitem(c, ch) and awful.util.table.hasitem(choices, ch) then
cbs[#cbs + 1] = rejected_cb and function() rejected_cb(ch) end or nil
end
end
-- add user extra choices (like the choice "None" for e.g.)
for _,x in pairs(extra_choices) do
if x[1] == c[1] then
cbs[#cbs + 1] = x[2]
end
end
m[#m + 1] = { name .. ": " .. table.concat(c, " + "), cbs }
end
end
return m
end
return {
iterate = iterate,
menu = menu
}