--[[ Licensed under GNU General Public License v2 * (c) 2017, Simon Désaulniers * (c) 2017, Uli Schlachter * (c) 2017, Jeferson Siqueira --]] -- Menu iterator with Naughty notifications -- lain.util.menu_iterator local naughty = require('naughty') local helpers = require('lain.helpers') local atable = require('awful.util').table local assert = assert local pairs = pairs local tconcat = table.concat local unpack = unpack or table.unpack -- lua 5.1 retro-compatibility local state = { cid = nil } local function naughty_destroy_callback(reason) local closed = naughty.notificationClosedReason if reason == closed.expired or reason == closed.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 menu. -- After the timeout, callbacks associated to the last visited choice are -- executed. Inputs: -- * menu: a list of {label, {callbacks}} pairs -- * timeout: time to wait before confirming the menu selection -- * icon: icon to display in the notification of the chosen 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 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 first argument of `iterate` function and -- suitable for the following cases: -- * all possible choices individually (partition of singletons); -- * all possible subsets of the set of choices (powerset). -- -- Inputs: -- * args: an array containing the following members: -- * choices: Array of choices (string) on which the menu will be -- generated. -- * name: Displayed name of the menu (in the form "name: choices"). -- * selected_cb: Callback to execute for each selected choice. Takes -- the choice as a string argument. Can be `nil` (no action -- to execute). -- * rejected_cb: Callback to execute for each rejected choice (possible -- choices which are not selected). Takes the choice as a -- string argument. Can be `nil` (no action to execute). -- * extra_choices: An array of extra { choice_str, callback_fun } pairs to be -- added to the menu. Each callback_fun can be `nil`. -- * combination: The combination of choices to generate. Possible values: -- "powerset" and "single" (default). -- Output: -- * m: menu to be iterated over. 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 = atable.join(ch_combinations, { { c[1] } }) end local m = {} -- the menu for _, c in pairs(ch_combinations) do if #c > 0 then local cbs = {} -- selected choices for _, ch in pairs(c) do if atable.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 atable.hasitem(c, ch) and atable.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 example) for _, x in pairs(extra_choices) do if x[1] == c[1] then cbs[#cbs + 1] = x[2] end end m[#m + 1] = { name .. ': ' .. tconcat(c, ' + '), cbs } end end return m end return { iterate = iterate, menu = menu }