add ime support

This commit is contained in:
endlesstravel 2016-11-06 15:06:58 +08:00
parent 3171286c1f
commit 4638fb1ea5
7 changed files with 162 additions and 40 deletions

View file

@ -23,7 +23,13 @@ More info and code is over at [readthedocs](http://suit.readthedocs.org/en/lates
local suit = require 'suit'
-- storage for text input
local input = {text = ""}
local input = {text = "", candidate_text = {text="", start=0, length=0}}
-- make love use font which support CJK text
function love.load()
local font = love.graphics.newFont("NotoSansHans-Regular.otf", 20)
love.graphics.setFont(font)
end
-- all the UI is defined in love.update or functions that are called from here
function love.update(dt)
@ -33,7 +39,7 @@ function love.update(dt)
-- put an input widget at the layout origin, with a cell size of 200 by 30 pixels
suit.Input(input, suit.layout:row(200,30))
-- put a label that displays the text below the first cell
-- the cell size is the same as the last one (200x30 px)
-- the label text will be aligned to the left
@ -41,7 +47,7 @@ function love.update(dt)
-- put an empty cell that has the same size as the last cell (200x30 px)
suit.layout:row()
-- put a button of size 200x30 px in the cell below
-- if the button is pressed, quit the game
if suit.Button("Close", suit.layout:row()).hit then
@ -54,6 +60,11 @@ function love.draw()
suit.draw()
end
function love.textedited(text, start, length)
-- for IME input
input.candidate_text = {text = text, start= start, length = length}
end
function love.textinput(t)
-- forward text input to SUIT
suit.textinput(t)

View file

@ -48,15 +48,29 @@ function suit:wasHovered(id)
return id == self.hovered_last
end
function suit:anyActive()
return self.active ~= nil
end
function suit:isActive(id)
return id == self.active
end
function suit:anyHit()
return self.hit ~= nil
end
function suit:isHit(id)
return id == self.hit
end
function suit:getStateName(id)
if self:isActive(id) then
return "active"
elseif self:isHovered(id) then
return "hovered"
elseif self:isHit(id) then
return "hit"
end
return "normal"
end
@ -84,7 +98,11 @@ function suit:registerHitbox(id, x,y,w,h)
end
function suit:mouseReleasedOn(id)
return not self.mouse_button_down and self:isActive(id) and self:isHovered(id)
if not self.mouse_button_down and self:isActive(id) and self:isHovered(id) then
self.hit = id
return true
end
return false
end
function suit:updateMouse(x, y, button_down)
@ -145,6 +163,7 @@ function suit:enterFrame()
self:updateMouse(love.mouse.getX(), love.mouse.getY(), love.mouse.isDown(1))
self.key_down, self.textchar = nil, ""
self:grabKeyboardFocus(NONE)
self.hit = nil
end
function suit:exitFrame()

View file

@ -46,6 +46,56 @@ Forwards a ``love.keypressed(key)`` event to SUIT.
Forwards a ``love.textinput(key)`` event to SUIT.
GUI State
^^^^^^^^^
.. function:: anyHovered()
:returns: ``true`` if any widget is hovered by the mouse.
Checks if any widget is hovered by the mouse.
.. function:: isHovered(id)
:param mixed id: Identifier of the widget.
:returns: ``true`` if the widget is hovered by the mouse.
Checks if the widget identified by ``id`` is hovered by the mouse.
.. function:: wasHovered(id)
:param mixed id: Identifier of the widget.
:returns: ``true`` if the widget was in the hovered by the mouse in the last frame.
Checks if the widget identified by ``id`` was hovered by the mouse in the last frame.
.. function:: anyActive()
:returns: ``true`` if any widget is in the ``active`` state.
Checks whether the mouse button is pressed and held on any widget.
.. function:: isActive(id)
:param mixed id: Identifier of the widget.
:returns: ``true`` if the widget is in the ``active`` state.
Checks whether the mouse button is pressed and held on the widget identified by ``id``.
.. function:: anyHit()
:returns: ``true`` if the mouse was pressed and released on any widget.
Check whether the mouse was pressed and released on any widget.
.. function:: isHit(id)
:param mixed id: Identifier of the widget.
:returns: ``true`` if the mouse was pressed and released on the widget.
Check whether the mouse was pressed and released on the widget identified by ``id``.
Internal Helpers
----------------
@ -73,36 +123,6 @@ Prepares GUI state when entering a frame.
Clears GUI state when exiting a frame.
GUI State
^^^^^^^^^
.. function:: anyHovered()
:returns: ``true`` if any widget is hovered by the mouse.
Checks if any widget is hovered by the mouse.
.. function:: isHovered(id)
:param mixed id: Identifier of the widget.
:returns: ``true`` if the widget is hovered by the mouse.
Checks if the widget identified by ``id`` is hovered by the mouse.
.. function:: wasHovered(id)
:param mixed id: Identifier of the widget.
:returns: ``true`` if the widget was in the hovered by the mouse in the last frame.
Checks if the widget identified by ``id`` was hovered by the mouse in the last frame.
.. function:: isActive(id)
:param mixed id: Identifier of the widget.
:returns: ``true`` if the widget is in the ``active`` state.
Checks whether the mouse button is pressed on the widget identified by ``id``.
Mouse Input
^^^^^^^^^^^

View file

@ -5,6 +5,8 @@ local suit = require(BASE .. "core")
local instance = suit.new()
return setmetatable({
_instance = instance,
new = suit.new,
getOptionsAndSize = suit.getOptionsAndSize,
@ -12,7 +14,10 @@ return setmetatable({
anyHovered = function(...) return instance:anyHovered(...) end,
isHovered = function(...) return instance:isHovered(...) end,
wasHovered = function(...) return instance:wasHovered(...) end,
anyActive = function(...) return instance:anyActive(...) end,
isActive = function(...) return instance:isActive(...) end,
anyHit = function(...) return instance:anyHit(...) end,
isHit = function(...) return instance:isHit(...) end,
mouseInRect = function(...) return instance:mouseInRect(...) end,
registerHitbox = function(...) return instance:registerHitbox(...) end,
@ -49,7 +54,7 @@ return setmetatable({
if k == "theme" then
instance.theme = v
else
rawset(t, k, v)
rawset(instance, k, v)
end
end,
__index = function(t, k)

View file

@ -52,10 +52,10 @@ return function(core, input, ...)
opt.state = core:registerHitbox(opt.id, x,y,w,h)
opt.hasKeyboardFocus = core:grabKeyboardFocus(opt.id)
if opt.hasKeyboardFocus then
if (input.candidate_text.text == "") and opt.hasKeyboardFocus then
local keycode,char = core:getPressedKey()
-- text input
if char ~= "" then
if char and char ~= "" then
local a,b = split(input.text, input.cursor)
input.text = table.concat{a, char, b}
input.cursor = input.cursor + utf8.len(char)

View file

@ -35,10 +35,22 @@ function Layout:nextRow()
return self._x, self._y + self._h + self._pady
end
Layout.nextDown = Layout.nextRow
function Layout:nextUp()
return self._x, self._y - self._h - self._pady
end
function Layout:nextCol()
return self._x + self._w + self._padx, self._y
end
Layout.nextRight = Layout.nextCol
function Layout:nextLeft()
return self._x - self._w - self._padx, self._y
end
function Layout:push(x,y)
self._stack[#self._stack+1] = {
self._x, self._y,
@ -59,6 +71,7 @@ function Layout:pop()
self._w, self._h,
self._widths, self._heights = unpack(self._stack[#self._stack])
self._isFirstCell = false
self._stack[#self._stack] = nil
self._w, self._h = math.max(w, self._w or 0), math.max(h, self._h or 0)
@ -135,6 +148,22 @@ function Layout:row(w, h)
return x,y,w,h
end
Layout.down = Layout.row
function Layout:up(w, h)
w,h = calc_width_height(self, w, h)
local x,y = self._x, self._y - (self._h or 0)
if not self._isFirstCell then
y = y - self._pady
end
self._isFirstCell = false
self._y, self._w, self._h = y, w, h
return x,y,w,h
end
function Layout:col(w, h)
w,h = calc_width_height(self, w, h)
@ -150,6 +179,22 @@ function Layout:col(w, h)
return x,y,w,h
end
Layout.right = Layout.col
function Layout:left(w, h)
w,h = calc_width_height(self, w, h)
local x,y = self._x - (self._w or 0), self._y
if not self._isFirstCell then
x = x - self._padx
end
self._isFirstCell = false
self._x, self._w, self._h = x, w, h
return x,y,w,h
end
local function layout_iterator(t, idx)
idx = (idx or 1) + 1
@ -323,6 +368,10 @@ return setmetatable({
pop = function(...) return instance:pop(...) end,
row = function(...) return instance:row(...) end,
col = function(...) return instance:col(...) end,
down = function(...) return instance:down(...) end,
up = function(...) return instance:up(...) end,
left = function(...) return instance:left(...) end,
right = function(...) return instance:right(...) end,
rows = function(...) return instance:rows(...) end,
cols = function(...) return instance:cols(...) end,
}, {__call = function(_,...) return Layout.new(...) end})

View file

@ -21,6 +21,10 @@ end
function theme.drawBox(x,y,w,h, colors, cornerRadius)
local colors = colors or theme.getColorForState(opt)
cornerRadius = cornerRadius or theme.cornerRadius
w = math.max(cornerRadius/2, w)
if h < cornerRadius/2 then
y,h = y - (cornerRadius - h), cornerRadius/2
end
love.graphics.setColor(colors.bg)
love.graphics.rectangle('fill', x,y, w,h, cornerRadius)
@ -89,7 +93,7 @@ function theme.Slider(fraction, opt, x,y,w,h)
local c = theme.getColorForState(opt)
theme.drawBox(x,y,w,h, c, opt.cornerRadius)
theme.drawBox(x,yb,wb,hb, {bg=c.fg}, opt.cornerRadius)
theme.drawBox(xb,yb,wb,hb, {bg=c.fg}, opt.cornerRadius)
if opt.state ~= nil and opt.state ~= "normal" then
love.graphics.setColor((opt.color and opt.color.active or {}).fg or theme.color.active.fg)
@ -119,12 +123,26 @@ function theme.Input(input, opt, x,y,w,h)
love.graphics.setFont(opt.font)
love.graphics.print(input.text, x, y+(h-th)/2)
-- candidate text
local tw = opt.font:getWidth(input.text)
local ctw = opt.font:getWidth(input.candidate_text.text)
love.graphics.setColor((opt.color and opt.color.normal and opt.color.normal.fg) or theme.color.normal.fg)
love.graphics.print(input.candidate_text.text, x + tw, y+(h-th)/2)
-- candidate text rectangle box
love.graphics.rectangle("line", x + tw, y+(h-th)/2, ctw, th)
-- cursor
if opt.hasKeyboardFocus and (love.timer.getTime() % 1) > .5 then
local ct = input.candidate_text;
local ss = ct.text:sub(1, utf8.offset(ct.text, ct.start))
local ws = opt.font:getWidth(ss)
if ct.start == 0 then ws = 0 end
love.graphics.setLineWidth(1)
love.graphics.setLineStyle('rough')
love.graphics.line(x + opt.cursor_pos, y + (h-th)/2,
x + opt.cursor_pos, y + (h+th)/2)
love.graphics.line(x + opt.cursor_pos + ws, y + (h-th)/2,
x + opt.cursor_pos + ws, y + (h+th)/2)
end
-- reset scissor