Fix #17: Support multiple instances
This commit is contained in:
parent
f77ab8e5e8
commit
aca8a297bb
14 changed files with 385 additions and 244 deletions
15
button.lua
15
button.lua
|
@ -1,9 +1,8 @@
|
||||||
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
||||||
|
|
||||||
local BASE = (...):match('(.-)[^%.]+$')
|
local BASE = (...):match('(.-)[^%.]+$')
|
||||||
local core = require(BASE .. 'core')
|
|
||||||
|
|
||||||
return function(text, ...)
|
return function(core, text, ...)
|
||||||
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
||||||
opt.id = opt.id or text
|
opt.id = opt.id or text
|
||||||
opt.font = opt.font or love.graphics.getFont()
|
opt.font = opt.font or love.graphics.getFont()
|
||||||
|
@ -11,14 +10,14 @@ return function(text, ...)
|
||||||
w = w or opt.font:getWidth(text) + 4
|
w = w or opt.font:getWidth(text) + 4
|
||||||
h = h or opt.font:getHeight() + 4
|
h = h or opt.font:getHeight() + 4
|
||||||
|
|
||||||
core.registerHitbox(opt.id, x,y,w,h)
|
opt.state = core:registerHitbox(opt.id, x,y,w,h)
|
||||||
core.registerDraw(core.theme.Button, text, opt, x,y,w,h)
|
core:registerDraw(core.theme.Button, text, opt, x,y,w,h)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id = opt.id,
|
id = opt.id,
|
||||||
hit = core.mouseReleasedOn(opt.id),
|
hit = core:mouseReleasedOn(opt.id),
|
||||||
hovered = core.isHot(opt.id),
|
hovered = core:isHovered(opt.id),
|
||||||
entered = core.isHot(opt.id) and not core.wasHot(opt.id),
|
entered = core:isHovered(opt.id) and not core:wasHovered(opt.id),
|
||||||
left = not core.isHot(opt.id) and core.wasHot(opt.id)
|
left = not core:isHovered(opt.id) and core:wasHovered(opt.id)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
15
checkbox.lua
15
checkbox.lua
|
@ -1,9 +1,8 @@
|
||||||
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
||||||
|
|
||||||
local BASE = (...):match('(.-)[^%.]+$')
|
local BASE = (...):match('(.-)[^%.]+$')
|
||||||
local core = require(BASE .. 'core')
|
|
||||||
|
|
||||||
return function(checkbox, ...)
|
return function(core, checkbox, ...)
|
||||||
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
||||||
opt.id = opt.id or checkbox
|
opt.id = opt.id or checkbox
|
||||||
opt.font = opt.font or love.graphics.getFont()
|
opt.font = opt.font or love.graphics.getFont()
|
||||||
|
@ -11,18 +10,18 @@ return function(checkbox, ...)
|
||||||
w = w or (opt.font:getWidth(checkbox.text) + opt.font:getHeight() + 4)
|
w = w or (opt.font:getWidth(checkbox.text) + opt.font:getHeight() + 4)
|
||||||
h = h or opt.font:getHeight() + 4
|
h = h or opt.font:getHeight() + 4
|
||||||
|
|
||||||
core.registerHitbox(opt.id, x,y,w,h)
|
opt.state = core:registerHitbox(opt.id, x,y,w,h)
|
||||||
local hit = core.mouseReleasedOn(opt.id)
|
local hit = core:mouseReleasedOn(opt.id)
|
||||||
if hit then
|
if hit then
|
||||||
checkbox.checked = not checkbox.checked
|
checkbox.checked = not checkbox.checked
|
||||||
end
|
end
|
||||||
core.registerDraw(core.theme.Checkbox, checkbox, opt, x,y,w,h)
|
core:registerDraw(core.theme.Checkbox, checkbox, opt, x,y,w,h)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id = opt.id,
|
id = opt.id,
|
||||||
hit = hit,
|
hit = hit,
|
||||||
hovered = core.isHot(opt.id),
|
hovered = core:isHovered(opt.id),
|
||||||
entered = core.isHot(opt.id) and not core.wasHot(opt.id),
|
entered = core:isHovered(opt.id) and not core:wasHovered(opt.id),
|
||||||
left = not core.isHot(opt.id) and core.wasHot(opt.id)
|
left = not core:isHovered(opt.id) and core:wasHovered(opt.id)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
191
core.lua
191
core.lua
|
@ -1,10 +1,34 @@
|
||||||
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
||||||
|
|
||||||
|
local NONE = {}
|
||||||
local BASE = (...):match('(.-)[^%.]+$')
|
local BASE = (...):match('(.-)[^%.]+$')
|
||||||
local theme = require(BASE..'theme')
|
local default_theme = require(BASE..'theme')
|
||||||
|
|
||||||
|
local suit = {}
|
||||||
|
suit.__index = suit
|
||||||
|
|
||||||
|
function suit.new(theme)
|
||||||
|
return setmetatable({
|
||||||
|
-- TODO: deep copy/copy on write? better to let user handle => documentation?
|
||||||
|
theme = theme or default_theme,
|
||||||
|
mouse_x = 0, mouse_y = 0,
|
||||||
|
mouse_button_down = false,
|
||||||
|
|
||||||
|
draw_queue = {n = 0},
|
||||||
|
|
||||||
|
Button = require(BASE.."button"),
|
||||||
|
ImageButton = require(BASE.."imagebutton"),
|
||||||
|
Label = require(BASE.."label"),
|
||||||
|
Checkbox = require(BASE.."checkbox"),
|
||||||
|
Input = require(BASE.."input"),
|
||||||
|
Slider = require(BASE.."slider"),
|
||||||
|
|
||||||
|
layout = require(BASE.."layout").new(),
|
||||||
|
}, suit)
|
||||||
|
end
|
||||||
|
|
||||||
-- helper
|
-- helper
|
||||||
local function getOptionsAndSize(opt, ...)
|
function suit.getOptionsAndSize(opt, ...)
|
||||||
if type(opt) == "table" then
|
if type(opt) == "table" then
|
||||||
return opt, ...
|
return opt, ...
|
||||||
end
|
end
|
||||||
|
@ -12,75 +36,83 @@ local function getOptionsAndSize(opt, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- gui state
|
-- gui state
|
||||||
local hot, hot_last, active
|
function suit:anyHovered()
|
||||||
local NONE = {}
|
return self.hovered ~= nil
|
||||||
|
|
||||||
local function anyHot()
|
|
||||||
return hot ~= nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function isHot(id)
|
function suit:isHovered(id)
|
||||||
return id == hot
|
return id == self.hovered
|
||||||
end
|
end
|
||||||
|
|
||||||
local function wasHot(id)
|
function suit:wasHovered(id)
|
||||||
return id == hot_last
|
return id == self.hovered_last
|
||||||
end
|
end
|
||||||
|
|
||||||
local function isActive(id)
|
function suit:isActive(id)
|
||||||
return id == active
|
return id == self.active
|
||||||
|
end
|
||||||
|
|
||||||
|
function suit:getStateName(id)
|
||||||
|
if self:isActive(id) then
|
||||||
|
return "active"
|
||||||
|
elseif self:isHovered(id) then
|
||||||
|
return "hovered"
|
||||||
|
end
|
||||||
|
return "normal"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- mouse handling
|
-- mouse handling
|
||||||
local mouse_x, mouse_y, mouse_button_down = 0,0, false
|
function suit:mouseInRect(x,y,w,h)
|
||||||
local function mouseInRect(x,y,w,h)
|
return self.mouse_x >= x and self.mouse_y >= y and
|
||||||
return not (mouse_x < x or mouse_y < y or mouse_x > x+w or mouse_y > y+h)
|
self.mouse_x <= x+w and self.mouse_y > y+h
|
||||||
end
|
end
|
||||||
|
|
||||||
local function registerMouseHit(id, ul_x, ul_y, hit)
|
function suit:registerMouseHit(id, ul_x, ul_y, hit)
|
||||||
if hit(mouse_x - ul_x, mouse_y - ul_y) then
|
if hit(self.mouse_x - ul_x, self.mouse_y - ul_y) then
|
||||||
hot = id
|
self.hovered = id
|
||||||
if active == nil and mouse_button_down then
|
if self.active == nil and self.mouse_button_down then
|
||||||
active = id
|
self.active = id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self:getStateName(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function registerHitbox(id, x,y,w,h)
|
function suit:registerHitbox(id, x,y,w,h)
|
||||||
return registerMouseHit(id, x,y, function(x,y)
|
return self:registerMouseHit(id, x,y, function(x,y)
|
||||||
return x >= 0 and x <= w and y >= 0 and y <= h
|
return x >= 0 and x <= w and y >= 0 and y <= h
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function mouseReleasedOn(id)
|
function suit:mouseReleasedOn(id)
|
||||||
return not mouse_button_down and isActive(id) and isHot(id)
|
return not self.mouse_button_down and self:isActive(id) and self:isHovered(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function updateMouse(x, y, button_down)
|
function suit:updateMouse(x, y, button_down)
|
||||||
mouse_x, mouse_y, mouse_button_down = x,y, button_down
|
self.mouse_x, self.mouse_y = x,y
|
||||||
|
if button_down ~= nil then
|
||||||
|
self.mouse_button_down = button_down
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getMousePosition()
|
function suit:getMousePosition()
|
||||||
return mouse_x, mouse_y
|
return self.mouse_x, self.mouse_y
|
||||||
end
|
end
|
||||||
|
|
||||||
-- keyboard handling
|
-- keyboard handling
|
||||||
local key_down, textchar, keyboardFocus
|
function suit:getPressedKey()
|
||||||
local function getPressedKey()
|
return self.key_down, self.textchar
|
||||||
return key_down, textchar
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function keypressed(key)
|
function suit:keypressed(key)
|
||||||
key_down = key
|
self.key_down = key
|
||||||
end
|
end
|
||||||
|
|
||||||
local function textinput(char)
|
function suit:textinput(char)
|
||||||
textchar = char
|
self.textchar = char
|
||||||
end
|
end
|
||||||
|
|
||||||
local function grabKeyboardFocus(id)
|
function suit:grabKeyboardFocus(id)
|
||||||
if isActive(id) then
|
if self:isActive(id) then
|
||||||
keyboardFocus = id
|
|
||||||
if love.system.getOS() == "Android" or love.system.getOS() == "iOS" then
|
if love.system.getOS() == "Android" or love.system.getOS() == "iOS" then
|
||||||
if id == NONE then
|
if id == NONE then
|
||||||
love.keyboard.setTextInput( false )
|
love.keyboard.setTextInput( false )
|
||||||
|
@ -88,81 +120,52 @@ local function grabKeyboardFocus(id)
|
||||||
love.keyboard.setTextInput( true )
|
love.keyboard.setTextInput( true )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
self.keyboardFocus = id
|
||||||
end
|
end
|
||||||
|
return self:hasKeyboardFocus(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function hasKeyboardFocus(id)
|
function suit:hasKeyboardFocus(id)
|
||||||
return keyboardFocus == id
|
return self.keyboardFocus == id
|
||||||
end
|
end
|
||||||
|
|
||||||
local function keyPressedOn(id, key)
|
function suit:keyPressedOn(id, key)
|
||||||
return hasKeyboardFocus(id) and key_down == key
|
return self:hasKeyboardFocus(id) and self.key_down == key
|
||||||
end
|
end
|
||||||
|
|
||||||
-- state update
|
-- state update
|
||||||
local function enterFrame()
|
function suit:enterFrame()
|
||||||
hot_last, hot = hot, nil
|
self.hovered_last, self.hovered = self.hovered, nil
|
||||||
updateMouse(love.mouse.getX(), love.mouse.getY(), love.mouse.isDown(1))
|
self:updateMouse(love.mouse.getX(), love.mouse.getY(), love.mouse.isDown(1))
|
||||||
key_down, textchar = nil, ""
|
self.key_down, self.textchar = nil, ""
|
||||||
grabKeyboardFocus(NONE)
|
self:grabKeyboardFocus(NONE)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function exitFrame()
|
function suit:exitFrame()
|
||||||
if not mouse_button_down then
|
if not self.mouse_button_down then
|
||||||
active = nil
|
self.active = nil
|
||||||
elseif active == nil then
|
elseif self.active == nil then
|
||||||
active = NONE
|
self.active = NONE
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- draw
|
-- draw
|
||||||
local draw_queue = {n = 0}
|
function suit:registerDraw(f, ...)
|
||||||
|
|
||||||
local function registerDraw(f, ...)
|
|
||||||
local args = {...}
|
local args = {...}
|
||||||
local nargs = select('#', ...)
|
local nargs = select('#', ...)
|
||||||
draw_queue.n = draw_queue.n + 1
|
self.draw_queue.n = self.draw_queue.n + 1
|
||||||
draw_queue[draw_queue.n] = function()
|
self.draw_queue[self.draw_queue.n] = function()
|
||||||
f(unpack(args, 1, nargs))
|
f(unpack(args, 1, nargs))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function draw()
|
function suit:draw()
|
||||||
exitFrame()
|
self:exitFrame()
|
||||||
for i = 1,draw_queue.n do
|
for i = 1,self.draw_queue.n do
|
||||||
draw_queue[i]()
|
self.draw_queue[i]()
|
||||||
end
|
end
|
||||||
draw_queue.n = 0
|
self.draw_queue.n = 0
|
||||||
enterFrame()
|
self:enterFrame()
|
||||||
end
|
end
|
||||||
|
|
||||||
local module = {
|
return suit
|
||||||
getOptionsAndSize = getOptionsAndSize,
|
|
||||||
|
|
||||||
anyHot = anyHot,
|
|
||||||
isHot = isHot,
|
|
||||||
wasHot = wasHot,
|
|
||||||
isActive = isActive,
|
|
||||||
|
|
||||||
mouseInRect = mouseInRect,
|
|
||||||
registerHitbox = registerHitbox,
|
|
||||||
registerMouseHit = registerMouseHit,
|
|
||||||
mouseReleasedOn = mouseReleasedOn,
|
|
||||||
updateMouse = updateMouse,
|
|
||||||
getMousePosition = getMousePosition,
|
|
||||||
|
|
||||||
getPressedKey = getPressedKey,
|
|
||||||
keypressed = keypressed,
|
|
||||||
textinput = textinput,
|
|
||||||
grabKeyboardFocus = grabKeyboardFocus,
|
|
||||||
hasKeyboardFocus = hasKeyboardFocus,
|
|
||||||
keyPressedOn = keyPressedOn,
|
|
||||||
|
|
||||||
enterFrame = enterFrame,
|
|
||||||
exitFrame = exitFrame,
|
|
||||||
registerDraw = registerDraw,
|
|
||||||
theme = theme,
|
|
||||||
draw = draw,
|
|
||||||
}
|
|
||||||
theme.core = module
|
|
||||||
return module
|
|
||||||
|
|
|
@ -76,23 +76,23 @@ Clears GUI state when exiting a frame.
|
||||||
GUI State
|
GUI State
|
||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|
|
||||||
.. function:: anyHot()
|
.. function:: anyHovered()
|
||||||
|
|
||||||
:returns: ``true`` if any widget is in the ``hot`` state.
|
:returns: ``true`` if any widget is hovered by the mouse.
|
||||||
|
|
||||||
Checks if any widget is in the hot state
|
Checks if any widget is hovered by the mouse.
|
||||||
|
|
||||||
.. function:: isHot(id)
|
.. function:: isHovered(id)
|
||||||
|
|
||||||
:param mixed id: Identifier of the widget.
|
:param mixed id: Identifier of the widget.
|
||||||
:returns: ``true`` if the widget is in the ``hot`` state.
|
:returns: ``true`` if the widget is hovered by the mouse.
|
||||||
|
|
||||||
Checks if the widget identified by ``id`` is hovered by the mouse.
|
Checks if the widget identified by ``id`` is hovered by the mouse.
|
||||||
|
|
||||||
.. function:: wasHot(id)
|
.. function:: wasHovered(id)
|
||||||
|
|
||||||
:param mixed id: Identifier of the widget.
|
:param mixed id: Identifier of the widget.
|
||||||
:returns: ``true`` if the widget was in the ``hot`` state in the last frame.
|
: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.
|
Checks if the widget identified by ``id`` was hovered by the mouse in the last frame.
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ Checks whether the mouse cursor is in the rectangle defined by ``x,y,w,h``.
|
||||||
:param function hit: Function to perform the hit test.
|
:param function hit: Function to perform the hit test.
|
||||||
|
|
||||||
Registers a hit-test defined by the function ``hit`` for the widget identified
|
Registers a hit-test defined by the function ``hit`` for the widget identified
|
||||||
by ``id``. Sets the widget to ``hot`` if th hit-test returns ``true``. Sets the
|
by ``id``. Sets the widget to ``hovered`` if th hit-test returns ``true``. Sets the
|
||||||
widget to ``active`` if the hit-test returns ``true`` and the mouse button is
|
widget to ``active`` if the hit-test returns ``true`` and the mouse button is
|
||||||
pressed.
|
pressed.
|
||||||
|
|
||||||
|
@ -186,3 +186,36 @@ Checks whether the widget identified by ``id`` currently has keyboard focus.
|
||||||
Checks whether the key ``key`` was pressed while the widget identified by
|
Checks whether the key ``key`` was pressed while the widget identified by
|
||||||
``id`` has keyboard focus.
|
``id`` has keyboard focus.
|
||||||
|
|
||||||
|
|
||||||
|
Instancing
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. function:: new()
|
||||||
|
|
||||||
|
:returns: Separate UI state.
|
||||||
|
|
||||||
|
Create a separate UI and layout state. Everything that happens in the new
|
||||||
|
state will not affect any other state. You can use the new state like the
|
||||||
|
"global" state ``suit``, but call functions with the colon syntax instead of
|
||||||
|
the dot syntax, e.g.::
|
||||||
|
|
||||||
|
function love.load()
|
||||||
|
dress = suit.new()
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.update()
|
||||||
|
dress.layout:reset()
|
||||||
|
dress:Label("Hello, World!", dress.layout:row(200,30))
|
||||||
|
dress:Input(input, dress.layout:row())
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.draw()
|
||||||
|
dress:draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Unlike UI and layout state, the theme might be shared with other states.
|
||||||
|
Changes in a shared theme will be shared across all themes.
|
||||||
|
See the :ref:`Instance Theme <instance-theme>` subsection in the
|
||||||
|
:doc:`gettingstarted` guide.
|
||||||
|
|
|
@ -93,7 +93,7 @@ draw it in ``love.draw()``::
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.draw()
|
function love.draw()
|
||||||
suit.core.draw()
|
suit.draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
This will produce this UI (after clicking the button):
|
This will produce this UI (after clicking the button):
|
||||||
|
@ -159,11 +159,11 @@ and ``textinput`` events to SUIT::
|
||||||
|
|
||||||
-- forward keyboard events
|
-- forward keyboard events
|
||||||
function love.textinput(t)
|
function love.textinput(t)
|
||||||
suit.core.textinput(t)
|
suit.textinput(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.keypressed(key)
|
function love.keypressed(key)
|
||||||
suit.core.keypressed(key)
|
suit.keypressed(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
.. image:: _static/keyboard.gif
|
.. image:: _static/keyboard.gif
|
||||||
|
@ -193,33 +193,34 @@ The first example can be written as follows::
|
||||||
function love.update(dt)
|
function love.update(dt)
|
||||||
-- put the layout origin at position (100,100)
|
-- put the layout origin at position (100,100)
|
||||||
-- cells will grow down and to the right of the origin
|
-- cells will grow down and to the right of the origin
|
||||||
suit.layout.reset(100,100)
|
-- note the colon syntax
|
||||||
|
suit.layout:reset(100,100)
|
||||||
|
|
||||||
-- put 10 extra pixels between cells in each direction
|
-- put 10 extra pixels between cells in each direction
|
||||||
suit.layout.padding(10,10)
|
suit.layout:padding(10,10)
|
||||||
|
|
||||||
-- construct a cell of size 300x30 px and put the button into it
|
-- construct a cell of size 300x30 px and put the button into it
|
||||||
if suit.Button("Hello, World!", suit.layout.row(300,30)).hit then
|
if suit.Button("Hello, World!", suit.layout:row(300,30)).hit then
|
||||||
show_message = true
|
show_message = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- add another cell below the first cell
|
-- add another cell below the first cell
|
||||||
-- the size of the cell is the same as the first cell
|
-- the size of the cell is the same as the first cell
|
||||||
if show_message then
|
if show_message then
|
||||||
suit.Label("How are you today?", suit.layout.row())
|
suit.Label("How are you today?", suit.layout:row())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.draw()
|
function love.draw()
|
||||||
suit.core.draw()
|
suit.draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
.. image:: _static/layout.gif
|
.. image:: _static/layout.gif
|
||||||
|
|
||||||
At the beginning of each frame, the layout origin (and some internal layout
|
At the beginning of each frame, the layout origin (and some internal layout
|
||||||
state) has to be reset. You can also define optional padding between cells.
|
state) has to be reset. You can also define optional padding between cells.
|
||||||
Cells are added using ``layout.row(w,h)`` (which puts the new cell below the
|
Cells are added using ``layout:row(w,h)`` (which puts the new cell below the
|
||||||
old cell) and ``layout.col(w,h)`` (which puts the new cell to the right of the
|
old cell) and ``layout:col(w,h)`` (which puts the new cell to the right of the
|
||||||
old cell). If omitted, the width and height of the new cell are copied from
|
old cell). If omitted, the width and height of the new cell are copied from
|
||||||
the old cell. There are also special identifiers that calculate the size from
|
the old cell. There are also special identifiers that calculate the size from
|
||||||
the sizes of all cells that were created since the last ``reset()``: ``max``,
|
the sizes of all cells that were created since the last ``reset()``: ``max``,
|
||||||
|
@ -235,31 +236,90 @@ Themeing
|
||||||
|
|
||||||
SUIT lets you customize how any widget (except :func:`ImageButton`) is drawn.
|
SUIT lets you customize how any widget (except :func:`ImageButton`) is drawn.
|
||||||
Each widget (except, :func:`you know <ImageButton>`) is drawn by a function in
|
Each widget (except, :func:`you know <ImageButton>`) is drawn by a function in
|
||||||
the table ``suit.core.theme``. Conveniently, the name of the function
|
the table ``suit.theme``. Conveniently, the name of the function
|
||||||
responsible for drawing a widget is named after it, so, a button is drawn by
|
responsible for drawing a widget is named after it, so, a button is drawn by
|
||||||
the function ``suit.core.theme.Button``. If you want to change how a button is
|
the function ``suit.theme.Button``. If you want to change how a button is
|
||||||
drawn, simply overwrite the function. If you want to redecorate completely, it
|
drawn, simply overwrite the function. If you want to redecorate completely, it
|
||||||
might be easiest to start from scratch and swap the whole table.
|
might be easiest to start from scratch and swap the whole table.
|
||||||
|
|
||||||
However, if you just don't like the colors, the default theme is open to change.
|
However, if you just don't like the colors, the default theme is open to change.
|
||||||
It requires you to change the background (``bg``) and foreground (``fg``) color
|
It requires you to change the background (``bg``) and foreground (``fg``) color
|
||||||
of three possible widget states: ``normal``, when nothing out of
|
of three possible widget states: ``normal``, when nothing out of
|
||||||
the ordinary happened, ``hover``, when the mouse hovers above a widget, and
|
the ordinary happened, ``hovered``, when the mouse hovers above a widget, and
|
||||||
``active``, when the mouse hovers above, and the mouse button is pressed (but
|
``active``, when the mouse hovers above, and the mouse button is pressed (but
|
||||||
not yet released) on the widget. The colors are saved in the table
|
not yet released) on the widget. The colors are saved in the table
|
||||||
``suit.core.theme.color``. The default color scheme is this::
|
``suit.theme.color``. The default color scheme is this::
|
||||||
|
|
||||||
suit.core.theme.color = {
|
suit.theme.color = {
|
||||||
normal = {bg = { 66, 66, 66}, fg = {188,188,188}},
|
normal = {bg = { 66, 66, 66}, fg = {188,188,188}},
|
||||||
hover = {bg = { 50,153,187}, fg = {255,255,255}},
|
hovered = {bg = { 50,153,187}, fg = {255,255,255}},
|
||||||
active = {bg = {255,153, 0}, fg = {225,225,225}}
|
active = {bg = {255,153, 0}, fg = {225,225,225}}
|
||||||
}
|
}
|
||||||
|
|
||||||
You can also do minimally invasive surgery::
|
You can also do minimally invasive surgery::
|
||||||
|
|
||||||
function love.load()
|
function love.load()
|
||||||
suit.core.theme.color.normal.fg = {255,255,255}
|
suit.theme.color.normal.fg = {255,255,255}
|
||||||
suit.core.theme.color.hover = {bg = {200,230,255}, fg = {0,0,0}}
|
suit.theme.color.hovered = {bg = {200,230,255}, fg = {0,0,0}}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
GUI Instances
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Sometimes you might feel the need to separate parts of the GUI. Maybe the
|
||||||
|
widgets should have a different theme, maybe certain should always be drawn
|
||||||
|
before or after other UI elements, or maybe you don't want the UI state to
|
||||||
|
"leak" (e.g., from a stacked pause gamestate to the main gamestate).
|
||||||
|
|
||||||
|
For this reason, SUIT allows you to create GUI instances::
|
||||||
|
|
||||||
|
local dress = suit.new()
|
||||||
|
|
||||||
|
The IO and layout state of ``dress`` is totally contained in the instance and
|
||||||
|
does not affect any other instances (including the "global" instance ``suit``).
|
||||||
|
In particular, ``suit.draw()`` will not draw anything from ``dress``. Luckily,
|
||||||
|
you can do that yourself::
|
||||||
|
|
||||||
|
dress:draw()
|
||||||
|
|
||||||
|
Notice that instances require that you use the colon syntax. This is true for
|
||||||
|
every `core <core>` function as well as the widgets. To create a button, for
|
||||||
|
example, you have to write::
|
||||||
|
|
||||||
|
dress:Button("Click?", dress.layout:row())
|
||||||
|
|
||||||
|
.. _instance-theme:
|
||||||
|
|
||||||
|
Instance Theme
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Unlike UI and layout state, themes **are** shared among instances. The reason
|
||||||
|
is that the ``suit.theme`` and ``dress.theme`` are **references**, and point to
|
||||||
|
the same table (unless you make either of them point somewhere else). Usually
|
||||||
|
this is a feature, but please still consider this
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Changes in a shared theme will be shared across GUI instances.
|
||||||
|
|
||||||
|
If this is an issue---for example because you only want to change the color
|
||||||
|
scheme of an instance---you can either `deep-copy
|
||||||
|
<http://hump.readthedocs.org/en/latest/class.html#class:clone>`_ the theme
|
||||||
|
table or use some metatable magic::
|
||||||
|
|
||||||
|
dress.theme = setmetatable({}, {__index = suit.theme})
|
||||||
|
|
||||||
|
-- NOTE: you have to replace the whole color table. E.g., replacing only
|
||||||
|
-- dress.theme.color.normal will also change suit.theme.color.normal!
|
||||||
|
dress.theme.color = {
|
||||||
|
normal = {bg = {188,188,188}, fg = { 66, 66, 66}},
|
||||||
|
hovered = {bg = {255,255,255}, fg = { 50,153,187}},
|
||||||
|
active = {bg = {255,255,255}, fg = {225,153, 0}}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dress.theme.Label(text, opt, x,y,w,h)
|
||||||
|
-- draw the label in a fancier way
|
||||||
end
|
end
|
||||||
|
|
||||||
.. [1] But it thinks you can handle that.
|
.. [1] But it thinks you can handle that.
|
||||||
|
|
|
@ -49,7 +49,7 @@ The following code will create this UI:
|
||||||
-- generate some assets (below)
|
-- generate some assets (below)
|
||||||
function love.load()
|
function love.load()
|
||||||
snd = generateClickySound()
|
snd = generateClickySound()
|
||||||
normal, hover, active = generateImageButton()
|
normal, hovered, active = generateImageButton()
|
||||||
smallerFont = love.graphics.newFont(10)
|
smallerFont = love.graphics.newFont(10)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -63,11 +63,11 @@ The following code will create this UI:
|
||||||
-- put the layout origin at position (100,100)
|
-- put the layout origin at position (100,100)
|
||||||
-- cells will grown down and to the right from this point
|
-- cells will grown down and to the right from this point
|
||||||
-- also set cell padding to 20 pixels to the right and to the bottom
|
-- also set cell padding to 20 pixels to the right and to the bottom
|
||||||
suit.layout.reset(100,100, 20,20)
|
suit.layout:reset(100,100, 20,20)
|
||||||
|
|
||||||
-- put a button at the layout origin
|
-- put a button at the layout origin
|
||||||
-- the cell of the button has a size of 200 by 30 pixels
|
-- the cell of the button has a size of 200 by 30 pixels
|
||||||
state = suit.Button("Click?", suit.layout.row(200,30))
|
state = suit.Button("Click?", suit.layout:row(200,30))
|
||||||
|
|
||||||
-- if the button was entered, play a sound
|
-- if the button was entered, play a sound
|
||||||
if state.entered then love.audio.play(snd) end
|
if state.entered then love.audio.play(snd) end
|
||||||
|
@ -78,67 +78,67 @@ The following code will create this UI:
|
||||||
-- put an input box below the button
|
-- put an input box below the button
|
||||||
-- the cell of the input box has the same size as the cell above
|
-- the cell of the input box has the same size as the cell above
|
||||||
-- if the input cell is submitted, print the text
|
-- if the input cell is submitted, print the text
|
||||||
if suit.Input(input, suit.layout.row()).submitted then
|
if suit.Input(input, suit.layout:row()).submitted then
|
||||||
print(input.text)
|
print(input.text)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- put a button below the input box
|
-- put a button below the input box
|
||||||
-- the width of the cell will be the same as above, the height will be 40 px
|
-- the width of the cell will be the same as above, the height will be 40 px
|
||||||
if suit.Button("Hover?", suit.layout.row(nil,40)).hovered then
|
if suit.Button("Hover?", suit.layout:row(nil,40)).hovered then
|
||||||
-- if the button is hovered, show two other buttons
|
-- if the button is hovered, show two other buttons
|
||||||
-- this will shift all other ui elements down
|
-- this will shift all other ui elements down
|
||||||
|
|
||||||
-- put a button below the previous button
|
-- put a button below the previous button
|
||||||
-- the cell height will be 30 px
|
-- the cell height will be 30 px
|
||||||
-- the label of the button will be aligned top left
|
-- the label of the button will be aligned top left
|
||||||
suit.Button("You can see", {align='left', valign='top'}, suit.layout.row(nil,30))
|
suit.Button("You can see", {align='left', valign='top'}, suit.layout:row(nil,30))
|
||||||
|
|
||||||
-- put a button below the previous button
|
-- put a button below the previous button
|
||||||
-- the cell size will be the same as the one above
|
-- the cell size will be the same as the one above
|
||||||
-- the label will be aligned bottom right
|
-- the label will be aligned bottom right
|
||||||
suit.Button("...but you can't touch!", {align='right', valign='bottom'},
|
suit.Button("...but you can't touch!", {align='right', valign='bottom'},
|
||||||
suit.layout.row())
|
suit.layout:row())
|
||||||
end
|
end
|
||||||
|
|
||||||
-- put a checkbox below the button
|
-- put a checkbox below the button
|
||||||
-- the size will be the same as above
|
-- the size will be the same as above
|
||||||
-- (NOTE: height depends on whether "Hover?" is hovered)
|
-- (NOTE: height depends on whether "Hover?" is hovered)
|
||||||
-- the label "Check?" will be aligned right
|
-- the label "Check?" will be aligned right
|
||||||
suit.Checkbox(chk, {align='right'}, suit.layout.row())
|
suit.Checkbox(chk, {align='right'}, suit.layout:row())
|
||||||
|
|
||||||
-- put a nested layout
|
-- put a nested layout
|
||||||
-- the size of the cell will be as big as the cell above or as big as the
|
-- the size of the cell will be as big as the cell above or as big as the
|
||||||
-- nested content, whichever is bigger
|
-- nested content, whichever is bigger
|
||||||
suit.layout.push(suit.layout.row())
|
suit.layout:push(suit.layout:row())
|
||||||
|
|
||||||
-- put a slider in the cell
|
-- put a slider in the cell
|
||||||
-- the inner cell will be 160 px wide and 20 px high
|
-- the inner cell will be 160 px wide and 20 px high
|
||||||
suit.Slider(slider, suit.layout.col(160, 20))
|
suit.Slider(slider, suit.layout:col(160, 20))
|
||||||
|
|
||||||
-- put a label that shows the slider value to the right of the slider
|
-- put a label that shows the slider value to the right of the slider
|
||||||
-- the width of the label will be 40 px
|
-- the width of the label will be 40 px
|
||||||
suit.Label(("%.02f"):format(slider.value), suit.layout.col(40))
|
suit.Label(("%.02f"):format(slider.value), suit.layout:col(40))
|
||||||
|
|
||||||
-- close the nested layout
|
-- close the nested layout
|
||||||
suit.layout.pop()
|
suit.layout:pop()
|
||||||
|
|
||||||
-- put an image button below the nested cell
|
-- put an image button below the nested cell
|
||||||
-- the size of the cell will be 200 by 100 px,
|
-- the size of the cell will be 200 by 100 px,
|
||||||
-- but the image may be bigger or smaller
|
-- but the image may be bigger or smaller
|
||||||
-- the button shows the image `normal' when the mouse is outside the image
|
-- the button shows the image `normal' when the mouse is outside the image
|
||||||
-- or above a transparent pixel
|
-- or above a transparent pixel
|
||||||
-- the button shows the image `hover` if the mouse is above an opaque pixel
|
-- the button shows the image `hovered` if the mouse is above an opaque pixel
|
||||||
-- of the image `normal'
|
-- of the image `normal'
|
||||||
-- the button shows the image `active` if the mouse is above an opaque pixel
|
-- the button shows the image `active` if the mouse is above an opaque pixel
|
||||||
-- of the image `normal' and the mouse button is pressed
|
-- of the image `normal' and the mouse button is pressed
|
||||||
suit.ImageButton(normal, {hover = hover, active = active}, suit.layout.row(200,100))
|
suit.ImageButton(normal, {hovered = hovered, active = active}, suit.layout:row(200,100))
|
||||||
|
|
||||||
-- if the checkbox is checked, display a precomputed layout
|
-- if the checkbox is checked, display a precomputed layout
|
||||||
if chk.checked then
|
if chk.checked then
|
||||||
-- the precomputed layout will be 3 rows below each other
|
-- the precomputed layout will be 3 rows below each other
|
||||||
-- the origin of the layout will be at (400,100)
|
-- the origin of the layout will be at (400,100)
|
||||||
-- the minimal height of the layout will be 300 px
|
-- the minimal height of the layout will be 300 px
|
||||||
rows = suit.layout.rows{pos = {400,100}, min_height = 300,
|
rows = suit.layout:rows{pos = {400,100}, min_height = 300,
|
||||||
{200, 30}, -- the first cell will measure 200 by 30 px
|
{200, 30}, -- the first cell will measure 200 by 30 px
|
||||||
{30, 'fill'}, -- the second cell will be 30 px wide and fill the
|
{30, 'fill'}, -- the second cell will be 30 px wide and fill the
|
||||||
-- remaining vertical space between the other cells
|
-- remaining vertical space between the other cells
|
||||||
|
@ -165,17 +165,17 @@ The following code will create this UI:
|
||||||
|
|
||||||
function love.draw()
|
function love.draw()
|
||||||
-- draw the gui
|
-- draw the gui
|
||||||
suit.core.draw()
|
suit.draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.textinput(t)
|
function love.textinput(t)
|
||||||
-- forward text input to SUIT
|
-- forward text input to SUIT
|
||||||
suit.core.textinput(t)
|
suit.textinput(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.keypressed(key)
|
function love.keypressed(key)
|
||||||
-- forward keypressed to SUIT
|
-- forward keypressed to SUIT
|
||||||
suit.core.keypressed(key)
|
suit.keypressed(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- generate assets (see love.load)
|
-- generate assets (see love.load)
|
||||||
|
@ -203,11 +203,11 @@ The following code will create this UI:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local normal, hover, active = love.image.newImageData(200,100), love.image.newImageData(200,100), love.image.newImageData(200,100)
|
local normal, hovered, active = love.image.newImageData(200,100), love.image.newImageData(200,100), love.image.newImageData(200,100)
|
||||||
normal:mapPixel(metaballs(.48, 188,188,188))
|
normal:mapPixel(metaballs(.48, 188,188,188))
|
||||||
hover:mapPixel(metaballs(.46, 50,153,187))
|
hovered:mapPixel(metaballs(.46, 50,153,187))
|
||||||
active:mapPixel(metaballs(.43, 255,153,0))
|
active:mapPixel(metaballs(.43, 255,153,0))
|
||||||
return love.graphics.newImage(normal), love.graphics.newImage(hover), love.graphics.newImage(active)
|
return love.graphics.newImage(normal), love.graphics.newImage(hovered), love.graphics.newImage(active)
|
||||||
end
|
end
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
|
|
|
@ -93,9 +93,9 @@ The specification is a table of tables, where each inner table follows the
|
||||||
convention of :func:`row` and :func:`col`.
|
convention of :func:`row` and :func:`col`.
|
||||||
The result is a layout definition object that can be used to access the cells.
|
The result is a layout definition object that can be used to access the cells.
|
||||||
|
|
||||||
There is almost only one reason to do so: You know the area of your layout in
|
There are almost only two reasons to do so: (1) You know the area of your
|
||||||
advance (say, the screen size), and want certain cells to dynamically fill the
|
layout in advance (say, the screen size), and want certain cells to dynamically
|
||||||
available space.
|
fill the available space; (2) You want to animate the cells.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Unlike immediate mode layouts, predefined layouts **can not be nested**.
|
Unlike immediate mode layouts, predefined layouts **can not be nested**.
|
||||||
|
@ -139,8 +139,7 @@ define the position (upper left corner) of the layout using the ``pos`` keyword:
|
||||||
Layout Definition Objects
|
Layout Definition Objects
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Once constructed, the layout can be executed using a layout definition object
|
Once constructed, the cells can be accessed in two ways:
|
||||||
in two ways:
|
|
||||||
|
|
||||||
- Using iterators::
|
- Using iterators::
|
||||||
|
|
||||||
|
@ -154,6 +153,27 @@ in two ways:
|
||||||
suit.Button("Button 3", definition.cell(3))
|
suit.Button("Button 3", definition.cell(3))
|
||||||
suit.Button("Button 2", definition.cell(2))
|
suit.Button("Button 2", definition.cell(2))
|
||||||
|
|
||||||
|
There is actually a third way: Because layout definitions are just tables, you
|
||||||
|
can access the cells directly::
|
||||||
|
|
||||||
|
local cell = definition[1]
|
||||||
|
suit.Button("Button 1", cell[1], cell[2], cell[3], cell[4])
|
||||||
|
-- or suit.Button("Button 1", unpack(cell))
|
||||||
|
|
||||||
|
This is especially useful if you want to animate the cells, for example with a
|
||||||
|
`tween <http://hump.readthedocs.org/en/latest/timer.html#Timer.tween>`_::
|
||||||
|
|
||||||
|
for i,cell in ipairs(definition)
|
||||||
|
local destination = {[2] = cell[2]} -- save cell y position
|
||||||
|
cell[2] = -cell[4] -- move cell just outside of the screen
|
||||||
|
|
||||||
|
-- let the cells fall into the screen one after another
|
||||||
|
timer.after(i / 10, function()
|
||||||
|
timer.tween(0.7, cell, destination, 'bounce')
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
Constructors
|
Constructors
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ theme.
|
||||||
The argument ``normal`` defines the image of the normal state as well as the
|
The argument ``normal`` defines the image of the normal state as well as the
|
||||||
area of the widget: The button activates when the mouse is over a pixel with
|
area of the widget: The button activates when the mouse is over a pixel with
|
||||||
non-zero alpha value.
|
non-zero alpha value.
|
||||||
You can provide additional ``hover`` and ``active`` images, but the widget area
|
You can provide additional ``hovered`` and ``active`` images, but the widget area
|
||||||
is always computed from the ``normal`` image.
|
is always computed from the ``normal`` image.
|
||||||
|
|
||||||
Note that ``ImageButton`` does not recieve width and height parameters. As
|
Note that ``ImageButton`` does not recieve width and height parameters. As
|
||||||
|
@ -52,11 +52,11 @@ such, it does not necessarily honor the cell size of a :doc:`layout`.
|
||||||
``normal``
|
``normal``
|
||||||
Image for the normal state of the widget. Defaults to widget payload.
|
Image for the normal state of the widget. Defaults to widget payload.
|
||||||
|
|
||||||
``hover``
|
``hovered``
|
||||||
Image for the hot state of the widget. Defaults to ``normal`` if omitted.
|
Image for the hovered state of the widget. Defaults to ``normal`` if omitted.
|
||||||
|
|
||||||
``active``
|
``active``
|
||||||
Image for the active state of the widget. Defaults to ``hover`` if omitted.
|
Image for the active state of the widget. Defaults to ``hovered`` if omitted.
|
||||||
|
|
||||||
Mutable Widgets
|
Mutable Widgets
|
||||||
---------------
|
---------------
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
||||||
|
|
||||||
local BASE = (...):match('(.-)[^%.]+$')
|
local BASE = (...):match('(.-)[^%.]+$')
|
||||||
local core = require(BASE .. 'core')
|
|
||||||
|
|
||||||
return function(normal, ...)
|
return function(core, normal, ...)
|
||||||
local opt, x,y = core.getOptionsAndSize(...)
|
local opt, x,y = core.getOptionsAndSize(...)
|
||||||
opt.normal = normal or opt.normal or opt[1]
|
opt.normal = normal or opt.normal or opt[1]
|
||||||
opt.hover = opt.hover or opt[2] or opt.normal
|
opt.hovered = opt.hovered or opt[2] or opt.normal
|
||||||
opt.active = opt.active or opt[3] or opt.hover
|
opt.active = opt.active or opt[3] or opt.hovered
|
||||||
assert(opt.normal, "Need at least `normal' state image")
|
assert(opt.normal, "Need at least `normal' state image")
|
||||||
opt.id = opt.id or opt.normal
|
opt.id = opt.id or opt.normal
|
||||||
|
|
||||||
core.registerMouseHit(opt.id, x,y, function(u,v)
|
opt.state = core:registerMouseHit(opt.id, x,y, function(u,v)
|
||||||
local id = opt.normal:getData()
|
local id = opt.normal:getData()
|
||||||
assert(id:typeOf("ImageData"), "Can only use uncompressed images")
|
assert(id:typeOf("ImageData"), "Can only use uncompressed images")
|
||||||
u, v = math.floor(u+.5), math.floor(v+.5)
|
u, v = math.floor(u+.5), math.floor(v+.5)
|
||||||
|
@ -23,20 +22,20 @@ return function(normal, ...)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local img = opt.normal
|
local img = opt.normal
|
||||||
if core.isActive(opt.id) then
|
if core:isActive(opt.id) then
|
||||||
img = opt.active
|
img = opt.active
|
||||||
elseif core.isHot(opt.id) then
|
elseif core:isHovered(opt.id) then
|
||||||
img = opt.hover
|
img = opt.hovered
|
||||||
end
|
end
|
||||||
|
|
||||||
core.registerDraw(love.graphics.setColor, 255,255,255)
|
core:registerDraw(love.graphics.setColor, 255,255,255)
|
||||||
core.registerDraw(love.graphics.draw, img, x,y)
|
core:registerDraw(love.graphics.draw, img, x,y)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id = opt.id,
|
id = opt.id,
|
||||||
hit = core.mouseReleasedOn(opt.id),
|
hit = core:mouseReleasedOn(opt.id),
|
||||||
hover = core.isHot(opt.id),
|
hovered = core:isHovered(opt.id),
|
||||||
entered = core.isHot(opt.id) and not core.wasHot(opt.id),
|
entered = core:isHovered(opt.id) and not core:wasHovered(opt.id),
|
||||||
left = not core.isHot(opt.id) and core.wasHot(opt.id)
|
left = not core:isHovered(opt.id) and core:wasHovered(opt.id)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
66
init.lua
66
init.lua
|
@ -1,14 +1,58 @@
|
||||||
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
||||||
|
|
||||||
local BASE = (...) .. '.'
|
local BASE = (...) .. "."
|
||||||
|
local suit = require(BASE .. "core")
|
||||||
|
|
||||||
return {
|
local instance = suit.new()
|
||||||
core = require(BASE .. 'core'),
|
return setmetatable({
|
||||||
layout = require(BASE .. 'layout'),
|
new = suit.new,
|
||||||
Button = require(BASE .. 'button'),
|
getOptionsAndSize = suit.getOptionsAndSize,
|
||||||
ImageButton = require(BASE .. 'imagebutton'),
|
|
||||||
Slider = require(BASE .. 'slider'),
|
-- core functions
|
||||||
Label = require(BASE .. 'label'),
|
anyHovered = function(...) return instance:anyHovered(...) end,
|
||||||
Input = require(BASE .. 'input'),
|
isHovered = function(...) return instance:isHovered(...) end,
|
||||||
Checkbox = require(BASE .. 'checkbox')
|
wasHovered = function(...) return instance:wasHovered(...) end,
|
||||||
}
|
isActive = function(...) return instance:isActive(...) end,
|
||||||
|
|
||||||
|
mouseInRect = function(...) return instance:mouseInRect(...) end,
|
||||||
|
registerHitbox = function(...) return instance:registerHitbox(...) end,
|
||||||
|
registerMouseHit = function(...) return instance:registerMouseHit(...) end,
|
||||||
|
mouseReleasedOn = function(...) return instance:mouseReleasedOn(...) end,
|
||||||
|
updateMouse = function(...) return instance:updateMouse(...) end,
|
||||||
|
getMousePosition = function(...) return instance:getMousePosition(...) end,
|
||||||
|
|
||||||
|
getPressedKey = function(...) return instance:getPressedKey(...) end,
|
||||||
|
keypressed = function(...) return instance:keypressed(...) end,
|
||||||
|
textinput = function(...) return instance:textinput(...) end,
|
||||||
|
grabKeyboardFocus = function(...) return instance:grabKeyboardFocus(...) end,
|
||||||
|
hasKeyboardFocus = function(...) return instance:hasKeyboardFocus(...) end,
|
||||||
|
keyPressedOn = function(...) return instance:keyPressedOn(...) end,
|
||||||
|
|
||||||
|
enterFrame = function(...) return instance:enterFrame(...) end,
|
||||||
|
exitFrame = function(...) return instance:exitFrame(...) end,
|
||||||
|
registerDraw = function(...) return instance:registerDraw(...) end,
|
||||||
|
draw = function(...) return instance:draw(...) end,
|
||||||
|
|
||||||
|
-- widgets
|
||||||
|
Button = function(...) return instance:Button(...) end,
|
||||||
|
ImageButton = function(...) return instance:ImageButton(...) end,
|
||||||
|
Label = function(...) return instance:Label(...) end,
|
||||||
|
Checkbox = function(...) return instance:Checkbox(...) end,
|
||||||
|
Input = function(...) return instance:Input(...) end,
|
||||||
|
Slider = function(...) return instance:Slider(...) end,
|
||||||
|
|
||||||
|
-- layout
|
||||||
|
layout = instance.layout
|
||||||
|
}, {
|
||||||
|
-- theme
|
||||||
|
__newindex = function(t, k, v)
|
||||||
|
if k == "theme" then
|
||||||
|
instance.theme = v
|
||||||
|
else
|
||||||
|
rawset(t, k, v)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
__index = function(t, k)
|
||||||
|
return k == "theme" and instance.theme or rawget(t, k)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
23
input.lua
23
input.lua
|
@ -1,7 +1,6 @@
|
||||||
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
||||||
|
|
||||||
local BASE = (...):match('(.-)[^%.]+$')
|
local BASE = (...):match('(.-)[^%.]+$')
|
||||||
local core = require(BASE .. 'core')
|
|
||||||
local utf8 = require 'utf8'
|
local utf8 = require 'utf8'
|
||||||
|
|
||||||
local function split(str, pos)
|
local function split(str, pos)
|
||||||
|
@ -9,7 +8,7 @@ local function split(str, pos)
|
||||||
return str:sub(1, offset-1), str:sub(offset)
|
return str:sub(1, offset-1), str:sub(offset)
|
||||||
end
|
end
|
||||||
|
|
||||||
return function(input, ...)
|
return function(core, input, ...)
|
||||||
local font = love.graphics.getFont()
|
local font = love.graphics.getFont()
|
||||||
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
||||||
opt.id = opt.id or input
|
opt.id = opt.id or input
|
||||||
|
@ -26,13 +25,11 @@ return function(input, ...)
|
||||||
-- ...
|
-- ...
|
||||||
-- position 6: hello|
|
-- position 6: hello|
|
||||||
|
|
||||||
core.registerHitbox(opt.id, x,y,w,h)
|
opt.state = core:registerHitbox(opt.id, x,y,w,h)
|
||||||
|
opt.hasKeyboardFocus = core:grabKeyboardFocus(opt.id)
|
||||||
core.grabKeyboardFocus(opt.id)
|
|
||||||
opt.hasKeyboardFocus = core.hasKeyboardFocus(opt.id)
|
|
||||||
|
|
||||||
if opt.hasKeyboardFocus then
|
if opt.hasKeyboardFocus then
|
||||||
local keycode,char = core.getPressedKey()
|
local keycode,char = core:getPressedKey()
|
||||||
-- text input
|
-- text input
|
||||||
if char ~= "" then
|
if char ~= "" then
|
||||||
local a,b = split(input.text, input.cursor)
|
local a,b = split(input.text, input.cursor)
|
||||||
|
@ -66,14 +63,14 @@ return function(input, ...)
|
||||||
-- TODO
|
-- TODO
|
||||||
end
|
end
|
||||||
|
|
||||||
core.registerDraw(core.theme.Input, input, opt, x,y,w,h)
|
core:registerDraw(core.theme.Input, input, opt, x,y,w,h)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id = opt.id,
|
id = opt.id,
|
||||||
hit = core.mouseReleasedOn(opt.id),
|
hit = core:mouseReleasedOn(opt.id),
|
||||||
submitted = core.keyPressedOn(opt.id, "return"),
|
submitted = core:keyPressedOn(opt.id, "return"),
|
||||||
hovered = core.isHot(opt.id),
|
hovered = core:isHovered(opt.id),
|
||||||
entered = core.isHot(opt.id) and not core.wasHot(opt.id),
|
entered = core:isHovered(opt.id) and not core:wasHovered(opt.id),
|
||||||
left = not core.isHot(opt.id) and core.wasHot(opt.id)
|
left = not core:isHovered(opt.id) and core:wasHovered(opt.id)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
15
label.lua
15
label.lua
|
@ -1,9 +1,8 @@
|
||||||
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
||||||
|
|
||||||
local BASE = (...):match('(.-)[^%.]+$')
|
local BASE = (...):match('(.-)[^%.]+$')
|
||||||
local core = require(BASE .. 'core')
|
|
||||||
|
|
||||||
return function(text, ...)
|
return function(core, text, ...)
|
||||||
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
||||||
opt.id = opt.id or text
|
opt.id = opt.id or text
|
||||||
opt.font = opt.font or love.graphics.getFont()
|
opt.font = opt.font or love.graphics.getFont()
|
||||||
|
@ -11,14 +10,14 @@ return function(text, ...)
|
||||||
w = w or opt.font:getWidth(text) + 4
|
w = w or opt.font:getWidth(text) + 4
|
||||||
h = h or opt.font:getHeight() + 4
|
h = h or opt.font:getHeight() + 4
|
||||||
|
|
||||||
core.registerHitbox(opt.id, x,y,w,h)
|
opt.state = core:registerHitbox(opt.id, x,y,w,h)
|
||||||
core.registerDraw(core.theme.Label, text, opt, x,y,w,h)
|
core:registerDraw(core.theme.Label, text, opt, x,y,w,h)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id = opt.id,
|
id = opt.id,
|
||||||
hit = core.mouseReleasedOn(opt.id),
|
hit = core:mouseReleasedOn(opt.id),
|
||||||
hovered = core.isHot(opt.id),
|
hovered = core:isHovered(opt.id),
|
||||||
entered = core.isHot(opt.id) and not core.wasHot(opt.id),
|
entered = core:isHovered(opt.id) and not core:wasHovered(opt.id),
|
||||||
left = not core.isHot(opt.id) and core.wasHot(opt.id)
|
left = not core:isHovered(opt.id) and core:wasHovered(opt.id)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
23
slider.lua
23
slider.lua
|
@ -1,9 +1,8 @@
|
||||||
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
-- This file is part of SUIT, copyright (c) 2016 Matthias Richter
|
||||||
|
|
||||||
local BASE = (...):match('(.-)[^%.]+$')
|
local BASE = (...):match('(.-)[^%.]+$')
|
||||||
local core = require(BASE .. 'core')
|
|
||||||
|
|
||||||
return function(info, ...)
|
return function(core, info, ...)
|
||||||
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
||||||
|
|
||||||
opt.id = opt.id or info
|
opt.id = opt.id or info
|
||||||
|
@ -14,11 +13,11 @@ return function(info, ...)
|
||||||
local fraction = (info.value - info.min) / (info.max - info.min)
|
local fraction = (info.value - info.min) / (info.max - info.min)
|
||||||
local value_changed = false
|
local value_changed = false
|
||||||
|
|
||||||
core.registerHitbox(opt.id, x,y,w,h)
|
opt.state = core:registerHitbox(opt.id, x,y,w,h)
|
||||||
|
|
||||||
if core.isActive(opt.id) then
|
if core:isActive(opt.id) then
|
||||||
-- mouse update
|
-- mouse update
|
||||||
local mx,my = core.getMousePosition()
|
local mx,my = core:getMousePosition()
|
||||||
if opt.vertical then
|
if opt.vertical then
|
||||||
fraction = math.min(1, math.max(0, (y+h - my) / h))
|
fraction = math.min(1, math.max(0, (y+h - my) / h))
|
||||||
else
|
else
|
||||||
|
@ -33,23 +32,23 @@ return function(info, ...)
|
||||||
-- keyboard update
|
-- keyboard update
|
||||||
local key_up = opt.vertical and 'up' or 'right'
|
local key_up = opt.vertical and 'up' or 'right'
|
||||||
local key_down = opt.vertical and 'down' or 'left'
|
local key_down = opt.vertical and 'down' or 'left'
|
||||||
if core.getPressedKey() == key_up then
|
if core:getPressedKey() == key_up then
|
||||||
info.value = math.min(info.max, info.value + info.step)
|
info.value = math.min(info.max, info.value + info.step)
|
||||||
value_changed = true
|
value_changed = true
|
||||||
elseif core.getPressedKey() == key_down then
|
elseif core:getPressedKey() == key_down then
|
||||||
info.value = math.max(info.min, info.value - info.step)
|
info.value = math.max(info.min, info.value - info.step)
|
||||||
value_changed = true
|
value_changed = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
core.registerDraw(core.theme.Slider, fraction, opt, x,y,w,h)
|
core:registerDraw(core.theme.Slider, fraction, opt, x,y,w,h)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id = opt.id,
|
id = opt.id,
|
||||||
hit = core.mouseReleasedOn(opt.id),
|
hit = core:mouseReleasedOn(opt.id),
|
||||||
changed = value_changed,
|
changed = value_changed,
|
||||||
hovered = core.isHot(opt.id),
|
hovered = core:isHovered(opt.id),
|
||||||
entered = core.isHot(opt.id) and not core.wasHot(opt.id),
|
entered = core:isHovered(opt.id) and not core:wasHovered(opt.id),
|
||||||
left = not core.isHot(opt.id) and core.wasHot(opt.id)
|
left = not core:isHovered(opt.id) and core:wasHovered(opt.id)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
25
theme.lua
25
theme.lua
|
@ -6,26 +6,16 @@ local theme = {}
|
||||||
theme.cornerRadius = 4
|
theme.cornerRadius = 4
|
||||||
|
|
||||||
theme.color = {
|
theme.color = {
|
||||||
normal = {bg = { 66, 66, 66}, fg = {188,188,188}},
|
normal = {bg = { 66, 66, 66}, fg = {188,188,188}},
|
||||||
hover = {bg = { 50,153,187}, fg = {255,255,255}},
|
hovered = {bg = { 50,153,187}, fg = {255,255,255}},
|
||||||
active = {bg = {255,153, 0}, fg = {225,225,225}}
|
active = {bg = {255,153, 0}, fg = {225,225,225}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
-- HELPER
|
-- HELPER
|
||||||
function theme.getStateName(id)
|
|
||||||
if theme.core.isActive(id) then
|
|
||||||
return 'active'
|
|
||||||
end
|
|
||||||
if theme.core.isHot(id) then
|
|
||||||
return 'hover'
|
|
||||||
end
|
|
||||||
return 'normal'
|
|
||||||
end
|
|
||||||
|
|
||||||
function theme.getColorForState(opt)
|
function theme.getColorForState(opt)
|
||||||
local s = theme.getStateName(opt.id)
|
local s = opt.state or "normal"
|
||||||
return (opt.color and opt.color[s]) or theme.color[s]
|
return (opt.color and opt.color[opt.state]) or theme.color[s]
|
||||||
end
|
end
|
||||||
|
|
||||||
function theme.drawBox(x,y,w,h, colors)
|
function theme.drawBox(x,y,w,h, colors)
|
||||||
|
@ -96,10 +86,9 @@ 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)
|
theme.drawBox(x,y,w,h, c)
|
||||||
love.graphics.setColor(c.fg)
|
theme.drawBox(x,yb,wb,hb, {bg=c.fg})
|
||||||
love.graphics.rectangle('fill', x,yb,wb,hb, theme.cornerRadius)
|
|
||||||
|
|
||||||
if theme.getStateName(opt.id) ~= "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)
|
||||||
if opt.vertical then
|
if opt.vertical then
|
||||||
love.graphics.circle('fill', x+wb/2, yb, r)
|
love.graphics.circle('fill', x+wb/2, yb, r)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue