Skip to content

Instantly share code, notes, and snippets.

@x4fx77x4f
Created September 21, 2022 00:21
Show Gist options
  • Save x4fx77x4f/d9e3e9b43e9ff2d7725acfc122c32543 to your computer and use it in GitHub Desktop.
Save x4fx77x4f/d9e3e9b43e9ff2d7725acfc122c32543 to your computer and use it in GitHub Desktop.
Work-in-progress on-screen keyboard for StarfallEx
--@client
local osk
do
osk = {}
local function enum(t_in)
local n = #t_in
local bits = 0
repeat
bits = bits+1
until n <= 2^bits
assert(bits <= 32, "bit count for enum cannot exceed 32")
local t_out = {}
t_out._BITS = bits
for i=1, #t_in do
t_out[t_in[i]] = i-1
end
return t_out
end
local function bitfield(t_in)
local bits = #t_in
assert(bits <= 31, "bit count for bitfield cannot exceed 31")
local t_out = {}
t_out._BITS = bits
local all = 0
for i=1, #t_in do
local b = bit.lshift(1, i-1)
t_out[t_in[i]] = b
all = bit.bor(all, b)
end
t_out._ALL = all
return t_out
end
osk.F = enum({
'BACKSPACE',
'TAB',
'CAPSLOCK',
'RETURN',
'SHIFT',
'CTRL',
'SUPER',
'ALT',
'MENU',
'OSK_HIDE',
'OSK_LOCK',
})
local labels = {
[osk.F.BACKSPACE] = "Backspace",
[osk.F.TAB] = "Tab",
[osk.F.CAPSLOCK] = "Caps Lock",
[osk.F.RETURN] = "Return",
[osk.F.SHIFT] = "Shift",
[osk.F.CTRL] = "Ctrl",
[osk.F.SUPER] = "Super",
[osk.F.ALT] = "Alt",
[osk.F.MENU] = "Menu",
[osk.F.OSK_HIDE] = "Hide",
[osk.F.OSK_LOCK] = "Lock",
}
osk.INPUTMODE = enum({
'TEXT',
'DECIMAL',
'NUMERIC',
'TEL',
'SEARCH',
'EMAIL',
'URL',
})
osk.FLAGS = bitfield({
'LETTERS',
'DIGITS', -- Digits and "!@#$%^&*()"
'PUNCTUATION', -- "`-=[]\\;',./~_+{}|:\"<>?"
'SHIFT', -- Shift and caps lock
'CTRL',
'SYSTEM', -- Tab, shift, ctrl, alt, super, and menu
})
local p = 4
local rc = 5
local e = {}
osk._e = e
e.w = 512
e.h = 170
e.x = 0
e.y = 512-e.h
local eiw, eih = e.w-p-p, (e.h-p)/rc-p
for i=1, 5 do
local ei = {}
e[i] = ei
ei.w = eiw
ei.h = eih
ei.x = p
ei.y = (i-1)*(eih+p)+p
end
local e1 = e[1]
local e1jw = (e1.w+p)/15
for j=1, 14 do
local e1j = {}
e1[j] = e1j
e1j.w = e1jw-p
e1j.h = e1.h
e1j.x = (j-1)*e1jw
e1j.y = 0
end
local s, s_shift, s_caps = '`1234567890-=', '~!@#$%^&*()_+'
for i=1, 13 do
local e1i = e1[i]
e1i.s = string.sub(s, i, i)
e1i.s_shift = string.sub(s_shift, i, i)
e1i.flags = osk.FLAGS.DIGITS
end
e1[1].flags = osk.FLAGS.PUNCTUATION
e1[12].flags = osk.FLAGS.PUNCTUATION
e1[13].flags = osk.FLAGS.PUNCTUATION
e1[14].f = osk.F.BACKSPACE
e1[14].flags = osk.FLAGS._ALL
e1[14].w = e1[14].w+e1jw
local e2 = e[2]
local e2_1w = e1jw*1.5
for j=2, 14 do
local e2j = {}
e2[j] = e2j
e2j.w = e1jw-p
e2j.h = e2.h
e2j.x = (j-2)*e1jw+e2_1w+p
e2j.y = 0
end
s, s_caps = '\x00qwertyuiop', '\x00QWERTYUIOP'
for i=2, 11 do
local e2i = e2[i]
e2i.s = string.sub(s, i, i)
e2i.s_caps = string.sub(s_caps, i, i)
e2i.flags = osk.FLAGS.LETTERS
end
local e2_1 = {}
e2[1] = e2_1
e2_1.f = osk.F.TAB
e2_1.flags = osk.FLAGS.SYSTEM
e2_1.w = e2_1w
e2_1.h = e2.h
e2_1.x = 0
e2_1.y = 0
e2[12].s = '['
e2[12].s_shift = '{'
e2[12].flags = osk.FLAGS.PUNCTUATION
e2[13].s = ']'
e2[13].s_shift = '}'
e2[13].flags = osk.FLAGS.PUNCTUATION
e2[14].s = '\\'
e2[14].s_shift = '|'
e2[14].flags = osk.FLAGS.PUNCTUATION
e2[14].w = e2.w-e2[14].x
local e3 = e[3]
local e3_1w = e1jw*1.8
for j=2, 13 do
local e3j = {}
e3[j] = e3j
e3j.w = e1jw-p
e3j.h = e3.h
e3j.x = (j-2)*e1jw+e3_1w+p
e3j.y = 0
end
s, s_caps = '\x00asdfghjkl', '\x00ASDFGHJKL'
for i=2, 12 do
local e3i = e3[i]
e3i.s = string.sub(s, i, i)
e3i.s_caps = string.sub(s_caps, i, i)
e3i.flags = osk.FLAGS.LETTERS
end
local e3_1 = {}
e3[1] = e3_1
e3_1.f = osk.F.CAPSLOCK
e3_1.flags = bit.bor(osk.FLAGS.SYSTEM, osk.FLAGS.SHIFT)
e3_1.w = e3_1w
e3_1.h = e3.h
e3_1.x = 0
e3_1.y = 0
e3[11].s = ';'
e3[11].s_shift = ':'
e3[11].flags = osk.FLAGS.PUNCTUATION
e3[12].s = '\''
e3[12].s_shift = '"'
e3[12].flags = osk.FLAGS.PUNCTUATION
e3[13].f = osk.F.RETURN
e3[13].flags = osk.FLAGS._ALL
e3[13].w = e3.w-e3[13].x
local e4 = e[4]
local e4_1w = e1jw*2.4
for j=2, 12 do
local e4j = {}
e4[j] = e4j
e4j.w = e1jw-p
e4j.h = e4.h
e4j.x = (j-2)*e1jw+e4_1w+p
e4j.y = 0
end
s, s_caps = '\x00zxcvbnm', '\x00ZXCVBNM'
for i=2, 9 do
local e4i = e4[i]
e4i.s = string.sub(s, i, i)
e4i.s_caps = string.sub(s_caps, i, i)
e4i.flags = osk.FLAGS.LETTERS
end
local e4_1 = {}
e4[1] = e4_1
e4_1.f = osk.F.SHIFT
e4_1.flags = bit.bor(osk.FLAGS.SYSTEM, osk.FLAGS.SHIFT)
e4_1.w = e4_1w
e4_1.h = e4.h
e4_1.x = 0
e4_1.y = 0
e4[9].s = ','
e4[9].s_shift = '<'
e4[9].flags = osk.FLAGS.PUNCTUATION
e4[10].s = '.'
e4[10].s_shift = '>'
e4[10].flags = osk.FLAGS.PUNCTUATION
e4[11].s = '/'
e4[11].s_shift = '?'
e4[11].flags = osk.FLAGS.PUNCTUATION
e4[12].f = osk.F.SHIFT
e4[12].flags = bit.bor(osk.FLAGS.SYSTEM, osk.FLAGS.SHIFT)
e4[12].w = e4.w-e4[12].x
local e5 = e[5]
local e5_4w = e1jw*5.5
local e5jw = (e5.w-e5_4w)/7
for j=1, 8 do
local e5j = {}
e5[j] = e5j
e5j.w = e5jw-p
e5j.h = e5.h
e5j.x = (j-1)*e5jw+(j > 4 and e5_4w-e5jw+p or 0)
e5j.y = 0
end
e5[1].f = osk.F.CTRL
e5[1].flags = bit.bor(osk.FLAGS.SYSTEM, osk.FLAGS.CTRL)
e5[2].f = osk.F.SUPER
e5[2].flags = osk.FLAGS.SYSTEM
e5[3].f = osk.F.ALT
e5[3].flags = osk.FLAGS.SYSTEM
e5[4].s = ' '
e5[4].flags = osk.FLAGS._ALL
e5[4].w = e5_4w
e5[5].f = osk.F.ALT
e5[5].flags = osk.FLAGS.SYSTEM
e5[6].f = osk.F.SUPER
e5[5].flags = osk.FLAGS.SYSTEM
e5[7].f = osk.F.MENU
e5[5].flags = osk.FLAGS.SYSTEM
e5[8].f = osk.F.CTRL
e5[5].flags = bit.bor(osk.FLAGS.SYSTEM, osk.FLAGS.CTRL)
osk.bg = Color(0, 0, 0)
osk.bg_k = Color('#474747')
osk.bg_k_special = Color('#222222')
osk.fg = Color('#ffffff')
osk.fg_outline = Color(255, 255, 255, 15)
osk.fg_off = Color(0, 0, 0)
osk.fg_on = Color('#44afd6')
osk.fg_on_alt = Color('#fcae00')
local function draw_outline(x, y, w, h, r)
render.drawRect(x, y, w, r)
render.drawRect(x, y+h-r, w, r)
render.drawRect(x, y, r, h)
render.drawRect(x+w-r, y, r, h)
end
local function draw_box(x, y, e)
local w, h = e.w, e.h
x, y = x+e.x, y+e.y
render.drawRect(x, y, w, h)
end
local function draw_box_outlined(x, y, e)
local w, h = e.w, e.h
x, y = x+e.x, y+e.y
render.drawRect(x, y, w, h)
render.setColor(osk.fg_outline)
draw_outline(x, y, w, h, 1)
end
local id = 'osk'
render.createRenderTarget(id)
hook.add('renderoffscreen', id, function()
hook.remove('renderoffscreen', id)
render.selectRenderTarget(id)
render.clear(Color(255, 255, 255, 0), true)
local x, y = 0, 0
render.setColor(osk.bg)
draw_box(x, y, e)
x, y = x+e.x, y+e.y
for i=1, #e do
local ei = e[i]
local x, y = x+ei.x, y+ei.y
for j=1, #ei do
local eij = ei[j]
render.setColor(eij.f ~= nil and osk.bg_k_special or osk.bg_k)
draw_box_outlined(x, y, eij)
local label = eij.s
if label == nil then
label = labels[eij.f]
end
if label ~= nil then
render.setColor(osk.fg)
render.setFont('DermaDefault')
render.drawSimpleText(x+eij.x+eij.w/2, y+eij.y+eij.h/2, label, TEXT_ALIGN.CENTER ,TEXT_ALIGN.CENTER)
end
end
end
end)
local color_white = Color(255, 255, 255)
function osk.draw_osk()
render.setColor(color_white)
render.setRenderTargetTexture(id)
render.drawTexturedRectUV(0, 0, 512, 512, 0, 0, 0.5, 0.5)
end
end
local color_bg = Color(63, 63, 63)
local color_fg = Color(255, 255, 255)
local color_outline = Color(127, 127, 127)
local function draw_outline(x, y, w, h, r)
--return render.drawRectOutline(x, y, w, h, r)
render.drawRect(x, y, w, r)
render.drawRect(x, y+h-r, w, r)
render.drawRect(x, y, r, h)
render.drawRect(x+w-r, y, r, h)
end
local function draw_box(x, y, e)
local w, h = e.w, e.h
x, y = x+e.x, y+e.y
render.setColor(color_bg)
render.drawRect(x, y, w, h)
render.setColor(color_outline)
draw_outline(x, y, w, h, 2)
end
hook.add('render', '', function()
render.setBackgroundColor(color_bg)
local p = 8
local x, y = p, p
render.setFont('DermaLarge')
local ew, eh = render.drawSimpleText(x, y, "On-screen keyboard demo")
y = y+eh+p
render.setFont('DermaDefault')
ew, eh = render.drawSimpleText(x, y, "Click by looking at element and pressing Use or Primary Fire.")
y = y+eh+p
osk.draw_osk()
end)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment