diff --git a/gfx/keyboard.lua b/gfx/keyboard.lua new file mode 100644 index 0000000..a1a8f85 --- /dev/null +++ b/gfx/keyboard.lua @@ -0,0 +1,215 @@ +-- 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 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{ + 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 + +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 \ No newline at end of file diff --git a/gfx/list.lua b/gfx/list.lua index f52ce39..1ebb2bf 100644 --- a/gfx/list.lua +++ b/gfx/list.lua @@ -1,3 +1,5 @@ +-- TODO: Rename to "Column" to better represent functionality + local Event = require("gfx.event") local Element = require("gfx.element") local Prop = require("gfx.prop")