From b94e701ff210085f73dbcd6f85f41914f4d696f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20D=C3=A9saulniers?= Date: Sat, 30 Dec 2017 07:05:41 -0500 Subject: [PATCH 1/3] menu_iterator: naughty.notify based menu utility --- util/menu_iterator.lua | 66 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 util/menu_iterator.lua diff --git a/util/menu_iterator.lua b/util/menu_iterator.lua new file mode 100644 index 0000000..071f16f --- /dev/null +++ b/util/menu_iterator.lua @@ -0,0 +1,66 @@ +--[[ + + 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 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 + +return { iterate = iterate } From 2bb4741bb4c5a97ebadf1eceafe0d5e2dae1fd29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20D=C3=A9saulniers?= Date: Wed, 14 Feb 2018 17:00:36 -0500 Subject: [PATCH 2/3] helpers: powerset and trivial_partition_set - the powerset is the set of all combinations of a given 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}}. --- helpers.lua | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/helpers.lua b/helpers.lua index ab87f76..ce65576 100644 --- a/helpers.lua +++ b/helpers.lua @@ -170,6 +170,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 From 1d2f4a102ed4fe4c536a76d03b91c19001f48ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20D=C3=A9saulniers?= Date: Wed, 14 Feb 2018 17:04:06 -0500 Subject: [PATCH 3/3] menu_iterator: generic menu creator function A simple example usage is given over on the amh project~[1] [1]: https://github.com/sim590/amh/blob/dcf101c8ef74921de513c0b1dee294606fc6befe/exec/mpv.lua#L32 --- util/menu_iterator.lua | 75 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/util/menu_iterator.lua b/util/menu_iterator.lua index 071f16f..913482a 100644 --- a/util/menu_iterator.lua +++ b/util/menu_iterator.lua @@ -10,6 +10,7 @@ -- Menu iterator using naughty.notify local naughty = require("naughty") +local util = require("lain.util") local state = { cid = nil } @@ -63,4 +64,76 @@ local function iterate(menu, timeout, icon) }).id end -return { iterate = iterate } +-- 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 +}