2
0
mirror of https://github.com/koreader/koreader synced 2024-11-10 01:10:34 +00:00
koreader/frontend/ui/widget/menu.lua

907 lines
27 KiB
Lua
Raw Normal View History

2013-10-18 20:38:07 +00:00
local InputContainer = require("ui/widget/container/inputcontainer")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local LeftContainer = require("ui/widget/container/leftcontainer")
local RightContainer = require("ui/widget/container/rightcontainer")
2013-10-18 20:38:07 +00:00
local FrameContainer = require("ui/widget/container/framecontainer")
local CenterContainer = require("ui/widget/container/centercontainer")
local BottomContainer = require("ui/widget/container/bottomcontainer")
local UnderlineContainer = require("ui/widget/container/underlinecontainer")
local FocusManager = require("ui/widget/focusmanager")
local TextWidget = require("ui/widget/textwidget")
local OverlapGroup = require("ui/widget/overlapgroup")
local VerticalSpan = require("ui/widget/verticalspan")
2014-09-05 13:07:21 +00:00
local HorizontalSpan = require("ui/widget/horizontalspan")
2013-10-18 20:38:07 +00:00
local VerticalGroup = require("ui/widget/verticalgroup")
local HorizontalGroup = require("ui/widget/horizontalgroup")
local Button = require("ui/widget/button")
local GestureRange = require("ui/gesturerange")
local Font = require("ui/font")
local Geom = require("ui/geometry")
local Device = require("device")
local Screen = require("device").screen
local Input = require("device").input
2013-10-18 20:38:07 +00:00
local UIManager = require("ui/uimanager")
local RenderText = require("ui/rendertext")
local InfoMessage = require("ui/widget/infomessage")
local util = require("ffi/util")
local logger = require("logger")
local Blitbuffer = require("ffi/blitbuffer")
2013-10-18 20:38:07 +00:00
local _ = require("gettext")
2017-02-04 04:26:47 +00:00
local getMenuText = require("util").getMenuText
--[[
Widget that displays a shortcut icon for menu item
--]]
2013-10-18 20:38:07 +00:00
local ItemShortCutIcon = WidgetContainer:new{
2014-03-13 13:52:43 +00:00
dimen = Geom:new{ w = 22, h = 22 },
key = nil,
bordersize = 2,
radius = 0,
style = "square",
}
function ItemShortCutIcon:init()
2014-03-13 13:52:43 +00:00
if not self.key then
return
end
local radius = 0
local background = Blitbuffer.COLOR_WHITE
2014-03-13 13:52:43 +00:00
if self.style == "rounded_corner" then
radius = math.floor(self.width/2)
elseif self.style == "grey_square" then
background = Blitbuffer.gray(0.2)
2014-03-13 13:52:43 +00:00
end
--@TODO calculate font size by icon size 01.05 2012 (houqp)
2016-01-03 07:44:23 +00:00
local sc_face
2014-03-13 13:52:43 +00:00
if self.key:len() > 1 then
sc_face = Font:getFace("ffont", 14)
else
sc_face = Font:getFace("scfont", 22)
end
self[1] = FrameContainer:new{
padding = 0,
bordersize = self.bordersize,
radius = radius,
background = background,
dimen = self.dimen,
CenterContainer:new{
dimen = self.dimen,
TextWidget:new{
text = self.key,
face = sc_face,
},
},
}
end
2012-12-07 15:30:15 +00:00
--[[
NOTICE:
@menu entry must be provided in order to close the menu
--]]
2013-10-18 20:38:07 +00:00
local MenuCloseButton = InputContainer:new{
overlap_align = "right",
2014-03-13 13:52:43 +00:00
menu = nil,
dimen = Geom:new{},
2012-12-07 15:30:15 +00:00
}
function MenuCloseButton:init()
2014-03-13 13:52:43 +00:00
self[1] = TextWidget:new{
text = "×",
face = Font:getFace("cfont", 32),
}
local text_size = self[1]:getSize()
self.dimen = Geom:new{
w = text_size.w*2,
h = text_size.h*2,
}
2014-03-13 13:52:43 +00:00
self.ges_events.Close = {
GestureRange:new{
ges = "tap",
range = self.dimen,
},
doc = "Close menu",
}
2012-12-07 15:30:15 +00:00
end
function MenuCloseButton:onClose()
2014-03-13 13:52:43 +00:00
self.menu:onClose()
return true
2012-12-07 15:30:15 +00:00
end
--[[
Widget that displays an item for menu
2012-12-07 15:30:15 +00:00
--]]
2013-10-18 20:38:07 +00:00
local MenuItem = InputContainer:new{
2014-03-13 13:52:43 +00:00
text = nil,
show_parent = nil,
detail = nil,
face = Font:getFace("cfont", 30),
info_face = Font:getFace("infont", 15),
dimen = nil,
shortcut = nil,
shortcut_style = "square",
_underline_container = nil,
}
function MenuItem:init()
2014-03-13 13:52:43 +00:00
local shortcut_icon_dimen = Geom:new()
if self.shortcut then
shortcut_icon_dimen.w = math.floor(self.dimen.h*4/5)
shortcut_icon_dimen.h = shortcut_icon_dimen.w
end
self.detail = self.text
-- 15 for HorizontalSpan,
self.content_width = self.dimen.w - shortcut_icon_dimen.w - 15
-- we need this table per-instance, so we declare it here
if Device:isTouchDevice() then
self.ges_events = {
TapSelect = {
GestureRange:new{
ges = "tap",
range = self.dimen,
},
doc = "Select Menu Item",
},
HoldSelect = {
GestureRange:new{
ges = "hold",
range = self.dimen,
},
doc = "Hold Menu Item",
},
}
end
if Device:hasKeys() then
2014-03-13 13:52:43 +00:00
self.active_key_events = {
Select = { {"Press"}, doc = "chose selected item" },
}
end
local mandatory = self.mandatory and ""..self.mandatory.." " or ""
2014-10-14 13:33:13 +00:00
local mandatory_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.info_face,
""..mandatory, true, self.bold).x
local state_button_width = self.state_size.w or 0
local my_text = self.text and ""..self.text or ""
2017-02-01 15:34:12 +00:00
local w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face, my_text, true, self.bold).x
2014-10-14 13:33:13 +00:00
if w + mandatory_w + state_button_width >= self.content_width then
if Device:hasKeyboard() then
2014-03-13 13:52:43 +00:00
self.active_key_events.ShowItemDetail = {
{"Right"}, doc = "show item detail"
}
end
local indicator = "\226\128\166 " -- an ellipsis
2014-10-14 13:33:13 +00:00
local indicator_w = RenderText:sizeUtf8Text(0, self.dimen.w, self.face,
indicator, true, self.bold).x
self.text = RenderText:getSubTextByWidth(my_text, self.face,
2014-10-14 13:33:13 +00:00
self.content_width - indicator_w - mandatory_w - state_button_width,
true, self.bold) .. indicator
2014-03-13 13:52:43 +00:00
end
2014-04-04 15:02:29 +00:00
2014-10-14 13:33:13 +00:00
local state_button = self.state or HorizontalSpan:new{
width = state_button_width,
}
local state_indent = self.state and self.state.indent or ""
local state_container = LeftContainer:new{
dimen = Geom:new{w = self.content_width/2, h = self.dimen.h},
HorizontalGroup:new{
HorizontalSpan:new{
width = RenderText:sizeUtf8Text(0, self.dimen.w, self.face,
state_indent, true, self.bold).x,
},
state_button
}
}
2014-03-13 13:52:43 +00:00
local text_container = LeftContainer:new{
dimen = Geom:new{w = self.content_width, h = self.dimen.h},
2014-10-14 13:33:13 +00:00
HorizontalGroup:new{
HorizontalSpan:new{
width = self.state_size.w,
},
TextWidget:new{
text = self.text,
face = self.face,
bold = self.bold,
}
2014-03-13 13:52:43 +00:00
}
}
2014-04-04 15:02:29 +00:00
2014-03-13 13:52:43 +00:00
local mandatory_container = RightContainer:new{
dimen = Geom:new{w = self.content_width, h = self.dimen.h},
TextWidget:new{
text = mandatory,
face = self.info_face,
2014-04-04 15:02:29 +00:00
bold = self.bold,
2014-03-13 13:52:43 +00:00
}
}
self._underline_container = UnderlineContainer:new{
2014-10-14 13:33:13 +00:00
vertical_align = "center",
2014-03-13 13:52:43 +00:00
dimen = Geom:new{
w = self.content_width,
h = self.dimen.h
},
HorizontalGroup:new{
align = "center",
OverlapGroup:new{
dimen = Geom:new{w = self.content_width, h = self.dimen.h},
2014-10-14 13:33:13 +00:00
state_container,
2014-03-13 13:52:43 +00:00
text_container,
mandatory_container,
},
}
}
self[1] = FrameContainer:new{
bordersize = 0,
padding = 0,
HorizontalGroup:new{
2014-10-14 13:33:13 +00:00
align = "center",
2014-03-13 13:52:43 +00:00
HorizontalSpan:new{ width = 5 },
ItemShortCutIcon:new{
dimen = shortcut_icon_dimen,
key = self.shortcut,
style = self.shortcut_style,
},
HorizontalSpan:new{ width = 10 },
self._underline_container
}
}
end
function MenuItem:onFocus()
self._underline_container.color = Blitbuffer.COLOR_BLACK
2014-03-13 13:52:43 +00:00
self.key_events = self.active_key_events
return true
end
function MenuItem:onUnfocus()
self._underline_container.color = Blitbuffer.COLOR_WHITE
2014-03-13 13:52:43 +00:00
self.key_events = {}
return true
end
function MenuItem:onShowItemDetail()
2014-03-13 13:52:43 +00:00
UIManager:show(InfoMessage:new{
text=self.detail,
})
return true
end
function MenuItem:getGesPosition(ges)
local dimen = self[1].dimen
return {
x = (ges.pos.x - dimen.x)/dimen.w,
y = (ges.pos.y - dimen.y)/dimen.h,
}
end
function MenuItem:onTapSelect(arg, ges)
local pos = self:getGesPosition(ges)
2014-03-13 13:52:43 +00:00
self[1].invert = true
local refreshfunc = function()
return "ui", self[1].dimen
end
UIManager:setDirty(self.show_parent, refreshfunc)
2014-03-13 13:52:43 +00:00
UIManager:scheduleIn(0.1, function()
self[1].invert = false
UIManager:setDirty(self.show_parent, refreshfunc)
logger.dbg("creating coroutine for menu select")
local co = coroutine.create(function()
self.menu:onMenuSelect(self.table, pos)
end)
coroutine.resume(co)
2014-03-13 13:52:43 +00:00
end)
return true
2012-11-11 06:00:52 +00:00
end
function MenuItem:onHoldSelect(arg, ges)
local pos = self:getGesPosition(ges)
2014-03-13 13:52:43 +00:00
self[1].invert = true
local refreshfunc = function()
return "ui", self[1].dimen
end
UIManager:setDirty(self.show_parent, refreshfunc)
2014-03-13 13:52:43 +00:00
UIManager:scheduleIn(0.1, function()
self[1].invert = false
UIManager:setDirty(self.show_parent, refreshfunc)
self.menu:onMenuHold(self.table, pos)
2014-03-13 13:52:43 +00:00
end)
return true
end
--[[
Widget that displays menu
2012-12-07 15:30:15 +00:00
--]]
2013-10-18 20:38:07 +00:00
local Menu = FocusManager:new{
2014-03-13 13:52:43 +00:00
show_parent = nil,
-- face for displaying item contents
cface = Font:getFace("cfont", 24),
-- face for menu title
tface = Font:getFace("tfont", 26),
-- face for paging info display
2014-06-10 13:32:49 +00:00
fface = Font:getFace("ffont", 20),
2014-03-13 13:52:43 +00:00
-- font for item shortcut
sface = Font:getFace("scfont", 20),
title = "No Title",
-- default width and height
width = 500,
-- height will be calculated according to item number if not given
height = nil,
header_padding = Screen:scaleBySize(10),
2014-03-13 13:52:43 +00:00
dimen = Geom:new{},
item_table = {},
item_shortcuts = {
"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P",
"A", "S", "D", "F", "G", "H", "J", "K", "L", "Del",
"Z", "X", "C", "V", "B", "N", "M", ".", "Sym", "Enter",
},
item_table_stack = nil,
is_enable_shortcut = true,
item_dimen = nil,
page = 1,
item_group = nil,
page_info = nil,
2014-09-05 13:07:21 +00:00
page_return = nil,
paths = {}, -- table to trace navigation path
2014-03-13 13:52:43 +00:00
-- set this to true to not paint as popup menu
is_borderless = false,
-- if you want to embed the menu widget into another widget, set
-- this to false
is_popout = true,
-- set this to true to add close button
has_close_button = true,
-- close_callback is a function, which is executed when menu is closed
-- it is usually set by the widget which creates the menu
close_callback = nil
}
function Menu:_recalculateDimen()
2014-03-13 13:52:43 +00:00
self.dimen.w = self.width
self.item_dimen = Geom:new{
w = self.dimen.w,
h = Screen:scaleBySize(46), -- hardcoded for now
2014-03-13 13:52:43 +00:00
}
-- if height not given, dynamically calculate it
self.dimen.h = self.height or (#self.item_table + 2) * self.item_dimen.h
if self.dimen.h > Screen:getHeight() then
self.dimen.h = Screen:getHeight()
end
-- header and footer should approximately take up space of 2 items
self.perpage = math.floor(self.dimen.h / self.item_dimen.h) - (self.no_title and 1 or 2)
2014-03-13 13:52:43 +00:00
self.page_num = math.ceil(#self.item_table / self.perpage)
end
function Menu:init()
2014-03-13 13:52:43 +00:00
self.show_parent = self.show_parent or self
self.item_table_stack = {}
self:_recalculateDimen()
self.page = 1
-----------------------------------
-- start to set up widget layout --
-----------------------------------
self.menu_title = TextWidget:new{
overlap_align = "center",
2014-03-13 13:52:43 +00:00
text = self.title,
face = self.tface,
}
-- group for title bar
self.title_bar = OverlapGroup:new{
dimen = {w = self.dimen.w, h = self.menu_title:getSize().h},
self.menu_title,
}
-- group for items
self.item_group = VerticalGroup:new{}
-- group for page info
self.page_info_left_chev = Button:new{
icon = "resources/icons/appbar.chevron.left.png",
callback = function() self:onPrevPage() end,
bordersize = 0,
2014-05-01 10:37:12 +00:00
show_parent = self,
2014-03-13 13:52:43 +00:00
}
self.page_info_right_chev = Button:new{
icon = "resources/icons/appbar.chevron.right.png",
callback = function() self:onNextPage() end,
bordersize = 0,
2014-05-01 10:37:12 +00:00
show_parent = self,
2014-03-13 13:52:43 +00:00
}
self.page_info_first_chev = Button:new{
icon = "resources/icons/appbar.chevron.first.png",
callback = function() self:onFirstPage() end,
bordersize = 0,
show_parent = self,
}
self.page_info_last_chev = Button:new{
icon = "resources/icons/appbar.chevron.last.png",
callback = function() self:onLastPage() end,
bordersize = 0,
show_parent = self,
}
2014-09-05 13:07:21 +00:00
self.page_info_spacer = HorizontalSpan:new{
width = Screen:scaleBySize(32),
}
2014-03-13 13:52:43 +00:00
self.page_info_left_chev:hide()
self.page_info_right_chev:hide()
self.page_info_first_chev:hide()
self.page_info_last_chev:hide()
self.page_info_text = Button:new{
2014-03-13 13:52:43 +00:00
text = "",
hold_input = {
title = _("Input page number"),
type = "number",
hint_func = function()
return "(" .. "1 - " .. self.page_num .. ")"
end,
callback = function(input)
local page = tonumber(input)
if page >= 1 and page <= self.page_num then
self:onGotoPage(page)
end
end,
},
bordersize = 0,
text_font_face = "cfont",
text_font_size = 20,
text_font_bold = false,
2014-03-13 13:52:43 +00:00
}
self.page_info = HorizontalGroup:new{
self.page_info_first_chev,
self.page_info_spacer,
2014-03-13 13:52:43 +00:00
self.page_info_left_chev,
self.page_info_text,
self.page_info_right_chev,
self.page_info_spacer,
self.page_info_last_chev,
2014-03-13 13:52:43 +00:00
}
2014-09-05 13:07:21 +00:00
-- return button
self.page_return_arrow = Button:new{
icon = "resources/icons/appbar.arrow.left.up.png",
callback = function() self:onReturn() end,
bordersize = 0,
show_parent = self,
}
self.page_return_arrow:hide()
self.return_button = HorizontalGroup:new{
HorizontalSpan:new{
width = Screen:scaleBySize(5),
2014-09-05 13:07:21 +00:00
},
self.page_return_arrow,
}
local header = VerticalGroup:new{
VerticalSpan:new{width = self.header_padding},
self.title_bar,
}
2014-03-13 13:52:43 +00:00
local body = self.item_group
local footer = BottomContainer:new{
dimen = self.dimen:copy(),
self.page_info,
}
2014-09-05 13:07:21 +00:00
local page_return = BottomContainer:new{
dimen = self.dimen:copy(),
WidgetContainer:new{
dimen = Geom:new{
w = Screen:getWidth(),
h = self.page_return_arrow:getSize().h,
},
self.return_button,
}
}
2014-03-13 13:52:43 +00:00
2016-01-03 07:44:23 +00:00
local content
2014-03-13 13:52:43 +00:00
if self.no_title then
content = OverlapGroup:new{
dimen = self.dimen:copy(),
VerticalGroup:new{
align = "left",
body,
},
2014-09-05 13:07:21 +00:00
page_return,
footer,
2014-03-13 13:52:43 +00:00
}
else
content = OverlapGroup:new{
dimen = self.dimen:copy(),
VerticalGroup:new{
align = "left",
header,
body,
},
2014-09-05 13:07:21 +00:00
page_return,
footer,
2014-03-13 13:52:43 +00:00
}
end
self[1] = FrameContainer:new{
background = Blitbuffer.COLOR_WHITE,
2014-03-13 13:52:43 +00:00
bordersize = self.is_borderless and 0 or 2,
padding = 0,
margin = 0,
2014-11-27 14:01:34 +00:00
radius = self.is_popout and math.floor(self.dimen.w/20) or 0,
2014-03-13 13:52:43 +00:00
content
}
------------------------------------------
-- start to set up input event callback --
------------------------------------------
if Device:isTouchDevice() then
if self.has_close_button then
table.insert(self.title_bar,
MenuCloseButton:new{ menu = self })
2014-03-13 13:52:43 +00:00
end
-- watch for outer region if it's a self contained widget
if self.is_popout then
self.ges_events.TapCloseAllMenus = {
GestureRange:new{
ges = "tap",
range = Geom:new{
x = 0, y = 0,
w = Screen:getWidth(),
h = Screen:getHeight(),
}
}
}
end
self.ges_events.Swipe = {
GestureRange:new{
ges = "swipe",
range = self.dimen,
}
}
2014-06-10 13:12:11 +00:00
self.ges_events.Close = self.on_close_ges
2014-03-13 13:52:43 +00:00
end
if not Device:hasKeyboard() then
-- remove menu item shortcut for K4
self.is_enable_shortcut = false
end
if Device:hasKeys() then
2014-03-13 13:52:43 +00:00
-- set up keyboard events
self.key_events.Close = { {"Back"}, doc = "close menu" }
2014-03-13 13:52:43 +00:00
self.key_events.NextPage = {
{Input.group.PgFwd}, doc = "goto next page of the menu"
2014-03-13 13:52:43 +00:00
}
self.key_events.PrevPage = {
{Input.group.PgBack}, doc = "goto previous page of the menu"
2014-03-13 13:52:43 +00:00
}
-- we won't catch presses to "Right", leave that to MenuItem.
self.key_events.FocusRight = nil
-- shortcut icon is not needed for touch device
if self.is_enable_shortcut then
self.key_events.SelectByShortCut = { {self.item_shortcuts} }
end
self.key_events.Select = {
{"Press"}, doc = "select current menu item"
2014-03-13 13:52:43 +00:00
}
end
if #self.item_table > 0 then
-- if the table is not yet initialized, this call
-- must be done manually:
2014-04-04 15:02:29 +00:00
self.page = math.ceil((self.item_table.current or 1) / self.perpage)
2014-03-13 13:52:43 +00:00
end
2017-01-10 18:02:46 +00:00
if self.path_items then
self:refreshPath()
else
2017-02-01 15:34:12 +00:00
self:updateItems()
2017-01-10 18:02:46 +00:00
end
end
2014-12-01 16:21:42 +00:00
function Menu:onCloseWidget()
2014-12-04 02:09:09 +00:00
-- FIXME:
-- we cannot refresh regionally using the dimen field
-- because some menus without menu title use VerticalGroup to include
-- a text widget which is not calculated into the dimen.
-- For example, it's a dirty hack to use two menus(one this menu and one
-- touch menu) in the filemanager in order to capture tap gesture to popup
-- the filemanager menu.
UIManager:setDirty(nil, "partial")
2014-12-01 16:21:42 +00:00
end
function Menu:updateItems(select_number)
local old_dimen = self.dimen and self.dimen:copy()
2014-03-13 13:52:43 +00:00
-- self.layout must be updated for focusmanager
self.layout = {}
self.item_group:clear()
self.page_info:resetLayout()
2014-09-05 13:07:21 +00:00
self.return_button:resetLayout()
2014-03-13 13:52:43 +00:00
self:_recalculateDimen()
-- default to select the first item
if not select_number then
select_number = 1
end
for c = 1, math.min(self.perpage, #self.item_table) do
2014-03-13 13:52:43 +00:00
-- calculate index in item_table
local i = (self.page - 1) * self.perpage + c
if i <= #self.item_table then
local item_shortcut = nil
local shortcut_style = "square"
if self.is_enable_shortcut then
-- give different shortcut_style to keys in different
-- lines of keyboard
if c >= 11 and c <= 20 then
--shortcut_style = "rounded_corner"
shortcut_style = "grey_square"
end
item_shortcut = self.item_shortcuts[c]
if item_shortcut == "Enter" then
item_shortcut = "Ent"
end
end
local item_tmp = MenuItem:new{
show_parent = self.show_parent,
state = self.item_table[i].state,
state_size = self.state_size or {},
2017-02-04 04:26:47 +00:00
text = getMenuText(self.item_table[i]),
mandatory = self.item_table[i].mandatory,
bold = self.item_table.current == i or self.item_table[i].bold == true,
face = self.cface,
dimen = self.item_dimen:new(),
shortcut = item_shortcut,
shortcut_style = shortcut_style,
table = self.item_table[i],
menu = self,
}
2014-03-13 13:52:43 +00:00
table.insert(self.item_group, item_tmp)
-- this is for focus manager
table.insert(self.layout, {item_tmp})
end -- if i <= self.items
end -- for c=1, self.perpage
if self.item_group[1] then
if not Device:isTouchDevice() then
-- only draw underline for nontouch device
-- reset focus manager accordingly
self.selected = { x = 1, y = select_number }
-- set focus to requested menu item
self.item_group[select_number]:onFocus()
end
-- update page information
self.page_info_text:setText(util.template(_("page %1 of %2"), self.page, self.page_num))
2014-03-13 13:52:43 +00:00
self.page_info_left_chev:showHide(self.page_num > 1)
self.page_info_right_chev:showHide(self.page_num > 1)
self.page_info_first_chev:showHide(self.page_num > 2)
self.page_info_last_chev:showHide(self.page_num > 2)
2014-09-05 13:07:21 +00:00
self.page_return_arrow:showHide(self.onReturn ~= nil)
2014-03-13 13:52:43 +00:00
self.page_info_left_chev:enableDisable(self.page > 1)
self.page_info_right_chev:enableDisable(self.page < self.page_num)
self.page_info_first_chev:enableDisable(self.page > 1)
self.page_info_last_chev:enableDisable(self.page < self.page_num)
2014-09-05 13:07:21 +00:00
self.page_return_arrow:enableDisable(#self.paths > 0)
2014-03-13 13:52:43 +00:00
else
2016-12-14 19:08:57 +00:00
self.page_info_text:setText(_("No choices available"))
2014-03-13 13:52:43 +00:00
end
UIManager:setDirty("all", function()
local refresh_dimen =
old_dimen and old_dimen:combine(self.dimen)
or self.dimen
return "ui", refresh_dimen
end)
end
--[[
the itemnumber paramter determines menu page number after switching item table
1. itemnumber >= 0
the page number is calculated with items per page
2. itemnumber == nil
the page number is 1
3. itemnumber is negative number
the page number is not changed, used when item_table is appended with
new entries
--]]
2017-02-01 14:24:21 +00:00
function Menu:switchItemTable(new_title, new_item_table, itemnumber)
2014-09-05 13:07:21 +00:00
if self.menu_title and new_title then
2014-03-13 13:52:43 +00:00
self.menu_title.text = new_title
end
if itemnumber == nil then
self.page = 1
elseif itemnumber >= 0 then
self.page = math.ceil(itemnumber / self.perpage)
end
2014-11-23 10:06:20 +00:00
-- make sure current page is in right page range
local max_pages = math.ceil(#new_item_table / self.perpage)
if self.page > max_pages then
self.page = max_pages
end
2014-03-13 13:52:43 +00:00
self.item_table = new_item_table
2017-02-01 15:34:12 +00:00
self:updateItems()
2012-06-11 05:46:19 +00:00
end
function Menu:onSelectByShortCut(_, keyevent)
2014-03-13 13:52:43 +00:00
for k,v in ipairs(self.item_shortcuts) do
if k > self.perpage then
break
elseif v == keyevent.key then
if self.item_table[(self.page-1)*self.perpage + k] then
self:onMenuSelect(self.item_table[(self.page-1)*self.perpage + k])
end
break
end
end
return true
end
function Menu:onWrapFirst()
2014-03-13 13:52:43 +00:00
if self.page > 1 then
self.page = self.page - 1
local end_position = self.perpage
if self.page == self.page_num then
end_position = #self.item_table % self.perpage
end
self:updateItems(end_position)
end
return false
end
function Menu:onWrapLast()
2014-03-13 13:52:43 +00:00
if self.page < self.page_num then
self:onNextPage()
end
return false
end
--[[
override this function to process the item selected in a different manner
]]--
function Menu:onMenuSelect(item)
2014-03-13 13:52:43 +00:00
if item.sub_item_table == nil then
self.close_callback()
self:onMenuChoice(item)
else
-- save menu title for later resume
self.item_table.title = self.title
table.insert(self.item_table_stack, self.item_table)
2017-02-01 14:24:21 +00:00
self:switchItemTable(item.text, item.sub_item_table)
2014-03-13 13:52:43 +00:00
end
return true
end
--[[
2014-03-13 13:52:43 +00:00
default to call item callback
override this function to handle the choice
--]]
function Menu:onMenuChoice(item)
2014-03-13 13:52:43 +00:00
if item.callback then
item.callback()
end
return true
end
--[[
override this function to process the item hold in a different manner
]]--
function Menu:onMenuHold(item)
2014-03-13 13:52:43 +00:00
return true
end
function Menu:onNextPage()
2014-09-05 13:07:21 +00:00
if self.onNext and self.page == self.page_num - 1 then
self:onNext()
end
2014-03-13 13:52:43 +00:00
if self.page < self.page_num then
self.page = self.page + 1
2017-02-01 15:34:12 +00:00
self:updateItems()
2014-03-13 13:52:43 +00:00
elseif self.page == self.page_num then
-- on the last page, we check if we're on the last item
local end_position = #self.item_table % self.perpage
if end_position == 0 then
end_position = self.perpage
end
if end_position ~= self.selected.y then
self:updateItems(end_position)
end
self.page = 1
2017-02-01 15:34:12 +00:00
self:updateItems()
2014-03-13 13:52:43 +00:00
end
return true
end
function Menu:onPrevPage()
2014-03-13 13:52:43 +00:00
if self.page > 1 then
self.page = self.page - 1
elseif self.page == 1 then
2015-08-01 09:50:10 +00:00
self.page = self.page_num
2014-03-13 13:52:43 +00:00
end
2017-02-01 15:34:12 +00:00
self:updateItems()
2014-03-13 13:52:43 +00:00
return true
end
function Menu:onFirstPage()
self.page = 1
2017-02-01 15:34:12 +00:00
self:updateItems()
return true
end
function Menu:onLastPage()
self.page = self.page_num
2017-02-01 15:34:12 +00:00
self:updateItems()
return true
end
function Menu:onGotoPage(page)
self.page = page
2017-02-01 15:34:12 +00:00
self:updateItems()
return true
end
function Menu:onSelect()
2016-06-27 16:45:01 +00:00
local item = self.item_table[(self.page-1)*self.perpage+self.selected.y]
if item then
self:onMenuSelect(item)
end
2014-03-13 13:52:43 +00:00
return true
end
function Menu:onClose()
2014-03-13 13:52:43 +00:00
local table_length = #self.item_table_stack
if table_length == 0 then
self:onCloseAllMenus()
else
-- back to parent menu
2016-01-03 07:44:23 +00:00
local parent_item_table = table.remove(self.item_table_stack, table_length)
2017-02-01 14:24:21 +00:00
self:switchItemTable(parent_item_table.title, parent_item_table)
2014-03-13 13:52:43 +00:00
end
return true
end
function Menu:onCloseAllMenus()
2014-03-13 13:52:43 +00:00
UIManager:close(self)
if self.close_callback then
self.close_callback()
end
return true
end
function Menu:onTapCloseAllMenus(arg, ges_ev)
2014-03-13 13:52:43 +00:00
if ges_ev.pos:notIntersectWith(self.dimen) then
self:onCloseAllMenus()
return true
end
end
function Menu:onSwipe(arg, ges_ev)
2014-03-13 13:52:43 +00:00
if ges_ev.direction == "west" then
self:onNextPage()
2014-03-13 13:52:43 +00:00
elseif ges_ev.direction == "east" then
self:onPrevPage()
2014-03-13 13:52:43 +00:00
end
end
2013-10-18 20:38:07 +00:00
function Menu.itemTableFromTouchMenu(t)
local item_t = {}
for k,v in pairs(t) do
local item = { text = k }
if v.callback then
item.callback = v.callback
else
item.sub_item_table = v
end
table.insert(item_t, item)
end
return item_t
end
2013-10-18 20:38:07 +00:00
return Menu