]> git.madduck.net Git - etc/awesome.git/blob - util/menu_iterator.lua

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

menu_iterator: generic menu creator function
[etc/awesome.git] / util / menu_iterator.lua
1 --[[
2
3      Licensed under GNU General Public License v2
4       * (c) 2017, Simon Désaulniers <sim.desaulniers@gmail.com>
5       * (c) 2017, Uli Schlachter
6       * (c) 2017, Jeferson Siqueira <jefersonlsiq@gmail.com>
7
8 --]]
9
10 -- Menu iterator using naughty.notify
11
12 local naughty = require("naughty")
13 local util = require("lain.util")
14
15 local state = { cid = nil }
16
17 local function naughty_destroy_callback(reason)
18     if reason == naughty.notificationClosedReason.expired or
19         reason == naughty.notificationClosedReason.dismissedByUser then
20         local actions = state.index and state.menu[state.index - 1][2]
21         if actions then
22             for _,action in pairs(actions) do
23                 -- don't try to call nil callbacks
24                 if action then action() end
25             end
26             state.index = nil
27         end
28     end
29 end
30
31 -- Iterates over a list of pairs {label, {callbacks}}. After timeout, the last
32 -- visited choice associated callbacks are executed.
33 -- * menu:    a list of pairs {label, {callbacks}
34 -- * timeout: time to wait before confirming menu selection
35 -- * icon:    icon to display left to the choiced label
36 local function iterate(menu, timeout, icon)
37     timeout = timeout or 4 -- default timeout for each menu entry
38     icon    = icon or nil  -- icon to display on the menu
39
40     -- Build the list of choices
41     if not state.index then
42         state.menu = menu
43         state.index = 1
44     end
45
46     -- Select one and display the appropriate notification
47     local label, action
48     local next  = state.menu[state.index]
49     state.index = state.index + 1
50
51     if not next then
52         label = "Cancel"
53         state.index = nil
54     else
55         label, _ = unpack(next)
56     end
57     state.cid = naughty.notify({
58         text = label,
59         icon = icon,
60         timeout = timeout,
61         screen = mouse.screen,
62         replaces_id = state.cid,
63         destroy = naughty_destroy_callback
64     }).id
65 end
66
67 -- Generates a menu compatible with the iterate function argument and suitable
68 -- for the following cases:
69 -- * all possible choices individually.
70 -- * all possible choices are all the possible subsets of the set of individual
71 --   choices (the powerset)
72 --
73 -- The following describes the function arguments:
74 -- * args: an array containing the following members:
75 --   * choices:       the list of choices from which to generate the menu
76 --   * name:          the displayed name of the menu (in the form "name: choices")
77 --   * selected_cb:   the callback to execute for each selected choice. Takes
78 --                    the choice as a string argument. The function
79 --                    menu_iterator.naughty_destroy_callback will handle nil
80 --                    callbacks. It is then fine to pass nil callbacks.
81 --   * rejected_cb:   the callback to execute for each rejected choice (in the
82 --                    set of possible choices, but not selected). Takes the
83 --                    choice as a string argument. The function
84 --                    menu_iterator.naughty_destroy_callback will handle nil
85 --                    callbacks. It is then fine to pass nil callbacks.
86 --   * extra_choices: an array of pairs { choice_text, cb } for extra choices to
87 --                    be added to the menu. The function
88 --                    menu_iterator.naughty_destroy_callback will handle nil
89 --                    callbacks. It is then fine to pass nil callbacks.
90 --   * combination:   the combination of choice to generate. Possible choices
91 --                    are "powerset" and "single" (the default).
92 local function menu(args)
93     local choices     = assert(args.choices or args[1])
94     local name        = assert(args.name or args[2])
95     local selected_cb = args.selected_cb
96     local rejected_cb = args.rejected_cb
97     local extra_choices = args.extra_choices or {}
98
99     local ch_combinations = args.combination == "powerset" and helpers.powerset(choices) or helpers.trivial_partition_set(choices)
100     for _,c in pairs(extra_choices) do
101         ch_combinations = awful.util.table.join(ch_combinations, {{c[1]}})
102     end
103
104     local m = {}
105     for _,c in pairs(ch_combinations) do
106         if #c > 0 then
107             local cbs = {}
108             -- selected choices
109             for _,ch in pairs(c) do
110                 if awful.util.table.hasitem(choices, ch) then
111                     cbs[#cbs + 1] = selected_cb and function() selected_cb(ch) end or nil
112                 end
113             end
114
115             -- rejected choices
116             for _,ch in pairs(choices) do
117                 if not awful.util.table.hasitem(c, ch) and awful.util.table.hasitem(choices, ch) then
118                     cbs[#cbs + 1] = rejected_cb and function() rejected_cb(ch) end or nil
119                 end
120             end
121
122             -- add user extra choices (like the choice "None" for e.g.)
123             for _,x in pairs(extra_choices) do
124                 if x[1] == c[1] then
125                     cbs[#cbs + 1] = x[2]
126                 end
127             end
128
129             m[#m + 1] = { name .. ": " .. table.concat(c, " + "), cbs }
130         end
131     end
132
133     return m
134 end
135
136 return {
137     iterate = iterate,
138     menu    = menu
139 }