Files
koreader/frontend/ui/widget/inputtext.lua
onde2rock e502bf04d3 [feat, UX] Support the virtualKeyboard on non touch-device (#3796)
* [VirtualKeyboard] Add support for keynaviguation

Also rename the variable "layout" to "keyboard_layout" because conflict
with the layout from the focusmanager

* Make the goto dialog compatible with key naviguation

My solution is to change the order of the widget. The last one will the
virtualkeybard so it catch all the keybinding, and below it, make the
dialog "is_always_active = true" so it can receive touch event.

* Correctly show the virtual keyboard on dpad devices

* change the order to call the virtualKeyboard so it end up on top

* Handle the multi input dialog

* Support reopening the virtualKeyboard by the Press key

* add check focusmanager

* Fix https://github.com/koreader/koreader/issues/3797

* MultiInputDialog : Now work on non touch-device

* Set the virtualkeyboard to be a modal widget

* Fix the layout in multiinputwidget

* Fix for the various combination of
hasKeys,hasDpad,isTouchDevice

* [Focusmanager] Better handling of malformed layout
2018-03-30 12:46:36 +02:00

335 lines
10 KiB
Lua

local Blitbuffer = require("ffi/blitbuffer")
local CheckButton = require("ui/widget/checkbutton")
local Device = require("device")
local FrameContainer = require("ui/widget/container/framecontainer")
local Font = require("ui/font")
local GestureRange = require("ui/gesturerange")
local InputContainer = require("ui/widget/container/inputcontainer")
local ScrollTextWidget = require("ui/widget/scrolltextwidget")
local TextBoxWidget = require("ui/widget/textboxwidget")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
local util = require("util")
local _ = require("gettext")
local Screen = Device.screen
local Keyboard
local InputText = InputContainer:new{
text = "",
hint = "demo hint",
charlist = nil, -- table to store input string
charpos = nil, -- position to insert a new char, or the position of the cursor
input_type = nil,
text_type = nil,
text_widget = nil, -- Text Widget for cursor movement
show_password_toggle = true,
width = nil,
height = nil,
face = Font:getFace("smallinfofont"),
padding = Screen:scaleBySize(5),
margin = Screen:scaleBySize(5),
bordersize = Screen:scaleBySize(2),
parent = nil, -- parent dialog that will be set dirty
scroll = false,
focused = true,
}
-- only use PhysicalKeyboard if the device does not have touch screen
if Device.isTouchDevice() or Device.hasDPad() then
Keyboard = require("ui/widget/virtualkeyboard")
if Device.isTouchDevice() then
function InputText:initEventListener()
self.ges_events = {
TapTextBox = {
GestureRange:new{
ges = "tap",
range = self.dimen
}
},
HoldTextBox = {
GestureRange:new{
ges = "hold",
range = self.dimen
}
},
}
end
function InputText:onTapTextBox(arg, ges)
if self.parent.onSwitchFocus then
self.parent:onSwitchFocus(self)
end
local x = ges.pos.x - self._frame_textwidget.dimen.x - self.bordersize - self.padding
local y = ges.pos.y - self._frame_textwidget.dimen.y - self.bordersize - self.padding
if x > 0 and y > 0 then
self.charpos = self.text_widget:moveCursor(x, y)
UIManager:setDirty(self.parent, function()
return "ui", self.dimen
end)
end
end
function InputText:onHoldTextBox(arg, ges)
if self.parent.onSwitchFocus then
self.parent:onSwitchFocus(self)
end
local x = ges.pos.x - self._frame_textwidget.dimen.x - self.bordersize - self.padding
local y = ges.pos.y - self._frame_textwidget.dimen.y - self.bordersize - self.padding
if x > 0 and y > 0 then
self.charpos = self.text_widget:moveCursor(x, y)
if Device:hasClipboard() and Device.input.hasClipboardText() then
self:addChars(Device.input.getClipboardText())
end
UIManager:setDirty(self.parent, function()
return "ui", self.dimen
end)
end
end
end
if Device.hasKeys() then
if not InputText.initEventListener then
function InputText:initEventListener() end
end
function InputText:onFocus()
--Event called by the focusmanager
self.key_events.ShowKeyboard = { {"Press"}, doc = "show keyboard" }
self:focus()
return true
end
function InputText:onUnfocus()
--Event called by the focusmanager
self.key_events = {}
self:unfocus()
return true
end
end
else
Keyboard = require("ui/widget/physicalkeyboard")
function InputText:initEventListener() end
end
function InputText:init()
self:initTextBox(self.text)
if self.readonly ~= true then
self:initKeyboard()
self:initEventListener()
end
end
function InputText:initTextBox(text, char_added, is_password_type)
self.text = text
if self.text_type == "password" then
is_password_type = true
end
local fgcolor
local show_charlist
local show_text = text
if show_text == "" or show_text == nil then
-- no preset value, use hint text if set
show_text = self.hint
fgcolor = Blitbuffer.COLOR_GREY
self.charlist = {}
self.charpos = 1
else
fgcolor = Blitbuffer.COLOR_BLACK
if self.text_type == "password" then
show_text = self.text:gsub(
"(.-).", function() return "*" end)
if char_added then
show_text = show_text:gsub(
"(.)$", function() return self.text:sub(-1) end)
end
end
self.charlist = util.splitToChars(text)
if self.charpos == nil then
self.charpos = #self.charlist + 1
end
end
if is_password_type and self.show_password_toggle then
self._check_button = self._check_button or CheckButton:new{
text = _("Show password"),
callback = function()
if self.text_type == "text" then
self.text_type = "password"
self._check_button:unCheck()
else
self.text_type = "text"
self._check_button:check()
end
self:setText(self:getText(), is_password_type)
end,
width = self.width,
height = self.height,
padding = self.padding,
margin = self.margin,
bordersize = self.bordersize,
}
self._password_toggle = FrameContainer:new{
bordersize = 0,
padding = self.padding,
margin = self.margin,
self._check_button,
}
else
self._password_toggle = nil
end
show_charlist = util.splitToChars(show_text)
if self.scroll then
self.text_widget = ScrollTextWidget:new{
text = show_text,
charlist = show_charlist,
charpos = self.charpos,
editable = self.focused,
face = self.face,
fgcolor = fgcolor,
width = self.width,
height = self.height,
dialog = self.parent,
}
else
self.text_widget = TextBoxWidget:new{
text = show_text,
charlist = show_charlist,
charpos = self.charpos,
editable = self.focused,
face = self.face,
fgcolor = fgcolor,
width = self.width,
height = self.height,
}
end
self._frame_textwidget = FrameContainer:new{
bordersize = self.bordersize,
padding = self.padding,
margin = self.margin,
color = self.focused and Blitbuffer.COLOR_BLACK or Blitbuffer.COLOR_GREY,
self.text_widget,
}
self._verticalgroup = VerticalGroup:new{
align = "left",
self._frame_textwidget,
self._password_toggle,
}
self._frame = FrameContainer:new{
bordersize = 0,
margin = 0,
padding = 0,
self._verticalgroup,
}
self[1] = self._frame
self.dimen = self._frame:getSize()
-- FIXME: self.parent is not always in the widget stack (BookStatusWidget)
UIManager:setDirty(self.parent, function()
return "ui", self.dimen
end)
end
function InputText:initKeyboard()
local keyboard_layout = 2
if self.input_type == "number" then
keyboard_layout = 4
end
self.key_events = nil
self.keyboard = Keyboard:new{
keyboard_layout = keyboard_layout,
inputbox = self,
width = Screen:getWidth(),
}
end
function InputText:unfocus()
self.focused = false
self.text_widget:unfocus()
self._frame_textwidget.color = Blitbuffer.COLOR_GREY
end
function InputText:focus()
self.focused = true
self.text_widget:focus()
self._frame_textwidget.color = Blitbuffer.COLOR_BLACK
end
function InputText:onShowKeyboard()
UIManager:show(self.keyboard)
return true
end
function InputText:onCloseKeyboard()
UIManager:close(self.keyboard)
end
function InputText:getKeyboardDimen()
return self.keyboard.dimen
end
function InputText:addChars(char)
if self.enter_callback and char == '\n' then
UIManager:scheduleIn(0.3, function() self.enter_callback() end)
return
end
table.insert(self.charlist, self.charpos, char)
self.charpos = self.charpos + #util.splitToChars(char)
self:initTextBox(table.concat(self.charlist), true)
end
function InputText:delChar()
if self.charpos == 1 then return end
self.charpos = self.charpos - 1
table.remove(self.charlist, self.charpos)
self:initTextBox(table.concat(self.charlist))
end
function InputText:leftChar()
if self.charpos == 1 then return end
self.charpos = self.charpos -1
self:initTextBox(table.concat(self.charlist))
end
function InputText:rightChar()
if self.charpos > #table.concat(self.charlist) then return end
self.charpos = self.charpos +1
self:initTextBox(table.concat(self.charlist))
end
function InputText:upLine()
if self.text_widget.moveCursorUp then
self.text_widget:moveCursorUp()
end
end
function InputText:downLine()
if self.text_widget.moveCursorDown then
self.text_widget:moveCursorDown()
end
end
function InputText:clear()
self.charpos = nil
self:initTextBox("")
UIManager:setDirty(self.parent, function()
return "ui", self[1][1].dimen
end)
end
function InputText:getText()
return self.text
end
function InputText:setText(text, is_password_type)
self.charpos = nil
self:initTextBox(text, nil, is_password_type)
UIManager:setDirty(self.parent, function()
return "partial", self[1].dimen
end)
end
return InputText