Fix bug in input.lua, make 0.9-ready, add utf8 editing
1) [input.lua] Pressing backspace while the cursor was at the beggining of the text removed the first character. Fixed thanks to riidom. 2) [LÖVE 0.9] Use setLine(Width|Style) instead of setLine. Split keyboard.pressed() into keyboard.pressed() and keyboard.textinput(). (see readme) 3) [utf8.lua] Add support for UTF-8 text editing. May still fail spectacurlarly with invalid UTF-8 strings.
This commit is contained in:
parent
ffd187dc17
commit
66a089a07f
6 changed files with 116 additions and 21 deletions
11
README.md
11
README.md
|
@ -135,7 +135,16 @@ Quickie is an [immediate mode gui][IMGUI] library for [LÖVE][LOVE]. Initial
|
|||
end
|
||||
|
||||
function love.keypressed(key, code)
|
||||
gui.keyboard.pressed(key, code)
|
||||
gui.keyboard.pressed(key)
|
||||
-- LÖVE 0.8: see if this code can be converted in a character
|
||||
if pcall(string.char, code) code > 0 then
|
||||
gui.keyboard.textinput(string.char(code))
|
||||
end
|
||||
end
|
||||
|
||||
-- LÖVE 0.9
|
||||
function love.textinput(str)
|
||||
gui.keyboard.textinput(str)
|
||||
end
|
||||
|
||||
# Documentation
|
||||
|
|
3
core.lua
3
core.lua
|
@ -99,7 +99,8 @@ local function draw()
|
|||
for i = 1,draw_items.n do draw_items[i]() end
|
||||
|
||||
-- restore graphics state
|
||||
love.graphics.setLine(lw, ls)
|
||||
love.graphics.setLineWidth(lw)
|
||||
love.graphics.setLineStyle(ls)
|
||||
if f then love.graphics.setFont(f) end
|
||||
love.graphics.setColor(c)
|
||||
|
||||
|
|
16
input.lua
16
input.lua
|
@ -29,6 +29,7 @@ local core = require(BASE .. 'core')
|
|||
local group = require(BASE .. 'group')
|
||||
local mouse = require(BASE .. 'mouse')
|
||||
local keyboard = require(BASE .. 'keyboard')
|
||||
local utf8 = require(BASE .. 'utf8')
|
||||
|
||||
-- {info = {text = "", cursor = text:len()}, pos = {x, y}, size={w, h}, widgetHit=widgetHit, draw=draw}
|
||||
return function(w)
|
||||
|
@ -45,11 +46,13 @@ return function(w)
|
|||
if not keyboard.hasFocus(id) then
|
||||
--[[nothing]]
|
||||
-- editing
|
||||
elseif keyboard.key == 'backspace' then
|
||||
w.info.text = w.info.text:sub(1,w.info.cursor-1) .. w.info.text:sub(w.info.cursor+1)
|
||||
elseif keyboard.key == 'backspace' and w.info.cursor > 0 then
|
||||
w.info.cursor = math.max(0, w.info.cursor-1)
|
||||
local left, right = utf8.split(w.info.text, w.info.cursor)
|
||||
w.info.text = left .. utf8.sub(right, 2)
|
||||
elseif keyboard.key == 'delete' then
|
||||
w.info.text = w.info.text:sub(1,w.info.cursor) .. w.info.text:sub(w.info.cursor+2)
|
||||
local left, right = utf8.split(w.info.text, w.info.cursor)
|
||||
w.info.text = left .. utf8.sub(right, 2)
|
||||
w.info.cursor = math.min(w.info.text:len(), w.info.cursor)
|
||||
-- movement
|
||||
elseif keyboard.key == 'left' then
|
||||
|
@ -64,10 +67,9 @@ return function(w)
|
|||
elseif keyboard.key == 'return' then
|
||||
keyboard.clearFocus()
|
||||
keyboard.pressed('', -1)
|
||||
elseif keyboard.code >= 32 and keyboard.code < 127 then
|
||||
local left = w.info.text:sub(1,w.info.cursor)
|
||||
local right = w.info.text:sub(w.info.cursor+1)
|
||||
w.info.text = table.concat{left, string.char(keyboard.code), right}
|
||||
elseif keyboard.str then
|
||||
local left, right = utf8.split(w.info.text, w.info.cursor)
|
||||
w.info.text = left .. keyboard.str .. right
|
||||
w.info.cursor = w.info.cursor + 1
|
||||
end
|
||||
|
||||
|
|
20
keyboard.lua
20
keyboard.lua
|
@ -24,7 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||
THE SOFTWARE.
|
||||
]]--
|
||||
|
||||
local key,code = nil, -1
|
||||
local key,str = nil, nil
|
||||
local focus, lastwidget
|
||||
local NO_WIDGET = {}
|
||||
|
||||
|
@ -34,11 +34,16 @@ local cycle = {
|
|||
next = {key = 'tab'},
|
||||
}
|
||||
|
||||
local function pressed(...)
|
||||
key, code = ...
|
||||
assert(type(key) == 'string', 'Invalid argument `key`. Expected string, got ' .. type(key))
|
||||
assert(type(code) == 'number', 'Invalid argument `code`. Expected number, got ' .. type(code))
|
||||
local function pressed(k)
|
||||
assert(type(k) == 'string', 'Invalid argument `key`. Expected string, got ' .. type(k))
|
||||
key = k
|
||||
end
|
||||
|
||||
local function textinput(s)
|
||||
assert(type(s) == 'string', 'Invalid argument `key`. Expected string, got ' .. type(s))
|
||||
str = s
|
||||
end
|
||||
|
||||
local function setFocus(id) focus = id end
|
||||
local function disable() focus = NO_WIDGET end
|
||||
local function clearFocus() focus = nil end
|
||||
|
@ -79,12 +84,13 @@ local function beginFrame()
|
|||
end
|
||||
|
||||
local function endFrame()
|
||||
key, code = nil, -1
|
||||
key, str = nil, nil
|
||||
end
|
||||
|
||||
return setmetatable({
|
||||
cycle = cycle,
|
||||
pressed = pressed,
|
||||
textinput = textinput,
|
||||
tryGrab = tryGrab,
|
||||
isBindingDown = isBindingDown,
|
||||
setFocus = setFocus,
|
||||
|
@ -99,4 +105,4 @@ return setmetatable({
|
|||
|
||||
beginFrame = beginFrame,
|
||||
endFrame = endFrame,
|
||||
}, {__index = function(_,k) return ({key = key, code = code})[k] end})
|
||||
}, {__index = function(_,k) return ({key = key, str = str})[k] end})
|
||||
|
|
|
@ -24,6 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||
THE SOFTWARE.
|
||||
]]--
|
||||
|
||||
local BASE = (...):match("(.-)[^%.]+$")
|
||||
local utf8 = require(BASE .. 'utf8')
|
||||
|
||||
-- default style
|
||||
local color = {
|
||||
normal = {bg = {78,78,78}, fg = {200,200,200}, border={20,20,20}},
|
||||
|
@ -43,7 +46,8 @@ end
|
|||
gradient:set(200,255)
|
||||
|
||||
local function box(x,y,w,h, bg, border, flip)
|
||||
love.graphics.setLine(1, 'rough')
|
||||
love.graphics.setLineWidth(1)
|
||||
love.graphics.setLineStyle('rough')
|
||||
|
||||
love.graphics.setColor(bg)
|
||||
local sy = flip and -h/2 or h/2
|
||||
|
@ -82,7 +86,8 @@ end
|
|||
local function Slider(state, fraction, vertical, x,y,w,h)
|
||||
local c = color[state]
|
||||
|
||||
love.graphics.setLine(1, 'rough')
|
||||
love.graphics.setLineWidth(1)
|
||||
love.graphics.setLineStyle('rough')
|
||||
love.graphics.setColor(c.bg)
|
||||
if vertical then
|
||||
love.graphics.rectangle('fill', x+w/2-2,y,4,h)
|
||||
|
@ -105,7 +110,8 @@ local function Slider2D(state, fraction, x,y,w,h)
|
|||
box(x,y,w,h, c.bg, c.border)
|
||||
|
||||
-- draw quadrants
|
||||
love.graphics.setLine(1, 'rough')
|
||||
love.graphics.setLineWidth(1)
|
||||
love.graphics.setLineStyle('rough')
|
||||
love.graphics.setColor(c.fg[1], c.fg[2], c.fg[3], math.min(127,c.fg[4] or 255))
|
||||
love.graphics.line(x+w/2,y, x+w/2,y+h)
|
||||
love.graphics.line(x,y+h/2, x+w,y+h/2)
|
||||
|
@ -124,7 +130,7 @@ local function Input(state, text, cursor, x,y,w,h)
|
|||
|
||||
local f = love.graphics.getFont()
|
||||
local th = f:getHeight(text)
|
||||
local cursorPos = x + 2 + f:getWidth(text:sub(1,cursor))
|
||||
local cursorPos = x + 2 + f:getWidth(utf8.sub(text, 1,cursor))
|
||||
local offset = 2 - math.floor((cursorPos-x) / (w-4)) * (w-4)
|
||||
|
||||
local tsx,tsy,tsw,tsh = x+1, y, w-2, h
|
||||
|
@ -139,7 +145,8 @@ local function Input(state, text, cursor, x,y,w,h)
|
|||
end
|
||||
|
||||
love.graphics.setScissor(tsx, tsy, tsw, tsh)
|
||||
love.graphics.setLine(1, 'rough')
|
||||
love.graphics.setLineWidth(1)
|
||||
love.graphics.setLineStyle('rough')
|
||||
love.graphics.setColor(color.normal.fg)
|
||||
love.graphics.print(text, x+offset,y+(h-th)/2)
|
||||
if state ~= 'normal' then
|
||||
|
|
70
utf8.lua
Normal file
70
utf8.lua
Normal file
|
@ -0,0 +1,70 @@
|
|||
local function iter(s, i)
|
||||
if i >= #s then return end
|
||||
local b, nbytes = s:byte(i+1,i+1), 1
|
||||
|
||||
-- determine width of the codepoint by counting the number of set bits in the first byte
|
||||
-- warning: there is no validation of the following bytes!
|
||||
if b >= 0xc0 and b <= 0xdf then nbytes = 2 -- 1100 0000 to 1101 1111
|
||||
elseif b >= 0xe0 and b <= 0xef then nbytes = 3 -- 1110 0000 to 1110 1111
|
||||
elseif b >= 0xf0 and b <= 0xf7 then nbytes = 4 -- 1111 0000 to 1111 0111
|
||||
elseif b >= 0xf8 and b <= 0xfb then nbytes = 5 -- 1111 1000 to 1111 1011
|
||||
elseif b >= 0xfc and b <= 0xfd then nbytes = 6 -- 1111 1100 to 1111 1101
|
||||
elseif b < 0x00 or b > 0x7f then error(("Invalid codepoint: 0x%02x"):format(b))
|
||||
end
|
||||
return i+nbytes, s:sub(i+1,i+nbytes), nbytes
|
||||
end
|
||||
|
||||
local function chars(s)
|
||||
return iter, s, 0
|
||||
end
|
||||
|
||||
local function len(s)
|
||||
-- assumes sane utf8 string: count the number of bytes that is *not* 10xxxxxx
|
||||
local _, c = s:gsub('[^\128-\191]', '')
|
||||
return c
|
||||
end
|
||||
|
||||
local function sub(s, i, j)
|
||||
local l = len(s)
|
||||
j = j or l
|
||||
if i < 0 then i = l + i + 1 end
|
||||
if j < 0 then j = l + j + 1 end
|
||||
if j < i then return '' end
|
||||
|
||||
local k, t = 1, {}
|
||||
for _, c in chars(s) do
|
||||
if k >= i then t[#t+1] = c end
|
||||
if k >= j then break end
|
||||
k = k + 1
|
||||
end
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
local function split(s, i)
|
||||
local l = len(s)
|
||||
if i < 0 then i = l + i + 1 end
|
||||
|
||||
local k, pos = 1, 0
|
||||
for byte in chars(s) do
|
||||
if k > i then break end
|
||||
pos, k = byte, k + 1
|
||||
end
|
||||
return s:sub(1, pos), s:sub(pos+1, -1)
|
||||
end
|
||||
|
||||
local function reverse(s)
|
||||
local t = {}
|
||||
for _, c in chars(s) do
|
||||
table.insert(t, 1, c)
|
||||
end
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
return {
|
||||
iter = iter,
|
||||
chars = chars,
|
||||
len = len,
|
||||
sub = sub,
|
||||
split = split,
|
||||
reverse = reverse,
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue