mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Keyboard: add Chinese stroke-based layout (#9572)
Basically it uses 5 keys for 5 basic stroke types and inputs characters by their stroke order. See https://en.wikipedia.org/wiki/Stroke_count_method
This commit is contained in:
349
frontend/ui/data/keyboardlayouts/zh_ime.lua
Normal file
349
frontend/ui/data/keyboardlayouts/zh_ime.lua
Normal file
@@ -0,0 +1,349 @@
|
||||
-----------------------------------------
|
||||
-- General Chinese input method engine --
|
||||
-----------------------------------------
|
||||
local logger = require("logger")
|
||||
local util = require("util")
|
||||
|
||||
local function binarysearch( tbl, value, fcompval, reversed )
|
||||
if not fcompval then return end
|
||||
local iStart,iEnd = 1,#tbl
|
||||
local iMid
|
||||
while iStart <= iEnd do
|
||||
iMid = math.floor( (iStart+iEnd)/2 )
|
||||
local value2 = fcompval( tbl[iMid] )
|
||||
if value == value2 then
|
||||
if iMid == 0 or fcompval( tbl[iMid-1] ) ~= value then
|
||||
return iMid
|
||||
end
|
||||
iEnd = iMid - 1
|
||||
while iStart <= iEnd do
|
||||
iMid = math.floor( (iStart+iEnd)/2 )
|
||||
value2 = fcompval( tbl[iMid] )
|
||||
if value2 == value then
|
||||
if fcompval( tbl[iMid-1] ) ~= value then
|
||||
return iMid
|
||||
else
|
||||
iEnd = iMid - 1
|
||||
end
|
||||
else
|
||||
if fcompval( tbl[iMid+1] ) == value then
|
||||
return iMid + 1
|
||||
else
|
||||
iStart = iMid + 2
|
||||
end
|
||||
end
|
||||
end
|
||||
return iMid
|
||||
elseif ( reversed and value2 < value ) or ( not reversed and value2 > value ) then
|
||||
iEnd = iMid - 1
|
||||
else
|
||||
iStart = iMid + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function stringReplaceAt(str, pos, r)
|
||||
return str:sub(1, pos-1) .. r .. str:sub(pos+1)
|
||||
end
|
||||
|
||||
local _stack
|
||||
local IME = {
|
||||
code_map = {},
|
||||
key_map = nil, -- input key to code map
|
||||
keys_string = "abcdefghijklmnopqrstuvwxyz",
|
||||
iter_map = nil, -- next code when using wildcard
|
||||
iter_map_last_key = nil,
|
||||
show_candi_callback = function() end,
|
||||
switch_char = "下一字", -- default
|
||||
separator = "分隔", -- default
|
||||
use_space_as_separator = true,
|
||||
local_del = "", -- default
|
||||
W = nil -- default no wildcard
|
||||
}
|
||||
|
||||
function IME:new(new_o)
|
||||
local o = new_o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o:init()
|
||||
return o
|
||||
end
|
||||
|
||||
function IME:init()
|
||||
self:clear_stack()
|
||||
|
||||
self.sorted_codes = {}
|
||||
for k,_ in pairs(self.code_map) do
|
||||
table.insert(self.sorted_codes, k)
|
||||
end
|
||||
table.sort(self.sorted_codes)
|
||||
|
||||
if not self.key_map and self.keys_string then
|
||||
self.key_map = {}
|
||||
for i=0, #self.keys_string do
|
||||
self.key_map[self.keys_string:sub(i, i)] = self.keys_string:sub(i, i)
|
||||
end
|
||||
end
|
||||
|
||||
if not self.iter_map and self.W then
|
||||
self.iter_map = {}
|
||||
local keys = util.splitToChars(self.keys_string)
|
||||
for i=1, #keys-1 do
|
||||
self.iter_map[keys[i]] = keys[i+1]
|
||||
end
|
||||
if #keys > 1 then
|
||||
self.iter_map[keys[#keys]] = keys[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function IME:clear_stack()
|
||||
_stack = { {code="", char="", index=1, candi={} } }
|
||||
self.last_key = ""
|
||||
self.last_index = 0
|
||||
self.hint_char_count = 0
|
||||
end
|
||||
|
||||
function IME:reset_status()
|
||||
self.last_key = ""
|
||||
self.last_index = 0
|
||||
end
|
||||
|
||||
function IME:searchStartWith(code)
|
||||
local result = binarysearch(self.sorted_codes, code, function(v) return string.sub(v or "", 1, #code) end)
|
||||
if result then
|
||||
local candi = self.code_map[self.sorted_codes[result]]
|
||||
if candi then
|
||||
logger.dbg("zh_kbd: got search result starting with", code, ":", candi)
|
||||
end
|
||||
if type(candi) == "string" then
|
||||
return { candi }
|
||||
end
|
||||
return candi
|
||||
end
|
||||
end
|
||||
|
||||
function IME:getCandiFromMap(code)
|
||||
local candi = self.code_map[code]
|
||||
if candi then
|
||||
logger.dbg("zh_kbd: got candi from map with", code, ":", candi)
|
||||
end
|
||||
if type(candi) == "string" then
|
||||
return { candi }
|
||||
end
|
||||
return candi
|
||||
end
|
||||
|
||||
function IME:getCandi(code)
|
||||
return self:getCandiFromMap(code) or self:searchStartWith(code) or {}
|
||||
end
|
||||
|
||||
function IME:getCandiWithWildcard(code, from_reset)
|
||||
logger.dbg("zh_kdb: getCandiWithWildcard:", code, "lastKey:", self.last_key)
|
||||
for i=#code, 1, -1 do
|
||||
if code:sub(i, i) == self.W then
|
||||
if self.last_key:sub(i, i) == self.iter_map_last_key then
|
||||
local next = self.iter_map[self.iter_map_last_key]
|
||||
self.last_key = stringReplaceAt(self.last_key, i, next)
|
||||
else
|
||||
self.last_key = stringReplaceAt(self.last_key, i, self.iter_map[self.last_key:sub(i, i)])
|
||||
self.last_candi = self:getCandi(self.last_key)
|
||||
if #self.last_candi > 0 then
|
||||
logger.dbg("zh_kbd: got candi with wildchard for key", self.last_key, ":", self.last_candi)
|
||||
return self.last_candi
|
||||
end
|
||||
return self:getCandiWithWildcard(code, from_reset)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- all wildcard reset
|
||||
self.last_candi = self:getCandi(self.last_key)
|
||||
if #self.last_candi > 0 then
|
||||
logger.dbg("zh_kbd: got candi with wildchard for key", self.last_key, ":", self.last_candi)
|
||||
return self.last_candi
|
||||
elseif not from_reset then
|
||||
return self:getCandiWithWildcard(code, true)
|
||||
end
|
||||
end
|
||||
|
||||
function IME:getCandidates(code)
|
||||
logger.dbg("zh_kbd: getCandidates", code)
|
||||
if self.W then
|
||||
local wildcard_count = select(2, string.gsub(code, self.W, ""))
|
||||
if wildcard_count > 5 then
|
||||
-- we limit the wildcard count to 5 due to performance conserns
|
||||
return
|
||||
elseif wildcard_count ~= 0 then
|
||||
if #code == #self.last_key then -- only index change, no new stroke
|
||||
local last_candi = _stack[#_stack].candi
|
||||
return self:getCandiWithWildcard(code), last_candi
|
||||
else
|
||||
self:reset_status()
|
||||
self.last_key = code:gsub(self.W, self.iter_map_last_key)
|
||||
return self:getCandiWithWildcard(code)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- no wildcard
|
||||
return self:getCandiFromMap(code) or self:searchStartWith(code)
|
||||
end
|
||||
|
||||
|
||||
--- inputbox operation
|
||||
function IME:delHintChars(inputbox)
|
||||
logger.dbg("zh_kbd: delete hint chars of count", self.hint_char_count)
|
||||
for i=1, self.hint_char_count do
|
||||
inputbox.delChar:raw_method_call()
|
||||
end
|
||||
end
|
||||
|
||||
function IME:getHintChars()
|
||||
self.hint_char_count = 0
|
||||
local hint_chars = ""
|
||||
for i=1, #_stack do
|
||||
hint_chars = hint_chars .. _stack[i].char
|
||||
if _stack[i].char ~= "" then
|
||||
self.hint_char_count = self.hint_char_count + #util.splitToChars(_stack[i].char)
|
||||
end
|
||||
end
|
||||
local imex = _stack[#_stack]
|
||||
local has_wildcard = self.W and imex.code:find(self.W)
|
||||
if self:show_candi_callback() and -- shows candidates
|
||||
#imex.candi ~= 0 and -- has candidates
|
||||
( #imex.code > 1 or imex.index > 1 ) and -- more than one key
|
||||
( #imex.candi > 1 or has_wildcard and imex.candi[1] ~= (imex.last_candi or {})[1] ) then -- one candidate but use wildcard, or more candidates
|
||||
hint_chars = hint_chars .. "["
|
||||
if #imex.candi > 1 then
|
||||
local remainder
|
||||
if not has_wildcard then
|
||||
remainder = (imex.index+1) % #imex.candi
|
||||
else
|
||||
remainder = (imex.index-self.last_index+1) % #imex.candi
|
||||
end
|
||||
local pos = remainder == 0 and #imex.candi or remainder
|
||||
if not (has_wildcard and pos == 1) then
|
||||
for i=1, math.min(#imex.candi-1, 5) do
|
||||
hint_chars = hint_chars .. imex.candi[pos]
|
||||
self.hint_char_count = self.hint_char_count + #util.splitToChars(imex.candi[pos])
|
||||
pos = pos == #imex.candi and 1 or pos+1
|
||||
if has_wildcard and pos == 1 then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if #imex.candi > 6 or has_wildcard then
|
||||
hint_chars = hint_chars .. "…"
|
||||
self.hint_char_count = self.hint_char_count + 1
|
||||
end
|
||||
hint_chars = hint_chars .. "]"
|
||||
self.hint_char_count = self.hint_char_count + 2
|
||||
end
|
||||
logger.dbg("zh_kbd: got hint chars:", hint_chars, "with count", self.hint_char_count)
|
||||
return hint_chars
|
||||
end
|
||||
|
||||
function IME:refreshHintChars(inpuxbox)
|
||||
self:delHintChars(inpuxbox)
|
||||
inpuxbox.addChars:raw_method_call(self:getHintChars())
|
||||
end
|
||||
|
||||
function IME:wrappedSeparate(inputbox)
|
||||
local imex = _stack[#_stack]
|
||||
if self:show_candi_callback() and ( #imex.candi > 1 or self.W and imex.code:find(self.W) ) then
|
||||
imex.candi = {}
|
||||
self:refreshHintChars(inputbox)
|
||||
end
|
||||
self:clear_stack()
|
||||
end
|
||||
|
||||
|
||||
|
||||
function IME:wrappedDelChar(inputbox)
|
||||
local imex = _stack[#_stack]
|
||||
-- stepped deletion
|
||||
if #imex.code > 1 then
|
||||
-- last char has over one input strokes
|
||||
imex.code = string.sub(imex.code, 1, -2)
|
||||
imex.index = 1
|
||||
imex.candi, imex.last_candi = self:getCandidates(imex.code)
|
||||
imex.char = imex.candi[1]
|
||||
self:refreshHintChars(inputbox)
|
||||
elseif #_stack > 1 then
|
||||
-- over one chars, last char has only one stroke
|
||||
_stack[#_stack] = nil
|
||||
self:refreshHintChars(inputbox)
|
||||
elseif #imex.code == 1 then
|
||||
-- one char with one stroke
|
||||
self:delHintChars(inputbox)
|
||||
self:clear_stack()
|
||||
else
|
||||
inputbox.delChar:raw_method_call()
|
||||
end
|
||||
end
|
||||
|
||||
function IME:wrappedAddChars(inputbox, char)
|
||||
local imex = _stack[#_stack]
|
||||
if char == self.switch_char then
|
||||
imex.index = imex.index + 1
|
||||
if self.W and imex.code:find(self.W) then
|
||||
if #imex.candi == 0 then
|
||||
return
|
||||
elseif imex.index - self.last_index > #imex.candi then
|
||||
self.last_index = self.last_index + #imex.candi
|
||||
imex.candi,imex.last_candi = self:getCandidates(imex.code)
|
||||
imex.char = imex.candi[1]
|
||||
else
|
||||
imex.char = imex.candi[imex.index - self.last_index]
|
||||
end
|
||||
elseif #imex.candi > 1 then
|
||||
local remainder = imex.index % #imex.candi
|
||||
imex.char = imex.candi[remainder==0 and #imex.candi or remainder]
|
||||
else
|
||||
return
|
||||
end
|
||||
self:refreshHintChars(inputbox)
|
||||
elseif char == self.separator or
|
||||
self.use_space_as_separator and char == " " and _stack[1].code ~= "" then
|
||||
imex.candi = {}
|
||||
self:refreshHintChars(inputbox)
|
||||
self:clear_stack()
|
||||
return
|
||||
elseif char == self.local_del then
|
||||
if #imex.code > 0 then
|
||||
imex.candi = {}
|
||||
imex.char = ""
|
||||
self:refreshHintChars(inputbox)
|
||||
self:clear_stack()
|
||||
else
|
||||
inputbox.delChar:raw_method_call()
|
||||
end
|
||||
else
|
||||
local key = self.key_map[char]
|
||||
if key then
|
||||
imex.index = 1
|
||||
self:reset_status()
|
||||
local new_candi
|
||||
new_candi,imex.last_candi = self:getCandidates(imex.code..key)
|
||||
if new_candi and #new_candi > 0 then
|
||||
imex.code = imex.code .. key
|
||||
imex.char = new_candi[1]
|
||||
imex.candi = new_candi
|
||||
self:refreshHintChars(inputbox)
|
||||
else
|
||||
new_candi,imex.last_candi = self:getCandidates(key) or {},nil -- single stroke
|
||||
table.insert(_stack, {code=key, index=1, char=new_candi[1], candi=new_candi})
|
||||
self:refreshHintChars(inputbox)
|
||||
end
|
||||
else
|
||||
if #imex.candi > 1 then
|
||||
imex.candi = {}
|
||||
self:refreshHintChars(inputbox)
|
||||
end
|
||||
self:clear_stack()
|
||||
inputbox.addChars:raw_method_call(char)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return IME
|
||||
202
frontend/ui/data/keyboardlayouts/zh_keyboard.lua
Normal file
202
frontend/ui/data/keyboardlayouts/zh_keyboard.lua
Normal file
@@ -0,0 +1,202 @@
|
||||
--[[--
|
||||
|
||||
Chinese stroke-based input method for Lua/KOReader.
|
||||
|
||||
Uses five basic strokes plus a wildcard stroke to input Chinese characters.
|
||||
Supports both simplified and traditional.
|
||||
Characters hardcoded on keys are uniform, no translation needed.
|
||||
In-place candidates can be turned off in keyboard settings.
|
||||
A Separation key 分隔 is used to finish inputting a character.
|
||||
A Switch key 换字 is used to iterate candidates.
|
||||
Stroke-wise deletion (input not finished) mapped to the default Del key.
|
||||
Character-wise deletion mapped to north of Separation key.
|
||||
|
||||
rf. https://en.wikipedia.org/wiki/Stroke_count_method
|
||||
|
||||
--]]
|
||||
|
||||
local IME = require("frontend/ui/data/keyboardlayouts/zh_ime")
|
||||
local util = require("util")
|
||||
local JA = require("ui/data/keyboardlayouts/ja_keyboard_keys")
|
||||
local _ = require("gettext")
|
||||
|
||||
local SHOW_CANDI_KEY = "keyboard_chinese_stroke_show_candidates"
|
||||
local s_3 = { alt_label = "%°#", "3", west = "%", north = "°", east = "#" }
|
||||
local s_8 = { alt_label = "&-/", "8", west = "&", north = "-", east = "/" }
|
||||
local comma_popup = { ",",
|
||||
north = ";",
|
||||
alt_label = ";",
|
||||
northeast = "(",
|
||||
northwest = "“",
|
||||
east = "《",
|
||||
west = "?",
|
||||
south = ",",
|
||||
southeast = "【",
|
||||
southwest = "「",
|
||||
"{",
|
||||
"[",
|
||||
";",
|
||||
}
|
||||
local period_popup = { "。",
|
||||
north = ":",
|
||||
alt_label = ":",
|
||||
northeast = ")",
|
||||
northwest = "”",
|
||||
east = "…",
|
||||
west = "!",
|
||||
south = ".",
|
||||
southeast = "】",
|
||||
southwest = "」",
|
||||
"}",
|
||||
"]",
|
||||
":",
|
||||
}
|
||||
|
||||
local H = "H" -- stroke_h 横
|
||||
local I = "I" -- stroke_s 竖
|
||||
local J = "J" -- stroke_p 撇
|
||||
local K = "K" -- stroke_n 捺
|
||||
local L = "L" -- stroke_z 折
|
||||
local W = "`" -- wildcard, * is not used because it can be input from symbols
|
||||
|
||||
local genMenuItems = function(self)
|
||||
return {
|
||||
{
|
||||
text = _("Show character candidates"),
|
||||
checked_func = function()
|
||||
return G_reader_settings:nilOrTrue(SHOW_CANDI_KEY)
|
||||
end,
|
||||
callback = function()
|
||||
G_reader_settings:flipNilOrTrue(SHOW_CANDI_KEY)
|
||||
end,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local code_map = require("frontend/ui/data/keyboardlayouts/zh_stroke_data")
|
||||
local ime = IME:new{
|
||||
code_map = code_map,
|
||||
key_map = {
|
||||
["㇐"] = H,
|
||||
["㇑"] = I,
|
||||
["㇒"] = J,
|
||||
["㇏"] = K,
|
||||
["㇜"] = L,
|
||||
[W] = W, -- wildcard
|
||||
},
|
||||
iter_map = {
|
||||
H = I,
|
||||
I = J,
|
||||
J = K,
|
||||
K = L,
|
||||
L = H,
|
||||
},
|
||||
iter_map_last_key = L,
|
||||
show_candi_callback = function()
|
||||
return G_reader_settings:nilOrTrue(SHOW_CANDI_KEY)
|
||||
end,
|
||||
W = W -- has wildcard function
|
||||
}
|
||||
|
||||
local wrappedAddChars = function(inputbox, char)
|
||||
ime:wrappedAddChars(inputbox, char)
|
||||
end
|
||||
|
||||
local function wrappedSeparate(inputbox)
|
||||
ime:wrappedSeparate(inputbox)
|
||||
end
|
||||
|
||||
local function wrappedDelChar(inputbox)
|
||||
ime:wrappedDelChar(inputbox)
|
||||
end
|
||||
|
||||
local function clear_stack()
|
||||
ime:clear_stack()
|
||||
end
|
||||
|
||||
local wrapInputBox = function(inputbox)
|
||||
if inputbox._zh_stroke_wrapped == nil then
|
||||
inputbox._zh_stroke_wrapped = true
|
||||
local wrappers = {}
|
||||
|
||||
-- Wrap all of the navigation and non-single-character-input keys with
|
||||
-- a callback to clear the tap window, but pass through to the
|
||||
-- original function.
|
||||
|
||||
-- -- Delete text.
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "delChar", wrappedDelChar, nil))
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "delToStartOfLine", nil, clear_stack))
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "clear", nil, clear_stack))
|
||||
-- -- Navigation.
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "leftChar", nil, wrappedSeparate))
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "rightChar", nil, wrappedSeparate))
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "upLine", nil, wrappedSeparate))
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "downLine", nil, wrappedSeparate))
|
||||
-- -- Move to other input box.
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "unfocus", nil, wrappedSeparate))
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "onCloseKeyboard", nil, wrappedSeparate))
|
||||
-- -- Gestures to move cursor.
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "onTapTextBox", nil, wrappedSeparate))
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "onHoldTextBox", nil, wrappedSeparate))
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "onSwipeTextBox", nil, wrappedSeparate))
|
||||
-- -- Others
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "onSwitchingKeyboardLayout", nil, wrappedSeparate))
|
||||
|
||||
-- addChars is the only method we need a more complicated wrapper for.
|
||||
table.insert(wrappers, util.wrapMethod(inputbox, "addChars", wrappedAddChars, nil))
|
||||
|
||||
return function()
|
||||
if inputbox._zh_stroke_wrapped then
|
||||
for _, wrapper in ipairs(wrappers) do
|
||||
wrapper:revert()
|
||||
end
|
||||
inputbox._zh_stroke_wrapped = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
min_layer = 1,
|
||||
max_layer = 2,
|
||||
shiftmode_keys = {["123"] = false},
|
||||
symbolmode_keys = {["Sym"] = false},
|
||||
utf8mode_keys = {["🌐"] = true},
|
||||
umlautmode_keys = {["Äéß"] = false}, -- Disabled 'umlaut' keys
|
||||
keys = {
|
||||
-- first row
|
||||
{
|
||||
{ label = "123" },
|
||||
{ JA.s_1, { label = "一", "㇐", north="——"} },
|
||||
{ JA.s_2, { label = "丨", "㇑"} },
|
||||
{ s_3, { label = "丿", "㇒"} },
|
||||
{ label = "", bold = false } -- backspace
|
||||
},
|
||||
-- second row
|
||||
{
|
||||
{ label = "←" },
|
||||
{ JA.s_4, { label = "丶", "㇏", north="、" } },
|
||||
{ JA.s_5, { label = "𠃋", "㇜" } },
|
||||
{ JA.s_6, { ime.separator, north=ime.local_del, alt_label=ime.local_del } },
|
||||
{ label = "→" },
|
||||
},
|
||||
-- third row
|
||||
{
|
||||
{ label = "↑" },
|
||||
{ JA.s_7, ime.switch_char },
|
||||
{ s_8, comma_popup },
|
||||
{ JA.s_9, period_popup },
|
||||
{ label = "↓" },
|
||||
},
|
||||
-- fourth row
|
||||
{
|
||||
{ label = "🌐" },
|
||||
{ label = "空格", " ", " ", width = 2.0 },
|
||||
{ JA.s_0, { label = "*", W } },
|
||||
{ label = "⮠", "\n", "\n", bold = true }, -- return
|
||||
},
|
||||
},
|
||||
|
||||
wrapInputBox = wrapInputBox,
|
||||
genMenuItems = genMenuItems,
|
||||
}
|
||||
3
frontend/ui/data/keyboardlayouts/zh_stroke_data.lua
Normal file
3
frontend/ui/data/keyboardlayouts/zh_stroke_data.lua
Normal file
File diff suppressed because one or more lines are too long
@@ -139,7 +139,9 @@ if Device:isTouchDevice() or Device:hasDPad() then
|
||||
self.is_keyboard_hidden = false
|
||||
end
|
||||
end
|
||||
if #self.charlist > 0 then -- Avoid cursor moving within a hint.
|
||||
-- zh keyboard with candidates shown here has _frame_textwidget.dimen = nil.
|
||||
-- Check to avoid crash.
|
||||
if #self.charlist > 0 and self._frame_textwidget.dimen then -- Avoid cursor moving within a hint.
|
||||
local textwidget_offset = self.margin + self.bordersize + self.padding
|
||||
local x = ges.pos.x - self._frame_textwidget.dimen.x - textwidget_offset
|
||||
local y = ges.pos.y - self._frame_textwidget.dimen.y - textwidget_offset
|
||||
|
||||
@@ -64,6 +64,7 @@ function VirtualKey:init()
|
||||
elseif self.keyboard.utf8mode_keys[self.label] ~= nil then
|
||||
self.key_chars = self:genKeyboardLayoutKeyChars()
|
||||
self.callback = function ()
|
||||
self.keyboard:onSwitchingKeyboardLayout()
|
||||
local current = G_reader_settings:readSetting("keyboard_layout")
|
||||
local default = G_reader_settings:readSetting("keyboard_layout_default")
|
||||
local keyboard_layouts = G_reader_settings:readSetting("keyboard_layouts", {})
|
||||
@@ -86,6 +87,7 @@ function VirtualKey:init()
|
||||
self.keyboard:setKeyboardLayout(next_layout)
|
||||
end
|
||||
self.hold_callback = function()
|
||||
self.keyboard:onSwitchingKeyboardLayout()
|
||||
if util.tableSize(self.key_chars) > 5 then -- 2 or more layouts enabled
|
||||
self.popup = VirtualKeyPopup:new{
|
||||
parent_key = self,
|
||||
@@ -100,6 +102,7 @@ function VirtualKey:init()
|
||||
end
|
||||
self.hold_cb_is_popup = true
|
||||
self.swipe_callback = function(ges)
|
||||
self.keyboard:onSwitchingKeyboardLayout()
|
||||
local key_function = self.key_chars[ges.direction.."_func"]
|
||||
if key_function then
|
||||
key_function()
|
||||
@@ -760,10 +763,12 @@ local VirtualKeyboard = FocusManager:new{
|
||||
ko_KR = "ko_KR_keyboard",
|
||||
ru = "ru_keyboard",
|
||||
tr = "tr_keyboard",
|
||||
zh = "zh_keyboard",
|
||||
},
|
||||
|
||||
lang_has_submenu = {
|
||||
ja = true,
|
||||
zh = true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1035,6 +1040,11 @@ function VirtualKeyboard:goToStartOfLine()
|
||||
self.inputbox:goToStartOfLine()
|
||||
end
|
||||
|
||||
-- Some keyboard with intermediate state (ie. zh) may need to be notified
|
||||
function VirtualKeyboard:onSwitchingKeyboardLayout()
|
||||
if self.inputbox.onSwitchingKeyboardLayout then self.inputbox:onSwitchingKeyboardLayout() end
|
||||
end
|
||||
|
||||
function VirtualKeyboard:goToEndOfLine()
|
||||
self.inputbox:goToEndOfLine()
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user