diff --git a/frontend/ui/menu.lua b/frontend/ui/menu.lua index 36e4d02ae..b5934c74a 100644 --- a/frontend/ui/menu.lua +++ b/frontend/ui/menu.lua @@ -50,11 +50,42 @@ function ItemShortCutIcon:init() } end +--[[ +NOTICE: +@menu entry must be provided in order to close the menu +--]] +MenuCloseButton = InputContainer:new{ + align = "right", + menu = nil, + dimen = Geom:new{}, +} + +function MenuCloseButton:init() + self[1] = TextWidget:new{ + text = "x ", + face = Font:getFace("cfont", 22), + } + + local text_size = self[1]:getSize() + self.dimen.w, self.dimen.h = text_size.w, text_size.h + + self.ges_events.Close = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = "Close menu", + } +end + +function MenuCloseButton:onClose() + self.menu:onClose() + return true +end --[[ Widget that displays an item for menu - -]] +--]] MenuItem = InputContainer:new{ text = nil, detail = nil, @@ -77,22 +108,30 @@ function MenuItem:init() self.content_width = self.dimen.w - shortcut_icon_dimen.w - 15 -- we need this table per-instance, so we declare it here - self.active_key_events = { - Select = { {"Press"}, doc = "chose selected item" }, - } - self.ges_events = { - TapSelect = { - GestureRange:new{ - ges = "tap", - range = self.dimen, + if Device:isTouchDevice() then + self.ges_events = { + TapSelect = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = "Select Menu Item", }, - doc = "Select Menu Item", - }, - } + } + else + self.active_key_events = { + Select = { {"Press"}, doc = "chose selected item" }, + } + end w = sizeUtf8Text(0, self.dimen.w, self.face, self.text, true).x if w >= self.content_width then - self.active_key_events.ShowItemDetail = { {"Right"}, doc = "show item detail" } + if Device:isTouchDevice() then + else + self.active_key_events.ShowItemDetail = { + {"Right"}, doc = "show item detail" + } + end indicator = " >>" indicator_w = sizeUtf8Text(0, self.dimen.w, self.face, indicator, true).x self.text = getSubTextByWidth(self.text, self.face, @@ -153,7 +192,7 @@ end --[[ Widget that displays menu -]] +--]] Menu = FocusManager:new{ -- face for displaying item contents cface = Font:getFace("cfont", 22), @@ -195,38 +234,31 @@ function Menu:init() self.page = 1 self.page_num = math.ceil(#self.item_table / self.perpage) - -- set up keyboard events - self.key_events.Close = { {"Back"}, doc = "close menu" } - self.key_events.NextPage = { - {Input.group.PgFwd}, doc = "goto next page of the menu" - } - self.key_events.PrevPage = { - {Input.group.PgBack}, doc = "goto previous page of the menu" - } - -- we won't catch presses to "Right" - self.key_events.FocusRight = nil - if self.is_enable_shortcut then - self.key_events.SelectByShortCut = { {self.item_shortcuts} } - end - self.key_events.Select = { {"Press"}, doc = "select current menu item"} - + ----------------------------------- + -- start to set up widget layout -- + ----------------------------------- self.menu_title = TextWidget:new{ + align = "center", text = self.title, face = self.tface, } - + -- group for title bar + self.title_bar = OverlapGroup:new{ + dimen = {w = self.dimen.w}, + self.menu_title, + } -- group for items self.item_group = VerticalGroup:new{} - self.page_info = TextWidget:new{ - face = self.fface, - } -- VerticalGroup - + self.page_info = TextWidget:new{ + face = self.fface, + } -- group for menu layout local content = VerticalGroup:new{ - self.menu_title, + self.title_bar, self.item_group, self.page_info, - } -- VerticalGroup + } + -- maintain reference to content so we can change it later self.content_group = content if not self.is_borderless then @@ -241,6 +273,7 @@ function Menu:init() -- we need to substract border, margin and padding self.item_dimen.w = self.item_dimen.w - 14 else + -- no border for the menu self[1] = FrameContainer:new{ background = 0, bordersize = 0, @@ -251,6 +284,33 @@ function Menu:init() } end + ------------------------------------------ + -- start to set up input event callback -- + ------------------------------------------ + if Device:isTouchDevice() then + table.insert(self.title_bar, + MenuCloseButton:new{ + menu = self, + }) + else + -- set up keyboard events + self.key_events.Close = { {"Back"}, doc = "close menu" } + self.key_events.NextPage = { + {Input.group.PgFwd}, doc = "goto next page of the menu" + } + self.key_events.PrevPage = { + {Input.group.PgBack}, doc = "goto previous page of the menu" + } + -- we won't catch presses to "Right" + 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 + end + self.key_events.Select = { {"Press"}, doc = "select current menu item"} + + if #self.item_table > 0 then -- if the table is not yet initialized, this call -- must be done manually: @@ -305,7 +365,9 @@ function Menu:updateItems(select_number) self.page_info.text = "no choices available" end - UIManager:setDirty(self) + -- FIXME: this is a dirty hack to clear the previous menu + UIManager.repaint_all = true + --UIManager:setDirty(self) end function Menu:swithItemTable(new_title, new_item_table) diff --git a/frontend/ui/ui.lua b/frontend/ui/ui.lua index 23b14eaa2..896804dd9 100644 --- a/frontend/ui/ui.lua +++ b/frontend/ui/ui.lua @@ -14,7 +14,11 @@ Input:init() -- there is only one instance of this UIManager = { -- change this to set refresh type for next refresh - refresh_type = 1, -- defaults to 1 initially and will be set to 1 after each refresh + -- defaults to 1 initially and will be set to 1 after each refresh + refresh_type = 1, + -- force to repaint all the widget is stack, will be reset to false + -- after each ui loop + repaint_all = false, _running = true, _window_stack = {}, @@ -153,7 +157,7 @@ function UIManager:run() local dirty = false local update_area = Geom:new{} for _, widget in ipairs(self._window_stack) do - if self._dirty[widget.widget] then + if self.repaint_all or self._dirty[widget.widget] then widget_dimen = widget.widget:getSize() if widget_dimen then widget_area = Geom:new{ @@ -171,6 +175,7 @@ function UIManager:run() dirty = true end end + self.repaint_all = false -- @TODO make use of update_area on refresh 19.06 2012 (houqp) --DEBUG(update_area) diff --git a/frontend/ui/widget.lua b/frontend/ui/widget.lua index 43a6f28af..7da05bc46 100644 --- a/frontend/ui/widget.lua +++ b/frontend/ui/widget.lua @@ -478,6 +478,56 @@ function VerticalGroup:free() WidgetContainer.free(self) end + +--[[ +A Layout widget that puts objects above each other +]] +OverlapGroup = WidgetContainer:new{ + _size = nil, +} + +function OverlapGroup:getSize() + if not self._size then + self._size = {w = 0, h = 0} + self._offsets = { x = math.huge, y = math.huge } + for i, widget in ipairs(self) do + local w_size = widget:getSize() + if self._size.h < w_size.h then + self._size.h = w_size.h + end + if self._size.w < w_size.w then + self._size.w = w_size.w + end + end + end + + if self.dimen.w then + self._size.w = self.dimen.w + end + if self.dimen.h then + self._size.h = self.dimen.h + end + + return self._size +end + +function OverlapGroup:paintTo(bb, x, y) + local size = self:getSize() + + for i, wget in ipairs(self) do + local wget_size = wget:getSize() + if wget.align == "right" then + wget:paintTo(bb, x+size.w-wget_size.w, y) + elseif wget.align == "center" then + wget:paintTo(bb, x+(size.w-wget_size.w)/2, y) + else + -- default to left + wget:paintTo(bb, x, y) + end + end +end + + --[[ Dummy Widget that reserves vertical space ]]