diff options
Diffstat (limited to 'apps/plugins/lua/include_lua/printsubmenu.lua')
-rw-r--r-- | apps/plugins/lua/include_lua/printsubmenu.lua | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/apps/plugins/lua/include_lua/printsubmenu.lua b/apps/plugins/lua/include_lua/printsubmenu.lua new file mode 100644 index 0000000000..3c61b5fda8 --- /dev/null +++ b/apps/plugins/lua/include_lua/printsubmenu.lua | |||
@@ -0,0 +1,263 @@ | |||
1 | --[[ | ||
2 | /*************************************************************************** | ||
3 | * __________ __ ___. | ||
4 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
5 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
6 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
7 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
8 | * \/ \/ \/ \/ \/ | ||
9 | * $Id$ | ||
10 | * | ||
11 | * Copyright (C) 2021 William Wilgus | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | ]] | ||
23 | if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end | ||
24 | menu_ctx = {} | ||
25 | local last_ctx = false | ||
26 | local p_settings | ||
27 | |||
28 | --[[root menu tables | ||
29 | expanded menus get inserted / removed | ||
30 | and context menus replace them but never overwritten | ||
31 | unless you want a new root menu | ||
32 | ]] | ||
33 | menu_t = {} | ||
34 | func_t = {} | ||
35 | |||
36 | require("printmenus") | ||
37 | |||
38 | local BUTTON = require("menubuttons") | ||
39 | local last_sel = 0 | ||
40 | |||
41 | local function display_context_menu() end -- forward declaration | ||
42 | |||
43 | local function dpad(x, xi, xir, y, yi, yir, timeout, overflow, selected) | ||
44 | local scroll_is_fixed = overflow ~= "manual" | ||
45 | if timeout == nil then timeout = -1 end | ||
46 | local cancel, select = 0, 0 | ||
47 | local x_chg, y_chg = 0, 0 | ||
48 | local button | ||
49 | while true do | ||
50 | button = rb.get_plugin_action(timeout) | ||
51 | |||
52 | if button == BUTTON.CANCEL then | ||
53 | cancel = 1 | ||
54 | break; | ||
55 | elseif button == BUTTON.EXIT then | ||
56 | cancel = 1 | ||
57 | break; | ||
58 | elseif button == BUTTON.SEL then | ||
59 | last_sel = 1 | ||
60 | timeout = timeout + 1 | ||
61 | elseif button == BUTTON.SELR then | ||
62 | last_sel = 2 | ||
63 | if display_context_menu(selected or -1) == true then | ||
64 | select = 1 | ||
65 | break; | ||
66 | end | ||
67 | timeout = timeout + 1 | ||
68 | elseif button == BUTTON.SELREL then | ||
69 | if last_sel == 1 then | ||
70 | select = 1 | ||
71 | end | ||
72 | last_sel = 0 | ||
73 | timeout = timeout + 1 | ||
74 | elseif button == BUTTON.LEFT then | ||
75 | x_chg = x_chg - xi | ||
76 | if scroll_is_fixed then | ||
77 | cancel = 1 | ||
78 | break; | ||
79 | end | ||
80 | elseif button == BUTTON.LEFTR then | ||
81 | x_chg = x_chg - xir | ||
82 | elseif button == BUTTON.RIGHT then | ||
83 | x_chg = x_chg + xi | ||
84 | if scroll_is_fixed then | ||
85 | select = 1 | ||
86 | timeout = timeout + 1 | ||
87 | end | ||
88 | elseif button == BUTTON.RIGHTR then | ||
89 | x_chg = x_chg + xir | ||
90 | elseif button == BUTTON.UP then | ||
91 | y_chg = y_chg + yi | ||
92 | elseif button == BUTTON.UPR then | ||
93 | y_chg = y_chg + yir | ||
94 | elseif button == BUTTON.DOWN then | ||
95 | y_chg = y_chg - yi | ||
96 | elseif button == BUTTON.DOWNR then | ||
97 | y_chg = y_chg - yir | ||
98 | elseif timeout >= 0 then--and rb.button_queue_count() < 1 then | ||
99 | break; | ||
100 | end | ||
101 | |||
102 | if x_chg ~= 0 or y_chg ~= 0 then | ||
103 | timeout = timeout + 1 | ||
104 | end | ||
105 | end | ||
106 | |||
107 | x = x + x_chg | ||
108 | y = y + y_chg | ||
109 | |||
110 | return cancel, select, x_chg, x, y_chg, y, 0xffff | ||
111 | end -- dpad | ||
112 | |||
113 | |||
114 | |||
115 | local function menu_set_defaults(settings, ctx) | ||
116 | p_settings = settings or {wrap = true, hasheader = true, justify = "left", dpad_fn = dpad} | ||
117 | menu_ctx = ctx or {collapse_fn = {}, lv = 0, update = false, start = 1} | ||
118 | end | ||
119 | |||
120 | local function ctx_loop() | ||
121 | local loopfn = ctx_loop | ||
122 | ctx_loop = function() end --prevent another execution | ||
123 | local mt, ft = get_menu() | ||
124 | local i | ||
125 | repeat | ||
126 | if menu_ctx.update then mt, ft = get_menu(); menu_ctx.update = false end | ||
127 | _, i = print_menu(mt, ft, menu_ctx.start, p_settings) | ||
128 | until menu_ctx.quit | ||
129 | |||
130 | ctx_loop = loopfn --restore for another run | ||
131 | end | ||
132 | |||
133 | function get_menu() | ||
134 | return menu_t, func_t | ||
135 | end | ||
136 | |||
137 | local function push_ctx(new_getmenu) | ||
138 | last_ctx = last_ctx or {} | ||
139 | table.insert(last_ctx, menu_ctx) | ||
140 | menu_ctx.getmenu = get_menu | ||
141 | menu_ctx.settings = p_settings | ||
142 | --menu_ctx is a new variable after this point | ||
143 | menu_set_defaults() | ||
144 | menu_ctx.update = true | ||
145 | if type(new_getmenu) == 'function' then | ||
146 | get_menu = new_getmenu | ||
147 | end | ||
148 | end | ||
149 | |||
150 | local function pop_ctx() | ||
151 | menu_ctx = table.remove(last_ctx) | ||
152 | if menu_ctx then | ||
153 | get_menu = menu_ctx.getmenu | ||
154 | p_settings = menu_ctx.settings | ||
155 | if menu_ctx.restorefn then | ||
156 | menu_ctx.restorefn(menu_t, func_t) | ||
157 | menu_ctx.restorefn = nil | ||
158 | end | ||
159 | menu_ctx.getmenu = nil | ||
160 | menu_ctx.settings = nil | ||
161 | menu_ctx.update = true | ||
162 | return true | ||
163 | end | ||
164 | end | ||
165 | |||
166 | local function display_context_menu_internal(sel) | ||
167 | |||
168 | if sel <= 0 or not menu_ctx.user_context_fn then return false end | ||
169 | local parent = get_parent() or 0 | ||
170 | local user_context_fn = menu_ctx.user_context_fn | ||
171 | local function display_context_menu(i, menu_t, func_t) | ||
172 | |||
173 | local function new_getmenu() | ||
174 | local mt, ft = user_context_fn(parent, i, menu_t, func_t) | ||
175 | ft[0] = pop_ctx --set back fn | ||
176 | return mt, ft | ||
177 | end | ||
178 | push_ctx(new_getmenu) | ||
179 | return true | ||
180 | end | ||
181 | |||
182 | local funct = func_t[sel] | ||
183 | local function restore_fn(mt, ft) | ||
184 | ft[sel] = funct | ||
185 | menu_ctx.start = sel - 1 | ||
186 | end | ||
187 | |||
188 | menu_ctx.restorefn = restore_fn | ||
189 | -- insert into the current fn table so it gets execd by the menu | ||
190 | func_t[sel] = display_context_menu | ||
191 | |||
192 | return true | ||
193 | end | ||
194 | |||
195 | function get_parent(lv) | ||
196 | lv = lv or #menu_ctx.collapse_fn | ||
197 | collectgarbage("step") | ||
198 | local t = menu_ctx.collapse_fn[lv] or {} | ||
199 | return t[2] or -1 | ||
200 | end | ||
201 | |||
202 | function set_menu(mt, ft, user_context_fn, settings) | ||
203 | local function empty_fn() end | ||
204 | menu_set_defaults(settings) | ||
205 | if type(user_context_fn) == 'function' then | ||
206 | display_context_menu = display_context_menu_internal | ||
207 | menu_ctx.user_context_fn = user_context_fn | ||
208 | else | ||
209 | display_context_menu = empty_fn | ||
210 | menu_ctx.user_context_fn = false | ||
211 | end | ||
212 | p_settings = settings or p_settings | ||
213 | menu_t, func_t = mt, ft | ||
214 | ctx_loop() | ||
215 | end | ||
216 | |||
217 | function create_sub_menu(lv, mt, ft) | ||
218 | if lv < 1 then error("Level < 1") end | ||
219 | -- everything in lua is 1 based menu level is no exception | ||
220 | local lv_tab = string.rep ("\t", lv) | ||
221 | local function submenu_closure(i, m, f) | ||
222 | menu_ctx.lv = lv | ||
223 | local lv_out, menusz_out, start_item | ||
224 | local item_in, item_out = i, i | ||
225 | if lv <= #menu_ctx.collapse_fn then --something else expanded?? | ||
226 | repeat | ||
227 | local collapse_fn = table.remove(menu_ctx.collapse_fn) | ||
228 | if collapse_fn then | ||
229 | lv_out, item_out, menusz_out = collapse_fn[1](i, m, f) | ||
230 | -- if the item i is below this menu, it needs to shift too | ||
231 | if item_in > item_out then i = i - (menusz_out) end | ||
232 | end | ||
233 | until not collapse_fn or lv >= lv_out | ||
234 | menu_ctx.start = i | ||
235 | if item_out == item_in then return end | ||
236 | end | ||
237 | |||
238 | local menu_sz = #mt | ||
239 | menu_ctx.start = i | ||
240 | start_item = i | ||
241 | menu_ctx.update = true | ||
242 | for item, _ in ipairs(mt) do | ||
243 | i = i + 1 | ||
244 | table.insert(m, i, lv_tab .. mt[item]) | ||
245 | table.insert(f, i, ft[item]) | ||
246 | end | ||
247 | |||
248 | local function collapse_closure(i, m, f) | ||
249 | --creates a closure around lv, start_item and menu_sz | ||
250 | for j = 1, menu_sz, 1 do | ||
251 | table.remove(m, start_item + 1) | ||
252 | table.remove(f, start_item + 1) | ||
253 | end | ||
254 | return lv, start_item, menu_sz | ||
255 | end | ||
256 | |||
257 | table.insert(menu_ctx.collapse_fn, lv, {collapse_closure, start_item}) | ||
258 | return true | ||
259 | end | ||
260 | |||
261 | return submenu_closure | ||
262 | end | ||
263 | |||