Kindle4NT improvements (#3745)

* [device][kindle4] add fake event to kindle4

* modify focusmanager to allow for more complex layout

The focusmanager now naviguate the layout by avoiding nil value
instead of relying on table lenght. It should be completely backward
compatible

* add Dpad naviguation to the touchmenu

* fix crash because virtualkeyboard on non touch device

the kindle4NT has no keyboard nor touch, the fix open the virtual
keyboard so koreader dont crash but it's not useable

* Enable device with keys to use the touchmenu

* Don't get stuck in reader progress statistics plugin

* [underlinecontainer] Fix and remove unused function

References #1898.
pull/3759/head
onde2rock 6 years ago committed by Frans de Jonge
parent 9849d89f0e
commit e8aab49ee9

@ -362,7 +362,12 @@ function FileManager:init()
end end
if Device:hasKeys() then if Device:hasKeys() then
self.key_events.Close = { {"Home"}, doc = "Close file manager" } self.key_events.Home = { {"Home"}, doc = "go home" }
if not Device:isSDL() then
--if not in the desktop emulator
--remove the old Back key to exit koreader
self.file_chooser.key_events.Close = nil
end
end end
self:handleEvent(Event:new("SetDimensions", self.dimen)) self:handleEvent(Event:new("SetDimensions", self.dimen))
@ -788,4 +793,8 @@ function FileManager:moveFile(from, to)
return util.execute(self.mv_bin, from, to) == 0 return util.execute(self.mv_bin, from, to) == 0
end end
function FileManager:onHome()
return self:goHome()
end
return FileManager return FileManager

@ -306,7 +306,7 @@ function FileManagerMenu:onShowMenu()
} }
local main_menu local main_menu
if Device:isTouchDevice() then if Device:isTouchDevice() or Device:hasDPad() then
local TouchMenu = require("ui/widget/touchmenu") local TouchMenu = require("ui/widget/touchmenu")
main_menu = TouchMenu:new{ main_menu = TouchMenu:new{
width = Screen:getWidth(), width = Screen:getWidth(),

@ -53,7 +53,6 @@ function ReaderMenu:init()
self.registered_widgets = {} self.registered_widgets = {}
if Device:hasKeys() then if Device:hasKeys() then
self.key_events = { Close = { { "Back" }, doc = "close menu" }, }
if Device:isTouchDevice() then if Device:isTouchDevice() then
self.key_events.TapShowMenu = { { "Menu" }, doc = "show menu", } self.key_events.TapShowMenu = { { "Menu" }, doc = "show menu", }
else else
@ -241,7 +240,7 @@ function ReaderMenu:onShowReaderMenu(tab_index)
} }
local main_menu local main_menu
if Device:isTouchDevice() then if Device:isTouchDevice() or Device:hasDPad() then
local TouchMenu = require("ui/widget/touchmenu") local TouchMenu = require("ui/widget/touchmenu")
main_menu = TouchMenu:new{ main_menu = TouchMenu:new{
width = Screen:getWidth(), width = Screen:getWidth(),
@ -294,8 +293,10 @@ function ReaderMenu:_getTabIndexFromLocation(ges)
if self.tab_item_table == nil then if self.tab_item_table == nil then
self:setUpdateItemTable() self:setUpdateItemTable()
end end
if not ges then
return self.last_tab_index
-- if the start position is far right -- if the start position is far right
if ges.pos.x > 2 * Screen:getWidth() / 3 then elseif ges.pos.x > 2 * Screen:getWidth() / 3 then
return #self.tab_item_table return #self.tab_item_table
-- if the start position is far left -- if the start position is far left
elseif ges.pos.x < Screen:getWidth() / 3 then elseif ges.pos.x < Screen:getWidth() / 3 then

@ -52,11 +52,6 @@ local T = require("ffi/util").template
local ReaderUI = InputContainer:new{ local ReaderUI = InputContainer:new{
name = "ReaderUI", name = "ReaderUI",
key_events = {
Close = { { "Home" },
doc = "close document", event = "Close" },
},
active_widgets = {}, active_widgets = {},
-- if we have a parent container, it must be referenced for now -- if we have a parent container, it must be referenced for now
@ -97,14 +92,17 @@ function ReaderUI:init()
self.dialog = self self.dialog = self
end end
self.doc_settings = DocSettings:open(self.document.file)
if Device:hasKeys() then if Device:hasKeys() then
self.key_events.Back = { self.key_events.Home = { {"Home"}, doc = "open file browser" }
{ "Back" }, doc = "close document", if Device:isSDL() then
event = "Close" } --if in the desktop emulator
--add the old Back key to exit koreader
self.key_events.Close = { {"Back"}, doc = "Exit koreader" }
end
end end
self.doc_settings = DocSettings:open(self.document.file)
-- a view container (so it must be child #1!) -- a view container (so it must be child #1!)
-- all paintable widgets need to be a child of reader view -- all paintable widgets need to be a child of reader view
self:registerModule("view", ReaderView:new{ self:registerModule("view", ReaderView:new{
@ -622,4 +620,8 @@ function ReaderUI:dealWithLoadDocumentFailure()
error("crengine failed recognizing or parsing this file: unsupported or invalid document") error("crengine failed recognizing or parsing this file: unsupported or invalid document")
end end
function ReaderUI:onHome()
return self:showFileManager()
end
return ReaderUI return ReaderUI

@ -317,6 +317,7 @@ function Kindle4:init()
} }
self.input.open("/dev/input/event0") self.input.open("/dev/input/event0")
self.input.open("/dev/input/event1") self.input.open("/dev/input/event1")
self.input.open("fake_events")
Kindle.init(self) Kindle.init(self)
end end

@ -2,6 +2,7 @@ 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 CloseButton = require("ui/widget/closebutton") local CloseButton = require("ui/widget/closebutton")
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 Geom = require("ui/geometry") local Geom = require("ui/geometry")
@ -25,7 +26,7 @@ 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 Screen = Device.screen
local template = require("ffi/util").template local template = require("ffi/util").template
local stats_book = {} local stats_book = {}
@ -82,6 +83,15 @@ function BookStatusWidget:init()
show_parent = self, show_parent = self,
readonly = self.readonly, readonly = self.readonly,
} }
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" }
}
end
local screen_size = Screen:getSize() local screen_size = Screen:getSize()
self[1] = FrameContainer:new{ self[1] = FrameContainer:new{
width = screen_size.w, width = screen_size.w,

@ -18,10 +18,6 @@ local UnderlineContainer = WidgetContainer:new{
} }
function UnderlineContainer:getSize() function UnderlineContainer:getSize()
return self:getContentSize()
end
function UnderlineContainer:getContentSize()
local contentSize = self[1]:getSize() local contentSize = self[1]:getSize()
return Geom:new{ return Geom:new{
w = contentSize.w, w = contentSize.w,
@ -36,7 +32,7 @@ function UnderlineContainer:paintTo(bb, x, y)
w = container_size.w, w = container_size.w,
h = container_size.h h = container_size.h
} }
local content_size = self:getContentSize() local content_size = self[1]:getSize()
local p_y = y local p_y = y
if self.vertical_align == "center" then if self.vertical_align == "center" then
p_y = math.floor((container_size.h - content_size.h) / 2) + y p_y = math.floor((container_size.h - content_size.h) / 2) + y

@ -9,18 +9,16 @@ supports a 2D model of active elements
e.g.: e.g.:
layout = { layout = {
{ textinput, textinput }, { textinput, textinput, item },
{ okbutton, cancelbutton } { okbutton, cancelbutton, item },
{ nil, item, nil },
{ nil, item, nil },
{ nil, item, nil },
} }
Navigate the layout by trying to avoid not set or nil value.
this is a dialog with 2 rows. in the top row, there is the Provide a simple wrap around in the vertical direction.
single (!) widget <textinput>. when the focus is in this The first element of the first table must be valid to ensure
group, left/right movement seems (!) to be doing nothing. to not get stuck in an invalid position.
in the second row, there are two widgets and you can move
left/right. also, you can go up from both to reach <textinput>,
and from that go down and (depending on internat coordinates)
reach either <okbutton> or <cancelbutton>.
but notice that this does _not_ do the layout for you, but notice that this does _not_ do the layout for you,
it rather defines an abstract layout. it rather defines an abstract layout.
@ -57,30 +55,17 @@ function FocusManager:onFocusMove(args)
end end
local current_item = self.layout[self.selected.y][self.selected.x] local current_item = self.layout[self.selected.y][self.selected.x]
while true do while true do
if self.selected.x + dx > #self.layout[self.selected.y] if not self.layout[self.selected.y + dy] then
or self.selected.x + dx < 1 then --vertical borders, try to wraparound
break -- abort when we run into horizontal borders if not self:wrapAround(dy) then
end
-- call widget wrap callbacks in vertical direction
if self.selected.y + dy > #self.layout then
if not self:onWrapLast() then
break
end
elseif self.selected.y + dy < 1 then
if not self:onWrapFirst() then
break break
end end
elseif not self.layout[self.selected.y + dy][self.selected.x + dx] then
--vertical border, no wraparound
break
else else
self.selected.y = self.selected.y + dy self.selected.y = self.selected.y + dy
if #self.layout[self.selected.y] == 0 then -- horizontal separator self.selected.x = self.selected.x + dx
self.selected.y = self.selected.y + dy -- skip it
end
end
self.selected.x = self.selected.x + dx
if self.selected.x > #self.layout[self.selected.y] then
-- smaller nb of items on new row than on prev row
self.selected.x = #self.layout[self.selected.y]
end end
if self.layout[self.selected.y][self.selected.x] ~= current_item if self.layout[self.selected.y][self.selected.x] ~= current_item
@ -88,24 +73,28 @@ function FocusManager:onFocusMove(args)
-- we found a different object to focus -- we found a different object to focus
current_item:handleEvent(Event:new("Unfocus")) current_item:handleEvent(Event:new("Unfocus"))
self.layout[self.selected.y][self.selected.x]:handleEvent(Event:new("Focus")) self.layout[self.selected.y][self.selected.x]:handleEvent(Event:new("Focus"))
-- trigger a repaint (we need to be the registered widget!) -- trigger a fast repaint, this seem to not count toward a fullscreen eink resfresh
-- TODO: is this really needed? -- TODO: is this really needed?
UIManager:setDirty(self.show_parent or self, "partial") UIManager:setDirty(self.show_parent or self, "fast")
break break
end end
end end
return true
end
function FocusManager:onWrapFirst()
self.selected.y = #self.layout
return true return true
end end
function FocusManager:onWrapLast() function FocusManager:wrapAround(dy)
self.selected.y = 1 --go to the last valid item directly above or below the current item
return true --return false if none could be found
local y = self.selected.y
while self.layout[y - dy] and self.layout[y - dy][self.selected.x] do
y = y - dy
end
if y ~= self.selected.y then
self.selected.y = y
return true
else
return false
end
end end
function FocusManager:getFocusItem() function FocusManager:getFocusItem()

@ -124,4 +124,19 @@ function IconButton:onHoldIconButton()
return true return true
end end
function IconButton:onFocus()
--quick and dirty, need better way to show focus
self.image.invert=true
return true
end
function IconButton:onUnfocus()
self.image.invert=false
return true
end
function IconButton:onTapSelect()
self:onTapIconButton()
end
return IconButton return IconButton

@ -88,6 +88,9 @@ if Device.isTouchDevice() then
end) end)
end end
end end
elseif not Device.hasKeyboard() then
Keyboard = require("ui/widget/virtualkeyboard")
function InputText:initEventListener() end --do nothing but doesn't crash for now
else else
Keyboard = require("ui/widget/physicalkeyboard") Keyboard = require("ui/widget/physicalkeyboard")
function InputText:initEventListener() end function InputText:initEventListener() end

@ -6,6 +6,8 @@ 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 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")
@ -20,12 +22,14 @@ local RightContainer = require("ui/widget/container/rightcontainer")
local Size = require("ui/size") local Size = require("ui/size")
local TextWidget = require("ui/widget/textwidget") local TextWidget = require("ui/widget/textwidget")
local UIManager = require("ui/uimanager") local UIManager = require("ui/uimanager")
local UnderlineContainer = require("ui/widget/container/underlinecontainer")
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 util = require("ffi/util") local util = require("ffi/util")
local _ = require("gettext") local _ = require("gettext")
local Screen = Device.screen
local getMenuText = require("util").getMenuText local getMenuText = require("util").getMenuText
local Input = Device.input
local Screen = Device.screen
--[[ --[[
TouchMenuItem widget TouchMenuItem widget
@ -97,7 +101,27 @@ function TouchMenuItem:init()
}, },
}, },
} }
self[1] = self.item_frame
self._underline_container = UnderlineContainer:new{
vertical_align = "center",
dimen =self.dimen,
self.item_frame
}
self[1] = self._underline_container
function self:isEnabled()
return item_enabled ~= false and true
end
end
function TouchMenuItem:onFocus()
self._underline_container.color = Blitbuffer.COLOR_BLACK
return true
end
function TouchMenuItem:onUnfocus()
self._underline_container.color = Blitbuffer.COLOR_WHITE
return true
end end
function TouchMenuItem:onTapSelect(arg, ges) function TouchMenuItem:onTapSelect(arg, ges)
@ -202,9 +226,11 @@ function TouchMenuBar:init()
callback = nil, callback = nil,
padding_left = icon_padding, padding_left = icon_padding,
padding_right = icon_padding, padding_right = icon_padding,
menu = self.menu,
} }
table.insert(self.icon_widgets, ib) table.insert(self.icon_widgets, ib)
table.insert(self.menu.layout, ib) -- for the focusmanager
-- we have to use local variable here for closure callback -- we have to use local variable here for closure callback
local _start_seg = end_seg + icon_sep_width local _start_seg = end_seg + icon_sep_width
@ -319,7 +345,7 @@ end
--[[ --[[
TouchMenu widget for hierarchical menus TouchMenu widget for hierarchical menus
--]] --]]
local TouchMenu = InputContainer:new{ local TouchMenu = FocusManager:new{
tab_item_table = {}, tab_item_table = {},
-- for returnning in multi-level menus -- for returnning in multi-level menus
item_table_stack = nil, item_table_stack = nil,
@ -351,6 +377,8 @@ function TouchMenu:init()
end end
end end
self.layout = {}
self.ges_events.TapCloseAllMenus = { self.ges_events.TapCloseAllMenus = {
GestureRange:new{ GestureRange:new{
ges = "tap", ges = "tap",
@ -368,7 +396,10 @@ function TouchMenu:init()
} }
} }
self.key_events.Close = { {"Back"}, doc = "close touch menu" } self.key_events.Back = { {"Back"}, doc = "back to upper menu or close touchmenu" }
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 = {} local icons = {}
for _,v in ipairs(self.tab_item_table) do for _,v in ipairs(self.tab_item_table) do
@ -509,7 +540,9 @@ function TouchMenu:updateItems()
local old_dimen = self.dimen and self.dimen:copy() local old_dimen = self.dimen and self.dimen:copy()
self:_recalculatePageLayout() self:_recalculatePageLayout()
self.item_group:clear() self.item_group:clear()
self.layout = {}
table.insert(self.item_group, self.bar) table.insert(self.item_group, self.bar)
table.insert(self.layout, self.bar.icon_widgets) --for the focusmanager
for c = 1, self.perpage do for c = 1, self.perpage do
-- calculate index in item_table -- calculate index in item_table
@ -526,6 +559,9 @@ function TouchMenu:updateItems()
show_parent = self.show_parent, show_parent = self.show_parent,
} }
table.insert(self.item_group, item_tmp) table.insert(self.item_group, item_tmp)
if item_tmp:isEnabled() then
table.insert(self.layout, {[self.cur_tab] = item_tmp}) --for the focusmanager
end
if item.separator and c ~= self.perpage then if item.separator and c ~= self.perpage then
-- insert split line -- insert split line
table.insert(self.item_group, self.split_line) table.insert(self.item_group, self.split_line)
@ -553,6 +589,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*2 self.dimen.h = self.item_group:getSize().h + self.bordersize*2 + self.padding*2
self.selected = { x = self.cur_tab, y = 1 } --reset the position of the focusmanager
UIManager:setDirty("all", function() UIManager:setDirty("all", function()
local refresh_dimen = local refresh_dimen =
@ -707,4 +744,12 @@ function TouchMenu:onClose()
self:closeMenu() self:closeMenu()
end end
function TouchMenu:onBack()
self:backToUpperMenu()
end
function TouchMenu:onPress()
self:getFocusItem():handleEvent(Event:new("TapSelect"))
end
return TouchMenu return TouchMenu

@ -1,6 +1,7 @@
local Blitbuffer = require("ffi/blitbuffer") local Blitbuffer = require("ffi/blitbuffer")
local CenterContainer = require("ui/widget/container/centercontainer") local CenterContainer = require("ui/widget/container/centercontainer")
local CloseButton = require("ui/widget/closebutton") local CloseButton = require("ui/widget/closebutton")
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 Geom = require("ui/geometry") local Geom = require("ui/geometry")
@ -18,7 +19,7 @@ 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 Screen = Device.screen
local LINE_COLOR = Blitbuffer.gray(0.4) local LINE_COLOR = Blitbuffer.gray(0.4)
local BG_COLOR = Blitbuffer.gray(0.2) local BG_COLOR = Blitbuffer.gray(0.2)
@ -46,6 +47,13 @@ function ReaderProgress:init()
UIManager:setDirty(self, function() UIManager:setDirty(self, function()
return "ui", self.dimen return "ui", self.dimen
end) end)
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" }
}
end
self[1] = FrameContainer:new{ self[1] = FrameContainer:new{
width = self.width, width = self.width,
height = self.height, height = self.height,

Loading…
Cancel
Save