<NME> zh_keyboard.lua
<BEF> --[[--

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 = 4,
    symbolmode_keys = {["123"] = true},
    utf8mode_keys = {["🌐"] = true},
    keys = {
        -- first row
        {
            { label = "123" },
            { "", { label = "一", "㇐", north="——"}, "", JA.s_1 },
            { "", { label = "丨", "㇑"}, "", JA.s_2 },
            { "", { label = "丿", "㇒"}, "", s_3 },
            { label = "", bold = false } -- backspace
        },
        -- second row
        {
            { label = "←" },
            { "", { label = "丶", "㇏", north="、" }, "", JA.s_4 },
            { "", { label = "𠃋", "㇜" }, "", JA.s_5 },
            { "", { ime.separator, north=ime.local_del, alt_label=ime.local_del }, "", JA.s_6 },
            { label = "→" },
        },
        -- third row
        {
            { label = "↑" },
            { "", ime.switch_char, "", JA.s_7 },
            { "", comma_popup, "", s_8 },
            { "", period_popup, "", JA.s_9 },
            { label = "↓" },
        },
        -- fourth row
        {
            { label = "🌐" },
            { label = "空格",  " ", " ", " ", " ", width = 2.0 },
            { "", { label = "＊", W }, "", JA.s_0 },
            { label = "⮠", "\n", "\n", "\n", "\n", bold = true }, -- return
        },
    },

    wrapInputBox = wrapInputBox,
    genMenuItems = genMenuItems,
}

<MSG> Generic keyboard IME bump: supports auto seperation when code is unique

<DFF> @@ -15,7 +15,7 @@ rf. https://en.wikipedia.org/wiki/Stroke_count_method
 
 --]]
 
-local IME = require("frontend/ui/data/keyboardlayouts/zh_ime")
+local IME = require("frontend/ui/data/keyboardlayouts/generic_ime")
 local util = require("util")
 local JA = require("ui/data/keyboardlayouts/ja_keyboard_keys")
 local _ = require("gettext")
@@ -95,6 +95,8 @@ local ime = IME:new{
     show_candi_callback = function()
         return  G_reader_settings:nilOrTrue(SHOW_CANDI_KEY)
     end,
+    separator = "分隔",
+    switch_char = "下一字",
     W = W -- has wildcard function
 }
 
@@ -102,8 +104,8 @@ local wrappedAddChars = function(inputbox, char)
     ime:wrappedAddChars(inputbox, char)
 end
 
-local function wrappedSeparate(inputbox)
-    ime:wrappedSeparate(inputbox)
+local function seperate(inputbox)
+    ime:separate(inputbox)
 end
 
 local function wrappedDelChar(inputbox)
@@ -128,19 +130,19 @@ local wrapInputBox = function(inputbox)
         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))
+        table.insert(wrappers, util.wrapMethod(inputbox, "leftChar",  nil, seperate))
+        table.insert(wrappers, util.wrapMethod(inputbox, "rightChar", nil, seperate))
+        table.insert(wrappers, util.wrapMethod(inputbox, "upLine",    nil, seperate))
+        table.insert(wrappers, util.wrapMethod(inputbox, "downLine",  nil, seperate))
         -- -- Move to other input box.
-        table.insert(wrappers, util.wrapMethod(inputbox, "unfocus",         nil, wrappedSeparate))
-        table.insert(wrappers, util.wrapMethod(inputbox, "onCloseKeyboard", nil, wrappedSeparate))
+        table.insert(wrappers, util.wrapMethod(inputbox, "unfocus",         nil, seperate))
+        table.insert(wrappers, util.wrapMethod(inputbox, "onCloseKeyboard", nil, seperate))
         -- -- 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))
+        table.insert(wrappers, util.wrapMethod(inputbox, "onTapTextBox",    nil, seperate))
+        table.insert(wrappers, util.wrapMethod(inputbox, "onHoldTextBox",   nil, seperate))
+        table.insert(wrappers, util.wrapMethod(inputbox, "onSwipeTextBox",  nil, seperate))
         -- -- Others
-        table.insert(wrappers, util.wrapMethod(inputbox, "onSwitchingKeyboardLayout", nil, wrappedSeparate))
+        table.insert(wrappers, util.wrapMethod(inputbox, "onSwitchingKeyboardLayout", nil, seperate))
 
         -- addChars is the only method we need a more complicated wrapper for.
         table.insert(wrappers, util.wrapMethod(inputbox, "addChars", wrappedAddChars, nil))

{'addition_count': 15, 'commit_subject': 'Generic keyboard IME bump: supports auto seperation when code is unique', 'deletion_count': 13, 'file_extension': '.lua', 'lang': 'lua', 'license': 'agpl-3.0', 'repo_name': 'koreader/koreader'}