255 lines
9.5 KiB
ReStructuredText
255 lines
9.5 KiB
ReStructuredText
Getting Started
|
|
===============
|
|
|
|
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**
|
|
- **Less is more**
|
|
|
|
Immediate mode?
|
|
---------------
|
|
|
|
With classical (retained) mode libraries you typically have a stage where you
|
|
create the whole UI when the program initializes. This includes what happens
|
|
when events like button presses or slider changes occur. After that point, the
|
|
GUI is expected to not change very much. This is great for word processors
|
|
where the interaction is consistent and straightforward, but bad for games,
|
|
where everything changes all the time.
|
|
|
|
With immediate mode libraries, on the other hand, the GUI is created every
|
|
frame from scratch. Because that would be wasteful, there are no widget
|
|
objects. Instead, widgets are created by functions that react to UI state and
|
|
present some data. Where this data comes from and how it is maintained does
|
|
not concern the widget at all. This is, after all, your job. This gives great
|
|
control over what is shown where and when. The widget code can be right next
|
|
to the code that does what should happen if the widget state changes. The
|
|
layout is also very flexible: adding a widget is one more function call, and if
|
|
you want to hide a widget, you simply don't call the corresponding function.
|
|
|
|
This separation of data and behaviour is great when a lot of stuff is going on,
|
|
but takes a bit of time getting used to.
|
|
|
|
|
|
What SUIT is
|
|
^^^^^^^^^^^^
|
|
|
|
SUIT is simple: It provides only a few basic widgets that are important for
|
|
games:
|
|
|
|
- :func:`Buttons <Button>` (including :func:`Image Buttons <ImageButton>`)
|
|
- :func:`Text Labels <Label>`
|
|
- :func:`Checkboxes <Checkbox>`
|
|
- :func:`Text Input <Input>`
|
|
- :func:`Value Sliders <Slider>`
|
|
|
|
SUIT is comfortable: It has a straightforward, yet effective row/column-based
|
|
layout engine.
|
|
|
|
SUIT is adaptable: It is possible to change the color scheme, single drawing
|
|
functions for all widget or the whole theme.
|
|
|
|
SUIT is hackable: Custom widgets can leverage the extensive :doc:`core library
|
|
<core>`.
|
|
|
|
**SUIT is good at games!**
|
|
|
|
|
|
What SUIT is not
|
|
^^^^^^^^^^^^^^^^
|
|
|
|
SUIT is not a complete GUI library: It does not provide dropdowns, table views,
|
|
menu bars, modal dialogs, etc.
|
|
|
|
SUIT is not a complete GUI library: It does not have a markup language or tools
|
|
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!**
|
|
|
|
|
|
Hello, World
|
|
------------
|
|
|
|
SUITing up is is straightforward: Define your GUI in ``love.update()``, and
|
|
draw it in ``love.draw()``::
|
|
|
|
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.core.draw()
|
|
end
|
|
|
|
As written above, the two widgets 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.
|
|
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.
|
|
|
|
Mutable state
|
|
-------------
|
|
|
|
Widgets that mutate some state - input boxes, checkboxes and sliders - receive
|
|
a table argument as payload, e.g.::
|
|
|
|
local slider = {value = 1, min = 0, max = 2}
|
|
function love.update(dt)
|
|
suit.Slider(slider, 100,100, 200,30)
|
|
suit.Label(tostring(slider.value), 300,100, 100,30)
|
|
end
|
|
|
|
The widget function updates the payload when some user interaction occurs. In
|
|
the above example, ``slider.value`` may be changed by the :func:`Slider`
|
|
widget. The value is then shown by a :func:`Label` next to the slider.
|
|
|
|
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::
|
|
|
|
local slider = {value = 1, max = 2}
|
|
function love.update(dt)
|
|
suit.Slider(slider, 100,100, 200,30)
|
|
suit.Label(tostring(slider.value), {align = "left"}, 300,100, 100,30)
|
|
end
|
|
|
|
What options are available and what they are doing depends on the widget and
|
|
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::
|
|
|
|
local input = {text = ""}
|
|
function love.update(dt)
|
|
suit.Input(input, 100,100,200,30)
|
|
suit.Label("Hello, "..input.text, {align="left"}, 100,150,200,30)
|
|
end
|
|
|
|
-- forward keyboard events
|
|
function love.textinput(t)
|
|
suit.core.textinput(t)
|
|
end
|
|
|
|
function love.keypressed(key)
|
|
suit.core.keypressed(key)
|
|
end
|
|
|
|
The 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.
|
|
|
|
Layout
|
|
------
|
|
|
|
It is tedious to calculate the position and size of each widget you want to put
|
|
on the screen. Especially when all you want is to put three buttons beneath
|
|
each other. SUIT implements a simple, yet effective layout engine. All the
|
|
engine does is put cells next to each other (below or right). It does not care
|
|
what you put into those cells, but assumes that you probably need them for
|
|
widgets. Cells are reported by four numbers (left, top, width and height) that
|
|
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::
|
|
|
|
suit = require 'suit'
|
|
|
|
local show_message = false
|
|
function love.update(dt)
|
|
-- put the layout origin at position (100,100)
|
|
-- cells will grow down and to the right of the origin
|
|
suit.layout.reset(100,100)
|
|
|
|
-- put 10 extra pixels between cells in each direction
|
|
suit.layout.padding(10,10)
|
|
|
|
-- 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
|
|
show_message = true
|
|
end
|
|
|
|
-- add another cell below the first cell
|
|
-- the size of the cell is the same as the first cell
|
|
if show_message then
|
|
suit.Label("How are you today?", suit.layout.row())
|
|
end
|
|
end
|
|
|
|
function love.draw()
|
|
suit.core.draw()
|
|
end
|
|
|
|
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.
|
|
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). 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 sizes of all cells that were created since the last ``reset()``: ``max``,
|
|
``min`` and ``median``. They do what you expect them to do.
|
|
|
|
It is also possible to nest cells and to let cells dynamically fill the
|
|
available space (but you have to tell how much space there is beforehand).
|
|
Refer to the :doc:`Layout <layout>` documentation for more information.
|
|
|
|
|
|
Themeing
|
|
--------
|
|
|
|
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
|
|
the table ``suit.core.theme``. Conveniently, the name of the function
|
|
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
|
|
drawn, simply overwrite the function. If you want to redecorate completely, it
|
|
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.
|
|
It requires you to change the background (``bg``), foreground (``fg``) and
|
|
border color of three possible widget states: ``normal``, when nothing out of
|
|
the ordinary happened, ``hot``, when the mouse hovers above a widget, and
|
|
``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
|
|
``suit.core.theme.color``. The default color scheme is this::
|
|
|
|
suit.core.theme.color = {
|
|
normal = {bg = {78,78,78}, fg = {200,200,200}, border={20,20,20}},
|
|
hot = {bg = {98,98,98}, fg = {69,201,84}, border={30,30,30}},
|
|
active = {bg = {88,88,88}, fg = {49,181,64}, border={10,10,10}}
|
|
}
|
|
|
|
You can also do minimally invasive surgery::
|
|
|
|
function love.load()
|
|
suit.core.theme.color.normal.fg = {255,255,255}
|
|
suit.core.theme.color.hot = {bg = {200,230,255}, {fg = {0,0,0}, border = {120,140,180}}}
|
|
end
|
|
|
|
.. [1] But it thinks you can handle that.
|
|
.. [2] Proportion determined by rigorous scientific experiments [3]_.
|
|
.. [3] And theoretic reasoning. Mostly that, actually.
|