If you followed my Moai tutorial series, you may recall in this post I mentioned that Android keyboard support currently didn’t exist, although there is an effort underway to fix that. Well, a user over on the Moai forums got sick of waiting and implemented a soft keyboard using MoaiGui, which I covered in this post.
It is written as a Lua module and the only dependency is you need to pass in your Moaigui object. Here is the code:
--utility function function distance ( x1, y1, x2, y2 ) return math.sqrt ((( x2 - x1 ) ^ 2 ) + (( y2 - y1 ) ^ 2 )) end k = {} local kboard = nil local keysWide = 1 local keySize = 5 local boardHeight = keySize local left = 50 - keySize/2 local top = 100 local xpos = left local ypos = top local rowCount = 0 local speed = 1000 local textbox = nil --release all gui objects k.destroyKeyboard = function (self, gui) if kboard ~= nil then for k, v in pairs(kboard) do gui:destroyWindow(v) end kboard = nil end end --setup a new keyboard, starting with only 1 row k.createKeyboard = function (self, gui, charsWide) if kboard ~= nil then destroyKeyboard(gui) end kboard = {} keysWide = charsWide boardHeight = keySize left = 50 - keySize*charsWide/2 xpos = left top = 100 ypos = top rowCount = 0 end local function keyClick(event, data) --give focus to selected textbox so that it receives the keypress if textbox ~= nil then data.g:setFocus(textbox) end --pass keypress to gui data.g:injectKeyDown(data.character) data.g:injectKeyUp(data.character) end k.addKey = function (self, gui, char) if kboard == nil then return end --empty string indicates skipping a key slot if char ~= "" then kboard[char] = gui:createButton() kboard[char]:setPos(xpos, ypos) kboard[char]:setDim(keySize, keySize) kboard[char]:setText(char) data = {} data.g = gui if char == "<" then data.character = 8 --backspace else data.character = string.byte(char) --ascii value of character end kboard[char]:registerEventHandler(kboard[char].EVENT_BUTTON_CLICK, nil, keyClick, data) end --increment to next key in row rowCount = rowCount + 1 xpos = xpos + keySize if rowCount >= keysWide then --new row xpos = left ypos = ypos + keySize boardHeight = boardHeight + keySize rowCount = 0 end end --moves the key to its place at the bottom of the screen (or off the screen if show=false). --NOTE: only returns once the move has finished local function moveKey(key, show) local x, y = key:getPos() --move onto the screen local newy = y - boardHeight --move off the screen if not show then newy = y + boardHeight end --not really the best way of getting the target location, but the easiest/quickest solution key:setPos(x, newy) local tx, ty = key._rootProp:getLoc() key:setPos(x,y) --calculate a travel time relevant to the distance being traveled local travelTime = distance(x, y, x, newy) / speed MOAIThread.blockOnAction(key._rootProp:seekLoc(tx, ty, travelTime, MOAIEaseType.LINEAR)) --the seekLoc only moves the prop, the prop container is not aware of the move, so tell it. key:setPos(x, newy) end --moves all keys to the desired location k.showKeyboard = function (self, gui, show) if kboard == nil then return end --only need to pass in show when you want to hide the keyboard if show == nil then show = true end for k, v in pairs(kboard) do --move keys in separate threads MOAIThread.new():run(moveKey, v, show) end end k.hideKeyboard = function (self, gui) self:showKeyboard(gui, false) end k.setTextbox = function(self, tbox) textbox = tbox end return k
And here is some code demonstrating the keyboard in action:
local kb = require "keyboard" kb:createKeyboard(gui, 11) keys = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "<", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "", "", "a", "s", "d", "f", "g", "h", "j", "k", "l", "", "", "", "z", "x", "c", "v", "b", "n", "m"} for k, v in pairs(keys) do kb:addKey(gui, v) end kb:showKeyboard(gui) local editBox = nil local function handleEditBoxGainFocus(self, event) self._cursorPos = #self._internalText + 1 self:_addCursor() kb:setTextbox(editBox) return self:_baseHandleGainFocus(event) end editBox = gui:createEditBox() editBox :setPos(40, 30) editBox :setDim(20, 5) editBox :setText("") editBox ._onHandleGainFocus = handleEditBoxGainFocus
None of this code is mine, all of the credit goes to lancew over on the Moai forums. The only thing I have done is slightly changed the formatting to make it a bit more legible. You can read the original thread right here.
Great work Lance!