Compare commits
17 commits
Author | SHA1 | Date | |
---|---|---|---|
|
07a1731419 | ||
|
45a4b65053 | ||
5b3dcdad9c | |||
|
faa7b93bc3 | ||
|
4aada674ad | ||
2634383ffd | |||
|
28abbc8bd1 | ||
|
1767782603 | ||
|
1781d5eec6 | ||
|
bec02e99c5 | ||
|
0a723777ed | ||
|
2f479ba30a | ||
|
eabad8c554 | ||
|
8ec0e638ce | ||
|
bd3811ec03 | ||
|
7985b38a95 | ||
|
2f3db27769 |
10 changed files with 198 additions and 66 deletions
|
@ -6,6 +6,7 @@ return function(core, text, ...)
|
|||
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
||||
opt.id = opt.id or text
|
||||
opt.font = opt.font or love.graphics.getFont()
|
||||
opt.color = opt.color or core.theme.color
|
||||
|
||||
w = w or opt.font:getWidth(text) + 4
|
||||
h = h or opt.font:getHeight() + 4
|
||||
|
@ -18,6 +19,10 @@ return function(core, text, ...)
|
|||
hit = core:mouseReleasedOn(opt.id),
|
||||
hovered = core:isHovered(opt.id),
|
||||
entered = core:isHovered(opt.id) and not core:wasHovered(opt.id),
|
||||
left = not core:isHovered(opt.id) and core:wasHovered(opt.id)
|
||||
left = not core:isHovered(opt.id) and core:wasHovered(opt.id),
|
||||
x = x,
|
||||
y = y,
|
||||
w = w,
|
||||
h = h
|
||||
}
|
||||
end
|
||||
|
|
19
core.lua
19
core.lua
|
@ -100,7 +100,7 @@ function suit:mouseInRect(x,y,w,h)
|
|||
end
|
||||
|
||||
function suit:registerMouseHit(id, ul_x, ul_y, hit)
|
||||
if hit(self.mouse_x - ul_x, self.mouse_y - ul_y) then
|
||||
if not self.hovered and hit(self.mouse_x - ul_x, self.mouse_y - ul_y) then
|
||||
self.hovered = id
|
||||
if self.active == nil and self.mouse_button_down then
|
||||
self.active = id
|
||||
|
@ -176,15 +176,24 @@ function suit:keyPressedOn(id, key)
|
|||
end
|
||||
|
||||
-- state update
|
||||
function suit:enterFrame()
|
||||
function suit:enterFrame(mouseX, mouseY)
|
||||
if not self.mouse_button_down then
|
||||
self.active = nil
|
||||
elseif self.active == nil then
|
||||
self.active = NONE
|
||||
end
|
||||
local mx = mouseX
|
||||
local my = mouseY
|
||||
|
||||
if mx == nil then
|
||||
mx = love.mouse.getX()
|
||||
end
|
||||
if my == nil then
|
||||
my = love.mouse.getY()
|
||||
end
|
||||
|
||||
self.hovered_last, self.hovered = self.hovered, nil
|
||||
self:updateMouse(love.mouse.getX(), love.mouse.getY(), love.mouse.isDown(1))
|
||||
self:updateMouse(mx, my, love.mouse.isDown(1))
|
||||
self.key_down, self.textchar = nil, ""
|
||||
self:grabKeyboardFocus(NONE)
|
||||
self.hit = nil
|
||||
|
@ -204,14 +213,14 @@ function suit:registerDraw(f, ...)
|
|||
end
|
||||
|
||||
function suit:draw()
|
||||
self:exitFrame()
|
||||
-- self:exitFrame() -- CALL these manually in your update method
|
||||
love.graphics.push('all')
|
||||
for i = self.draw_queue.n,1,-1 do
|
||||
self.draw_queue[i]()
|
||||
end
|
||||
love.graphics.pop()
|
||||
self.draw_queue.n = 0
|
||||
self:enterFrame()
|
||||
-- self:enterFrame() -- CALL these manually in your update method
|
||||
end
|
||||
|
||||
return suit
|
||||
|
|
|
@ -5,7 +5,7 @@ Before actually getting started, it is important to understand the motivation
|
|||
and mechanics behind SUIT:
|
||||
|
||||
- **Immediate mode is better than retained mode**
|
||||
- **Layout does not care about widgets**
|
||||
- **Layout should not care about content**
|
||||
- **Less is more**
|
||||
|
||||
Immediate mode?
|
||||
|
@ -68,11 +68,11 @@ to design a user interface.
|
|||
SUIT is not a complete GUI library: It does not take control of the runtime.
|
||||
You have to do everything yourself [1]_.
|
||||
|
||||
**SUIT is not good at word processors!**
|
||||
**SUIT is not good at processing words!**
|
||||
|
||||
|
||||
Hello, World
|
||||
------------
|
||||
Hello, World!
|
||||
-------------
|
||||
|
||||
SUITing up is is straightforward: Define your GUI in ``love.update()``, and
|
||||
draw it in ``love.draw()``::
|
||||
|
@ -96,15 +96,19 @@ draw it in ``love.draw()``::
|
|||
suit.draw()
|
||||
end
|
||||
|
||||
This will produce this UI (after clicking the button):
|
||||
This will produce this UI:
|
||||
|
||||
.. image:: _static/hello-world.gif
|
||||
|
||||
As written above, the two widgets are each created by a function call
|
||||
The two widgets (the button and the label) are each created by a function call
|
||||
(:func:`suit.Button <Button>` and :func:`suit.Label <Label>`). The first
|
||||
argument to a widget function is always the "payload" of the widget, and the
|
||||
last four arguments define the position and dimension of the widget. The
|
||||
function returns a table that indicates the UI state of the widget.
|
||||
argument to a widget function always defines the *payload* of the widget.
|
||||
Different widgets expect different payloads.
|
||||
Here, both :func:`suit.Button <Button>` and :func:`suit.Label <Label>` expect a
|
||||
string.
|
||||
The last four arguments of a widget function define the position and dimension
|
||||
of the widget.
|
||||
The function returns a table that indicates the UI state of the widget.
|
||||
Here, the state ``hit`` is used to figure out if the mouse was clicked and
|
||||
released on the button. See :doc:`Widgets <widgets>` for more info on widget
|
||||
states.
|
||||
|
@ -112,8 +116,8 @@ states.
|
|||
Mutable state
|
||||
-------------
|
||||
|
||||
Widgets that mutate some state - input boxes, checkboxes and sliders - receive
|
||||
a table argument as payload, e.g.::
|
||||
Widgets that mutate some state - input boxes, checkboxes and sliders - expect
|
||||
a table as their payload, e.g.::
|
||||
|
||||
local slider = {value = 1, min = 0, max = 2}
|
||||
function love.update(dt)
|
||||
|
@ -131,8 +135,7 @@ Options
|
|||
-------
|
||||
|
||||
You can define optional, well, options after the payload. Most options affect
|
||||
how the widget is drawn. For example, to align the label text to the left in
|
||||
the above example, you would write::
|
||||
how the widget is drawn. For example, to align the label text to the left::
|
||||
|
||||
local slider = {value = 1, max = 2}
|
||||
function love.update(dt)
|
||||
|
@ -148,8 +151,8 @@ the theme. See :doc:`Widgets <widgets>` for more info on widget options.
|
|||
Keyboard input
|
||||
--------------
|
||||
|
||||
The :func:`input widget <Input>` requires that you forward the ``keypressed``
|
||||
and ``textinput`` events to SUIT::
|
||||
The :func:`Input` widget requires that you forward the ``keypressed`` and
|
||||
``textinput`` events to SUIT::
|
||||
|
||||
local input = {text = ""}
|
||||
function love.update(dt)
|
||||
|
@ -168,7 +171,7 @@ and ``textinput`` events to SUIT::
|
|||
|
||||
.. image:: _static/keyboard.gif
|
||||
|
||||
The slider widget can also react to keyboard input. The mouse state is
|
||||
The :func:`Slider` widget can also react to keyboard input. The mouse state is
|
||||
automatically updated, but you can provide your own version of reality if you
|
||||
need to. See the :doc:`Core functions <core>` for more details.
|
||||
|
||||
|
@ -185,7 +188,7 @@ you can directly pass as the final four arguments to the widget functions.
|
|||
If you have ever dabbled with `Qt's <http://qt.io>`_ ``QBoxLayout``, you
|
||||
already know 89% [2]_ of what you need to know.
|
||||
|
||||
The first example can be written as follows::
|
||||
Hello, World! can be rewritten as follows::
|
||||
|
||||
suit = require 'suit'
|
||||
|
||||
|
@ -234,10 +237,10 @@ Refer to the :doc:`Layout <layout>` documentation for more information.
|
|||
Widget ids
|
||||
----------
|
||||
|
||||
Each widget is identified by an ``id`` [4]_. Internally, this ``id`` is used t
|
||||
Each widget is identified by an ``id`` [4]_. Internally, this ``id`` is used to
|
||||
figure out which widget should handle user input like mouse clicks and keyboard
|
||||
presses.
|
||||
Unless specified otherwise, the ``id`` is the same as the first argument, i.e.,
|
||||
Unless specified otherwise, the ``id`` is the same as the payload, i.e.,
|
||||
the ``id`` of ``Button("Hello, World!", ...)`` will be the string
|
||||
``"Hello, World!"``.
|
||||
In almost all of the cases, this will work fine and you don't have to worry about
|
||||
|
@ -332,10 +335,10 @@ You can also do minimally invasive surgery::
|
|||
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).
|
||||
Sometimes you might feel the need to separate parts of the GUI. Maybe certain
|
||||
UI elements 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::
|
||||
|
||||
|
|
|
@ -12,14 +12,35 @@ You may also download the sourcecode as a `zip
|
|||
<http://github.com/vrld/SUIT/zipball/master>`_ or `tar
|
||||
<http://github.com/vrld/SUIT/tarball/master>`_ file.
|
||||
|
||||
Using `Git <http://git-scm.com>`_, you can clone the project by running::
|
||||
Otherwise, use `Git <http://git-scm.com>`_::
|
||||
|
||||
git clone git://github.com/vrld/SUIT
|
||||
|
||||
Once done, you can check for updates by running::
|
||||
Update::
|
||||
|
||||
git pull
|
||||
|
||||
Hello, Suit::
|
||||
|
||||
suit = require 'suit'
|
||||
|
||||
local show_message = false
|
||||
function love.update(dt)
|
||||
-- Put a button on the screen. If hit, show a message.
|
||||
if suit.Button("Hello, World!", 100,100, 300,30).hit then
|
||||
show_message = true
|
||||
end
|
||||
|
||||
-- if the button was pressed at least one time, but a label below
|
||||
if show_message then
|
||||
suit.Label("How are you today?", 100,150, 300,30)
|
||||
end
|
||||
end
|
||||
|
||||
function love.draw()
|
||||
suit.draw()
|
||||
end
|
||||
|
||||
|
||||
Read on
|
||||
-------
|
||||
|
@ -49,7 +70,7 @@ The following code will create this UI:
|
|||
-- generate some assets (below)
|
||||
function love.load()
|
||||
snd = generateClickySound()
|
||||
normal, hovered, active = generateImageButton()
|
||||
normal, hovered, active, mask = generateImageButton()
|
||||
smallerFont = love.graphics.newFont(10)
|
||||
end
|
||||
|
||||
|
@ -128,13 +149,14 @@ The following code will create this UI:
|
|||
-- put an image button below the nested cell
|
||||
-- the size of the cell will be 200 by 100 px,
|
||||
-- but the image may be bigger or smaller
|
||||
-- the button shows the image `normal' when the mouse is outside the image
|
||||
-- or above a transparent pixel
|
||||
-- the button shows the image `hovered` if the mouse is above an opaque pixel
|
||||
-- of the image `normal'
|
||||
-- the button shows the image `normal' when the button is inactive
|
||||
-- the button shows the image `hovered` if the mouse is over an opaque pixel
|
||||
-- of the ImageData `mask`
|
||||
-- the button shows the image `active` if the mouse is above an opaque pixel
|
||||
-- of the image `normal' and the mouse button is pressed
|
||||
suit.ImageButton(normal, {hovered = hovered, active = active}, suit.layout:row(200,100))
|
||||
-- of the ImageData `mask` and the mouse button is pressed
|
||||
-- if `mask` is omitted, the alpha-test will be swapped for a test whether
|
||||
-- the mouse is in the area occupied by the widget
|
||||
suit.ImageButton(normal, {mask = mask, hovered = hovered, active = active}, suit.layout:row(200,50))
|
||||
|
||||
-- if the checkbox is checked, display a precomputed layout
|
||||
if chk.checked then
|
||||
|
@ -200,17 +222,17 @@ The following code will create this UI:
|
|||
local d2 = math.exp(-((px+.7)^2 + (py+.1)^2) * 2)
|
||||
local d = (d1 + d2)/2
|
||||
if d > t then
|
||||
return r,g,b, 255 * ((d-t) / (1-t))^.2
|
||||
return r,g,b, ((d-t) / (1-t))^.2
|
||||
end
|
||||
return 0,0,0,0
|
||||
end
|
||||
end
|
||||
|
||||
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))
|
||||
hovered:mapPixel(metaballs(.46, 50,153,187))
|
||||
active:mapPixel(metaballs(.43, 255,153,0))
|
||||
return love.graphics.newImage(normal), love.graphics.newImage(hovered), love.graphics.newImage(active)
|
||||
normal:mapPixel(metaballs(.48, .74,.74,.74))
|
||||
hovered:mapPixel(metaballs(.46, .2,.6,.6))
|
||||
active:mapPixel(metaballs(.43, 1,.6,0))
|
||||
return love.graphics.newImage(normal), love.graphics.newImage(hovered), love.graphics.newImage(active), normal
|
||||
end
|
||||
|
||||
Indices and tables
|
||||
|
|
|
@ -30,7 +30,7 @@ Creates a label at position ``(x,y)`` with width ``w`` and height ``h``.
|
|||
|
||||
.. function:: ImageButton(normal, options, x,y)
|
||||
|
||||
:param Image normal: Image of the button in normal state.
|
||||
:param mixed normal: Image of the button in normal state.
|
||||
:param table options: Widget options.
|
||||
:param numbers x,y: Upper left corner of the widget.
|
||||
:returns: Return state (see below).
|
||||
|
@ -39,13 +39,21 @@ Creates an image button widget at position ``(x,y)``.
|
|||
Unlike all other widgets, an ``ImageButton`` is not affected by the current
|
||||
theme.
|
||||
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
|
||||
non-zero alpha value.
|
||||
area of the widget.
|
||||
The button activates when the mouse enters the area occupied by the widget.
|
||||
If the option ``mask`` defined, the button activates only if the mouse is over
|
||||
a pixel with non-zero alpha.
|
||||
You can provide additional ``hovered`` and ``active`` images, but the widget area
|
||||
is always computed from the ``normal`` image.
|
||||
You can provide additional ``hovered`` and ``active`` images, but the widget area
|
||||
is always computed from the ``normal`` image.
|
||||
|
||||
**Additional Options:**
|
||||
|
||||
``mask``
|
||||
Alpha-mask of the button, i.e. an ``ImageData`` of the same size as the
|
||||
``normal`` image that has non-zero alpha where the button should activate.
|
||||
|
||||
``normal``
|
||||
Image for the normal state of the widget. Defaults to widget payload.
|
||||
|
||||
|
|
|
@ -2,36 +2,49 @@
|
|||
|
||||
local BASE = (...):match('(.-)[^%.]+$')
|
||||
|
||||
local function isType(val, typ)
|
||||
return type(val) == "userdata" and val.typeOf and val:typeOf(typ)
|
||||
end
|
||||
|
||||
return function(core, normal, ...)
|
||||
local opt, x,y = core.getOptionsAndSize(...)
|
||||
opt.normal = normal or opt.normal or opt[1]
|
||||
opt.hovered = opt.hovered or opt[2] or opt.normal
|
||||
opt.active = opt.active or opt[3] or opt.hovered
|
||||
assert(opt.normal, "Need at least `normal' state image")
|
||||
opt.id = opt.id or opt.normal
|
||||
|
||||
opt.state = core:registerMouseHit(opt.id, x,y, function(u,v)
|
||||
local id = opt.normal:getData()
|
||||
assert(id:typeOf("ImageData"), "Can only use uncompressed images")
|
||||
local image = assert(opt.normal, "No image for state `normal'")
|
||||
|
||||
core:registerMouseHit(opt.id, x, y, function(u,v)
|
||||
-- mouse in image?
|
||||
u, v = math.floor(u+.5), math.floor(v+.5)
|
||||
if u < 0 or u >= opt.normal:getWidth() or v < 0 or v >= opt.normal:getHeight() then
|
||||
if u < 0 or u >= image:getWidth() or v < 0 or v >= image:getHeight() then
|
||||
return false
|
||||
end
|
||||
local _,_,_,a = id:getPixel(u,v)
|
||||
return a > 0
|
||||
|
||||
if opt.mask then
|
||||
-- alpha test
|
||||
assert(isType(opt.mask, "ImageData"), "Option `mask` is not a love.image.ImageData")
|
||||
assert(u < mask:getWidth() and v < mask:getHeight(), "Mask may not be smaller than image.")
|
||||
local _,_,_,a = mask:getPixel(u,v)
|
||||
return a > 0
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
local img = opt.normal
|
||||
if core:isActive(opt.id) then
|
||||
img = opt.active
|
||||
image = opt.active
|
||||
elseif core:isHovered(opt.id) then
|
||||
img = opt.hovered
|
||||
image = opt.hovered
|
||||
end
|
||||
|
||||
core:registerDraw(opt.draw or function(img,x,y, r,g,b,a)
|
||||
assert(isType(image, "Image"), "state image is not a love.graphics.image")
|
||||
|
||||
core:registerDraw(opt.draw or function(image,x,y, r,g,b,a)
|
||||
love.graphics.setColor(r,g,b,a)
|
||||
love.graphics.draw(img,x,y)
|
||||
end, img, x,y, love.graphics.getColor())
|
||||
love.graphics.draw(image,x,y)
|
||||
end, image, x,y, love.graphics.getColor())
|
||||
|
||||
return {
|
||||
id = opt.id,
|
||||
|
|
|
@ -6,6 +6,7 @@ return function(core, text, ...)
|
|||
local opt, x,y,w,h = core.getOptionsAndSize(...)
|
||||
opt.id = opt.id or text
|
||||
opt.font = opt.font or love.graphics.getFont()
|
||||
opt.color = opt.color or core.theme.color
|
||||
|
||||
w = w or opt.font:getWidth(text) + 4
|
||||
h = h or opt.font:getHeight() + 4
|
||||
|
|
|
@ -15,6 +15,7 @@ function Layout:reset(x,y, padx,pady)
|
|||
self._widths = {}
|
||||
self._heights = {}
|
||||
self._isFirstCell = true
|
||||
self._start_x = self._x
|
||||
|
||||
return self
|
||||
end
|
||||
|
@ -35,6 +36,12 @@ function Layout:nextRow()
|
|||
return self._x, self._y + self._h + self._pady
|
||||
end
|
||||
|
||||
function Layout:newRow()
|
||||
self._x = self._start_x
|
||||
self._y = self._y + self._h + self._pady
|
||||
self._w = nil
|
||||
end
|
||||
|
||||
Layout.nextDown = Layout.nextRow
|
||||
|
||||
function Layout:nextCol()
|
||||
|
|
28
suit-0.1-1.rockspec
Normal file
28
suit-0.1-1.rockspec
Normal file
|
@ -0,0 +1,28 @@
|
|||
package = "suit"
|
||||
version = "0.1-1"
|
||||
source = {
|
||||
url = "git://github.com/vrld/suit.git"
|
||||
}
|
||||
description = {
|
||||
summary="Immediate mode GUI library in pure Lua.",
|
||||
homepage = "https://suit.readthedocs.io",
|
||||
license = "MIT",
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["suit"] = "init.lua",
|
||||
["suit.button"] = "button.lua",
|
||||
["suit.checkbox"] = "checkbox.lua",
|
||||
["suit.core"] = "core.lua",
|
||||
["suit.imagebutton"] = "imagebutton.lua",
|
||||
["suit.input"] = "input.lua",
|
||||
["suit.label"] = "label.lua",
|
||||
["suit.layout"] = "layout.lua",
|
||||
["suit.slider"] = "slider.lua",
|
||||
["suit.theme"] = "theme.lua",
|
||||
}
|
||||
}
|
50
theme.lua
50
theme.lua
|
@ -6,9 +6,9 @@ local theme = {}
|
|||
theme.cornerRadius = 4
|
||||
|
||||
theme.color = {
|
||||
normal = {bg = { 66, 66, 66}, fg = {188,188,188}},
|
||||
hovered = {bg = { 50,153,187}, fg = {255,255,255}},
|
||||
active = {bg = {255,153, 0}, fg = {225,225,225}}
|
||||
normal = {bg = { 0.25, 0.25, 0.25}, fg = {0.73,0.73,0.73}},
|
||||
hovered = {bg = { 0.19,0.6,0.73}, fg = {1,1,1}},
|
||||
active = {bg = {1,0.6, 0}, fg = {1,1,1}}
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,16 +18,32 @@ function theme.getColorForState(opt)
|
|||
return (opt.color and opt.color[opt.state]) or theme.color[s]
|
||||
end
|
||||
|
||||
function theme.drawBox(x,y,w,h, colors, cornerRadius)
|
||||
function theme.drawBox(x,y,w,h, colors, cornerRadius, outline, scale)
|
||||
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
|
||||
|
||||
-- by default, scale will operate about the center of the box. meaning it grows
|
||||
-- out/in in all directions
|
||||
if scale and scale ~= 1.0 then
|
||||
local baseW, baseH = w, h
|
||||
w = w * scale
|
||||
h = h * scale
|
||||
x = x - (w - baseW) / 2
|
||||
y = y - (h - baseH) / 2
|
||||
cornerRadius = cornerRadius * scale
|
||||
end
|
||||
|
||||
love.graphics.setColor(colors.bg)
|
||||
love.graphics.rectangle('fill', x,y, w,h, cornerRadius)
|
||||
if outline ~= nil then
|
||||
love.graphics.setColor(colors.fg)
|
||||
love.graphics.setLineWidth(outline)
|
||||
love.graphics.rectangle('line', x,y, w,h, cornerRadius)
|
||||
end
|
||||
end
|
||||
|
||||
function theme.getVerticalOffsetForAlign(valign, font, h)
|
||||
|
@ -51,13 +67,33 @@ end
|
|||
|
||||
function theme.Button(text, opt, x,y,w,h)
|
||||
local c = theme.getColorForState(opt)
|
||||
local scale = opt.scale or 1.0
|
||||
|
||||
theme.drawBox(x,y,w,h, c, opt.cornerRadius)
|
||||
theme.drawBox(x,y,w,h, c, opt.cornerRadius, opt.outline, scale)
|
||||
love.graphics.setColor(c.fg)
|
||||
love.graphics.setFont(opt.font)
|
||||
|
||||
-- ensure text remains aligned regardless of scale
|
||||
local align = opt.align or "center"
|
||||
local textX = x
|
||||
local textY = y
|
||||
local textW = w
|
||||
if scale ~= 1.0 then
|
||||
-- For center alignment, shift x so the scaled text stays centered
|
||||
if align == "center" then
|
||||
textX = x + (w - w * scale) / 2
|
||||
elseif align == "right" then
|
||||
textX = x + (w - w * scale)
|
||||
end
|
||||
end
|
||||
|
||||
y = y + theme.getVerticalOffsetForAlign(opt.valign, opt.font, h)
|
||||
love.graphics.printf(text, x+2, y, w-4, opt.align or "center")
|
||||
love.graphics.printf(text, textX+2, textY, textW-4,
|
||||
align,
|
||||
0,
|
||||
scale,
|
||||
scale
|
||||
)
|
||||
end
|
||||
|
||||
function theme.Checkbox(chk, opt, x,y,w,h)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue