1
0
Fork 0
forked from len0rd/rockbox

lua submenus add a way to dynamically add items

updated example script and renamed some functions as well

fixed bug in printtable cursor position if greater than maxlines for the
screen would reset to item 1

now we move the list start and select it
(and try to center it on the screen)

fixed a few bugs in the add_menu code

Change-Id: I01dead0481ef2e925af8b4cc6c14e36c2859dbba
This commit is contained in:
William Wilgus 2021-04-29 01:56:49 -04:00 committed by William Wilgus
parent 48b77898dc
commit 20cd89908d
3 changed files with 139 additions and 50 deletions

View file

@ -21,15 +21,31 @@
****************************************************************************/ ****************************************************************************/
]] ]]
if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end if not rb.lcd_framebuffer then rb.splash(rb.HZ, "No Support!") return nil end
--GLOBALS
menu_ctx = {} menu_ctx = {}
submenu_insert = table.insert
submenu_remove = table.remove
local last_ctx = false local last_ctx = false
local p_settings local p_settings
local function empty_fn() end
--[[root menu tables --[[root menu tables
expanded menus get inserted / removed expanded menus get inserted / removed
and context menus replace them but never overwritten and context menus replace them but unless you
unless you want a new root menu want a new root menu they are never overwritten
func_t functions get 3 variables passed by the menu_system
func_t[i] =function sample(i, menu_t, func_t]
this function gets run on user selection
for every function in func_t:
'i' is the selected item
'menu_t' is the current strings table
'func_t' is the current function table
menu_t[i] will returnm the text of the item user selected and
func_t[i] will return the function we are currently in
]] ]]
menu_t = {} menu_t = {}
func_t = {} func_t = {}
@ -110,16 +126,9 @@ local function dpad(x, xi, xir, y, yi, yir, timeout, overflow, selected)
return cancel, select, x_chg, x, y_chg, y, 0xffff return cancel, select, x_chg, x, y_chg, y, 0xffff
end -- dpad end -- dpad
local function menu_set_defaults(settings, ctx)
p_settings = settings or {wrap = true, hasheader = true, justify = "left", dpad_fn = dpad}
menu_ctx = ctx or {collapse_fn = {}, lv = 0, update = false, start = 1}
end
local function ctx_loop() local function ctx_loop()
local loopfn = ctx_loop local loopfn = ctx_loop
ctx_loop = function() end --prevent another execution ctx_loop = empty_fn() --prevent another execution
local mt, ft = get_menu() local mt, ft = get_menu()
local i local i
repeat repeat
@ -130,25 +139,23 @@ local function ctx_loop()
ctx_loop = loopfn --restore for another run ctx_loop = loopfn --restore for another run
end end
function get_menu() --[[ push_ctx() save surrent menu and load another ]]
return menu_t, func_t
end
local function push_ctx(new_getmenu) local function push_ctx(new_getmenu)
last_ctx = last_ctx or {} last_ctx = last_ctx or {}
table.insert(last_ctx, menu_ctx) submenu_insert(last_ctx, menu_ctx)
menu_ctx.getmenu = get_menu menu_ctx.getmenu = get_menu
menu_ctx.settings = p_settings menu_ctx.settings = p_settings
--menu_ctx is a new variable after this point --menu_ctx is a new variable after this point
menu_set_defaults() submenu_set_defaults()
menu_ctx.update = true menu_ctx.update = true
if type(new_getmenu) == 'function' then if type(new_getmenu) == 'function' then
get_menu = new_getmenu get_menu = new_getmenu
end end
end end
--[[ pop_ctx() restore last menu ]]
local function pop_ctx() local function pop_ctx()
menu_ctx = table.remove(last_ctx) menu_ctx = submenu_remove(last_ctx)
if menu_ctx then if menu_ctx then
get_menu = menu_ctx.getmenu get_menu = menu_ctx.getmenu
p_settings = menu_ctx.settings p_settings = menu_ctx.settings
@ -163,13 +170,17 @@ local function pop_ctx()
end end
end end
--[[ display_context_menu_internal() supplies a new get_menu function that returns
the context menu 'user_context_fn' supplied by set_menu()
this menu is immediately displayed and when finished will
automatically restore the last menu
]]
local function display_context_menu_internal(sel) local function display_context_menu_internal(sel)
if sel <= 0 or not menu_ctx.user_context_fn then return false end if sel <= 0 or not menu_ctx.user_context_fn then return false end
local parent = get_parent() or 0 local parent = submenu_get_parent() or 0
local user_context_fn = menu_ctx.user_context_fn local user_context_fn = menu_ctx.user_context_fn
local function display_context_menu(i, menu_t, func_t)
local function display_context_menu(i, menu_t, func_t)
local function new_getmenu() local function new_getmenu()
local mt, ft = user_context_fn(parent, i, menu_t, func_t) local mt, ft = user_context_fn(parent, i, menu_t, func_t)
ft[0] = pop_ctx --set back fn ft[0] = pop_ctx --set back fn
@ -179,6 +190,7 @@ local function display_context_menu_internal(sel)
return true return true
end end
--save the current function in closure restore_fn for later
local funct = func_t[sel] local funct = func_t[sel]
local function restore_fn(mt, ft) local function restore_fn(mt, ft)
ft[sel] = funct ft[sel] = funct
@ -192,30 +204,43 @@ local function display_context_menu_internal(sel)
return true return true
end end
function get_parent(lv) --[[ submenu_get_parent() gets the parent of the top most level
lv = lv or #menu_ctx.collapse_fn if lv is supplied it instead gets the parent of that level ]]
function submenu_get_parent(lv)
lv = lv or #menu_ctx.collapse_fn or 1
collectgarbage("step") collectgarbage("step")
local t = menu_ctx.collapse_fn[lv] or {} local t = menu_ctx.collapse_fn[lv] or {}
return t[2] or -1 return t[2] or -1, lv
end end
function set_menu(mt, ft, user_context_fn, settings) --[[ submenu_collapse() collapses submenu till level or ROOT is reached ]]
local function empty_fn() end function submenu_collapse(parent, lv)
menu_set_defaults(settings) local lv_out, menu_sz = 0, 0
if type(user_context_fn) == 'function' then local item_out = -1
display_context_menu = display_context_menu_internal local items_removed = 0
menu_ctx.user_context_fn = user_context_fn if lv <= #menu_ctx.collapse_fn then
else repeat
display_context_menu = empty_fn local collapse_fn = submenu_remove(menu_ctx.collapse_fn)
menu_ctx.user_context_fn = false if collapse_fn then
lv_out, item_out, menu_sz = collapse_fn[1](parent, menu_t, func_t)
items_removed = items_removed + menu_sz
end
until not collapse_fn or lv >= lv_out
end end
p_settings = settings or p_settings return lv_out, item_out, items_removed
menu_t, func_t = mt, ft
ctx_loop()
end end
function create_sub_menu(lv, mt, ft) --[[ submenu_create() supply level of submenu > 0, ROOT is lv 0
supply menu strings table and function table
closure returned run this function to expand the menu
]]
function submenu_create(lv, mt, ft)
if lv < 1 then error("Level < 1") end if lv < 1 then error("Level < 1") end
if type(mt) ~= 'table' or type(ft) ~= 'table' then
error("mt and ft must be tables")
end
-- everything in lua is 1 based menu level is no exception -- everything in lua is 1 based menu level is no exception
local lv_tab = string.rep ("\t", lv) local lv_tab = string.rep ("\t", lv)
local function submenu_closure(i, m, f) local function submenu_closure(i, m, f)
@ -224,7 +249,7 @@ function create_sub_menu(lv, mt, ft)
local item_in, item_out = i, i local item_in, item_out = i, i
if lv <= #menu_ctx.collapse_fn then --something else expanded?? if lv <= #menu_ctx.collapse_fn then --something else expanded??
repeat repeat
local collapse_fn = table.remove(menu_ctx.collapse_fn) local collapse_fn = submenu_remove(menu_ctx.collapse_fn)
if collapse_fn then if collapse_fn then
lv_out, item_out, menusz_out = collapse_fn[1](i, m, f) lv_out, item_out, menusz_out = collapse_fn[1](i, m, f)
-- if the item i is below this menu, it needs to shift too -- if the item i is below this menu, it needs to shift too
@ -241,23 +266,49 @@ function create_sub_menu(lv, mt, ft)
menu_ctx.update = true menu_ctx.update = true
for item, _ in ipairs(mt) do for item, _ in ipairs(mt) do
i = i + 1 i = i + 1
table.insert(m, i, lv_tab .. mt[item]) submenu_insert(m, i, lv_tab .. mt[item])
table.insert(f, i, ft[item]) submenu_insert(f, i, ft[item])
end end
local function collapse_closure(i, m, f) local function collapse_closure(i, m, f)
--creates a closure around lv, start_item and menu_sz --creates a closure around lv, start_item and menu_sz
for j = 1, menu_sz, 1 do for j = 1, menu_sz, 1 do
table.remove(m, start_item + 1) submenu_remove(m, start_item + 1)
table.remove(f, start_item + 1) submenu_remove(f, start_item + 1)
end end
return lv, start_item, menu_sz return lv, start_item, menu_sz
end end
table.insert(menu_ctx.collapse_fn, lv, {collapse_closure, start_item}) submenu_insert(menu_ctx.collapse_fn, lv, {collapse_closure, start_item})
return true return true
end end
return submenu_closure return submenu_closure
end end
--
function submenu_set_defaults(settings, ctx)
p_settings = settings or {wrap = true, hasheader = true, justify = "left", dpad_fn = dpad}
menu_ctx = ctx or {collapse_fn = {}, lv = 0, update = false, start = 1}
end
--[[ get_menu() returns the ROOT string and fn tables]]
function get_menu()
return menu_t, func_t
end
--[[ set_menu() set your menu and the menu has now been entered ]]
function set_menu(mt, ft, user_context_fn, settings)
submenu_set_defaults(settings)
if type(user_context_fn) == 'function' then
display_context_menu = display_context_menu_internal
menu_ctx.user_context_fn = user_context_fn
else
display_context_menu = empty_fn
menu_ctx.user_context_fn = false
end
p_settings = settings or p_settings
menu_t, func_t = mt, ft
ctx_loop()
end

View file

@ -336,6 +336,16 @@ function print_table(t, t_count, settings)
table_p = init_position(15, 5) table_p = init_position(15, 5)
line, maxline = _print.opt.area(5, 1, rb.LCD_WIDTH - 10 - sb_width, rb.LCD_HEIGHT - 2) line, maxline = _print.opt.area(5, 1, rb.LCD_WIDTH - 10 - sb_width, rb.LCD_HEIGHT - 2)
if curpos > maxline then
local c = maxline / 2
start = (start or 1) + curpos - maxline
curpos = maxline
while start + maxline <= t_count and curpos > c do
curpos = curpos - 1
start = start + 1
end
end
maxline = math.min(maxline, t_count) maxline = math.min(maxline, t_count)
-- allow user to start at a position other than the beginning -- allow user to start at a position other than the beginning

View file

@ -13,17 +13,17 @@ end
local function ITEM_MENU() local function ITEM_MENU()
local function flung(i, menu_t, func_t) local function flung(i, menu_t, func_t)
local parent = get_parent() or 0 local parent = submenu_get_parent() or 0
rb.splash(100, "flung " .. (menu_t[parent] or "?")) rb.splash(100, "flung " .. (menu_t[parent] or "?"))
end end
local function foo(i, menu_t, func_t) local function foo(i, menu_t, func_t)
local parent = get_parent() or 0 local parent = submenu_get_parent() or 0
rb.splash(100, "FOO " .. menu_t[parent]) rb.splash(100, "FOO " .. menu_t[parent])
end end
local function far(i, menu_t, func_t) local function far(i, menu_t, func_t)
local parent = get_parent() or 0 local parent = submenu_get_parent() or 0
rb.splash(100, "far" .. menu_t[parent]) rb.splash(100, "far" .. menu_t[parent])
end end
@ -32,16 +32,44 @@ local function ITEM_MENU()
end end
local function USERITEMS() local function USERITEMS()
local lv = 2
local mt = {"Item_1", "Item_2", "Item_3"}
local ft = {}
return {"Item_1", "Item_2", "Item_3"}, local function insert_item(i, name, func) --closure
{create_sub_menu(2, ITEM_MENU()), create_sub_menu(2, ITEM_MENU()), submenu_insert(mt, i, name)
create_sub_menu(2, ITEM_MENU()), function() end} submenu_insert(ft, i, func)
end
for i = 1, #mt, 1 do
ft[i] = submenu_create(lv, ITEM_MENU())
end
local function add_new(i, menu_t, func_t)
local parent, lv = submenu_get_parent(lv - 1)
local last = #mt
local name = "Item_" .. tostring(last)
local func = submenu_create(lv + 1, ITEM_MENU())
local lv_out, item_out, removed = submenu_collapse(parent, lv + 1)-- collapse others
submenu_collapse(parent, lv) -- collapse the parent
insert_item(last, name, func)
func_t[parent](parent, menu_t, func_t) -- reopen parent
menu_ctx.start = i - removed
return true
end
local next = #mt + 1
insert_item(next, "Add New", add_new)
return mt, ft
end end
local function MAIN_MENU() local function MAIN_MENU()
local function go_back(i, m, f) local function go_back(i, m, f)
local parent = get_parent() or 0 local parent = submenu_get_parent() or 0
if parent > 0 then if parent > 0 then
f[parent](parent, m, f) f[parent](parent, m, f)
else else
@ -60,7 +88,7 @@ local function MAIN_MENU()
local ft = { local ft = {
[0] = go_back, --if user cancels do this function [0] = go_back, --if user cancels do this function
[1] = false, -- shouldn't happen title occupies this slot [1] = false, -- shouldn't happen title occupies this slot
[2] = create_sub_menu(1, USERITEMS()), [2] = submenu_create(1, USERITEMS()),
[3] = go_back, [3] = go_back,
} }
return mt, ft, get_ctx_menu return mt, ft, get_ctx_menu