[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"),
id = "close",
callback = function()
UIManager:close(input_dialog)
end,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -155,18 +155,23 @@ end
function Kindle:setDateTime(year, month, day, hour, min, sec)
if hour == nil or min == nil then return true end
local command
local commands = {}
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
command = string.format("date -s '%d:%d'",hour, min)
table.insert(commands,string.format("date -s '%d:%d'",hour, min))
end
if os.execute(command) == 0 then
os.execute('hwclock -u -w')
return true
else
return false
for _, command in ipairs(commands) do
if os.execute(command) == 0 then
os.execute("hwclock -u -w")
return true
end
end
return false
end
function Kindle:usbPlugIn()

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

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

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

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

@ -24,12 +24,10 @@ local ButtonTable = FocusManager:new{
zero_sep = false,
button_font_face = "cfont",
button_font_size = 20,
auto_focus_first_button = true,
}
function ButtonTable:init()
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.button_by_id = {}
self.container = VerticalGroup:new{ width = self.width }
@ -100,10 +98,7 @@ function ButtonTable:init()
self:addHorizontalSep(true, false, false)
if Device:hasDPad() then
self.layout = self.buttons_layout
if self.auto_focus_first_button then
self.layout[1][1]:onFocus()
end
self.key_events.SelectByKeyPress = { {{"Press"}} }
self:refocusWidget()
else
self.key_events = {} -- deregister all key press event listeners
end
@ -129,15 +124,6 @@ function ButtonTable:addHorizontalSep(vspan_before, add_line, vspan_after, black
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)
return self.button_by_id[id] -- nil if not found
end

@ -16,7 +16,6 @@ Example:
local Blitbuffer = require("ffi/blitbuffer")
local CheckMark = require("ui/widget/checkmark")
local Device = require("device")
local Font = require("ui/font")
local FrameContainer = require("ui/widget/container/framecontainer")
local GestureRange = require("ui/gesturerange")
@ -97,32 +96,30 @@ function CheckButton:initCheckButton(checked)
self.dimen = self._frame:getSize()
self[1] = self._frame
if Device:isTouchDevice() then
self.ges_events = {
TapCheckButton = {
GestureRange:new{
ges = "tap",
range = self.dimen,
},
doc = "Tap Button",
self.ges_events = {
TapCheckButton = {
GestureRange:new{
ges = "tap",
range = self.dimen,
},
HoldCheckButton = {
GestureRange:new{
ges = "hold",
range = self.dimen,
},
doc = "Hold Button",
doc = "Tap Button",
},
HoldCheckButton = {
GestureRange:new{
ges = "hold",
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
HoldReleaseCheckButton = {
GestureRange:new{
ges = "hold_release",
range = self.dimen,
},
doc = "Hold Release Button",
}
doc = "Hold Release Button",
}
end
}
end
function CheckButton:onTapCheckButton()
@ -225,4 +222,20 @@ function CheckButton:disable()
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

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

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

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

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

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

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

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

@ -1,8 +1,10 @@
local bit = require("bit")
local Device = require("device")
local Event = require("ui/event")
local InputContainer = require("ui/widget/container/inputcontainer")
local logger = require("logger")
local UIManager = require("ui/uimanager")
local util = require("util")
--[[
Wrapper Widget that manages focus for a whole dialog
@ -33,20 +35,153 @@ local FocusManager = InputContainer:new{
function FocusManager:init()
if not self.selected then
self.selected = { x = 1, y = 1 }
else
self.selected = self.selected -- make sure current FocusManager has its own selected field
end
if Device:hasDPad() then
self.key_events = {
-- these will all generate the same event, just with different arguments
FocusUp = { {"Up"}, doc = "move focus up", event = "FocusMove", args = {0, -1} },
FocusDown = { {"Down"}, doc = "move focus down", event = "FocusMove", args = {0, 1} },
FocusLeft = { {"Left"}, doc = "move focus left", event = "FocusMove", args = {-1, 0} },
FocusRight = { {"Right"}, doc = "move focus right", event = "FocusMove", args = {1, 0} },
}
if Device:hasFewKeys() then
self.key_events.FocusLeft = nil
local event_keys = {}
-- these will all generate the same event, just with different arguments
table.insert(event_keys, {"FocusUp", { {"Up"}, doc = "move focus up", event = "FocusMove", args = {0, -1} } })
table.insert(event_keys, {"FocusRight", { {"Right"}, doc = "move focus right", event = "FocusMove", args = {1, 0} } })
table.insert(event_keys, {"FocusDown", { {"Down"}, doc = "move focus down", event = "FocusMove", args = {0, 1} } })
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
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
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
function FocusManager:onFocusMove(args)
@ -101,11 +236,50 @@ function FocusManager:onFocusMove(args)
return true
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.
-- @return false if none could be found
function FocusManager:_wrapAroundX(dx)
local x = self.selected.x
while self.layout[x - dx] do
while self.layout[self.selected.y][x - dx] do
x = x - dx
end
if x ~= self.selected.x then
@ -168,7 +342,7 @@ function FocusManager:getFocusItem()
return self.layout[self.selected.y][self.selected.x]
end
function FocusManager:sendTapEventToFocusedWidget()
function FocusManager:_sendGestureEventToFocusedWidget(gesture)
local focused_widget = self:getFocusItem()
if focused_widget then
-- center of widget position
@ -177,8 +351,9 @@ function FocusManager:sendTapEventToFocusedWidget()
point.y = point.y + point.h / 2
point.w = 0
point.h = 0
logger.dbg("FocusManager: Send " .. gesture .. " to " .. point.x .. ", " .. point.y)
UIManager:sendEvent(Event:new("Gesture", {
ges = "tap",
ges = gesture,
pos = point,
}))
return true
@ -186,14 +361,26 @@ function FocusManager:sendTapEventToFocusedWidget()
return false
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
return
end
if not pos then
pos = #self.layout + 1 -- end of row
end
for _, row in ipairs(child.layout) do
table.insert(self.layout, row)
table.insert(self.layout, pos, row)
pos = pos + 1
end
child:disableFocusManagement()
child:disableFocusManagement(self)
end
function FocusManager:mergeLayoutInHorizontal(child)
@ -210,18 +397,39 @@ function FocusManager:mergeLayoutInHorizontal(child)
table.insert(prow, widget)
end
end
child:disableFocusManagement()
child:disableFocusManagement(self)
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
end
--- Container call this method after init to let first widget render in focus style
function FocusManager:focusTopLeftWidget()
if Device:hasDPad() then
-- trigger selected widget in focused style
self:onFocusMove({0, 0})
-- constant for refocusWidget method to ease code reading
FocusManager.RENDER_IN_NEXT_TICK = true
--- Container calls this method to re-set focus widget style
--- 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

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

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

@ -1,6 +1,7 @@
local Blitbuffer = require("ffi/blitbuffer")
local CheckButton = require("ui/widget/checkbutton")
local Device = require("device")
local FocusManager = require("ui/widget/focusmanager")
local FrameContainer = require("ui/widget/container/framecontainer")
local Font = require("ui/font")
local Geom = require("ui/geometry")
@ -19,6 +20,7 @@ local _ = require("gettext")
local Screen = Device.screen
local Keyboard
local FocusManagerInstance = FocusManager:new{}
local InputText = InputContainer:new{
text = "",
@ -274,7 +276,6 @@ if Device:isTouchDevice() or Device:hasDPad() then
-- used for taking a screenshot)
return false
end
end
if Device:hasDPad() then
if not InputText.initEventListener then
@ -283,14 +284,17 @@ if Device:isTouchDevice() or Device:hasDPad() then
function InputText:onFocus()
-- 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()
return true
end
function InputText:onUnfocus()
-- Event called by the focusmanager
self.key_events = {}
self:unfocus()
return true
end
@ -542,7 +546,11 @@ end
-- is shown. Mostly likely to be in the emulator, but could be Android + BT
-- keyboard, or a "coder's keyboard" Android input method.
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
if not key["Ctrl"] and not key["Shift"] and not key["Alt"] then
@ -567,6 +575,10 @@ function InputText:onKeyPress(key)
self:addChars("\n")
elseif key["Tab"] then
self:addChars(" ")
elseif key["Back"] then
if self.focused then
self:unfocus()
end
else
handled = false
end
@ -581,15 +593,46 @@ function InputText:onKeyPress(key)
else
handled = false
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
end
-- Handle text coming directly as text from the Device layer (eg. soft keyboard
-- or via SDL's keyboard mapping).
function InputText:onTextInput(text)
self:addChars(text)
return true
-- for more than one InputText, let the focused one add chars
if self.focused then
self:addChars(text)
return true
end
return false
end
function InputText:onShowKeyboard(ignore_first_hold_release)

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

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

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

@ -34,7 +34,6 @@ local TextBoxWidget = require("ui/widget/textboxwidget")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local logger = require("logger")
local _ = require("gettext")
local Screen = require("device").screen
@ -72,9 +71,7 @@ function MultiConfirmBox:init()
}
end
if Device:hasKeys() then
self.key_events = {
Close = { {Device.input.group.Back}, doc = "cancel" }
}
self.key_events.Close = { {Device.input.group.Back}, doc = "cancel" }
end
end
local content = HorizontalGroup:new{
@ -173,17 +170,4 @@ function MultiConfirmBox:onTapClose(arg, ges)
return false
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

@ -32,6 +32,7 @@ Example for input of two strings and a number:
{
{
text = _("Cancel"),
id = "close",
callback = function()
UIManager:close(sample_input)
end
@ -132,10 +133,7 @@ function MultiInputDialog:init()
auto_para_direction = field.auto_para_direction or self.auto_para_direction,
alignment_strict = field.alignment_strict or self.alignment_strict,
}
if Device:hasDPad() then
-- 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
table.insert(self.layout, #self.layout, {input_field[k]})
if field.description then
input_description[k] = FrameContainer:new{
padding = self.description_padding,
@ -164,10 +162,6 @@ function MultiInputDialog:init()
})
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
table.insert(VerticalGroupData,CenterContainer:new{
dimen = Geom:new{

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

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

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

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

@ -45,7 +45,6 @@ function SkimToWidget:init()
end
self.buttons_layout = {}
self.selected = { x = 1, y = 2 }
local frame_width = math.floor(math.min(screen_width, screen_height) * 0.95)
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 },
}
self.layout = self.buttons_layout
self.layout[2][1]:onFocus()
self.key_events.SelectByKeyPress = { { "Press" }, doc = "select focused item" }
self:moveFocusTo(1, 2)
end
if Device:hasKeyboard() then
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.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:refocusWidget(FocusManager.RENDER_IN_NEXT_TICK)
end
function SkimToWidget:addOriginToLocationStack(add_current)
@ -378,15 +377,6 @@ function SkimToWidget:onAnyKeyPressed()
return true
end
function SkimToWidget:onSelectByKeyPress()
local item = self:getFocusItem()
if item then
item.callback()
return true
end
return false
end
function SkimToWidget:onFirstRowKeyPress(percent)
local page = Math.round(percent * self.page_count)
self:addOriginToLocationStack()

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

@ -7,7 +7,6 @@ local Button = require("ui/widget/button")
local CenterContainer = require("ui/widget/container/centercontainer")
local CheckMark = require("ui/widget/checkmark")
local Device = require("device")
local Event = require("ui/event")
local FocusManager = require("ui/widget/focusmanager")
local Font = require("ui/font")
local FrameContainer = require("ui/widget/container/framecontainer")
@ -490,7 +489,6 @@ function TouchMenu:init()
end
self.key_events.NextPage = { {Input.group.PgFwd}, doc = "next page" }
self.key_events.PrevPage = { {Input.group.PgBack}, doc = "previous page" }
self.key_events.Press = { {"Press"}, doc = "chose selected item" }
local icons = {}
for _, v in ipairs(self.tab_item_table) do
@ -713,7 +711,7 @@ function TouchMenu:updateItems()
-- recalculate dimen based on new layout
self.dimen.w = self.width
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,
-- in order to optionally flash on initial menu popup...
@ -942,13 +940,4 @@ function TouchMenu:onBack()
self:backToUpperMenu()
end
function TouchMenu:onPress()
local item = self:getFocusItem()
if item then
item:handleEvent(Event:new("TapSelect"))
return true
end
return false
end
return TouchMenu

@ -2,7 +2,6 @@ local Blitbuffer = require("ffi/blitbuffer")
local BottomContainer = require("ui/widget/container/bottomcontainer")
local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device")
local Event = require("ui/event")
local FocusManager = require("ui/widget/focusmanager")
local Font = require("ui/font")
local FrameContainer = require("ui/widget/container/framecontainer")
@ -245,40 +244,38 @@ function VirtualKey:init()
h = self.height,
}
--self.dimen = self[1]:getSize()
if Device:isTouchDevice() then
self.ges_events = {
TapSelect = {
GestureRange:new{
ges = "tap",
range = self.dimen,
},
self.ges_events = {
TapSelect = {
GestureRange:new{
ges = "tap",
range = self.dimen,
},
HoldSelect = {
GestureRange:new{
ges = "hold",
range = self.dimen,
},
},
HoldSelect = {
GestureRange:new{
ges = "hold",
range = self.dimen,
},
HoldReleaseKey = {
GestureRange:new{
ges = "hold_release",
range = self.dimen,
},
},
HoldReleaseKey = {
GestureRange:new{
ges = "hold_release",
range = self.dimen,
},
PanReleaseKey = {
GestureRange:new{
ges = "pan_release",
range = self.dimen,
},
},
PanReleaseKey = {
GestureRange:new{
ges = "pan_release",
range = self.dimen,
},
SwipeKey = {
GestureRange:new{
ges = "swipe",
range = self.dimen,
},
},
SwipeKey = {
GestureRange:new{
ges = "swipe",
range = self.dimen,
},
}
end
},
}
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.symbolmode_keys[self.label] ~= nil and self.keyboard.symbolmode) then
@ -479,15 +476,6 @@ function VirtualKeyPopup:onCloseWidget()
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()
local parent_key = self.parent_key
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 = TimeVal:new{ usec = self.tap_interval_override }
if Device:hasDPad() then
self.key_events.PressKey = { {"Press"}, doc = "select key" }
end
if Device:hasKeys() then
self.key_events.Close = { {Device.input.group.Back}, doc = "close keyboard" }
end
@ -803,16 +788,13 @@ function VirtualKeyboard:init()
self:initLayer(self.keyboard_layer)
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 }
if Device:hasDPad() then
self.key_events.PressKey = { {"Press"}, doc = "select key" }
end
if Device:hasKeys() then
self.key_events.Close = { {"Back"}, doc = "close keyboard" }
end
if keyboard.wrapInputBox then
self.uwrap_func = keyboard.wrapInputBox(self.inputbox) or self.uwrap_func
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
-- 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
@ -820,14 +802,39 @@ function VirtualKeyboard:init()
-- 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.
-- So, disable all key_event handled by FocusManager
self.key_events.FocusLeft = nil
self.key_events.FocusRight = nil
self.key_events.FocusUp = nil
self.key_events.FocusDown = nil
self.key_events.PressKey = nil -- added above
if Device:isTouchDevice() then
-- Remove all FocusManager key event handlers.
for k, _ in pairs(self.builtin_key_events) do
self.key_events[k] = nil
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
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()
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")
@ -856,16 +863,12 @@ end
function VirtualKeyboard:onClose()
UIManager:close(self)
return true
end
function VirtualKeyboard:onPressKey()
local item = self:getFocusItem()
if item then
item:handleEvent(Event:new("TapSelect"))
return true
if self.inputbox and Device:hasDPad() then
-- let input text handle Back event to unfocus
-- otherwise, another extra Back event needed
return false
end
return false
return true
end
function VirtualKeyboard:_refresh(want_flash, fullscreen)

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

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

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

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

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

@ -135,24 +135,22 @@ function ListMenuItem:init()
self.detail = self.text
-- we need this table per-instance, so we declare it here
if Device:isTouchDevice() then
self.ges_events = {
TapSelect = {
GestureRange:new{
ges = "tap",
range = self.dimen,
},
doc = "Select Menu Item",
self.ges_events = {
TapSelect = {
GestureRange:new{
ges = "tap",
range = self.dimen,
},
HoldSelect = {
GestureRange:new{
ges = "hold",
range = self.dimen,
},
doc = "Hold Menu Item",
doc = "Select Menu Item",
},
HoldSelect = {
GestureRange:new{
ges = "hold",
range = self.dimen,
},
}
end
doc = "Hold Menu Item",
},
}
-- 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 = {{
{
text = _("Cancel"),
id = "close",
callback = function()
UIManager:close(file_input)
end,

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

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

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

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

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

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

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

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

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

@ -1,14 +1,24 @@
describe("FocusManager module", function()
local FocusManager
local layout
local Up,Down,Left,Right
Up = function(self) self:onFocusMove({0, -1}) end
Down = function(self) self:onFocusMove({0, 1}) end
Left = function(self) self:onFocusMove({-1, 0}) end
Right = function(self) self:onFocusMove({1, 0}) end
local layout, big_layout
local Key
local Input
local Up = function(self) self:onFocusMove({0, -1}) end
local Down = function(self) self:onFocusMove({0, 1}) 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()
require("commonrequire")
FocusManager = require("ui/widget/focusmanager")
Key = require("device/key")
Input = require("device/input")
local Widget = require("ui/widget/textwidget")
local w = Widget:new{}
layout= {
@ -16,7 +26,13 @@ describe("FocusManager module", function()
{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)
it("should go right", function()
local focusmanager = FocusManager:new{}
@ -88,4 +104,129 @@ describe("FocusManager module", function()
Right(focusmanager)
assert.are.same({y = 2,x = 2}, focusmanager.selected)
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)

Loading…
Cancel
Save