Implement on-screen keyboard element

This commit is contained in:
Gabriel Tofvesson 2025-05-25 16:35:52 +02:00
parent 8d3b351083
commit 3282023edd
2 changed files with 217 additions and 0 deletions

215
gfx/keyboard.lua Normal file
View File

@ -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

View File

@ -1,3 +1,5 @@
-- TODO: Rename to "Column" to better represent functionality
local Event = require("gfx.event") local Event = require("gfx.event")
local Element = require("gfx.element") local Element = require("gfx.element")
local Prop = require("gfx.prop") local Prop = require("gfx.prop")