-- On-screen keyboard with hooks for standard key input events local List = require("gfx.list") local Padding = require("gfx.padding") local Text = require("gfx.text") local Orientation = require("gfx.prop.orientation") local Children = require("gfx.prop.children") local DEFAULT_COLOR_BG = colors.gray local DEFAULT_COLOR_KEY = colors.gray local DEFAULT_PADDING_H = 1 local DEFAULT_PADDING_V = 1 local HANDLER_IGNORE_CLICK = function() return true end local ID_KEY_LIST = "$Keyboard$List" local ID_PADDED_KEY = "$Keyboard$List$Padding" local Keyboard = Padding:new{ bgColor = DEFAULT_COLOR_BG, keyColor = DEFAULT_COLOR_KEY, left = DEFAULT_PADDING_H, right = DEFAULT_PADDING_H, top = DEFAULT_PADDING_V, bottom = DEFAULT_PADDING_V, onKeyPress = function(key) end, onBackspace = function() end } function Keyboard:new(o) local template = o or {} if type(template.layout) == "function" then template.layout = template.layout() elseif type(template.layout) ~= "table" then template.layout = Keyboard.Layout.English() end template.colorKey = template.colorKey or DEFAULT_COLOR_KEY template.keySlop = not not template.keySlop template.onClick = HANDLER_IGNORE_CLICK template.onKey = function(_, keyCode, _) if keyCode == keys.backspace then template.onBackspace() return true elseif keyCode == keys.enter then template.onKeyPress("\n") return true end return false end template.onChar = function(_, charCode) template.onKeyPress(charCode) return true end template.element = List:new{ bgColor = template.bgColor, [Orientation:getId()] = Orientation.VERTICAL, [Children:getId()] = {} } local obj = Padding.new(self, template) self:setLayout(self.layout) return obj end function Keyboard:setLayout(layout) self.layout = layout local this = self local KEY_BACKSPACE = "backspace" local KEY_ENTER = "enter" local function charInputKeyList(chars, backspace, enter, spacebar) local keysEntries = { } for i=1,#chars do local key = chars:sub(i, i) local keyFunc = function() this.onKeyPress(key) return true end local keySlopFunc = this.keySlop and keyFunc or HANDLER_IGNORE_CLICK -- ((not backspace) and i == #keys and 0) or 1 table.insert(keysEntries, Padding:new{ onClick = keySlopFunc, bgColor = this.bgColor, right = 1, element = Text:new{ id = key, text = key, bgColor = this.colorKey, onClick = keyFunc }}) end if enter then table.insert(keysEntries, Text:new{ id = KEY_ENTER, text = "[<]", bgColor = this.colorKey, onClick = function() this.onKeyPress("\n") return true end }) end if backspace then table.insert(keysEntries, Text:new{ id = KEY_BACKSPACE, text = "[x]", bgColor = this.colorKey, onClick = function() this.onBackspace() return true end }) end if spacebar then table.insert(keysEntries, Text:new{ id = " ", text = "[SPACE]", bgColor = this.colorKey, onClick = function() this.onKeyPress(" ") return true end }) end return List:new{ id = ID_KEY_LIST, bgColor = this.colorBg, [Orientation:getId()] = Orientation.HORIZONTAL, [Children:getId()] = keysEntries } end local keyboardLines = {} for _,line in ipairs(this.layout) do local keyLineList = charInputKeyList(line[1], line.backspace, line.enter) table.insert(keyboardLines, keyLineList) end self.element[Children:getId()] = keyboardLines self:setDirty() end function Keyboard:setKeyColor(color) if color ~= self.keyColor then self.keyColor = color self:setDirty() end end function Keyboard:setDirty(fullInvalidate) local keyList = self:findById(ID_KEY_LIST) for _,key in pairs(keyList[Children:getId()]) do if key:getId() == ID_PADDED_KEY then key:setBgColor(self.bgColor) key.element:setBgColor(self.keyColor) else key:setBgColor(self.keyColor) end end Padding.setDirty(self, fullInvalidate) end local function layoutNumbers(layout) table.insert(layout, 1, { "1234567890" }) return layout end local function layoutEnter(rowIndex, layout) for index,layoutRow in pairs(layout) do if index == rowIndex then layoutRow.enter = true else layoutRow.enter = nil end end return layout end local function layoutBackspace(rowIndex, layout) for index,layoutRow in pairs(layout) do if index == rowIndex then layoutRow.backspace = true else layoutRow.backspace = nil end end return layout end local function layoutSpacebar(layout) table.insert(layout, { spacebar = true, "" }) return layout end local function appendNumberLayout(cond, layout) return cond and layoutNumbers(layout) or layout end Keyboard.Layout = { ItemSearch = function() return layoutBackspace(1, layoutNumbers({ { "qwertyuiop" }, { "asdfghjkl" }, { "zxcvbnm_:" } })) end, English = function(numberRow) return layoutSpacebar(layoutBackspace(1, appendNumberLayout(numberRow, layoutEnter(2, { { "qwertyuiop" }, { "asdfghjkl" }, { "zxcvbnm" } })))) end, Swedish = function(numberRow) return layoutSpacebar(layoutBackspace(1, appendNumberLayout(numberRow, layoutEnter(2, { { "qwertyuiopå" }, { "asdfghjklöä" }, { "zxcvbnm" } })))) end, Special = function(numberRow) return layoutSpacebar(layoutBackspace(1, appendNumberLayout(numberRow, { { "!\"#¤%&/()=?" }, { "@£${[]}\\+^" }, { "§<>|;:,.-_'*" } }))) end } return Keyboard