]> 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:

widget.calendar: some cleaning; #379
[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 with Naughty notifications
11 -- lain.util.menu_iterator
12
13 local naughty = require("naughty")
14 local util    = require("lain.util")
15 local atable  = require("awful.util").table
16 local assert  = assert
17 local pairs   = pairs
18 local tconcat = table.concat
19 local unpack  = unpack
20
21 local state = { cid = nil }
22
23 local function naughty_destroy_callback(reason)
24     local closed = naughty.notificationClosedReason
25     if reason == closed.expired or reason == closed.dismissedByUser then
26         local actions = state.index and state.menu[state.index - 1][2]
27         if actions then
28             for _,action in pairs(actions) do
29                 -- don't try to call nil callbacks
30                 if action then action() end
31             end
32             state.index = nil
33         end
34     end
35 end
36
37 -- Iterates over a menu.
38 -- After the timeout, callbacks associated to the last visited choice are
39 -- executed. Inputs:
40 -- * menu:    a list of {label, {callbacks}} pairs
41 -- * timeout: time to wait before confirming the menu selection
42 -- * icon:    icon to display in the notification of the chosen label
43 local function iterate(menu, timeout, icon)
44     local timeout = timeout or 4 -- default timeout for each menu entry
45     local icon    = icon or nil  -- icon to display on the menu
46
47     -- Build the list of choices
48     if not state.index then
49         state.menu = menu
50         state.index = 1
51     end
52
53     -- Select one and display the appropriate notification
54     local label
55     local next = state.menu[state.index]
56     state.index = state.index + 1
57
58     if not next then
59         label = "Cancel"
60         state.index = nil
61     else
62         label, _ = unpack(next)
63     end
64
65     state.cid = naughty.notify({
66         text        = label,
67         icon        = icon,
68         timeout     = timeout,
69         screen      = mouse.screen,
70         replaces_id = state.cid,
71         destroy     = naughty_destroy_callback
72     }).id
73 end
74
75 -- Generates a menu compatible with the first argument of `iterate` function and
76 -- suitable for the following cases:
77 -- * all possible choices individually (partition of singletons);
78 -- * all possible subsets of the set of choices (powerset).
79 --
80 -- Inputs:
81 -- * args: an array containing the following members:
82 --   * choices:       Array of choices (string) on which the menu will be
83 --                    generated.
84 --   * name:          Displayed name of the menu (in the form "name: choices").
85 --   * selected_cb:   Callback to execute for each selected choice. Takes
86 --                    the choice as a string argument. Can be `nil` (no action
87 --                    to execute).
88 --   * rejected_cb:   Callback to execute for each rejected choice (possible
89 --                    choices which are not selected). Takes the choice as a
90 --                    string argument. Can be `nil` (no action to execute).
91 --   * extra_choices: An array of extra { choice_str, callback_fun } pairs to be
92 --                    added to the menu. Each callback_fun can be `nil`.
93 --   * combination:   The combination of choices to generate. Possible values:
94 --                    "powerset" and "single" (default).
95 -- Output:
96 -- * m: menu to be iterated over.
97 local function menu(args)
98     local choices       = assert(args.choices or args[1])
99     local name          = assert(args.name or args[2])
100     local selected_cb   = args.selected_cb
101     local rejected_cb   = args.rejected_cb
102     local extra_choices = args.extra_choices or {}
103
104     local ch_combinations = args.combination == "powerset" and helpers.powerset(choices) or helpers.trivial_partition_set(choices)
105
106     for _,c in pairs(extra_choices) do
107         ch_combinations = atable.join(ch_combinations, {{c[1]}})
108     end
109
110     local m = {} -- the menu
111
112     for _,c in pairs(ch_combinations) do
113         if #c > 0 then
114             local cbs = {}
115
116             -- selected choices
117             for _,ch in pairs(c) do
118                 if atable.hasitem(choices, ch) then
119                     cbs[#cbs + 1] = selected_cb and function() selected_cb(ch) end or nil
120                 end
121             end
122
123             -- rejected choices
124             for _,ch in pairs(choices) do
125                 if not atable.hasitem(c, ch) and atable.hasitem(choices, ch) then
126                     cbs[#cbs + 1] = rejected_cb and function() rejected_cb(ch) end or nil
127                 end
128             end
129
130             -- add user extra choices (like the choice "None" for example)
131             for _,x in pairs(extra_choices) do
132                 if x[1] == c[1] then
133                     cbs[#cbs + 1] = x[2]
134                 end
135             end
136
137             m[#m + 1] = { name .. ": " .. tconcat(c, " + "), cbs }
138         end
139     end
140
141     return m
142 end
143
144 return { iterate = iterate, menu = menu }