add ime support
This commit is contained in:
parent
3171286c1f
commit
4638fb1ea5
7 changed files with 162 additions and 40 deletions
13
README.md
13
README.md
|
@ -23,7 +23,13 @@ More info and code is over at [readthedocs](http://suit.readthedocs.org/en/lates
|
||||||
local suit = require 'suit'
|
local suit = require 'suit'
|
||||||
|
|
||||||
-- storage for text input
|
-- 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
|
-- all the UI is defined in love.update or functions that are called from here
|
||||||
function love.update(dt)
|
function love.update(dt)
|
||||||
|
@ -54,6 +60,11 @@ function love.draw()
|
||||||
suit.draw()
|
suit.draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function love.textedited(text, start, length)
|
||||||
|
-- for IME input
|
||||||
|
input.candidate_text = {text = text, start= start, length = length}
|
||||||
|
end
|
||||||
|
|
||||||
function love.textinput(t)
|
function love.textinput(t)
|
||||||
-- forward text input to SUIT
|
-- forward text input to SUIT
|
||||||
suit.textinput(t)
|
suit.textinput(t)
|
||||||
|
|
21
core.lua
21
core.lua
|
@ -48,15 +48,29 @@ function suit:wasHovered(id)
|
||||||
return id == self.hovered_last
|
return id == self.hovered_last
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function suit:anyActive()
|
||||||
|
return self.active ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
function suit:isActive(id)
|
function suit:isActive(id)
|
||||||
return id == self.active
|
return id == self.active
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function suit:anyHit()
|
||||||
|
return self.hit ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function suit:isHit(id)
|
||||||
|
return id == self.hit
|
||||||
|
end
|
||||||
|
|
||||||
function suit:getStateName(id)
|
function suit:getStateName(id)
|
||||||
if self:isActive(id) then
|
if self:isActive(id) then
|
||||||
return "active"
|
return "active"
|
||||||
elseif self:isHovered(id) then
|
elseif self:isHovered(id) then
|
||||||
return "hovered"
|
return "hovered"
|
||||||
|
elseif self:isHit(id) then
|
||||||
|
return "hit"
|
||||||
end
|
end
|
||||||
return "normal"
|
return "normal"
|
||||||
end
|
end
|
||||||
|
@ -84,7 +98,11 @@ function suit:registerHitbox(id, x,y,w,h)
|
||||||
end
|
end
|
||||||
|
|
||||||
function suit:mouseReleasedOn(id)
|
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
|
end
|
||||||
|
|
||||||
function suit:updateMouse(x, y, button_down)
|
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:updateMouse(love.mouse.getX(), love.mouse.getY(), love.mouse.isDown(1))
|
||||||
self.key_down, self.textchar = nil, ""
|
self.key_down, self.textchar = nil, ""
|
||||||
self:grabKeyboardFocus(NONE)
|
self:grabKeyboardFocus(NONE)
|
||||||
|
self.hit = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function suit:exitFrame()
|
function suit:exitFrame()
|
||||||
|
|
|
@ -46,6 +46,56 @@ Forwards a ``love.keypressed(key)`` event to SUIT.
|
||||||
Forwards a ``love.textinput(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
|
Internal Helpers
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
@ -73,36 +123,6 @@ Prepares GUI state when entering a frame.
|
||||||
|
|
||||||
Clears GUI state when exiting 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
|
Mouse Input
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
|
7
init.lua
7
init.lua
|
@ -5,6 +5,8 @@ local suit = require(BASE .. "core")
|
||||||
|
|
||||||
local instance = suit.new()
|
local instance = suit.new()
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
|
_instance = instance,
|
||||||
|
|
||||||
new = suit.new,
|
new = suit.new,
|
||||||
getOptionsAndSize = suit.getOptionsAndSize,
|
getOptionsAndSize = suit.getOptionsAndSize,
|
||||||
|
|
||||||
|
@ -12,7 +14,10 @@ return setmetatable({
|
||||||
anyHovered = function(...) return instance:anyHovered(...) end,
|
anyHovered = function(...) return instance:anyHovered(...) end,
|
||||||
isHovered = function(...) return instance:isHovered(...) end,
|
isHovered = function(...) return instance:isHovered(...) end,
|
||||||
wasHovered = function(...) return instance:wasHovered(...) end,
|
wasHovered = function(...) return instance:wasHovered(...) end,
|
||||||
|
anyActive = function(...) return instance:anyActive(...) end,
|
||||||
isActive = function(...) return instance:isActive(...) 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,
|
mouseInRect = function(...) return instance:mouseInRect(...) end,
|
||||||
registerHitbox = function(...) return instance:registerHitbox(...) end,
|
registerHitbox = function(...) return instance:registerHitbox(...) end,
|
||||||
|
@ -49,7 +54,7 @@ return setmetatable({
|
||||||
if k == "theme" then
|
if k == "theme" then
|
||||||
instance.theme = v
|
instance.theme = v
|
||||||
else
|
else
|
||||||
rawset(t, k, v)
|
rawset(instance, k, v)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
__index = function(t, k)
|
__index = function(t, k)
|
||||||
|
|
|
@ -52,10 +52,10 @@ return function(core, input, ...)
|
||||||
opt.state = core:registerHitbox(opt.id, x,y,w,h)
|
opt.state = core:registerHitbox(opt.id, x,y,w,h)
|
||||||
opt.hasKeyboardFocus = core:grabKeyboardFocus(opt.id)
|
opt.hasKeyboardFocus = core:grabKeyboardFocus(opt.id)
|
||||||
|
|
||||||
if opt.hasKeyboardFocus then
|
if (input.candidate_text.text == "") and opt.hasKeyboardFocus then
|
||||||
local keycode,char = core:getPressedKey()
|
local keycode,char = core:getPressedKey()
|
||||||
-- text input
|
-- text input
|
||||||
if char ~= "" then
|
if char and char ~= "" then
|
||||||
local a,b = split(input.text, input.cursor)
|
local a,b = split(input.text, input.cursor)
|
||||||
input.text = table.concat{a, char, b}
|
input.text = table.concat{a, char, b}
|
||||||
input.cursor = input.cursor + utf8.len(char)
|
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
|
return self._x, self._y + self._h + self._pady
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Layout.nextDown = Layout.nextRow
|
||||||
|
|
||||||
|
function Layout:nextUp()
|
||||||
|
return self._x, self._y - self._h - self._pady
|
||||||
|
end
|
||||||
|
|
||||||
function Layout:nextCol()
|
function Layout:nextCol()
|
||||||
return self._x + self._w + self._padx, self._y
|
return self._x + self._w + self._padx, self._y
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Layout.nextRight = Layout.nextCol
|
||||||
|
|
||||||
|
function Layout:nextLeft()
|
||||||
|
return self._x - self._w - self._padx, self._y
|
||||||
|
end
|
||||||
|
|
||||||
function Layout:push(x,y)
|
function Layout:push(x,y)
|
||||||
self._stack[#self._stack+1] = {
|
self._stack[#self._stack+1] = {
|
||||||
self._x, self._y,
|
self._x, self._y,
|
||||||
|
@ -59,6 +71,7 @@ function Layout:pop()
|
||||||
self._w, self._h,
|
self._w, self._h,
|
||||||
self._widths, self._heights = unpack(self._stack[#self._stack])
|
self._widths, self._heights = unpack(self._stack[#self._stack])
|
||||||
self._isFirstCell = false
|
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)
|
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
|
return x,y,w,h
|
||||||
end
|
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)
|
function Layout:col(w, h)
|
||||||
w,h = calc_width_height(self, w, h)
|
w,h = calc_width_height(self, w, h)
|
||||||
|
|
||||||
|
@ -150,6 +179,22 @@ function Layout:col(w, h)
|
||||||
return x,y,w,h
|
return x,y,w,h
|
||||||
end
|
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)
|
local function layout_iterator(t, idx)
|
||||||
idx = (idx or 1) + 1
|
idx = (idx or 1) + 1
|
||||||
|
@ -323,6 +368,10 @@ return setmetatable({
|
||||||
pop = function(...) return instance:pop(...) end,
|
pop = function(...) return instance:pop(...) end,
|
||||||
row = function(...) return instance:row(...) end,
|
row = function(...) return instance:row(...) end,
|
||||||
col = function(...) return instance:col(...) 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,
|
rows = function(...) return instance:rows(...) end,
|
||||||
cols = function(...) return instance:cols(...) end,
|
cols = function(...) return instance:cols(...) end,
|
||||||
}, {__call = function(_,...) return Layout.new(...) 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)
|
function theme.drawBox(x,y,w,h, colors, cornerRadius)
|
||||||
local colors = colors or theme.getColorForState(opt)
|
local colors = colors or theme.getColorForState(opt)
|
||||||
cornerRadius = cornerRadius or theme.cornerRadius
|
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.setColor(colors.bg)
|
||||||
love.graphics.rectangle('fill', x,y, w,h, cornerRadius)
|
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)
|
local c = theme.getColorForState(opt)
|
||||||
theme.drawBox(x,y,w,h, c, opt.cornerRadius)
|
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
|
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)
|
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.setFont(opt.font)
|
||||||
love.graphics.print(input.text, x, y+(h-th)/2)
|
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
|
-- cursor
|
||||||
if opt.hasKeyboardFocus and (love.timer.getTime() % 1) > .5 then
|
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.setLineWidth(1)
|
||||||
love.graphics.setLineStyle('rough')
|
love.graphics.setLineStyle('rough')
|
||||||
love.graphics.line(x + opt.cursor_pos, y + (h-th)/2,
|
love.graphics.line(x + opt.cursor_pos + ws, y + (h-th)/2,
|
||||||
x + opt.cursor_pos, y + (h+th)/2)
|
x + opt.cursor_pos + ws, y + (h+th)/2)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- reset scissor
|
-- reset scissor
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue