airstruck / luigi

Lovely User Interfaces for Game Inventors
MIT License
113 stars 23 forks source link

Support CJK IME input #14

Open shakesoda opened 8 years ago

shakesoda commented 8 years ago

See love's textedited event, which is totally undocumented. The attached code is probably the most complete example of its usage that exists. SDL backend will be exactly the same, since love's functions don't do much of anything extra.

Compose events are used for some other cases that aren't CJK as well, but I think only CJK languages use candidate lists.

Love 0.9.2 doesn't actually include everything needed for proper IME support unless you FFI in l.k.setTextInput with additional arguments and update SDL.

(the unabridged version of the following code is here. You may want to look at draw(), and you'll want to ensure that start_editing is called every time the contents change)

-- 0.9 compat. Only *really* works if you've updated SDL, though.
-- function console.textedit(...)
--  return console.textedited(...)
-- end

-- CJK IME's need to know where the cursor is in the window and the size of the
-- text box in order to function properly.
-- 
-- All code tested with Mozc on Windows and Linux.
-- Pretend this is whatever code is needed to get the text cursor position in luigi instead of this console...
local function get_position()
    local prefix = console.ps .. " "
    local x = console.x + console.margin + console.font:getWidth(prefix)
    local y = console.y + console.h + (console.lineHeight - console.fontSize) / 2 -1
    local h = console.font:getHeight()
    local text_l = utf8.sub(console.input, 1, console.cursor)
    local text_r = utf8.sub(console.input, console.cursor+1, -1)
    local pos = x + console.font:getWidth(text_l)
    local w = console.w - x - 3
    return pos, y, w, h
end

local function start_editing()
    local x, y, w, h = get_position()
    love.keyboard.setTextInput(console.visible, x, y, w, h)
    love.keyboard.setKeyRepeat(true)
end

local function stop_editing()
    console.editBuffer = nil
    love.keyboard.setTextInput(false)
    love.keyboard.setKeyRepeat(false)
end

-- alias so it's a little more clear why we're calling start() so much.
local update_ime = start_editing

-- You receive this event while a string is still being composed, so it's up to
-- you to display it properly as this happens.
-- "Properly" meaning you should draw a highlighted box with the contents of
-- editbuffer wherever the text cursor is (so you'll need to do some measuring
-- and temporarily insert characters into your display text)
function console.textedited(t, s, l)
    if not console.visible then
        return
    end
    if t == "" then
        console.editBuffer = nil
    else
        console.editBuffer = { text = t, sel = s }
    end
    update_ime()
end

function console.focus(f)
    if not console.visible then
        return
    end
    if f then
        -- Work around for Windows' stupidity.
        stop_editing()
        start_editing()
    else
        stop_editing()
    end
end

-- Note: You only receive this event when the input has been finalized!
function console.textinput(t)
    if t ~= console._KEY_TOGGLE and console.visible then
        local text_l = utf8.sub(console.input, 1, console.cursor)
        local text_r = utf8.sub(console.input, console.cursor+1, -1)
        console.input = text_l .. t .. text_r
        console.cursor = console.cursor + utf8.len(t)
        update_ime()
        return true
    end
    return false
end
airstruck commented 8 years ago

I'd probably accept a pull request for this, but I really have no experience with IME and wouldn't even know how to test if it's working properly.

I'd probably lean toward leaving the functionality out for 0.9.2 rather than hacking it in; so far the Love stuff is "pure" and doesn't rely on any FFI/SDL.

shakesoda commented 8 years ago

alright, I'll probably have a look at implementing this myself in the near future since I'd like to use this for Noodler.