Vietnamese keyboard: TELEX method backed by generic_ime

reviewable/pr9631/r1
weijiuqiao 2 years ago committed by poire-z
parent 6a5a13dfc8
commit ae8156ff9e

@ -3,6 +3,7 @@
---------------------------------
local logger = require("logger")
local util = require("util")
local Utf8Proc = require("ffi/utf8proc")
local function binarysearch( tbl, value, fcompval, reversed )
if not fcompval then return end
@ -59,6 +60,8 @@ local IME = {
partial_separators = { " " }, -- when in state act as separator, otherwise input itself
auto_separate_callback = function() return false end,
local_del = "", -- default
has_case = false,
exact_match = false,
W = nil -- default no wildcard
}
@ -137,6 +140,7 @@ function IME:uniqueMap(code)
end
function IME:searchStartWith(code)
if self.exact_match then return end
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]]
@ -290,6 +294,30 @@ function IME:separate(inputbox)
self:clear_stack()
end
function IME:tweak_case(new_candi, old_imex, new_stroke_upper)
if self.has_case then
local old_chars = util.splitToChars(old_imex.char)
logger.dbg("zh_ime: tweak_case old chars", old_chars, "new_candi", new_candi)
for i=1, #new_candi do
local new_chars = util.splitToChars(new_candi[i])
for j=1, math.max(#new_chars, #old_chars) do
local old_char = old_chars[j]
local new_char = new_chars[j]
if new_char ~= old_char then
if not old_char and new_stroke_upper then
-- tweak new_char
new_chars[j] = Utf8Proc.uppercase_dumb(new_char)
elseif old_char and new_char and old_char == Utf8Proc.uppercase_dumb(old_char) then
-- tweak new_char when corresponding old char is uppercase
new_chars[j] = Utf8Proc.uppercase_dumb(new_char)
end
end
end
new_candi[i] = table.concat(new_chars)
end
end
end
function IME:wrappedDelChar(inputbox)
local imex = _stack[#_stack]
-- stepped deletion
@ -297,7 +325,9 @@ function IME:wrappedDelChar(inputbox)
-- 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)
local new_candi, last_candi = self:getCandidates(imex.code)
self:tweak_case(new_candi, imex)
imex.candi, imex.last_candi = new_candi, last_candi
imex.char = imex.candi[1]
self:refreshHintChars(inputbox)
elseif #_stack > 1 then
@ -313,7 +343,7 @@ function IME:wrappedDelChar(inputbox)
end
end
function IME:wrappedAddChars(inputbox, char)
function IME:wrappedAddChars(inputbox, char, orig_char)
local imex = _stack[#_stack]
if char == self.switch_char then
imex.index = imex.index + 1
@ -361,6 +391,9 @@ function IME:wrappedAddChars(inputbox, char)
new_candi,imex.last_candi = self:getCandidates(imex.code..key)
if new_candi and #new_candi > 0 then
imex.code = imex.code .. key
self:tweak_case(new_candi, imex, orig_char and orig_char ~= char)
imex.char = new_candi[1]
imex.candi = new_candi
self:refreshHintChars(inputbox)
@ -371,7 +404,10 @@ function IME:wrappedAddChars(inputbox, char)
if self.auto_separate_callback() then -- flush current stack
self:separate(inputbox)
end
new_candi,imex.last_candi = self:getCandidates(key) or {},nil -- single stroke
new_candi,imex.last_candi = self:getCandidates(key) or {orig_char or char},nil -- single stroke
self:tweak_case(new_candi, {}, orig_char and orig_char ~= char)
if self.auto_separate_callback() then
_stack[1] = { code=key, index=1, char=new_candi[1] or "", candi=new_candi }
else
@ -381,7 +417,7 @@ function IME:wrappedAddChars(inputbox, char)
end
else
self:separate(inputbox)
inputbox.addChars:raw_method_call(char)
inputbox.addChars:raw_method_call(orig_char or char)
end
end
end

@ -0,0 +1,76 @@
-- Start with the english keyboard layout (deep copy, to not alter it)
local vi_keyboard = require("util").tableDeepCopy(require("ui/data/keyboardlayouts/en_keyboard"))
local IME = require("frontend/ui/data/keyboardlayouts/generic_ime")
local util = require("util")
-- see https://www.hieuthi.com/blog/2017/03/21/all-vietnamese-syllables.html
local code_map = require("frontend/ui/data/keyboardlayouts/vi_telex_data")
local ime = IME:new{
code_map = code_map,
partial_separators = {},
has_case = true,
exact_match = true,
}
local wrappedAddChars = function(inputbox, char)
local lowercase = char:lower()
ime:wrappedAddChars(inputbox, lowercase, char)
end
local function separate(inputbox)
ime:separate(inputbox)
end
local function wrappedDelChar(inputbox)
ime:wrappedDelChar(inputbox)
end
local function clear_stack()
ime:clear_stack()
end
local wrapInputBox = function(inputbox)
if inputbox._vi_wrapped == nil then
inputbox._vi_wrapped = true
local wrappers = {}
-- Wrap all of the navigation and non-single-character-input keys with
-- a callback to finish (separate) the input status, 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, separate))
table.insert(wrappers, util.wrapMethod(inputbox, "rightChar", nil, separate))
table.insert(wrappers, util.wrapMethod(inputbox, "upLine", nil, separate))
table.insert(wrappers, util.wrapMethod(inputbox, "downLine", nil, separate))
-- -- Move to other input box.
table.insert(wrappers, util.wrapMethod(inputbox, "unfocus", nil, separate))
table.insert(wrappers, util.wrapMethod(inputbox, "onCloseKeyboard", nil, separate))
-- -- Gestures to move cursor.
table.insert(wrappers, util.wrapMethod(inputbox, "onTapTextBox", nil, separate))
table.insert(wrappers, util.wrapMethod(inputbox, "onHoldTextBox", nil, separate))
table.insert(wrappers, util.wrapMethod(inputbox, "onSwipeTextBox", nil, separate))
-- 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._vi_wrapped then
for _, wrapper in ipairs(wrappers) do
wrapper:revert()
end
inputbox._vi_wrapped = nil
end
end
end
end
vi_keyboard.wrapInputBox = wrapInputBox
vi_keyboard.keys[5][4].label = "dấu cách"
return vi_keyboard

File diff suppressed because one or more lines are too long

@ -769,6 +769,7 @@ local VirtualKeyboard = FocusManager:extend{
ru = "ru_keyboard",
th = "th_keyboard",
tr = "tr_keyboard",
vi = "vi_keyboard",
zh = "zh_keyboard",
},

Loading…
Cancel
Save