Files
koreader/frontend/ui/widget/virtualkeyboard.lua
alexandrahurst 71d38ac647 Refactor modifier keys and add basic Japanese keyboard layout (#2930)
* Add a basic Japanese keyboard layout

Allow for entring Japanese text into text boxes.  Only supports kana
and latin letters due to limitations of current VirtualKeyboard
implementation (complex text input with conversion is not possible).

* Allow keyboard layouts to declare custom modifiers

Refactor modifier key setup code and existing keyboard layouts so
that each layout can declare custom modifier key labels.  This
allows for greater extensibility and for future keyboard layouts.
2017-06-03 11:14:36 +02:00

301 lines
9.5 KiB
Lua

local Blitbuffer = require("ffi/blitbuffer")
local BottomContainer = require("ui/widget/container/bottomcontainer")
local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device")
local Font = require("ui/font")
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 TextWidget = require("ui/widget/textwidget")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local logger = require("logger")
local Screen = Device.screen
local VirtualKey = InputContainer:new{
key = nil,
icon = nil,
label = nil,
keyboard = nil,
callback = nil,
width = nil,
height = math.max(Screen:getWidth(), Screen:getHeight())*0.33,
bordersize = 2,
face = Font:getFace("infont"),
}
function VirtualKey:init()
if self.keyboard.symbolmode_keys[self.label] ~= nil then
self.callback = function () self.keyboard:setLayout("Sym") end
elseif self.keyboard.shiftmode_keys[self.label] ~= nil then
self.callback = function () self.keyboard:setLayout("Shift") end
elseif self.keyboard.utf8mode_keys[self.label] ~= nil then
self.callback = function () self.keyboard:setLayout("IM") end
elseif self.keyboard.umlautmode_keys[self.label] ~= nil then
self.callback = function () self.keyboard:setLayout("Äéß") end
elseif self.label == "Backspace" then
self.callback = function () self.keyboard:delChar() end
self.hold_callback = function () self.keyboard:clear() end
else
self.callback = function () self.keyboard:addChar(self.key) end
end
local label_widget
if self.icon then
label_widget = ImageWidget:new{
file = self.icon,
}
else
label_widget = TextWidget:new{
text = self.label,
face = self.face,
}
end
self[1] = FrameContainer:new{
margin = 0,
bordersize = self.bordersize,
background = Blitbuffer.COLOR_WHITE,
radius = 5,
padding = 0,
CenterContainer:new{
dimen = Geom:new{
w = self.width - 2*self.bordersize,
h = self.height - 2*self.bordersize,
},
label_widget,
},
}
self.dimen = Geom:new{
w = self.width,
h = self.height,
}
--self.dimen = self[1]:getSize()
if Device:isTouchDevice() then
self.ges_events = {
TapSelect = {
GestureRange:new{
ges = "tap",
range = self.dimen,
},
},
HoldSelect = {
GestureRange:new{
ges = "hold",
range = self.dimen,
},
},
}
end
end
function VirtualKey:update_keyboard()
UIManager:setDirty(self.keyboard, function()
logger.dbg("update key region", self[1].dimen)
return "ui", self[1].dimen
end)
end
function VirtualKey:onTapSelect()
self[1].invert = true
self:update_keyboard()
if self.callback then
self.callback()
end
UIManager:scheduleIn(0.2, function() self:invert(false) end)
return true
end
function VirtualKey:onHoldSelect()
self[1].invert = true
self:update_keyboard()
if self.hold_callback then
self.hold_callback()
end
UIManager:scheduleIn(0.2, function() self:invert(false) end)
return true
end
function VirtualKey:invert(invert)
self[1].invert = invert
self:update_keyboard()
end
local VirtualKeyboard = InputContainer:new{
is_always_active = true,
disable_double_tap = true,
inputbox = nil,
KEYS = {}, -- table to store layouts
shiftmode_keys = {},
symbolmode_keys = {},
utf8mode_keys = {},
umlautmode_keys = {},
min_layout = 2,
max_layout = 12,
layout = 2,
shiftmode = false,
symbolmode = false,
utf8mode = false,
umlautmode = false,
width = Screen:scaleBySize(600),
height = nil,
bordersize = Screen:scaleBySize(2),
padding = Screen:scaleBySize(2),
key_padding = Screen:scaleBySize(6),
}
local lang_to_keyboard_layout = {
el = "el_keyboard",
ja = "ja_keyboard",
pl = "pl_keyboard",
pt_BR = "pt_keyboard",
}
function VirtualKeyboard:init()
local lang = G_reader_settings:readSetting("language")
local keyboard_layout = lang_to_keyboard_layout[lang] or "std"
local keyboard = require("ui/data/keyboardlayouts/" .. keyboard_layout)
self.KEYS = keyboard.keys
self.shiftmode_keys = keyboard.shiftmode_keys
self.symbolmode_keys = keyboard.symbolmode_keys
self.utf8mode_keys = keyboard.utf8mode_keys
self.umlautmode_keys = keyboard.umlautmode_keys
self.height = Screen:scaleBySize(64 * #self.KEYS)
self:initLayout(self.layout)
end
function VirtualKeyboard:_refresh()
-- TODO: Ideally, ui onShow & partial onClose
UIManager:setDirty(self, function()
return "partial", self[1][1].dimen
end)
end
function VirtualKeyboard:onShow()
self:_refresh()
return true
end
function VirtualKeyboard:onCloseWidget()
self:_refresh()
return true
end
function VirtualKeyboard:initLayout(layout)
local function VKLayout(b1, b2, b3, b4)
local function boolnum(bool)
return bool and 1 or 0
end
return 2 - boolnum(b1) + 2 * boolnum(b2) + 4 * boolnum(b3) + 8 * boolnum(b4)
end
if layout then
-- to be sure layout is selected properly
layout = math.max(layout, self.min_layout)
layout = math.min(layout, self.max_layout)
self.layout = layout
-- fill the layout modes
self.shiftmode = (layout == 1 or layout == 3 or layout == 5 or layout == 7 or layout == 9 or layout == 11)
self.symbolmode = (layout == 3 or layout == 4 or layout == 7 or layout == 8 or layout == 11 or layout == 12)
self.utf8mode = (layout == 5 or layout == 6 or layout == 7 or layout == 8)
self.umlautmode = (layout == 9 or layout == 10 or layout == 11 or layout == 12)
else -- or, without input parameter, restore layout from current layout modes
self.layout = VKLayout(self.shiftmode, self.symbolmode, self.utf8mode, self.umlautmode)
end
self:addKeys()
end
function VirtualKeyboard:addKeys()
local base_key_width = math.floor((self.width - (#self.KEYS[1] + 1)*self.key_padding - 2*self.padding)/#self.KEYS[1])
local base_key_height = math.floor((self.height - (#self.KEYS + 1)*self.key_padding - 2*self.padding)/#self.KEYS)
local h_key_padding = HorizontalSpan:new{width = self.key_padding}
local v_key_padding = VerticalSpan:new{width = self.key_padding}
local vertical_group = VerticalGroup:new{}
for i = 1, #self.KEYS do
local horizontal_group = HorizontalGroup:new{}
for j = 1, #self.KEYS[i] do
local width_factor = self.KEYS[i][j].width or 1.0
local key_width = math.floor((base_key_width + self.key_padding) * width_factor)
- self.key_padding
local key_height = base_key_height
local label = self.KEYS[i][j].label or self.KEYS[i][j][self.layout]
local key = VirtualKey:new{
key = self.KEYS[i][j][self.layout],
icon = self.KEYS[i][j].icon,
label = label,
keyboard = self,
width = key_width,
height = key_height,
}
table.insert(horizontal_group, key)
if j ~= #self.KEYS[i] then
table.insert(horizontal_group, h_key_padding)
end
end
table.insert(vertical_group, horizontal_group)
if i ~= #self.KEYS then
table.insert(vertical_group, v_key_padding)
end
end
local keyboard_frame = FrameContainer:new{
margin = 0,
bordersize = self.bordersize,
background = Blitbuffer.COLOR_WHITE,
radius = 0,
padding = self.padding,
CenterContainer:new{
dimen = Geom:new{
w = self.width - 2*self.bordersize -2*self.padding,
h = self.height - 2*self.bordersize -2*self.padding,
},
vertical_group,
}
}
self[1] = BottomContainer:new{
dimen = Screen:getSize(),
keyboard_frame,
}
self.dimen = keyboard_frame:getSize()
end
function VirtualKeyboard:setLayout(key)
if key == "Shift" then
self.shiftmode = not self.shiftmode
elseif key == "Sym" or key == "ABC" then
self.symbolmode = not self.symbolmode
elseif key == "Äéß" then
self.umlautmode = not self.umlautmode
if self.umlautmode then self.utf8mode = false end
elseif key == "IM" then
self.utf8mode = not self.utf8mode
if self.utf8mode then self.umlautmode = false end
end
self:initLayout()
self:_refresh()
end
function VirtualKeyboard:addChar(key)
logger.dbg("add char", key)
self.inputbox:addChar(key)
end
function VirtualKeyboard:delChar()
logger.dbg("delete char")
self.inputbox:delChar()
end
function VirtualKeyboard:clear()
logger.dbg("clear input")
self.inputbox:clear()
end
return VirtualKeyboard