From: Luca CPZ <1777696+lcpz@users.noreply.github.com> Date: Sat, 17 Feb 2018 11:19:53 +0000 (+0100) Subject: Merge pull request #377 from sim590/menu-iterator X-Git-Url: https://git.madduck.net/etc/awesome.git/commitdiff_plain/af3427c7dfcf991c56a099ac1450e54db867ae51?hp=3136a45015a6662f191c4c8aea6fee19aff76169 Merge pull request #377 from sim590/menu-iterator menu_iterator: naughty.notify based menu utility --- diff --git a/helpers.lua b/helpers.lua index 6073ddd..313579f 100644 --- a/helpers.lua +++ b/helpers.lua @@ -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 diff --git a/util/menu_iterator.lua b/util/menu_iterator.lua new file mode 100644 index 0000000..913482a --- /dev/null +++ b/util/menu_iterator.lua @@ -0,0 +1,139 @@ +--[[ + + Licensed under GNU General Public License v2 + * (c) 2017, Simon Désaulniers + * (c) 2017, Uli Schlachter + * (c) 2017, Jeferson Siqueira + +--]] + +-- 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 +}