From 66a089a07f4d24557cb8e06f78eefb07a344ea32 Mon Sep 17 00:00:00 2001 From: Matthias Richter Date: Wed, 11 Dec 2013 15:19:04 +0100 Subject: Fix bug in input.lua, make 0.9-ready, add utf8 editing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- README.md | 11 ++++++++- core.lua | 3 ++- input.lua | 16 +++++++------ keyboard.lua | 20 ++++++++++------ style-default.lua | 17 ++++++++++---- utf8.lua | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 21 deletions(-) create mode 100644 utf8.lua diff --git a/README.md b/README.md index bb76e4c..734202c 100644 --- a/README.md +++ b/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 diff --git a/core.lua b/core.lua index cbccbae..42b1ae1 100644 --- a/core.lua +++ b/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) diff --git a/input.lua b/input.lua index ad0683f..b161993 100644 --- a/input.lua +++ b/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 diff --git a/keyboard.lua b/keyboard.lua index 4850df3..0e52eb3 100644 --- a/keyboard.lua +++ b/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}) diff --git a/style-default.lua b/style-default.lua index e970d7a..71f412a 100644 --- a/style-default.lua +++ b/style-default.lua @@ -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 diff --git a/utf8.lua b/utf8.lua new file mode 100644 index 0000000..f661b1b --- /dev/null +++ b/utf8.lua @@ -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, +} -- cgit v1.2.3-54-g00ecf