add ime support
This commit is contained in:
parent
3171286c1f
commit
4638fb1ea5
7 changed files with 162 additions and 40 deletions
17
README.md
17
README.md
|
@ -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)
|
||||
|
|
21
core.lua
21
core.lua
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
^^^^^^^^^^^
|
||||
|
|
7
init.lua
7
init.lua
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
49
layout.lua
49
layout.lua
|
@ -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})
|
||||
|
|
24
theme.lua
24
theme.lua
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue