[feat] Non-touch improvements (#8859)

FocusManager: fix round x use y layout
FocusManager: add tab and shift tab focus navigation support
FocusManager: handle Press key by default
FocusManager: make sure selected in instance level
FocusManager: add hold event support
FocusManager: Half move instead of edge move
FocusManager: add keymap override support
FocusManager: refocusWidget will delegate to parent FocusManager
Focusmanager: refocusWidget can execute on next tick
inputtext: can move out of focus on back
inputtext: fix cannot exit for non-touch device
inputtext: fix cannot input text with kindle dx physical keyboard
fontlightwidget: add non-touch support
datetimewidget: add non-touch support
datetimewidget: fix set date failed in kindle DX, fix datetimewidget month range to 1~23 by default
datetimewidget: make hour max value to 23
multiinputdialog: add non-touch support
checkbox: focusable and focus style
virtualkeyboard: no need to press two back to unfocus inputtext
virtualkeyboard: collect FocusManager event key names to let VirtualKeyboard disable them
openwithdialog: add non-touch support
inputdialog: can close via back button
enable all InputDialog and MultiInputDialog can be close by back
keyboardlayoutdialog: non-touch support
readertoc: non touch device can expand/collapse in toc
bookstatuswidget: non touch support
keyvaluepage: non-touch support
calendarview: non-touch support
reviewable/pr8876/r1
Philip Chan 2 years ago committed by GitHub
parent a2b641822f
commit 107156c0a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -641,6 +641,7 @@ function CloudStorage:createFolder(url)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(input_dialog) UIManager:close(input_dialog)
end, end,

@ -131,6 +131,7 @@ function DropBox:config(item, callback)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self.settings_dialog:onClose() self.settings_dialog:onClose()
UIManager:close(self.settings_dialog) UIManager:close(self.settings_dialog)

@ -125,6 +125,7 @@ Username and password are optional.]])
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self.settings_dialog:onClose() self.settings_dialog:onClose()
UIManager:close(self.settings_dialog) UIManager:close(self.settings_dialog)

@ -110,6 +110,7 @@ The start folder is appended to the server path.]])
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self.settings_dialog:onClose() self.settings_dialog:onClose()
UIManager:close(self.settings_dialog) UIManager:close(self.settings_dialog)

@ -255,6 +255,7 @@ function FileManager:setupLayout()
buttons = {{ buttons = {{
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
enabled = true, enabled = true,
callback = function() callback = function()
UIManager:close(file_manager.rename_dialog) UIManager:close(file_manager.rename_dialog)
@ -1015,6 +1016,7 @@ function FileManager:createFolder()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(input_dialog) UIManager:close(input_dialog)
end, end,

@ -125,6 +125,7 @@ function FileSearcher:onShowFileSearch(search_string)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(self.search_dialog) UIManager:close(self.search_dialog)
end, end,

@ -112,6 +112,7 @@ function SetDefaults:init()
local cancel_button = { local cancel_button = {
text = _("Cancel"), text = _("Cancel"),
id = "close",
enabled = true, enabled = true,
callback = function() callback = function()
self:close() self:close()

@ -82,6 +82,7 @@ function FileManagerShortcuts:addNewFolder()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(add_folder_input) UIManager:close(add_folder_input)
end, end,
@ -168,6 +169,7 @@ function FileManagerShortcuts:editFolderShortcut(item)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(edit_folder_input) UIManager:close(edit_folder_input)
end, end,

@ -1055,6 +1055,7 @@ function ReaderBookmark:renameBookmark(item, from_highlight, is_new_note, new_te
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(self.input) UIManager:close(self.input)
if is_new_note then -- "Add note" cancelled, remove saved highlight if is_new_note then -- "Add note" cancelled, remove saved highlight
@ -1121,6 +1122,7 @@ function ReaderBookmark:onSearchBookmark(bm_menu)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(input_dialog) UIManager:close(input_dialog)
end, end,

@ -689,6 +689,7 @@ function ReaderDictionary:onShowDictionaryLookup()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(self.dictionary_lookup_dialog) UIManager:close(self.dictionary_lookup_dialog)
end, end,

@ -642,6 +642,7 @@ function ReaderFooter:set_custom_text(touchmenu_instance)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(text_dialog) UIManager:close(text_dialog)
end, end,

@ -81,6 +81,7 @@ x for an absolute page number
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self:close() self:close()
end, end,

@ -114,6 +114,7 @@ function ReaderSearch:onShowFulltextSearchInput()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(self.input_dialog) UIManager:close(self.input_dialog)
end, end,

@ -812,15 +812,20 @@ function ReaderToc:onShowToc()
end end
function toc_menu:onMenuHold(item) function toc_menu:onMenuHold(item)
-- Match the items' width if not Device:isTouchDevice() and (item.state and item.state.callback) then
local infomessage = InfoMessage:new{ -- non touch to expand toc
width = Screen:getWidth() - (Size.padding.fullscreen * (can_collapse and 4 or 3)), item.state.callback()
alignment = "center", else
show_icon = false, -- Match the items' width
text = item.text, local infomessage = InfoMessage:new{
face = Font:getFace("infofont", self.items_font_size), width = Screen:getWidth() - (Size.padding.fullscreen * (can_collapse and 4 or 3)),
} alignment = "center",
UIManager:show(infomessage) show_icon = false,
text = item.text,
face = Font:getFace("infofont", self.items_font_size),
}
UIManager:show(infomessage)
end
return true return true
end end

@ -188,6 +188,7 @@ function ReaderUserHyph:modifyUserEntry(word)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(input_dialog) UIManager:close(input_dialog)
end, end,

@ -44,6 +44,7 @@ function ReaderWikipedia:lookupInput()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(self.input_dialog) UIManager:close(self.input_dialog)
end, end,
@ -153,6 +154,7 @@ function ReaderWikipedia:addToMainMenu(menu_items)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(wikilang_input) UIManager:close(wikilang_input)
end, end,

@ -661,6 +661,7 @@ function ReaderUI:unlockDocumentWithPassword(document, try_again)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
enabled = true, enabled = true,
callback = function() callback = function()
self:closeDialog() self:closeDialog()

@ -155,18 +155,23 @@ end
function Kindle:setDateTime(year, month, day, hour, min, sec) function Kindle:setDateTime(year, month, day, hour, min, sec)
if hour == nil or min == nil then return true end if hour == nil or min == nil then return true end
local command local commands = {}
if year and month and day then if year and month and day then
command = string.format("date -s '%d-%d-%d %d:%d:%d'", year, month, day, hour, min, sec) table.insert(commands, string.format("date -s '%d-%d-%d %d:%d:%d'", year, month, day, hour, min, sec))
--Kindle DX
--BusyBox v1.7.2 (2011-01-13 18:01:58 PST) multi-call binary
--Usage: date [OPTION]... [MMDDhhmm[[CC]YY][.ss]] [+FORMAT]
table.insert(commands, string.format("date -s '%02d%02d%02d%02d%04d.%02d'", month, day, hour, min, year, sec))
else else
command = string.format("date -s '%d:%d'",hour, min) table.insert(commands,string.format("date -s '%d:%d'",hour, min))
end end
if os.execute(command) == 0 then for _, command in ipairs(commands) do
os.execute('hwclock -u -w') if os.execute(command) == 0 then
return true os.execute("hwclock -u -w")
else return true
return false end
end end
return false
end end
function Kindle:usbPlugIn() function Kindle:usbPlugIn()

@ -65,6 +65,7 @@ return {
[1073742050] = "Alt", -- left alt [1073742050] = "Alt", -- left alt
[1073742054] = "AA", -- right alt key [1073742054] = "AA", -- right alt key
[1073741925] = "ContextMenu", -- Context menu key [1073741925] = "ContextMenu", -- Context menu key
[1073741942] = "ContextMenu", -- Context menu key
[0x400000E0] = "Ctrl", -- Left Ctrl [0x400000E0] = "Ctrl", -- Left Ctrl
[0x400000E4] = "Ctrl", -- Right Ctrl [0x400000E4] = "Ctrl", -- Right Ctrl
[1073742051] = "Win", -- Left Win/Cmd [1073742051] = "Win", -- Left Win/Cmd

@ -365,6 +365,7 @@ function Screensaver:setMessage()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(self.input_dialog) UIManager:close(self.input_dialog)
end, end,

@ -3,13 +3,13 @@ local Button = require("ui/widget/button")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device") local Device = require("device")
local Font = require("ui/font") local Font = require("ui/font")
local FocusManager = require("ui/widget/focusmanager")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange") local GestureRange = require("ui/gesturerange")
local HorizontalGroup = require("ui/widget/horizontalgroup") local HorizontalGroup = require("ui/widget/horizontalgroup")
local HorizontalSpan = require("ui/widget/horizontalspan") local HorizontalSpan = require("ui/widget/horizontalspan")
local ImageWidget = require("ui/widget/imagewidget") local ImageWidget = require("ui/widget/imagewidget")
local InputContainer = require("ui/widget/container/inputcontainer")
local InputDialog = require("ui/widget/inputdialog") local InputDialog = require("ui/widget/inputdialog")
local InputText = require("ui/widget/inputtext") local InputText = require("ui/widget/inputtext")
local LeftContainer = require("ui/widget/container/leftcontainer") local LeftContainer = require("ui/widget/container/leftcontainer")
@ -39,7 +39,7 @@ local stats_book = {}
["status"] = "Reading" ["status"] = "Reading"
["modified"] = "24.01.2016" ["modified"] = "24.01.2016"
},]] },]]
local BookStatusWidget = InputContainer:new{ local BookStatusWidget = FocusManager:new{
padding = Size.padding.fullscreen, padding = Size.padding.fullscreen,
settings = nil, settings = nil,
thumbnail = nil, thumbnail = nil,
@ -54,6 +54,7 @@ local BookStatusWidget = InputContainer:new{
} }
function BookStatusWidget:init() function BookStatusWidget:init()
self.layout = {}
if self.settings then if self.settings then
-- What a blank, full summary table should look like -- What a blank, full summary table should look like
local new_summary = { local new_summary = {
@ -100,11 +101,7 @@ function BookStatusWidget:init()
} }
if Device:hasKeys() then if Device:hasKeys() then
self.key_events = { self.key_events.Close = { { Device.input.group.Back }, doc = "close dialog" }
--don't get locked in on non touch devices
AnyKeyPressed = { { Device.input.group.Any },
seqtext = "any key", doc = "close dialog" }
}
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events.Swipe = { self.ges_events.Swipe = {
@ -252,28 +249,35 @@ function BookStatusWidget:setStar(num)
self.stars_container:clear() self.stars_container:clear()
local stars_group = HorizontalGroup:new{ align = "center" } local stars_group = HorizontalGroup:new{ align = "center" }
local row = {}
if num then if num then
self.summary.rating = num self.summary.rating = num
self:saveSummary() self:saveSummary()
for i = 1, num do for i = 1, num do
table.insert(stars_group, self.star:new{ local star = self.star:new{
icon = "star.full", icon = "star.full",
callback = function() self:setStar(i) end callback = function() self:setStar(i) end
}) }
table.insert(stars_group, star)
table.insert(row, star)
end end
else else
num = 0 num = 0
end end
for i = num + 1, 5 do for i = num + 1, 5 do
table.insert(stars_group, self.star:new{ callback = function() self:setStar(i) end }) local star = self.star:new{ callback = function() self:setStar(i) end }
table.insert(stars_group, star)
table.insert(row, star)
end end
self.layout[1] = row
table.insert(self.stars_container, stars_group) table.insert(self.stars_container, stars_group)
-- Individual stars are Button, w/ flash_ui, they'll have their own flash. -- Individual stars are Button, w/ flash_ui, they'll have their own flash.
-- And we need to redraw the full widget, because we don't know the coordinates of stars_container :/. -- And we need to redraw the full widget, because we don't know the coordinates of stars_container :/.
self:refocusWidget()
UIManager:setDirty(self, "ui", nil, true) UIManager:setDirty(self, "ui", nil, true)
return true return true
end end
@ -487,6 +491,7 @@ function BookStatusWidget:genSummaryGroup(width)
readonly = self.readonly, readonly = self.readonly,
hint = _("A few words about the book"), hint = _("A few words about the book"),
} }
table.insert(self.layout, {self.input_note})
return VerticalGroup:new{ return VerticalGroup:new{
VerticalSpan:new{ width = Size.span.vertical_large }, VerticalSpan:new{ width = Size.span.vertical_large },
@ -562,6 +567,7 @@ function BookStatusWidget:generateSwitchGroup(width)
readonly = self.readonly, readonly = self.readonly,
} }
switch:setPosition(position) switch:setPosition(position)
self:mergeLayoutInVertical(switch)
return VerticalGroup:new{ return VerticalGroup:new{
VerticalSpan:new{ width = Screen:scaleBySize(10) }, VerticalSpan:new{ width = Screen:scaleBySize(10) },
@ -582,11 +588,6 @@ function BookStatusWidget:onConfigChoose(values, name, event, args, position)
end) end)
end end
function BookStatusWidget:onAnyKeyPressed()
return self:onClose()
end
function BookStatusWidget:onSwipe(arg, ges_ev) function BookStatusWidget:onSwipe(arg, ges_ev)
if ges_ev.direction == "south" then if ges_ev.direction == "south" then
-- Allow easier closing with swipe down -- Allow easier closing with swipe down
@ -631,6 +632,7 @@ function BookStatusWidget:onSwitchFocus(inputbox)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self:closeInputDialog() self:closeInputDialog()
end, end,
@ -653,6 +655,7 @@ end
function BookStatusWidget:closeInputDialog() function BookStatusWidget:closeInputDialog()
UIManager:close(self.note_dialog) UIManager:close(self.note_dialog)
self.input_note:onUnfocus();
end end
return BookStatusWidget return BookStatusWidget

@ -253,6 +253,7 @@ function ButtonProgressWidget:update()
table.insert(self.layout[1], button) table.insert(self.layout[1], button)
end end
self:refocusWidget()
UIManager:setDirty(self.show_parrent, function() UIManager:setDirty(self.show_parrent, function()
return "ui", self.dimen return "ui", self.dimen
end) end)

@ -24,12 +24,10 @@ local ButtonTable = FocusManager:new{
zero_sep = false, zero_sep = false,
button_font_face = "cfont", button_font_face = "cfont",
button_font_size = 20, button_font_size = 20,
auto_focus_first_button = true,
} }
function ButtonTable:init() function ButtonTable:init()
self.width = self.width or math.floor(math.min(Screen:getWidth(), Screen:getHeight()) * 0.9) self.width = self.width or math.floor(math.min(Screen:getWidth(), Screen:getHeight()) * 0.9)
self.selected = { x = 1, y = 1 }
self.buttons_layout = {} self.buttons_layout = {}
self.button_by_id = {} self.button_by_id = {}
self.container = VerticalGroup:new{ width = self.width } self.container = VerticalGroup:new{ width = self.width }
@ -100,10 +98,7 @@ function ButtonTable:init()
self:addHorizontalSep(true, false, false) self:addHorizontalSep(true, false, false)
if Device:hasDPad() then if Device:hasDPad() then
self.layout = self.buttons_layout self.layout = self.buttons_layout
if self.auto_focus_first_button then self:refocusWidget()
self.layout[1][1]:onFocus()
end
self.key_events.SelectByKeyPress = { {{"Press"}} }
else else
self.key_events = {} -- deregister all key press event listeners self.key_events = {} -- deregister all key press event listeners
end end
@ -129,15 +124,6 @@ function ButtonTable:addHorizontalSep(vspan_before, add_line, vspan_after, black
end end
end end
function ButtonTable:onSelectByKeyPress()
local item = self:getFocusItem()
if item and item.enabled then
item.callback()
return true
end
return false
end
function ButtonTable:getButtonById(id) function ButtonTable:getButtonById(id)
return self.button_by_id[id] -- nil if not found return self.button_by_id[id] -- nil if not found
end end

@ -16,7 +16,6 @@ Example:
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local CheckMark = require("ui/widget/checkmark") local CheckMark = require("ui/widget/checkmark")
local Device = require("device")
local Font = require("ui/font") local Font = require("ui/font")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
local GestureRange = require("ui/gesturerange") local GestureRange = require("ui/gesturerange")
@ -97,32 +96,30 @@ function CheckButton:initCheckButton(checked)
self.dimen = self._frame:getSize() self.dimen = self._frame:getSize()
self[1] = self._frame self[1] = self._frame
if Device:isTouchDevice() then self.ges_events = {
self.ges_events = { TapCheckButton = {
TapCheckButton = { GestureRange:new{
GestureRange:new{ ges = "tap",
ges = "tap", range = self.dimen,
range = self.dimen,
},
doc = "Tap Button",
}, },
HoldCheckButton = { doc = "Tap Button",
GestureRange:new{ },
ges = "hold", HoldCheckButton = {
range = self.dimen, GestureRange:new{
}, ges = "hold",
doc = "Hold Button", range = self.dimen,
},
doc = "Hold Button",
},
-- Safe-guard for when used inside a MovableContainer
HoldReleaseCheckButton = {
GestureRange:new{
ges = "hold_release",
range = self.dimen,
}, },
-- Safe-guard for when used inside a MovableContainer doc = "Hold Release Button",
HoldReleaseCheckButton = {
GestureRange:new{
ges = "hold_release",
range = self.dimen,
},
doc = "Hold Release Button",
}
} }
end }
end end
function CheckButton:onTapCheckButton() function CheckButton:onTapCheckButton()
@ -225,4 +222,20 @@ function CheckButton:disable()
end) end)
end end
function CheckButton:onFocus()
if not self.enabled then
return false
end
self._frame.invert = true
return true
end
function CheckButton:onUnfocus()
if not self.enabled then
return false
end
self._frame.invert = false
return true
end
return CheckButton return CheckButton

@ -665,9 +665,7 @@ end
function ConfigOption:_itemGroupToLayoutLine(option_items_group) function ConfigOption:_itemGroupToLayoutLine(option_items_group)
local layout_line = {} local layout_line = {}
-- Insert items (skpping item_spacing without a .name attribute), -- Insert items (skpping item_spacing without a .name attribute),
-- skipping indices at the beginning of the line in the layout local j = 1 -- no nil in row head
-- to align it with the current selected tab
local j = self.config.panel_index
for i, v in ipairs(option_items_group) do for i, v in ipairs(option_items_group) do
if v.name then if v.name then
if v.layout and v.disableFocusManagement then -- it is a FocusManager if v.layout and v.disableFocusManagement then -- it is a FocusManager
@ -680,7 +678,7 @@ function ConfigOption:_itemGroupToLayoutLine(option_items_group)
j = j + 1 j = j + 1
end end
end end
v:disableFocusManagement() v:disableFocusManagement(self.config)
else else
layout_line[j] = v layout_line[j] = v
j = j + 1 j = j + 1
@ -883,9 +881,6 @@ function ConfigDialog:init()
local close_keys = Device:hasFewKeys() and { "Back", "Left" } or Device.input.group.Back local close_keys = Device:hasFewKeys() and { "Back", "Left" } or Device.input.group.Back
self.key_events.Close = { { close_keys }, doc = "close config menu" } self.key_events.Close = { { close_keys }, doc = "close config menu" }
end end
if Device:hasDPad() then
self.key_events.Select = { {"Press"}, doc = "select current menu item" }
end
end end
function ConfigDialog:updateConfigPanel(index) function ConfigDialog:updateConfigPanel(index)
@ -893,6 +888,7 @@ function ConfigDialog:updateConfigPanel(index)
end end
function ConfigDialog:update() function ConfigDialog:update()
self:moveFocusTo(1, 1) -- reset selected for re-created layout
self.layout = {} self.layout = {}
if self.config_menubar then if self.config_menubar then
@ -927,8 +923,7 @@ function ConfigDialog:update()
self.dialog_frame.dimen = old_dimen self.dialog_frame.dimen = old_dimen
-- Reset the focusmanager cursor -- Reset the focusmanager cursor
self.selected.y=#self.layout self:moveFocusTo(self.panel_index, #self.layout, FocusManager.NOT_FOCUS)
self.selected.x=self.panel_index
self[1] = BottomContainer:new{ self[1] = BottomContainer:new{
dimen = Screen:getSize(), dimen = Screen:getSize(),
@ -1463,8 +1458,4 @@ function ConfigDialog:onClose()
return true return true
end end
function ConfigDialog:onSelect()
return self:sendTapEventToFocusedWidget()
end
return ConfigDialog return ConfigDialog

@ -35,7 +35,6 @@ local TextBoxWidget = require("ui/widget/textboxwidget")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup") local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan") local VerticalSpan = require("ui/widget/verticalspan")
local logger = require("logger")
local _ = require("gettext") local _ = require("gettext")
local Screen = Device.screen local Screen = Device.screen
@ -72,9 +71,7 @@ function ConfirmBox:init()
} }
end end
if Device:hasKeys() then if Device:hasKeys() then
self.key_events = { self.key_events.Close = { {Device.input.group.Back}, doc = "cancel" }
Close = { {Device.input.group.Back}, doc = "cancel" }
}
end end
end end
local text_widget = TextBoxWidget:new{ local text_widget = TextBoxWidget:new{
@ -214,15 +211,4 @@ function ConfirmBox:onTapClose(arg, ges)
return true return true
end end
function ConfirmBox:onSelect()
logger.dbg("selected:", self.selected.x)
if self.selected.x == 1 then
self:ok_callback()
else
self:cancel_callback()
end
UIManager:close(self)
return true
end
return ConfirmBox return ConfirmBox

@ -62,22 +62,27 @@ end
function FrameContainer:onFocus() function FrameContainer:onFocus()
if not self.focusable then if not self.focusable then
return return false
end end
self._origin_bordersize = self.bordersize self._origin_bordersize = self.bordersize
self._origin_border_color = self.color self._origin_border_color = self.color
self.bordersize = self.focus_border_size self.bordersize = self.focus_border_size
self.color = self.focus_border_color self.color = self.focus_border_color
self._focused = true
return true return true
end end
function FrameContainer:onUnfocus() function FrameContainer:onUnfocus()
if not self.focusable then if not self.focusable then
return return false
end end
self.bordersize = self._origin_bordersize if self._focused then
self.color = self._origin_border_color self.bordersize = self._origin_bordersize
return true self.color = self._origin_border_color
self._focused = nil
return true
end
return false
end end

@ -282,6 +282,7 @@ function InputContainer:onInput(input, ignore_first_hold_release)
{ {
{ {
text = input.cancel_text or _("Cancel"), text = input.cancel_text or _("Cancel"),
id = "close",
callback = function() callback = function()
self:closeInputDialog() self:closeInputDialog()
end, end,

@ -39,12 +39,12 @@ local Blitbuffer = require("ffi/blitbuffer")
local ButtonTable = require("ui/widget/buttontable") local ButtonTable = require("ui/widget/buttontable")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device") local Device = require("device")
local FocusManager = require("ui/widget/focusmanager")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange") local GestureRange = require("ui/gesturerange")
local Font = require("ui/font") local Font = require("ui/font")
local HorizontalGroup = require("ui/widget/horizontalgroup") local HorizontalGroup = require("ui/widget/horizontalgroup")
local InputContainer = require("ui/widget/container/inputcontainer")
local NumberPickerWidget = require("ui/widget/numberpickerwidget") local NumberPickerWidget = require("ui/widget/numberpickerwidget")
local Size = require("ui/size") local Size = require("ui/size")
local TextBoxWidget = require("ui/widget/textboxwidget") local TextBoxWidget = require("ui/widget/textboxwidget")
@ -56,7 +56,7 @@ local _ = require("gettext")
local Screen = Device.screen local Screen = Device.screen
local T = require("ffi/util").template local T = require("ffi/util").template
local DateTimeWidget = InputContainer:new{ local DateTimeWidget = FocusManager:new{
title_face = Font:getFace("x_smalltfont"), title_face = Font:getFace("x_smalltfont"),
info_text = nil, info_text = nil,
width = nil, width = nil,
@ -66,7 +66,6 @@ local DateTimeWidget = InputContainer:new{
month = 1, month = 1,
year = 2021, year = 2021,
hour = 12, hour = 12,
hour_max = 23,
min = 0, min = 0,
ok_text = _("Apply"), ok_text = _("Apply"),
cancel_text = _("Close"), cancel_text = _("Close"),
@ -76,14 +75,13 @@ local DateTimeWidget = InputContainer:new{
} }
function DateTimeWidget:init() function DateTimeWidget:init()
self.layout = {}
self.screen_width = Screen:getWidth() self.screen_width = Screen:getWidth()
self.screen_height = Screen:getHeight() self.screen_height = Screen:getHeight()
self.width = self.width or math.floor(math.min(self.screen_width, self.screen_height) * self.width = self.width or math.floor(math.min(self.screen_width, self.screen_height) *
(self.is_date and 0.8 or 0.6)) (self.is_date and 0.8 or 0.6))
if Device:hasKeys() then if Device:hasKeys() then
self.key_events = { self.key_events.Close = { {Device.input.group.Back}, doc = "close date widget" }
Close = { {Device.input.group.Back}, doc = "close date widget" }
}
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
@ -100,11 +98,11 @@ function DateTimeWidget:init()
end end
-- Actually the widget layout -- Actually the widget layout
self:layout() self:createLayout()
end end
local year_widget, month_hour_widget, day_min_widget local year_widget, month_hour_widget, day_min_widget
function DateTimeWidget:layout() function DateTimeWidget:createLayout()
year_widget = NumberPickerWidget:new{ year_widget = NumberPickerWidget:new{
show_parent = self, show_parent = self,
value = self.year, value = self.year,
@ -113,14 +111,18 @@ function DateTimeWidget:layout()
value_step = 1, value_step = 1,
value_hold_step = self.year_hold_step or 4, value_hold_step = self.year_hold_step or 4,
} }
if self.is_date then
self:mergeLayoutInHorizontal(year_widget)
end
month_hour_widget = NumberPickerWidget:new{ month_hour_widget = NumberPickerWidget:new{
show_parent = self, show_parent = self,
value = self.is_date and self.month or self.hour, value = self.is_date and self.month or self.hour,
value_min = self.hour_min or self.month_min or (self.is_date and 1 or 0), value_min = self.hour_min or self.month_min or (self.is_date and 1 or 0),
value_max = self.hour_max or self.month_max or (self.is_date and 12 or 24), value_max = self.hour_max or self.month_max or (self.is_date and 12 or 23),
value_step = 1, value_step = 1,
value_hold_step = self.hour_hold_step or self.month_hold_step or 3, value_hold_step = self.hour_hold_step or self.month_hold_step or 3,
} }
self:mergeLayoutInHorizontal(month_hour_widget)
day_min_widget = NumberPickerWidget:new{ day_min_widget = NumberPickerWidget:new{
show_parent = self, show_parent = self,
value = self.is_date and self.day or self.min, value = self.is_date and self.day or self.min,
@ -131,6 +133,7 @@ function DateTimeWidget:layout()
date_month_hour = month_hour_widget, date_month_hour = month_hour_widget,
date_year = year_widget, date_year = year_widget,
} }
self:mergeLayoutInHorizontal(day_min_widget)
local separator_space = TextBoxWidget:new{ local separator_space = TextBoxWidget:new{
text = self.is_date and "" or ":", text = self.is_date and "" or ":",
alignment = "center", alignment = "center",
@ -221,6 +224,7 @@ function DateTimeWidget:layout()
zero_sep = true, zero_sep = true,
show_parent = self, show_parent = self,
} }
self:mergeLayoutInVertical(ok_cancel_buttons)
self.date_frame = FrameContainer:new{ self.date_frame = FrameContainer:new{
radius = Size.radius.window, radius = Size.radius.window,
@ -260,6 +264,7 @@ function DateTimeWidget:layout()
self.date_frame, self.date_frame,
} }
} }
self:refocusWidget()
UIManager:setDirty(self, function() UIManager:setDirty(self, function()
return "ui", self.date_frame.dimen return "ui", self.date_frame.dimen
end) end)

@ -1227,6 +1227,7 @@ function DictQuickLookup:lookupInputWord(hint)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self:closeInputDialog() self:closeInputDialog()
end, end,

@ -66,7 +66,6 @@ function DoubleSpinWidget:init()
end end
if Device:hasKeys() then if Device:hasKeys() then
self.key_events.Close = { {Device.input.group.Back}, doc = "close doublespin widget" } self.key_events.Close = { {Device.input.group.Back}, doc = "close doublespin widget" }
self.key_events.Press = { {"Press"}, doc = "press button" }
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
@ -229,7 +228,6 @@ function DoubleSpinWidget:update(numberpicker_left_value, numberpicker_right_val
buttons = buttons, buttons = buttons,
zero_sep = true, zero_sep = true,
show_parent = self, show_parent = self,
auto_focus_first_button = false,
} }
self:mergeLayoutInVertical(button_table) self:mergeLayoutInVertical(button_table)
@ -269,7 +267,7 @@ function DoubleSpinWidget:update(numberpicker_left_value, numberpicker_right_val
}, },
self.movable, self.movable,
} }
self:focusTopLeftWidget() self:refocusWidget()
UIManager:setDirty(self, function() UIManager:setDirty(self, function()
return "ui", self.widget_frame.dimen return "ui", self.widget_frame.dimen
end) end)
@ -313,8 +311,4 @@ function DoubleSpinWidget:onClose()
return true return true
end end
function DoubleSpinWidget:onPress()
return self:sendTapEventToFocusedWidget()
end
return DoubleSpinWidget return DoubleSpinWidget

@ -1,8 +1,10 @@
local bit = require("bit")
local Device = require("device") local Device = require("device")
local Event = require("ui/event") local Event = require("ui/event")
local InputContainer = require("ui/widget/container/inputcontainer") local InputContainer = require("ui/widget/container/inputcontainer")
local logger = require("logger") local logger = require("logger")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local util = require("util")
--[[ --[[
Wrapper Widget that manages focus for a whole dialog Wrapper Widget that manages focus for a whole dialog
@ -33,20 +35,153 @@ local FocusManager = InputContainer:new{
function FocusManager:init() function FocusManager:init()
if not self.selected then if not self.selected then
self.selected = { x = 1, y = 1 } self.selected = { x = 1, y = 1 }
else
self.selected = self.selected -- make sure current FocusManager has its own selected field
end end
if Device:hasDPad() then if Device:hasDPad() then
self.key_events = { local event_keys = {}
-- these will all generate the same event, just with different arguments -- these will all generate the same event, just with different arguments
FocusUp = { {"Up"}, doc = "move focus up", event = "FocusMove", args = {0, -1} }, table.insert(event_keys, {"FocusUp", { {"Up"}, doc = "move focus up", event = "FocusMove", args = {0, -1} } })
FocusDown = { {"Down"}, doc = "move focus down", event = "FocusMove", args = {0, 1} }, table.insert(event_keys, {"FocusRight", { {"Right"}, doc = "move focus right", event = "FocusMove", args = {1, 0} } })
FocusLeft = { {"Left"}, doc = "move focus left", event = "FocusMove", args = {-1, 0} }, table.insert(event_keys, {"FocusDown", { {"Down"}, doc = "move focus down", event = "FocusMove", args = {0, 1} } })
FocusRight = { {"Right"}, doc = "move focus right", event = "FocusMove", args = {1, 0} }, table.insert(event_keys, {"Press", { {"Press"}, doc = "tap the widget", event="Press" }})
} local FEW_KEYS_END_INDEX = #event_keys -- Few keys device: only setup up, down, right and press
if Device:hasFewKeys() then
self.key_events.FocusLeft = nil table.insert(event_keys, {"FocusLeft", { {"Left"}, doc = "move focus left", event = "FocusMove", args = {-1, 0} } })
local NORMAL_KEYS_END_INDEX = #event_keys
-- Advanced Feature: following event handlers can be enabled via settings.reader.lua
-- Key combinations (Sym, Alt+Up, Tab, Shift+Tab and so on) are not used but shown as examples here
table.insert(event_keys, {"Hold", { {"Sym", "AA"}, doc = "tap and hold the widget", event="Hold" } })
-- half rows/columns move, it is helpful for slow device like Kindle DX to move quickly
table.insert(event_keys, {"HalfFocusUp", { {"Alt", "Up"}, doc = "move focus half columns up", event = "FocusHalfMove", args = {"up"} } })
table.insert(event_keys, {"HalfFocusRight", { {"Alt", "Right"}, doc = "move focus half rows right", event = "FocusHalfMove", args = {"right"} } })
table.insert(event_keys, {"HalfFocusDown", { {"Alt", "Down"}, doc = "move focus half columns down", event = "FocusHalfMove", args = {"down"} } })
table.insert(event_keys, {"HalfFocusLeft", { {"Alt", "Left"}, doc = "move focus half rows left", event = "FocusHalfMove", args = {"left"} } })
-- for PC navigation behavior support
table.insert(event_keys, {"FocusNext", { {"Tab"}, doc = "move focus to next widget", event="FocusNext"} })
table.insert(event_keys, {"FocusPrevious", { {"Shift", "Tab"}, doc = "move focus to previous widget", event="FocusPrevious"} })
self.key_events = {}
self.builtin_key_events = {}
self.extra_key_events = {}
for i = 1, FEW_KEYS_END_INDEX do
local key_name = event_keys[i][1]
self.key_events[key_name] = event_keys[i][2]
self.builtin_key_events[key_name] = event_keys[i][2]
end
if not Device:hasFewKeys() then
for i = FEW_KEYS_END_INDEX+1, NORMAL_KEYS_END_INDEX do
local key_name = event_keys[i][1]
self.key_events[key_name] = event_keys[i][2]
self.builtin_key_events[key_name] = event_keys[i][2]
end
local focus_manager_setting = G_reader_settings:child("focus_manager")
-- Enable advanced feature, like Hold, FocusNext, FocusPrevious
-- Can also add extra arrow keys like using A, W, D, S for Left, Up, Right, Down
local alternative_keymaps = focus_manager_setting:readSetting("alternative_keymaps")
if type(alternative_keymaps) == "table" then
for i = 1, #event_keys do
local key_name = event_keys[i][1]
local alternative_keymap = alternative_keymaps[key_name]
if alternative_keymap then
local handler_defition = util.tableDeepCopy(event_keys[i][2])
handler_defition[1] = alternative_keymap -- replace sample key combinations
local new_event_key = "Alternative" .. key_name
self.key_events[new_event_key] = handler_defition
self.extra_key_events[new_event_key] = handler_defition
end
end
end
end
end
end
function FocusManager:isAlternativeKey(key)
for _, seq in pairs(self.extra_key_events) do
for _, oneseq in ipairs(seq) do
if key:match(oneseq) then
return true
end
end end
end end
return false
end
function FocusManager:onFocusHalfMove(args)
if not self.layout then
return false
end
local direction = unpack(args)
local x, y = self.selected.x, self.selected.y
local row = self.layout[self.selected.y]
local dx, dy = 0, 0
if direction == "up" then
dy = - math.floor(#self.layout / 2)
if dy == 0 then
dy = -1
elseif dy + y <= 0 then
dy = -y + 1 -- first row
end
elseif direction == "down" then
dy = math.floor(#self.layout / 2)
if dy == 0 then
dy = 1
elseif dy + y > #self.layout then
dy = #self.layout - y -- last row
end
elseif direction == "left" then
dx = - math.floor(#row / 2)
if dx == 0 then
dx = -1
elseif dx + x <= 0 then
dx = -x + 1 -- first column
end
elseif direction == "right" then
dx = math.floor(#row / 2)
if dx == 0 then
dx = 1
elseif dx + x > #row then
dx = #row - y -- last column
end
end
return self:onFocusMove({dx, dy})
end
function FocusManager:onPress()
return self:sendTapEventToFocusedWidget()
end
function FocusManager:onHold()
return self:sendHoldEventToFocusedWidget()
end
-- for tab key
function FocusManager:onFocusNext()
if not self.layout then
return false
end
local x, y = self.selected.x, self.selected.y
local row = self.layout[y]
local dx, dy = 1, 0
if not row[x + dx] then -- beyond end of column, go to next row
dx, dy = 0, 1
end
return self:onFocusMove({dx, dy})
end
-- for backtab key
function FocusManager:onFocusPrevious()
if not self.layout then
return false
end
local x, y = self.selected.x, self.selected.y
local row = self.layout[y]
local dx, dy = -1, 0
if not row[x + dx] then -- beyond start of column, go to previous row
dx, dy = 0, -1
end
return self:onFocusMove({dx, dy})
end end
function FocusManager:onFocusMove(args) function FocusManager:onFocusMove(args)
@ -101,11 +236,50 @@ function FocusManager:onFocusMove(args)
return true return true
end end
-- constant, used to reset focus widget after layout recreation
-- not send Unfocus event
FocusManager.NOT_UNFOCUS = 1
-- not need to send Focus event
FocusManager.NOT_FOCUS = 2
--- Move focus to specified widget
function FocusManager:moveFocusTo(x, y, focus_flags)
focus_flags = focus_flags or 0
if not self.layout then
return false
end
local current_item = nil
if self.layout[self.selected.y] then
current_item = self.layout[self.selected.y][self.selected.x]
end
local target_item = nil
if self.layout[y] then
target_item = self.layout[y][x]
end
if target_item then
logger.dbg("Move focus position to: " .. y .. ", " .. x)
self.selected.x = x
self.selected.y = y
-- widget create new layout on update, previous may be removed from new layout.
if Device:hasDPad() then
if not bit.band(focus_flags, FocusManager.NOT_UNFOCUS) and current_item and current_item ~= target_item then
current_item:handleEvent(Event:new("Unfocus"))
end
if not bit.band(focus_flags, FocusManager.NOT_FOCUS) then
target_item:handleEvent(Event:new("Focus"))
UIManager:setDirty(self.show_parent or self, "fast")
end
end
return true
end
return false
end
--- Go to the last valid item directly left or right of the current item. --- Go to the last valid item directly left or right of the current item.
-- @return false if none could be found -- @return false if none could be found
function FocusManager:_wrapAroundX(dx) function FocusManager:_wrapAroundX(dx)
local x = self.selected.x local x = self.selected.x
while self.layout[x - dx] do while self.layout[self.selected.y][x - dx] do
x = x - dx x = x - dx
end end
if x ~= self.selected.x then if x ~= self.selected.x then
@ -168,7 +342,7 @@ function FocusManager:getFocusItem()
return self.layout[self.selected.y][self.selected.x] return self.layout[self.selected.y][self.selected.x]
end end
function FocusManager:sendTapEventToFocusedWidget() function FocusManager:_sendGestureEventToFocusedWidget(gesture)
local focused_widget = self:getFocusItem() local focused_widget = self:getFocusItem()
if focused_widget then if focused_widget then
-- center of widget position -- center of widget position
@ -177,8 +351,9 @@ function FocusManager:sendTapEventToFocusedWidget()
point.y = point.y + point.h / 2 point.y = point.y + point.h / 2
point.w = 0 point.w = 0
point.h = 0 point.h = 0
logger.dbg("FocusManager: Send " .. gesture .. " to " .. point.x .. ", " .. point.y)
UIManager:sendEvent(Event:new("Gesture", { UIManager:sendEvent(Event:new("Gesture", {
ges = "tap", ges = gesture,
pos = point, pos = point,
})) }))
return true return true
@ -186,14 +361,26 @@ function FocusManager:sendTapEventToFocusedWidget()
return false return false
end end
function FocusManager:mergeLayoutInVertical(child) function FocusManager:sendTapEventToFocusedWidget()
return self:_sendGestureEventToFocusedWidget("tap")
end
function FocusManager:sendHoldEventToFocusedWidget()
return self:_sendGestureEventToFocusedWidget("hold")
end
function FocusManager:mergeLayoutInVertical(child, pos)
if not child.layout then if not child.layout then
return return
end end
if not pos then
pos = #self.layout + 1 -- end of row
end
for _, row in ipairs(child.layout) do for _, row in ipairs(child.layout) do
table.insert(self.layout, row) table.insert(self.layout, pos, row)
pos = pos + 1
end end
child:disableFocusManagement() child:disableFocusManagement(self)
end end
function FocusManager:mergeLayoutInHorizontal(child) function FocusManager:mergeLayoutInHorizontal(child)
@ -210,18 +397,39 @@ function FocusManager:mergeLayoutInHorizontal(child)
table.insert(prow, widget) table.insert(prow, widget)
end end
end end
child:disableFocusManagement() child:disableFocusManagement(self)
end end
function FocusManager:disableFocusManagement() function FocusManager:disableFocusManagement(parent)
self._parent = parent
-- unfocus current widget in current child container
-- parent container will call refocusWidget to focus another one
local row = self.layout[self.selected.y]
if row and row[self.selected.x] then
row[self.selected.x]:handleEvent(Event:new("Unfocus"))
end
self.layout = nil -- turn off focus feature self.layout = nil -- turn off focus feature
end end
--- Container call this method after init to let first widget render in focus style -- constant for refocusWidget method to ease code reading
function FocusManager:focusTopLeftWidget() FocusManager.RENDER_IN_NEXT_TICK = true
if Device:hasDPad() then
-- trigger selected widget in focused style --- Container calls this method to re-set focus widget style
self:onFocusMove({0, 0}) --- Some container regenerate layout on update and lose focus style
function FocusManager:refocusWidget(nextTick)
if not self._parent then
if not nextTick then
self:moveFocusTo(self.selected.x, self.selected.y)
else
-- sometimes refocusWidget called in widget's action callback
-- widget may force repaint after callback, like Button with vsync = true
-- then focus style will be lost, set focus style to next tick to make sure focus style painted
UIManager:nextTick(function()
self:moveFocusTo(self.selected.x, self.selected.y)
end)
end
else
self._parent:refocusWidget(nextTick)
end end
end end

@ -2,13 +2,13 @@ local Blitbuffer = require("ffi/blitbuffer")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device") local Device = require("device")
local FocusManager = require("ui/widget/focusmanager")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange") local GestureRange = require("ui/gesturerange")
local Font = require("ui/font") local Font = require("ui/font")
local HorizontalGroup = require("ui/widget/horizontalgroup") local HorizontalGroup = require("ui/widget/horizontalgroup")
local HorizontalSpan = require("ui/widget/horizontalspan") local HorizontalSpan = require("ui/widget/horizontalspan")
local InputContainer = require("ui/widget/container/inputcontainer")
local Math = require("optmath") local Math = require("optmath")
local NaturalLight = require("ui/widget/naturallightwidget") local NaturalLight = require("ui/widget/naturallightwidget")
local ProgressWidget = require("ui/widget/progresswidget") local ProgressWidget = require("ui/widget/progresswidget")
@ -23,7 +23,7 @@ local WidgetContainer = require("ui/widget/container/widgetcontainer")
local _ = require("gettext") local _ = require("gettext")
local Screen = Device.screen local Screen = Device.screen
local FrontLightWidget = InputContainer:new{ local FrontLightWidget = FocusManager:new{
width = nil, width = nil,
height = nil, height = nil,
-- This should stay active during natural light configuration -- This should stay active during natural light configuration
@ -78,9 +78,7 @@ function FrontLightWidget:init()
show_parent = self, show_parent = self,
} }
if Device:hasKeys() then if Device:hasKeys() then
self.key_events = { self.key_events.Close = { {Device.input.group.Back}, doc = "close frontlight" }
Close = { {Device.input.group.Back}, doc = "close frontlight" }
}
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
@ -221,6 +219,7 @@ function FrontLightWidget:setProgress(num, step, num_warmth)
item_level, item_level,
button_plus, button_plus,
} }
self.layout[1] = {button_minus, button_plus}
local button_table_down = HorizontalGroup:new{ local button_table_down = HorizontalGroup:new{
align = "center", align = "center",
button_min, button_min,
@ -229,6 +228,7 @@ function FrontLightWidget:setProgress(num, step, num_warmth)
empty_space, empty_space,
button_max, button_max,
} }
self.layout[2] = {button_min, button_toggle, button_max}
if self.natural_light then if self.natural_light then
-- Only insert 'brightness' caption if we also add 'warmth' -- Only insert 'brightness' caption if we also add 'warmth'
-- widgets below. -- widgets below.
@ -259,12 +259,13 @@ function FrontLightWidget:setProgress(num, step, num_warmth)
end, end,
} }
table.insert(vertical_group, self.configure_button) table.insert(vertical_group, self.configure_button)
self.layout[5] = {self.configure_button}
end end
end end
table.insert(self.fl_container, vertical_group) table.insert(self.fl_container, vertical_group)
-- Reset container height to what it actually contains -- Reset container height to what it actually contains
self.fl_container.dimen.h = vertical_group:getSize().h self.fl_container.dimen.h = vertical_group:getSize().h
self:refocusWidget()
UIManager:setDirty(self, function() UIManager:setDirty(self, function()
return "ui", self.light_frame.dimen return "ui", self.light_frame.dimen
end) end)
@ -370,12 +371,14 @@ function FrontLightWidget:addWarmthWidgets(num_warmth, step, vertical_group)
item_level, item_level,
button_plus, button_plus,
} }
self.layout[3] = {button_minus, button_plus}
local button_table_down = HorizontalGroup:new{ local button_table_down = HorizontalGroup:new{
align = "center", align = "center",
button_min, button_min,
empty_space, empty_space,
button_max, button_max,
} }
self.layout[4] = {button_min, button_max}
table.insert(vertical_group, text_warmth) table.insert(vertical_group, text_warmth)
table.insert(button_group_up, button_table_up) table.insert(button_group_up, button_table_up)
@ -388,7 +391,6 @@ function FrontLightWidget:addWarmthWidgets(num_warmth, step, vertical_group)
table.insert(vertical_group, padding_span) table.insert(vertical_group, padding_span)
table.insert(vertical_group, button_group_down) table.insert(vertical_group, button_group_down)
table.insert(vertical_group, padding_span) table.insert(vertical_group, padding_span)
end end
function FrontLightWidget:setFrontLightIntensity(num) function FrontLightWidget:setFrontLightIntensity(num)
@ -409,6 +411,7 @@ function FrontLightWidget:setFrontLightIntensity(num)
end end
function FrontLightWidget:update() function FrontLightWidget:update()
self.layout = {}
local title_bar = TitleBar:new{ local title_bar = TitleBar:new{
title = _("Frontlight"), title = _("Frontlight"),
width = self.width, width = self.width,

@ -22,6 +22,7 @@ Example:
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(sample_input) UIManager:close(sample_input)
end, end,
@ -99,12 +100,12 @@ local ButtonTable = require("ui/widget/buttontable")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local CheckButton = require("ui/widget/checkbutton") local CheckButton = require("ui/widget/checkbutton")
local Device = require("device") local Device = require("device")
local FocusManager = require("ui/widget/focusmanager")
local Font = require("ui/font") local Font = require("ui/font")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange") local GestureRange = require("ui/gesturerange")
local InfoMessage = require("ui/widget/infomessage") local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer")
local InputText = require("ui/widget/inputtext") local InputText = require("ui/widget/inputtext")
local MovableContainer = require("ui/widget/container/movablecontainer") local MovableContainer = require("ui/widget/container/movablecontainer")
local MultiConfirmBox = require("ui/widget/multiconfirmbox") local MultiConfirmBox = require("ui/widget/multiconfirmbox")
@ -118,7 +119,7 @@ local Screen = Device.screen
local T = require("ffi/util").template local T = require("ffi/util").template
local _ = require("gettext") local _ = require("gettext")
local InputDialog = InputContainer:new{ local InputDialog = FocusManager:new{
is_always_active = true, is_always_active = true,
title = "", title = "",
input = "", input = "",
@ -197,6 +198,7 @@ local InputDialog = InputContainer:new{
} }
function InputDialog:init() function InputDialog:init()
self.layout = {{}}
self.screen_width = Screen:getWidth() self.screen_width = Screen:getWidth()
self.screen_height = Screen:getHeight() self.screen_height = Screen:getHeight()
if self.fullscreen then if self.fullscreen then
@ -370,13 +372,12 @@ function InputDialog:init()
top_line_num = self._top_line_num, top_line_num = self._top_line_num,
charpos = self._charpos, charpos = self._charpos,
} }
table.insert(self.layout[1], self._input_widget)
if self.allow_newline then -- remove any enter_callback if self.allow_newline then -- remove any enter_callback
self._input_widget.enter_callback = nil self._input_widget.enter_callback = nil
end end
if Device:hasDPad() then self:mergeLayoutInVertical(self.button_table)
--little hack to piggyback on the layout of the button_table to handle the new InputText self:refocusWidget()
table.insert(self.button_table.layout, 1, {self._input_widget})
end
-- Complementary setup for some of our added buttons -- Complementary setup for some of our added buttons
if self.save_callback then if self.save_callback then
local save_button = self.button_table:getButtonById("save") local save_button = self.button_table:getButtonById("save")
@ -440,6 +441,9 @@ function InputDialog:init()
}, },
} }
end end
if Device:hasKeys() then
self.key_events.CloseDialog = { {Device.input.group.Back}, doc = "close dialog" }
end
if self._added_widgets then if self._added_widgets then
for _, widget in ipairs(self._added_widgets) do for _, widget in ipairs(self._added_widgets) do
self:addWidget(widget, true) self:addWidget(widget, true)
@ -448,6 +452,7 @@ function InputDialog:init()
end end
function InputDialog:addWidget(widget, re_init) function InputDialog:addWidget(widget, re_init)
table.insert(self.layout, #self.layout, {widget})
if not re_init then -- backup widget for re-init if not re_init then -- backup widget for re-init
widget = CenterContainer:new{ widget = CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{
@ -554,6 +559,15 @@ function InputDialog:onKeyboardHeightChanged()
UIManager:setDirty("all", "flashui") UIManager:setDirty("all", "flashui")
end end
function InputDialog:onCloseDialog()
local close_button = self.button_table:getButtonById("close")
if close_button and close_button.enabled then
close_button.callback()
return true
end
return false
end
function InputDialog:onClose() function InputDialog:onClose()
-- Remember current view & position in case of re-init -- Remember current view & position in case of re-init
self._top_line_num = self._input_widget.top_line_num self._top_line_num = self._input_widget.top_line_num
@ -769,6 +783,7 @@ function InputDialog:_addScrollButtons(nav_bar)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(input_dialog) UIManager:close(input_dialog)
self.keyboard_hidden = keyboard_hidden_state self.keyboard_hidden = keyboard_hidden_state
@ -856,6 +871,7 @@ function InputDialog:_addScrollButtons(nav_bar)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(input_dialog) UIManager:close(input_dialog)
self.keyboard_hidden = keyboard_hidden_state self.keyboard_hidden = keyboard_hidden_state

@ -1,6 +1,7 @@
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local CheckButton = require("ui/widget/checkbutton") local CheckButton = require("ui/widget/checkbutton")
local Device = require("device") local Device = require("device")
local FocusManager = require("ui/widget/focusmanager")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
local Font = require("ui/font") local Font = require("ui/font")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
@ -19,6 +20,7 @@ local _ = require("gettext")
local Screen = Device.screen local Screen = Device.screen
local Keyboard local Keyboard
local FocusManagerInstance = FocusManager:new{}
local InputText = InputContainer:new{ local InputText = InputContainer:new{
text = "", text = "",
@ -274,7 +276,6 @@ if Device:isTouchDevice() or Device:hasDPad() then
-- used for taking a screenshot) -- used for taking a screenshot)
return false return false
end end
end end
if Device:hasDPad() then if Device:hasDPad() then
if not InputText.initEventListener then if not InputText.initEventListener then
@ -283,14 +284,17 @@ if Device:isTouchDevice() or Device:hasDPad() then
function InputText:onFocus() function InputText:onFocus()
-- Event called by the focusmanager -- Event called by the focusmanager
self.key_events.ShowKeyboard = { {"Press"}, doc = "show keyboard" } if self.parent.onSwitchFocus then
self.parent:onSwitchFocus(self)
else
self:onShowKeyboard()
end
self:focus() self:focus()
return true return true
end end
function InputText:onUnfocus() function InputText:onUnfocus()
-- Event called by the focusmanager -- Event called by the focusmanager
self.key_events = {}
self:unfocus() self:unfocus()
return true return true
end end
@ -542,7 +546,11 @@ end
-- is shown. Mostly likely to be in the emulator, but could be Android + BT -- is shown. Mostly likely to be in the emulator, but could be Android + BT
-- keyboard, or a "coder's keyboard" Android input method. -- keyboard, or a "coder's keyboard" Android input method.
function InputText:onKeyPress(key) function InputText:onKeyPress(key)
-- only handle key on focused status, otherwise there are more than one InputText
-- the first one always handle key pressed
if not self.focused then
return false
end
local handled = true local handled = true
if not key["Ctrl"] and not key["Shift"] and not key["Alt"] then if not key["Ctrl"] and not key["Shift"] and not key["Alt"] then
@ -567,6 +575,10 @@ function InputText:onKeyPress(key)
self:addChars("\n") self:addChars("\n")
elseif key["Tab"] then elseif key["Tab"] then
self:addChars(" ") self:addChars(" ")
elseif key["Back"] then
if self.focused then
self:unfocus()
end
else else
handled = false handled = false
end end
@ -581,15 +593,46 @@ function InputText:onKeyPress(key)
else else
handled = false handled = false
end end
if not handled and Device:hasDPad() then
-- FocusManager may turn on alternative key maps.
-- These key map maybe single text keys.
-- It will cause unexpected focus move instead of enter text to InputText
local is_alternative_key = FocusManagerInstance:isAlternativeKey(key)
if not is_alternative_key and Device:isSDL() then
-- SDL already insert char via TextInput event
-- Stop event propagate to FocusManager
return true
end
-- if it is single text char, insert it
local key_code = key.key -- is in upper case
if not Device.isSDL() and #key_code == 1 then
if not key["Shift"] then
key_code = string.lower(key_code)
end
for modifier, flag in pairs(key.modifiers) do
if modifier ~= "Shift" and flag then -- Other modifier: not a single char insert
return true
end
end
self:addChars(key_code)
return true
end
if is_alternative_key then
return true -- Stop event propagate to FocusManager to void focus move
end
end
return handled return handled
end end
-- Handle text coming directly as text from the Device layer (eg. soft keyboard -- Handle text coming directly as text from the Device layer (eg. soft keyboard
-- or via SDL's keyboard mapping). -- or via SDL's keyboard mapping).
function InputText:onTextInput(text) function InputText:onTextInput(text)
self:addChars(text) -- for more than one InputText, let the focused one add chars
return true if self.focused then
self:addChars(text)
return true
end
return false
end end
function InputText:onShowKeyboard(ignore_first_hold_release) function InputText:onShowKeyboard(ignore_first_hold_release)

@ -6,9 +6,9 @@ local Blitbuffer = require("ffi/blitbuffer")
local ButtonTable = require("ui/widget/buttontable") local ButtonTable = require("ui/widget/buttontable")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local FFIUtil = require("ffi/util") local FFIUtil = require("ffi/util")
local FocusManager = require("ui/widget/focusmanager")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local InputContainer = require("ui/widget/container/inputcontainer")
local Language = require("ui/language") local Language = require("ui/language")
local MovableContainer = require("ui/widget/container/movablecontainer") local MovableContainer = require("ui/widget/container/movablecontainer")
local RadioButtonTable = require("ui/widget/radiobuttontable") local RadioButtonTable = require("ui/widget/radiobuttontable")
@ -20,9 +20,10 @@ local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan") local VerticalSpan = require("ui/widget/verticalspan")
local util = require("util") local util = require("util")
local _ = require("gettext") local _ = require("gettext")
local Screen = require("device").screen local Device = require("device")
local Screen = Device.screen
local KeyboardLayoutDialog = InputContainer:new{ local KeyboardLayoutDialog = FocusManager:new{
is_always_active = true, is_always_active = true,
modal = true, modal = true,
stop_events_propagation = true, stop_events_propagation = true,
@ -31,6 +32,7 @@ local KeyboardLayoutDialog = InputContainer:new{
} }
function KeyboardLayoutDialog:init() function KeyboardLayoutDialog:init()
self.layout = {}
self.width = self.width or math.floor(math.min(Screen:getWidth(), Screen:getHeight()) * 0.8) self.width = self.width or math.floor(math.min(Screen:getWidth(), Screen:getHeight()) * 0.8)
self.title_bar = TitleBar:new{ self.title_bar = TitleBar:new{
width = self.width, width = self.width,
@ -68,6 +70,7 @@ function KeyboardLayoutDialog:init()
table.insert(buttons, { table.insert(buttons, {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(self.parent.keyboard_layout_dialog) UIManager:close(self.parent.keyboard_layout_dialog)
end, end,
@ -93,6 +96,7 @@ function KeyboardLayoutDialog:init()
parent = self, parent = self,
show_parent = self, show_parent = self,
} }
self:mergeLayoutInVertical(self.radio_button_table)
-- Buttons Table -- Buttons Table
self.button_table = ButtonTable:new{ self.button_table = ButtonTable:new{
@ -101,6 +105,7 @@ function KeyboardLayoutDialog:init()
zero_sep = true, zero_sep = true,
show_parent = self, show_parent = self,
} }
self:mergeLayoutInVertical(self.button_table)
local max_radio_button_container_height = math.floor(Screen:getHeight()*0.9 local max_radio_button_container_height = math.floor(Screen:getHeight()*0.9
- self.title_bar:getHeight() - self.title_bar:getHeight()
@ -163,6 +168,9 @@ function KeyboardLayoutDialog:init()
ignore_if_over = "height", ignore_if_over = "height",
self.movable, self.movable,
} }
if Device:hasKeys() then
self.key_events.CloseDialog = { {Device.input.group.Back}, doc = "close dialog" }
end
end end
function KeyboardLayoutDialog:onShow() function KeyboardLayoutDialog:onShow()
@ -177,4 +185,13 @@ function KeyboardLayoutDialog:onCloseWidget()
end) end)
end end
function KeyboardLayoutDialog:onCloseDialog()
local close_button = self.button_table:getButtonById("close")
if close_button and close_button.enabled then
close_button.callback()
return true
end
return false
end
return KeyboardLayoutDialog return KeyboardLayoutDialog

@ -25,6 +25,7 @@ local BottomContainer = require("ui/widget/container/bottomcontainer")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
local Device = require("device") local Device = require("device")
local Font = require("ui/font") local Font = require("ui/font")
local FocusManager = require("ui/widget/focusmanager")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange") local GestureRange = require("ui/gesturerange")
@ -65,7 +66,7 @@ local KeyValueItem = InputContainer:new{
} }
function KeyValueItem:init() function KeyValueItem:init()
self.dimen = Geom:new{w = self.width, h = self.height} self.dimen = Geom:new{ w = self.width, h = self.height }
-- self.value may contain some control characters (\n \t...) that would -- self.value may contain some control characters (\n \t...) that would
-- be rendered as a square. Replace them with a shorter and nicer '|'. -- be rendered as a square. Replace them with a shorter and nicer '|'.
@ -162,33 +163,35 @@ function KeyValueItem:init()
-- For debugging positioning: -- For debugging positioning:
-- value_widget = FrameContainer:new{ padding=0, margin=0, bordersize=1, value_widget } -- value_widget = FrameContainer:new{ padding=0, margin=0, bordersize=1, value_widget }
if Device:isTouchDevice() then self.ges_events.Tap = {
self.ges_events.Tap = { GestureRange:new{
GestureRange:new{ ges = "tap",
ges = "tap", range = self.dimen,
range = self.dimen,
}
} }
self.ges_events.Hold = { }
GestureRange:new{ self.ges_events.Hold = {
ges = "hold", GestureRange:new{
range = self.dimen, ges = "hold",
} range = self.dimen,
} }
end }
local content_dimen = self.dimen:copy()
content_dimen.h = content_dimen.h - Size.border.thin * 2 -- reduced by 2 border sizes
content_dimen.w = content_dimen.w - Size.border.thin * 2 -- reduced by 2 border sizes
self[1] = FrameContainer:new{ self[1] = FrameContainer:new{
padding = frame_padding, padding = frame_padding,
padding_top = 0, padding_top = 0,
padding_bottom = 0, padding_bottom = 0,
bordersize = 0, bordersize = 0,
focusable = true,
focus_border_size = Size.border.thin,
background = Blitbuffer.COLOR_WHITE, background = Blitbuffer.COLOR_WHITE,
HorizontalGroup:new{ HorizontalGroup:new{
dimen = self.dimen:copy(), dimen = content_dimen,
LeftContainer:new{ LeftContainer:new{
dimen = { dimen = {
w = key_w, w = key_w,
h = self.height h = content_dimen.h
}, },
key_widget, key_widget,
}, },
@ -198,7 +201,7 @@ function KeyValueItem:init()
LeftContainer:new{ LeftContainer:new{
dimen = { dimen = {
w = value_w, w = value_w,
h = self.height h = content_dimen.h
}, },
value_widget, value_widget,
} }
@ -270,7 +273,7 @@ function KeyValueItem:onShowKeyValue()
end end
local KeyValuePage = InputContainer:new{ local KeyValuePage = FocusManager:new{
title = "", title = "",
width = nil, width = nil,
height = nil, height = nil,
@ -293,11 +296,9 @@ function KeyValuePage:init()
end end
if Device:hasKeys() then if Device:hasKeys() then
self.key_events = { self.key_events.Close = {{Input.group.Back}, doc = "close page" }
Close = { {Input.group.Back}, doc = "close page" }, self.key_events.NextPage = {{Input.group.PgFwd}, doc = "next page"}
NextPage = {{Input.group.PgFwd}, doc = "next page"}, self.key_events.PrevPage = {{Input.group.PgBack}, doc = "prev page"}
PrevPage = {{Input.group.PgBack}, doc = "prev page"},
}
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events.Swipe = { self.ges_events.Swipe = {
@ -535,6 +536,7 @@ end
-- make sure self.item_margin and self.item_height are set before calling this -- make sure self.item_margin and self.item_height are set before calling this
function KeyValuePage:_populateItems() function KeyValuePage:_populateItems()
self.layout = {}
self.page_info:resetLayout() self.page_info:resetLayout()
self.return_button:resetLayout() self.return_button:resetLayout()
self.main_content:clear() self.main_content:clear()
@ -545,7 +547,7 @@ function KeyValuePage:_populateItems()
if entry == nil then break end if entry == nil then break end
if type(entry) == "table" then if type(entry) == "table" then
table.insert(self.main_content, KeyValueItem:new{ local kv_item = KeyValueItem:new{
height = self.item_height, height = self.item_height,
width = self.item_width, width = self.item_width,
font_size = self.items_font_size, font_size = self.items_font_size,
@ -561,7 +563,9 @@ function KeyValuePage:_populateItems()
kv_pairs_idx = kv_pairs_idx, kv_pairs_idx = kv_pairs_idx,
kv_page = self, kv_page = self,
show_parent = self, show_parent = self,
}) }
table.insert(self.main_content, kv_item)
table.insert(self.layout, { kv_item })
if entry.separator then if entry.separator then
table.insert(self.main_content, LineWidget:new{ table.insert(self.main_content, LineWidget:new{
background = Blitbuffer.COLOR_LIGHT_GRAY, background = Blitbuffer.COLOR_LIGHT_GRAY,
@ -615,7 +619,7 @@ function KeyValuePage:_populateItems()
self.page_info_first_chev:hide() self.page_info_first_chev:hide()
self.page_info_last_chev:hide() self.page_info_last_chev:hide()
end end
self:moveFocusTo(1, 1, FocusManager.NOT_UNFOCUS)
UIManager:setDirty(self, function() UIManager:setDirty(self, function()
return "ui", self.dimen return "ui", self.dimen
end) end)

@ -122,24 +122,22 @@ function MenuItem:init()
self.detail = self.text self.detail = self.text
-- we need this table per-instance, so we declare it here -- we need this table per-instance, so we declare it here
if Device:isTouchDevice() then self.ges_events = {
self.ges_events = { TapSelect = {
TapSelect = { GestureRange:new{
GestureRange:new{ ges = "tap",
ges = "tap", range = self.dimen,
range = self.dimen,
},
doc = "Select Menu Item",
}, },
HoldSelect = { doc = "Select Menu Item",
GestureRange:new{ },
ges = "hold", HoldSelect = {
range = self.dimen, GestureRange:new{
}, ges = "hold",
doc = "Hold Menu Item", range = self.dimen,
}, },
} doc = "Hold Menu Item",
end },
}
local max_item_height = self.dimen.h - 2 * self.linesize local max_item_height = self.dimen.h - 2 * self.linesize
@ -889,37 +887,35 @@ function Menu:init()
------------------------------------------ ------------------------------------------
-- start to set up input event callback -- -- start to set up input event callback --
------------------------------------------ ------------------------------------------
if Device:isTouchDevice() then -- watch for outer region if it's a self contained widget
-- watch for outer region if it's a self contained widget if self.is_popout then
if self.is_popout then self.ges_events.TapCloseAllMenus = {
self.ges_events.TapCloseAllMenus = { GestureRange:new{
GestureRange:new{ ges = "tap",
ges = "tap", range = Geom:new{
range = Geom:new{ x = 0, y = 0,
x = 0, y = 0, w = Screen:getWidth(),
w = Screen:getWidth(), h = Screen:getHeight(),
h = Screen:getHeight(),
}
} }
} }
end }
-- delegate swipe gesture to GestureManager in filemanager end
if not self.filemanager then -- delegate swipe gesture to GestureManager in filemanager
self.ges_events.Swipe = { if not self.filemanager then
GestureRange:new{ self.ges_events.Swipe = {
ges = "swipe", GestureRange:new{
range = self.dimen, ges = "swipe",
} range = self.dimen,
} }
self.ges_events.MultiSwipe = { }
GestureRange:new{ self.ges_events.MultiSwipe = {
ges = "multiswipe", GestureRange:new{
range = self.dimen, ges = "multiswipe",
} range = self.dimen,
} }
end }
self.ges_events.Close = self.on_close_ges
end end
self.ges_events.Close = self.on_close_ges
if not Device:hasKeyboard() then if not Device:hasKeyboard() then
-- remove menu item shortcut for K4 -- remove menu item shortcut for K4
@ -947,9 +943,6 @@ function Menu:init()
if self.is_enable_shortcut then if self.is_enable_shortcut then
self.key_events.SelectByShortCut = { {self.item_shortcuts} } self.key_events.SelectByShortCut = { {self.item_shortcuts} }
end end
self.key_events.Select = {
{"Press"}, doc = "select current menu item"
}
self.key_events.Right = { self.key_events.Right = {
{"Right"}, doc = "hold menu item" {"Right"}, doc = "hold menu item"
} }
@ -997,7 +990,7 @@ function Menu:updatePageInfo(select_number)
if self.item_group[1] then if self.item_group[1] then
if Device:hasDPad() then if Device:hasDPad() then
-- reset focus manager accordingly -- reset focus manager accordingly
self.selected = { x = 1, y = select_number } self:moveFocusTo(1, select_number)
end end
-- update page information -- update page information
self.page_info_text:setText(FFIUtil.template(_("Page %1 of %2"), self.page, self.page_num)) self.page_info_text:setText(FFIUtil.template(_("Page %1 of %2"), self.page, self.page_num))
@ -1299,20 +1292,8 @@ function Menu:onGotoPage(page)
return true return true
end end
function Menu:onSelect()
local item = self.item_table[(self.page-1)*self.perpage+self.selected.y]
if item then
self:onMenuSelect(item)
end
return true
end
function Menu:onRight() function Menu:onRight()
local item = self.item_table[(self.page-1)*self.perpage+self.selected.y] return self:sendHoldEventToFocusedWidget()
if item then
self:onMenuHold(item)
end
return true
end end
function Menu:onClose() function Menu:onClose()

@ -34,7 +34,6 @@ local TextBoxWidget = require("ui/widget/textboxwidget")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup") local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan") local VerticalSpan = require("ui/widget/verticalspan")
local logger = require("logger")
local _ = require("gettext") local _ = require("gettext")
local Screen = require("device").screen local Screen = require("device").screen
@ -72,9 +71,7 @@ function MultiConfirmBox:init()
} }
end end
if Device:hasKeys() then if Device:hasKeys() then
self.key_events = { self.key_events.Close = { {Device.input.group.Back}, doc = "cancel" }
Close = { {Device.input.group.Back}, doc = "cancel" }
}
end end
end end
local content = HorizontalGroup:new{ local content = HorizontalGroup:new{
@ -173,17 +170,4 @@ function MultiConfirmBox:onTapClose(arg, ges)
return false return false
end end
function MultiConfirmBox:onSelect()
logger.dbg("selected:", self.selected.x)
if self.selected.x == 1 then
self:choice1_callback()
elseif self.selected.x == 2 then
self:choice2_callback()
elseif self.selected.x == 0 then
self:cancle_callback()
end
UIManager:close(self)
return true
end
return MultiConfirmBox return MultiConfirmBox

@ -32,6 +32,7 @@ Example for input of two strings and a number:
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(sample_input) UIManager:close(sample_input)
end end
@ -132,10 +133,7 @@ function MultiInputDialog:init()
auto_para_direction = field.auto_para_direction or self.auto_para_direction, auto_para_direction = field.auto_para_direction or self.auto_para_direction,
alignment_strict = field.alignment_strict or self.alignment_strict, alignment_strict = field.alignment_strict or self.alignment_strict,
} }
if Device:hasDPad() then table.insert(self.layout, #self.layout, {input_field[k]})
-- little hack to piggyback on the layout of the button_table to handle the new InputText
table.insert(self.button_table.layout, #self.button_table.layout, {input_field[k]})
end
if field.description then if field.description then
input_description[k] = FrameContainer:new{ input_description[k] = FrameContainer:new{
padding = self.description_padding, padding = self.description_padding,
@ -164,10 +162,6 @@ function MultiInputDialog:init()
}) })
end end
if Device:hasDPad() then
-- remove the not needed hack in inputdialog
table.remove(self.button_table.layout, 1)
end
-- Add same vertical space after than before InputText -- Add same vertical space after than before InputText
table.insert(VerticalGroupData,CenterContainer:new{ table.insert(VerticalGroupData,CenterContainer:new{
dimen = Geom:new{ dimen = Geom:new{

@ -294,6 +294,7 @@ function NetworkItem:onEditNetwork()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(password_input) UIManager:close(password_input)
end, end,
@ -336,6 +337,7 @@ function NetworkItem:onAddNetwork()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(password_input) UIManager:close(password_input)
end, end,

@ -136,6 +136,7 @@ function NumberPickerWidget:init()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(input_dialog) UIManager:close(input_dialog)
end, end,
@ -186,6 +187,9 @@ function NumberPickerWidget:init()
show_parent = self.show_parent, show_parent = self.show_parent,
callback = callback_input, callback = callback_input,
} }
if callback_input then
table.insert(self.layout, 2, {self.text_value})
end
local widget_spinner = VerticalGroup:new{ local widget_spinner = VerticalGroup:new{
align = "center", align = "center",
@ -210,9 +214,7 @@ function NumberPickerWidget:init()
} }
self.dimen = self.frame:getSize() self.dimen = self.frame:getSize()
self[1] = self.frame self[1] = self.frame
if Device:hasDPad() then self:refocusWidget()
self.key_events.Press = { {"Press"}, doc = "press button" }
end
UIManager:setDirty(self.show_parent, function() UIManager:setDirty(self.show_parent, function()
return "ui", self.dimen return "ui", self.dimen
end) end)
@ -229,6 +231,7 @@ function NumberPickerWidget:update()
self.text_value:setText(tostring(self.formatted_value), self.width) self.text_value:setText(tostring(self.formatted_value), self.width)
self:refocusWidget()
UIManager:setDirty(self.show_parent, function() UIManager:setDirty(self.show_parent, function()
return "ui", self.dimen return "ui", self.dimen
end) end)
@ -289,8 +292,4 @@ function NumberPickerWidget:getValue()
return self.value, self.value_index return self.value, self.value_index
end end
function NumberPickerWidget:onPress()
return self:sendTapEventToFocusedWidget()
end
return NumberPickerWidget return NumberPickerWidget

@ -42,6 +42,7 @@ function OpenWithDialog:init()
end end
end end
} }
self:mergeLayoutInVertical(self.radio_button_table, #self.layout) -- before bottom buttons
self._input_widget = self.radio_button_table self._input_widget = self.radio_button_table
local vertical_span = VerticalSpan:new{ local vertical_span = VerticalSpan:new{
@ -85,11 +86,13 @@ function OpenWithDialog:init()
text = _("Always use this engine for this file"), text = _("Always use this engine for this file"),
parent = self, parent = self,
} }
table.insert(self.layout, #self.layout, {self._check_file_button}) -- before bottom buttons
self:addWidget(self._check_file_button) self:addWidget(self._check_file_button)
self._check_global_button = self._check_global_button or CheckButton:new{ self._check_global_button = self._check_global_button or CheckButton:new{
text = _("Always use this engine for file type"), text = _("Always use this engine for file type"),
parent = self, parent = self,
} }
table.insert(self.layout, #self.layout, {self._check_global_button}) -- before bottom buttons
self:addWidget(self._check_global_button) self:addWidget(self._check_global_button)
self.dialog_frame = FrameContainer:new{ self.dialog_frame = FrameContainer:new{
@ -110,6 +113,7 @@ function OpenWithDialog:init()
}, },
self.movable, self.movable,
} }
self:refocusWidget()
end end
function OpenWithDialog:onCloseWidget() function OpenWithDialog:onCloseWidget()

@ -31,7 +31,6 @@ local RadioButtonTable = FocusManager:new{
} }
function RadioButtonTable:init() function RadioButtonTable:init()
self.selected = { x = 1, y = 1 }
self.radio_buttons_layout = {} self.radio_buttons_layout = {}
self.container = VerticalGroup:new{ width = self.width } self.container = VerticalGroup:new{ width = self.width }
table.insert(self, self.container) table.insert(self, self.container)
@ -120,7 +119,7 @@ function RadioButtonTable:init()
if Device:hasDPad() or Device:hasKeyboard() then if Device:hasDPad() or Device:hasKeyboard() then
self.layout = self.radio_buttons_layout self.layout = self.radio_buttons_layout
self.key_events.SelectByKeyPress = { {{"Press"}} } self:refocusWidget()
else else
self.key_events = {} -- deregister all key press event listeners self.key_events = {} -- deregister all key press event listeners
end end
@ -146,15 +145,6 @@ function RadioButtonTable:addHorizontalSep(vspan_before, add_line, vspan_after,
end end
end end
function RadioButtonTable:onSelectByKeyPress()
local item = self:getFocusItem()
if item then
item.callback()
return true
end
return false
end
function RadioButtonTable:_checkButton(button) function RadioButtonTable:_checkButton(button)
-- nothing to do -- nothing to do
if button.checked then return end if button.checked then return end

@ -45,7 +45,6 @@ function SkimToWidget:init()
end end
self.buttons_layout = {} self.buttons_layout = {}
self.selected = { x = 1, y = 2 }
local frame_width = math.floor(math.min(screen_width, screen_height) * 0.95) local frame_width = math.floor(math.min(screen_width, screen_height) * 0.95)
local frame_border_size = Size.border.window local frame_border_size = Size.border.window
@ -296,8 +295,7 @@ function SkimToWidget:init()
{ button_minus_ten, button_minus, self.current_page_text, button_plus, button_plus_ten }, { button_minus_ten, button_minus, self.current_page_text, button_plus, button_plus_ten },
} }
self.layout = self.buttons_layout self.layout = self.buttons_layout
self.layout[2][1]:onFocus() self:moveFocusTo(1, 2)
self.key_events.SelectByKeyPress = { { "Press" }, doc = "select focused item" }
end end
if Device:hasKeyboard() then if Device:hasKeyboard() then
self.key_events.QKey = { { "Q" }, event = "FirstRowKeyPress", args = 0 } self.key_events.QKey = { { "Q" }, event = "FirstRowKeyPress", args = 0 }
@ -323,6 +321,7 @@ function SkimToWidget:update()
self.progress_bar.percentage = self.curr_page / self.page_count self.progress_bar.percentage = self.curr_page / self.page_count
self.current_page_text:setText(self.current_page_text:text_func(), self.current_page_text.width) self.current_page_text:setText(self.current_page_text:text_func(), self.current_page_text.width)
self.button_bookmark_toggle:setText(self.button_bookmark_toggle:text_func(), self.button_bookmark_toggle.width) self.button_bookmark_toggle:setText(self.button_bookmark_toggle:text_func(), self.button_bookmark_toggle.width)
self:refocusWidget(FocusManager.RENDER_IN_NEXT_TICK)
end end
function SkimToWidget:addOriginToLocationStack(add_current) function SkimToWidget:addOriginToLocationStack(add_current)
@ -378,15 +377,6 @@ function SkimToWidget:onAnyKeyPressed()
return true return true
end end
function SkimToWidget:onSelectByKeyPress()
local item = self:getFocusItem()
if item then
item.callback()
return true
end
return false
end
function SkimToWidget:onFirstRowKeyPress(percent) function SkimToWidget:onFirstRowKeyPress(percent)
local page = Math.round(percent * self.page_count) local page = Math.round(percent * self.page_count)
self:addOriginToLocationStack() self:addOriginToLocationStack()

@ -65,7 +65,6 @@ function SpinWidget:init()
end end
if Device:hasKeys() then if Device:hasKeys() then
self.key_events.Close = { {Device.input.group.Back}, doc = "close spin widget" } self.key_events.Close = { {Device.input.group.Back}, doc = "close spin widget" }
self.key_events.Press = { {"Press"}, doc = "press button" }
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events = { self.ges_events = {
@ -196,7 +195,6 @@ function SpinWidget:update(numberpicker_value, numberpicker_value_index)
buttons = buttons, buttons = buttons,
zero_sep = true, zero_sep = true,
show_parent = self, show_parent = self,
auto_focus_first_button = false,
} }
self:mergeLayoutInVertical(ok_cancel_buttons) self:mergeLayoutInVertical(ok_cancel_buttons)
local vgroup = VerticalGroup:new{ local vgroup = VerticalGroup:new{
@ -236,7 +234,7 @@ function SpinWidget:update(numberpicker_value, numberpicker_value_index)
}, },
self.movable, self.movable,
} }
self:focusTopLeftWidget() self:refocusWidget()
UIManager:setDirty(self, function() UIManager:setDirty(self, function()
return "ui", self.spin_frame.dimen return "ui", self.spin_frame.dimen
end) end)
@ -280,8 +278,4 @@ function SpinWidget:onClose()
return true return true
end end
function SpinWidget:onPress()
return self:sendTapEventToFocusedWidget()
end
return SpinWidget return SpinWidget

@ -7,7 +7,6 @@ local Button = require("ui/widget/button")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local CheckMark = require("ui/widget/checkmark") local CheckMark = require("ui/widget/checkmark")
local Device = require("device") local Device = require("device")
local Event = require("ui/event")
local FocusManager = require("ui/widget/focusmanager") local FocusManager = require("ui/widget/focusmanager")
local Font = require("ui/font") local Font = require("ui/font")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
@ -490,7 +489,6 @@ function TouchMenu:init()
end end
self.key_events.NextPage = { {Input.group.PgFwd}, doc = "next page" } self.key_events.NextPage = { {Input.group.PgFwd}, doc = "next page" }
self.key_events.PrevPage = { {Input.group.PgBack}, doc = "previous page" } self.key_events.PrevPage = { {Input.group.PgBack}, doc = "previous page" }
self.key_events.Press = { {"Press"}, doc = "chose selected item" }
local icons = {} local icons = {}
for _, v in ipairs(self.tab_item_table) do for _, v in ipairs(self.tab_item_table) do
@ -713,7 +711,7 @@ function TouchMenu:updateItems()
-- recalculate dimen based on new layout -- recalculate dimen based on new layout
self.dimen.w = self.width self.dimen.w = self.width
self.dimen.h = self.item_group:getSize().h + self.bordersize*2 + self.padding -- (no padding at top) self.dimen.h = self.item_group:getSize().h + self.bordersize*2 + self.padding -- (no padding at top)
self.selected = { x = self.cur_tab, y = 1 } -- reset the position of the focusmanager self:moveFocusTo(self.cur_tab, 1, FocusManager.NOT_FOCUS) -- reset the position of the focusmanager
-- NOTE: We use a slightly ugly hack to detect a brand new menu vs. a tab switch, -- NOTE: We use a slightly ugly hack to detect a brand new menu vs. a tab switch,
-- in order to optionally flash on initial menu popup... -- in order to optionally flash on initial menu popup...
@ -942,13 +940,4 @@ function TouchMenu:onBack()
self:backToUpperMenu() self:backToUpperMenu()
end end
function TouchMenu:onPress()
local item = self:getFocusItem()
if item then
item:handleEvent(Event:new("TapSelect"))
return true
end
return false
end
return TouchMenu return TouchMenu

@ -2,7 +2,6 @@ local Blitbuffer = require("ffi/blitbuffer")
local BottomContainer = require("ui/widget/container/bottomcontainer") local BottomContainer = require("ui/widget/container/bottomcontainer")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device") local Device = require("device")
local Event = require("ui/event")
local FocusManager = require("ui/widget/focusmanager") local FocusManager = require("ui/widget/focusmanager")
local Font = require("ui/font") local Font = require("ui/font")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
@ -245,40 +244,38 @@ function VirtualKey:init()
h = self.height, h = self.height,
} }
--self.dimen = self[1]:getSize() --self.dimen = self[1]:getSize()
if Device:isTouchDevice() then self.ges_events = {
self.ges_events = { TapSelect = {
TapSelect = { GestureRange:new{
GestureRange:new{ ges = "tap",
ges = "tap", range = self.dimen,
range = self.dimen,
},
}, },
HoldSelect = { },
GestureRange:new{ HoldSelect = {
ges = "hold", GestureRange:new{
range = self.dimen, ges = "hold",
}, range = self.dimen,
}, },
HoldReleaseKey = { },
GestureRange:new{ HoldReleaseKey = {
ges = "hold_release", GestureRange:new{
range = self.dimen, ges = "hold_release",
}, range = self.dimen,
}, },
PanReleaseKey = { },
GestureRange:new{ PanReleaseKey = {
ges = "pan_release", GestureRange:new{
range = self.dimen, ges = "pan_release",
}, range = self.dimen,
}, },
SwipeKey = { },
GestureRange:new{ SwipeKey = {
ges = "swipe", GestureRange:new{
range = self.dimen, ges = "swipe",
}, range = self.dimen,
}, },
} },
end }
if (self.keyboard.shiftmode_keys[self.label] ~= nil and self.keyboard.shiftmode) or if (self.keyboard.shiftmode_keys[self.label] ~= nil and self.keyboard.shiftmode) or
(self.keyboard.umlautmode_keys[self.label] ~= nil and self.keyboard.umlautmode) or (self.keyboard.umlautmode_keys[self.label] ~= nil and self.keyboard.umlautmode) or
(self.keyboard.symbolmode_keys[self.label] ~= nil and self.keyboard.symbolmode) then (self.keyboard.symbolmode_keys[self.label] ~= nil and self.keyboard.symbolmode) then
@ -479,15 +476,6 @@ function VirtualKeyPopup:onCloseWidget()
end) end)
end end
function VirtualKeyPopup:onPressKey()
local item = self:getFocusItem()
if item then
item:handleEvent(Event:new("TapSelect"))
return true
end
return false
end
function VirtualKeyPopup:init() function VirtualKeyPopup:init()
local parent_key = self.parent_key local parent_key = self.parent_key
local key_chars = parent_key.key_chars local key_chars = parent_key.key_chars
@ -680,9 +668,6 @@ function VirtualKeyPopup:init()
self.tap_interval_override = G_reader_settings:readSetting("ges_tap_interval_on_keyboard", 0) self.tap_interval_override = G_reader_settings:readSetting("ges_tap_interval_on_keyboard", 0)
self.tap_interval_override = TimeVal:new{ usec = self.tap_interval_override } self.tap_interval_override = TimeVal:new{ usec = self.tap_interval_override }
if Device:hasDPad() then
self.key_events.PressKey = { {"Press"}, doc = "select key" }
end
if Device:hasKeys() then if Device:hasKeys() then
self.key_events.Close = { {Device.input.group.Back}, doc = "close keyboard" } self.key_events.Close = { {Device.input.group.Back}, doc = "close keyboard" }
end end
@ -803,16 +788,13 @@ function VirtualKeyboard:init()
self:initLayer(self.keyboard_layer) self:initLayer(self.keyboard_layer)
self.tap_interval_override = G_reader_settings:readSetting("ges_tap_interval_on_keyboard", 0) self.tap_interval_override = G_reader_settings:readSetting("ges_tap_interval_on_keyboard", 0)
self.tap_interval_override = TimeVal:new{ usec = self.tap_interval_override } self.tap_interval_override = TimeVal:new{ usec = self.tap_interval_override }
if Device:hasDPad() then
self.key_events.PressKey = { {"Press"}, doc = "select key" }
end
if Device:hasKeys() then if Device:hasKeys() then
self.key_events.Close = { {"Back"}, doc = "close keyboard" } self.key_events.Close = { {"Back"}, doc = "close keyboard" }
end end
if keyboard.wrapInputBox then if keyboard.wrapInputBox then
self.uwrap_func = keyboard.wrapInputBox(self.inputbox) or self.uwrap_func self.uwrap_func = keyboard.wrapInputBox(self.inputbox) or self.uwrap_func
end end
if Device:hasDPad() and Device:hasKeyboard() and Device:isTouchDevice() then if Device:hasDPad() then
-- hadDPad() would have FocusManager handle arrow keys strokes to navigate -- hadDPad() would have FocusManager handle arrow keys strokes to navigate
-- and activate this VirtualKeyboard's touch keys (needed on non-touch Kindle). -- and activate this VirtualKeyboard's touch keys (needed on non-touch Kindle).
-- If we have a keyboard, we'd prefer arrow keys (and Enter, and Del) to be -- If we have a keyboard, we'd prefer arrow keys (and Enter, and Del) to be
@ -820,14 +802,39 @@ function VirtualKeyboard:init()
-- add newline and delete chars. And if we are a touch device, we don't -- add newline and delete chars. And if we are a touch device, we don't
-- need focus manager to help us navigate keys and fields. -- need focus manager to help us navigate keys and fields.
-- So, disable all key_event handled by FocusManager -- So, disable all key_event handled by FocusManager
self.key_events.FocusLeft = nil if Device:isTouchDevice() then
self.key_events.FocusRight = nil -- Remove all FocusManager key event handlers.
self.key_events.FocusUp = nil for k, _ in pairs(self.builtin_key_events) do
self.key_events.FocusDown = nil self.key_events[k] = nil
self.key_events.PressKey = nil -- added above end
for k, _ in pairs(self.extra_key_events) do
self.key_events[k] = nil
end
elseif Device:hasKeyboard() then
-- Use physical keyboard for most characters
-- For special characters not available in physical keyboard
-- Use arrow and Press keys to select in VirtualKeyboard
for k, seq in pairs(self.extra_key_events) do
if self:_isTextKeyWithoutModifier(seq) then
self.key_events[k] = nil
end
end
end
end end
end end
function VirtualKeyboard:_isTextKeyWithoutModifier(seq)
for _, oneseq in ipairs(seq) do
if #oneseq ~= 1 then -- has modifier key combination
return false
end
if #oneseq[1] ~= 1 then -- not simple text key, like Home, End
return false
end
end
return true
end
function VirtualKeyboard:getKeyboardLayout() function VirtualKeyboard:getKeyboardLayout()
if G_reader_settings:isFalse("keyboard_remember_layout") and not keyboard_state.force_current_layout then if G_reader_settings:isFalse("keyboard_remember_layout") and not keyboard_state.force_current_layout then
local lang = G_reader_settings:readSetting("keyboard_layout_default") local lang = G_reader_settings:readSetting("keyboard_layout_default")
@ -856,16 +863,12 @@ end
function VirtualKeyboard:onClose() function VirtualKeyboard:onClose()
UIManager:close(self) UIManager:close(self)
return true if self.inputbox and Device:hasDPad() then
end -- let input text handle Back event to unfocus
-- otherwise, another extra Back event needed
function VirtualKeyboard:onPressKey() return false
local item = self:getFocusItem()
if item then
item:handleEvent(Event:new("TapSelect"))
return true
end end
return false return true
end end
function VirtualKeyboard:_refresh(want_flash, fullscreen) function VirtualKeyboard:_refresh(want_flash, fullscreen)

@ -124,6 +124,7 @@ function SSH:show_port_dialog(touchmenu_instance)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(self.port_dialog) UIManager:close(self.port_dialog)
end, end,

@ -451,6 +451,7 @@ function AutoWarmth:getLocationMenu()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(location_name_dialog) UIManager:close(location_name_dialog)
end, end,

@ -307,6 +307,7 @@ function Calibre:getWirelessMenuTable()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(url_dialog) UIManager:close(url_dialog)
end, end,

@ -207,6 +207,7 @@ function CalibreSearch:ShowSearch()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
enabled = true, enabled = true,
callback = function() callback = function()
self.search_dialog:onClose() self.search_dialog:onClose()

@ -422,6 +422,7 @@ function CalibreWireless:setPassword()
buttons = {{ buttons = {{
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(password_dialog) UIManager:close(password_dialog)
end, end,

@ -135,24 +135,22 @@ function ListMenuItem:init()
self.detail = self.text self.detail = self.text
-- we need this table per-instance, so we declare it here -- we need this table per-instance, so we declare it here
if Device:isTouchDevice() then self.ges_events = {
self.ges_events = { TapSelect = {
TapSelect = { GestureRange:new{
GestureRange:new{ ges = "tap",
ges = "tap", range = self.dimen,
range = self.dimen,
},
doc = "Select Menu Item",
}, },
HoldSelect = { doc = "Select Menu Item",
GestureRange:new{ },
ges = "hold", HoldSelect = {
range = self.dimen, GestureRange:new{
}, ges = "hold",
doc = "Hold Menu Item", range = self.dimen,
}, },
} doc = "Hold Menu Item",
end },
}
-- We now build the minimal widget container that won't change after udpate() -- We now build the minimal widget container that won't change after udpate()

@ -350,6 +350,7 @@ function CoverImage:choosePathFile(touchmenu_instance, key, folder_only, new_fil
buttons = {{ buttons = {{
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(file_input) UIManager:close(file_input)
end, end,

@ -137,6 +137,7 @@ function Exporter:addToMainMenu(menu_items)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(url_dialog) UIManager:close(url_dialog)
end end
@ -176,6 +177,7 @@ function Exporter:addToMainMenu(menu_items)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(auth_dialog) UIManager:close(auth_dialog)
end end
@ -246,6 +248,7 @@ For more information, please visit https://github.com/koreader/koreader/wiki/Hig
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(auth_dialog) UIManager:close(auth_dialog)
end end

@ -329,6 +329,7 @@ function Gestures:multiswipeRecorder(touchmenu_instance)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(multiswipe_recorder) UIManager:close(multiswipe_recorder)
end, end,

@ -597,6 +597,7 @@ function NewsDownloader:editFeedAttribute(id, key, value)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(input_dialog) UIManager:close(input_dialog)
UIManager:show(kv) UIManager:show(kv)

@ -154,6 +154,7 @@ function OPDSBrowser:addNewCatalog()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self.add_server_dialog:onClose() self.add_server_dialog:onClose()
UIManager:close(self.add_server_dialog) UIManager:close(self.add_server_dialog)
@ -204,6 +205,7 @@ function OPDSBrowser:editCalibreServer()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self.add_server_dialog:onClose() self.add_server_dialog:onClose()
UIManager:close(self.add_server_dialog) UIManager:close(self.add_server_dialog)
@ -740,6 +742,7 @@ function OPDSBrowser:showDownloads(item)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(input_dialog) UIManager:close(input_dialog)
end, end,
@ -818,6 +821,7 @@ function OPDSBrowser:browseSearchable(browse_url, username, password)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(self.search_server_dialog) UIManager:close(self.search_server_dialog)
end, end,
@ -912,6 +916,7 @@ function OPDSBrowser:editOPDSServer(item)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self.edit_server_dialog:onClose() self.edit_server_dialog:onClose()
UIManager:close(self.edit_server_dialog) UIManager:close(self.edit_server_dialog)

@ -136,6 +136,7 @@ function PerceptionExpander:showSettingsDialog()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self.settings_dialog:onClose() self.settings_dialog:onClose()
UIManager:close(self.settings_dialog) UIManager:close(self.settings_dialog)

@ -64,6 +64,7 @@ function Profiles:getSubMenuItems()
buttons = {{ buttons = {{
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(name_input) UIManager:close(name_input)
end, end,

@ -4,6 +4,7 @@ local BottomContainer = require("ui/widget/container/bottomcontainer")
local Button = require("ui/widget/button") local Button = require("ui/widget/button")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device") local Device = require("device")
local FocusManager = require("ui/widget/focusmanager")
local Font = require("ui/font") local Font = require("ui/font")
local FrameContainer = require("ui/widget/container/framecontainer") local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry") local Geom = require("ui/geometry")
@ -94,20 +95,18 @@ function CalendarDay:init()
if self.filler then if self.filler then
return return
end end
if self.callback and Device:isTouchDevice() then self.ges_events.Tap = {
self.ges_events.Tap = { GestureRange:new{
GestureRange:new{ ges = "tap",
ges = "tap", range = self.dimen,
range = self.dimen,
}
} }
self.ges_events.Hold = { }
GestureRange:new{ self.ges_events.Hold = {
ges = "hold", GestureRange:new{
range = self.dimen, ges = "hold",
} range = self.dimen,
} }
end }
self.daynum_w = TextWidget:new{ self.daynum_w = TextWidget:new{
text = " " .. tostring(self.daynum), text = " " .. tostring(self.daynum),
@ -144,6 +143,8 @@ function CalendarDay:init()
bordersize = self.border, bordersize = self.border,
width = self.width, width = self.width,
height = self.height, height = self.height,
focusable = true,
focus_border_color = Blitbuffer.COLOR_GRAY,
OverlapGroup:new{ OverlapGroup:new{
dimen = { w = inner_w }, dimen = { w = inner_w },
self.daynum_w, self.daynum_w,
@ -366,7 +367,7 @@ end
-- Fetched from db, cached as local as it might be expensive -- Fetched from db, cached as local as it might be expensive
local MIN_MONTH = nil local MIN_MONTH = nil
local CalendarView = InputContainer:new{ local CalendarView = FocusManager:new{
reader_statistics = nil, reader_statistics = nil,
monthTranslation = nil, monthTranslation = nil,
shortDayOfWeekTranslation = nil, shortDayOfWeekTranslation = nil,
@ -395,11 +396,9 @@ function CalendarView:init()
end end
if Device:hasKeys() then if Device:hasKeys() then
self.key_events = { self.key_events.Close = {{Input.group.Back}, doc = "close page" }
Close = { {Input.group.Back}, doc = "close page" }, self.key_events.NextMonth = {{Input.group.PgFwd}, doc = "next page"}
NextMonth = {{Input.group.PgFwd}, doc = "next page"}, self.key_events.PrevMonth = {{Input.group.PgBack}, doc = "prev page"}
PrevMonth = {{Input.group.PgBack}, doc = "prev page"},
}
end end
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.ges_events.Swipe = { self.ges_events.Swipe = {
@ -620,6 +619,7 @@ function CalendarView:init()
end end
function CalendarView:_populateItems() function CalendarView:_populateItems()
self.layout = {}
self.page_info:resetLayout() self.page_info:resetLayout()
self.main_content:clear() self.main_content:clear()
@ -650,6 +650,7 @@ function CalendarView:_populateItems()
local cur_date = os.date("*t", cur_ts) local cur_date = os.date("*t", cur_ts)
local this_month = cur_date.month local this_month = cur_date.month
local cur_week local cur_week
local layout_row
while true do while true do
cur_date = os.date("*t", cur_ts) cur_date = os.date("*t", cur_ts)
if cur_date.month ~= this_month then if cur_date.month ~= this_month then
@ -672,6 +673,8 @@ function CalendarView:_populateItems()
font_size = self.span_font_size, font_size = self.span_font_size,
show_parent = self, show_parent = self,
} }
layout_row = {}
table.insert(self.layout, layout_row)
table.insert(self.weeks, cur_week) table.insert(self.weeks, cur_week)
table.insert(self.main_content, cur_week) table.insert(self.main_content, cur_week)
if cur_date.wday ~= self.start_day_of_week then if cur_date.wday ~= self.start_day_of_week then
@ -702,7 +705,7 @@ function CalendarView:_populateItems()
hour = 0, hour = 0,
}) })
local is_future = day_s > today_s local is_future = day_s > today_s
cur_week:addDay(CalendarDay:new{ local calendar_day = CalendarDay:new{
show_histo = self.show_hourly_histogram, show_histo = self.show_hourly_histogram,
histo_height = self.span_height, histo_height = self.span_height,
font_face = self.font_face, font_face = self.font_face,
@ -733,13 +736,15 @@ function CalendarView:_populateItems()
callback_return = function() end, -- to just have that return button shown callback_return = function() end, -- to just have that return button shown
}) })
end end
}) }
cur_week:addDay(calendar_day)
table.insert(layout_row, calendar_day)
cur_ts = cur_ts + 86400 -- add one day cur_ts = cur_ts + 86400 -- add one day
end end
for _, week in ipairs(self.weeks) do for _, week in ipairs(self.weeks) do
week:update() week:update()
end end
self:moveFocusTo(1, 1, FocusManager.NOT_UNFOCUS)
UIManager:setDirty(self, function() UIManager:setDirty(self, function()
return "ui", self.dimen return "ui", self.dimen
end) end)

@ -350,6 +350,7 @@ Do you want to proceed?]]),
buttons = {{ buttons = {{
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(file_input) UIManager:close(file_input)
end, end,

@ -902,6 +902,7 @@ function Wallabag:setFilterTag(touchmenu_instance)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(self.tag_dialog) UIManager:close(self.tag_dialog)
end, end,
@ -933,6 +934,7 @@ function Wallabag:setIgnoreTags(touchmenu_instance)
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
UIManager:close(self.ignore_tags_dialog) UIManager:close(self.ignore_tags_dialog)
end, end,
@ -1000,6 +1002,7 @@ Restart KOReader after editing the config file.]]), BD.dirpath(DataStorage:getSe
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self.settings_dialog:onClose() self.settings_dialog:onClose()
UIManager:close(self.settings_dialog) UIManager:close(self.settings_dialog)
@ -1048,6 +1051,7 @@ function Wallabag:editClientSettings()
{ {
{ {
text = _("Cancel"), text = _("Cancel"),
id = "close",
callback = function() callback = function()
self.client_settings_dialog:onClose() self.client_settings_dialog:onClose()
UIManager:close(self.client_settings_dialog) UIManager:close(self.client_settings_dialog)

@ -1,14 +1,24 @@
describe("FocusManager module", function() describe("FocusManager module", function()
local FocusManager local FocusManager
local layout local layout, big_layout
local Up,Down,Left,Right local Key
Up = function(self) self:onFocusMove({0, -1}) end local Input
Down = function(self) self:onFocusMove({0, 1}) end local Up = function(self) self:onFocusMove({0, -1}) end
Left = function(self) self:onFocusMove({-1, 0}) end local Down = function(self) self:onFocusMove({0, 1}) end
Right = function(self) self:onFocusMove({1, 0}) end local Left = function(self) self:onFocusMove({-1, 0}) end
local Right = function(self) self:onFocusMove({1, 0}) end
local Next = function(self) self:onFocusNext() end
local Previous = function(self) self:onFocusPrevious() end
local HalfMoveUp = function(self) self:onFocusHalfMove({"up"}) end
local HalfMoveDown = function(self) self:onFocusHalfMove({"down"}) end
local HalfMoveLeft = function(self) self:onFocusHalfMove({"left"}) end
local HalfMoveRight = function(self) self:onFocusHalfMove({"right"}) end
local MoveTo = function(self, x, y) self:moveFocusTo(x, y) end
setup(function() setup(function()
require("commonrequire") require("commonrequire")
FocusManager = require("ui/widget/focusmanager") FocusManager = require("ui/widget/focusmanager")
Key = require("device/key")
Input = require("device/input")
local Widget = require("ui/widget/textwidget") local Widget = require("ui/widget/textwidget")
local w = Widget:new{} local w = Widget:new{}
layout= { layout= {
@ -16,7 +26,13 @@ describe("FocusManager module", function()
{nil,w,nil}, {nil,w,nil},
{nil,w,nil}, {nil,w,nil},
} }
big_layout = {
{w, w, w, w, w},
{w, w, w, w, w},
{w, w, w, w, w},
{w, w, w, w, w},
{w, w, w, w, w},
}
end) end)
it("should go right", function() it("should go right", function()
local focusmanager = FocusManager:new{} local focusmanager = FocusManager:new{}
@ -88,4 +104,129 @@ describe("FocusManager module", function()
Right(focusmanager) Right(focusmanager)
assert.are.same({y = 2,x = 2}, focusmanager.selected) assert.are.same({y = 2,x = 2}, focusmanager.selected)
end) end)
it("should move next right", function()
local focusmanager = FocusManager:new{}
focusmanager.layout = layout
focusmanager.selected = {y = 1,x = 2}
Next(focusmanager)
assert.are.same({y = 1,x = 3}, focusmanager.selected)
end)
it("should move next row at end of row", function()
local focusmanager = FocusManager:new{}
focusmanager.layout = layout
focusmanager.selected = {y = 1,x = 3}
Next(focusmanager)
assert.are.same({y = 2,x = 2}, focusmanager.selected)
end)
it("should move next left", function()
local focusmanager = FocusManager:new{}
focusmanager.layout = layout
focusmanager.selected = {y = 1,x = 2}
Previous(focusmanager)
assert.are.same({y = 1,x = 1}, focusmanager.selected)
end)
it("should move previous at start of row", function()
local focusmanager = FocusManager:new{}
focusmanager.layout = layout
focusmanager.selected = {y = 3,x = 2}
Previous(focusmanager)
assert.are.same({y = 2,x = 2}, focusmanager.selected)
end)
it("should move half rows or columns", function()
local focusmanager = FocusManager:new{}
focusmanager.layout = big_layout
focusmanager.selected = {x = 1, y = 1}
HalfMoveRight(focusmanager)
assert.are.same({y = 1,x = 3}, focusmanager.selected)
HalfMoveDown(focusmanager)
assert.are.same({y = 3,x = 3}, focusmanager.selected)
HalfMoveLeft(focusmanager)
assert.are.same({y = 3,x = 1}, focusmanager.selected)
HalfMoveUp(focusmanager)
assert.are.same({y = 1,x = 1}, focusmanager.selected)
end)
it("should move to specified position", function()
local focusmanager = FocusManager:new{}
focusmanager.layout = big_layout
focusmanager.selected = {x = 1, y = 1}
MoveTo(focusmanager, 3, 4)
assert.are.same({y = 4,x = 3}, focusmanager.selected)
end)
it("should set layout to nil", function()
local focusmanager = FocusManager:new{}
focusmanager.layout = layout
focusmanager:disableFocusManagement()
assert.is_nil(focusmanager.layout)
end)
it("should merge into rows", function()
local w = layout[1][1]
local fm1 = FocusManager:new{}
fm1.layout = {
{w, w, w}
}
local fm2 = FocusManager:new{}
fm2.layout = {
{w, w},
}
fm1:mergeLayoutInVertical(fm2)
local expected = {
{w, w, w},
{w, w}
}
assert.are.same(expected, fm1.layout)
end)
it("should merge into rows at specified position", function()
local w = layout[1][1]
local fm1 = FocusManager:new{}
fm1.layout = {
{w, w, w},
{w, w, w},
}
local fm2 = FocusManager:new{}
fm2.layout = {
{w, w},
}
fm1:mergeLayoutInVertical(fm2, 2)
local expected = {
{w, w, w},
{w, w},
{w, w, w},
}
assert.are.same(expected, fm1.layout)
end)
it("should merge into columns", function()
local w = layout[1][1]
local fm1 = FocusManager:new{}
fm1.layout = {
{w},
{w},
}
local fm2 = FocusManager:new{}
fm2.layout = {
{w, w},
{w},
}
fm1:mergeLayoutInHorizontal(fm2, 2)
local expected = {
{w, w, w},
{w, w},
}
assert.are.same(expected, fm1.layout)
end)
it("alternative key", function()
local focusmanager = FocusManager:new{}
focusmanager.extra_key_events = {
Hold = { {"Sym", "AA"}, doc = "tap and hold the widget", event="Hold" },
HalfFocusUp = { {"Alt", "Up"}, doc = "move focus half columns up", event = "FocusHalfMove", args = {"up"} },
}
local m = Input.modifiers;
m.Sym = true
assert.is_true(focusmanager:isAlternativeKey(Key:new("AA", m)))
m.Sym = false
m.Alt = true
assert.is_true(focusmanager:isAlternativeKey(Key:new("Up", m)))
m.Alt = false
assert.is_false(focusmanager:isAlternativeKey(Key:new("AA", m)))
assert.is_false(focusmanager:isAlternativeKey(Key:new("Up", m)))
end)
end) end)

Loading…
Cancel
Save