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

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