Class Block
START HERE.
Parent of every element in the system.
Introduction
(words/letters in bold in this section refer to the ones in the Fig. 1 below)
- A block is used to denote an element of a graphical user interface in this library (BLOCK), another famous name for this is widget but I will be using element / block interchangeable here.
- The coordinate system as it is in love2d.
- A rectangle we define by the coordinate of its upper-left corner and its width
and height. I.e. it is a tuple (x, y, w, h).
In Lua a rectangle would be a table with 4 keys
x, y, w, h
:local rectangle = { x = 10, y = 20, w = 100, h = 50, }
- The enclosing cell of a block is a rectangle which the block is placed in, (X, Y, W, H) . In order to place the block use its place method. NOTE: you are not supposed to use this method on every block in your GUI, you usually call it once on the root block. See Layout. So placing an element on the screen requires not only the coordinate but the whole rectangle. NOTE: place itself doesn't draw anything. Use the block's draw method to actually draw it.
likeliHUD
was created with the following objectives:
- Declarative construction (similar to QML)
- Everything is a rectangle
- Automatic layout (but see the
offset
parameter below)
A typical user workflow consists of at least 3 steps when using this library
- Create
- Place
- Draw
Create
Declarative construction means you create a block tree which represents your UI.
Blocks can have properties and embedded (children) blocks.
Properties go in the hash part of the block (in the end every block is a Lua table)
i.e. key-value pairs; children elements is the array part of the block. However, see the
on
property in the description of the new method. A simple example of
an object tree which has the following diagram (properties and their values are written in the
parentheses)
A1 (a: 1) ├───B1 (b: 2) │ ├───B2 (b: 3) │ ├───B3 (b: 4) │ ├───B4 (b: 5) │ ├───C1 (c: 1) ├───C2 (c: 2)
can be created with the following code:
local root = A1 { a = 1, B1 { b = 2, B2 { b = 3, }, B3 { b = 4, }, B4 { b = 5, }, }, C1 { c = 1, C2 { c = 2, } } }
Place
This library treats every block (BLOCK) as a rectangle of some width a and height b placed inside its enclosing cell (X, Y, W, H). It may also have some pads pad and/or offset (Xo, Yo, Wo, Ho). Below you can see a figure which we'll be referring to in this documentation a lot:
Fig.1
Enclosing cell
┆
┆ offset
┆ ┆
(X, Y) ▼ ┆
■───────────────────────────────────┆────────────────────╮
│ ▲ ┆ │
│ Yo ┆ │
│ ▼ ▼ │
│ ■──────────────────────────────────┬──────╮ │
│ │ ▲ │ │ │
│◀︎ Xo ▶︎│ Pad │ │ │
│ │ (Xs, Ys) ▼ │ │ │
│ │ ■──────────────────╮ │ │ │
│ │ │ │ │ │ │
│ │◀︎ Pad ▶︎│ BLOCK b B │ │
│ │ │ │ │ Ho H
│ │ ╰────────a─────────╯ │ │ │
│ │ │ │ │
│ │ │ │ │
│ │ │ │ │
│ ├─────────────────A────────────────╯ │ │
│ │ │ │
│ ╰───────────────────Wo────────────────────╯ │
│ │
│ │
│ │
╰───────────────────────────W────────────────────────────╯
This is the code that corresponds to the figure:
local BLOCK = ui.Rectangle { w = a, h = b, offset = { x = Xo, y = Yo, w = Wo, h = Ho, }, pad = Pad, } BLOCK:place(X, Y, W, H)
One more time: to place an element you call its place method with the enclosing cell as an argument (actually it is 4 arguments, but you get the idea). Don't be afraid - you are not going to call this method on each element in your UI - usually only the root element is placed explicitly.
Draw
In order to draw an element call its draw method. Similarly to place you usually call it once (per frame) for the root element.
See below for details.
Class Block
block:new () | Constructor. |
block:size (includePad) | Returns the size of the block. |
block:cell () | Enclosing cell. |
block:place (x, y, w, h) | Places the block within specified enclosing cell. |
block:draw () | Draws the block. |
block:traverse () | Block iterator. |
User defined blocks
block:doDraw () | A block must override this function to be drawn. |
block:doPlace (x, y, w, h) | Placement. |
Class Block
- block:new ()
-
Constructor. The user is not supposed to explicitly call this method. It's called automatically when an instance of Block is created. See here for details. When created a block can be given properties. The list of the properties below is common for all blocks in this library. NOTE: though can be given some properties might be ignored by a block either because of the type of the block and/or different circumstances. F.e. the
fill
property only makes sense when the block is placed inside a Layout.visible
: boolean.true
(default) means the element is visible (drawn). Otherwise it is not visiblecolor
: a color like in love2d (the table variant). For different type of a block it has different meaning. See concrete type. Default is nil.pad
: a non-negative number. The minimum gap between the block's border and the enclosing cell (see Fig. 1). It can be greater than the pad because of theoffset
parameter. NOTE: the pad along the x-axis is eqaul to the pad along the y-axis.align
: Alignment of the block inside its enclosing cell. A string containing one or two words'center'
(default),'top', 'bottom', 'left', 'right'
delimited by the+
sign. For example,'top+right'
(equivalent to'right+top'
). NOTE: this string must be consistent: it doesn't make sense to have something like'top+bottom'
('top'
will be set), or'left+right'
('right'
will be set). In other words there are only 9 options of alignment (* means default):Fig.2 ╭────────────────┬────────────┬────────────────╮ │ top + left │ top │ top + right │ │ │ │ │ │ │ │ │ ├────────────────┼────────────┼────────────────┤ │ │ │ │ │ left │ center* │ right │ │ │ │ │ ├────────────────┼────────────┼────────────────┤ │ │ │ │ │ │ │ │ │ bottom + left │ bottom │ bottom + right │ ╰────────────────┴────────────┴────────────────╯
fill
: a table of the form{ x = boolean, y = boolean }
. Tells whether the block should occupy free space (along the corresponding axis) if placed inside a Layout. See Layout:new.offset
: a rectangle (Xo, Yo, Wo, Ho). Modifies the enclosing cell (X, Y, W, H) of the element (which was given by calling the place method) by adding (Xo, Yo) to (X, Y) and overriding (W, H) by (Wo, Ho). In other words, placing the element with theoffset
field (Xo, Yo, Wo, Ho) inside the cell (X, Y, W, H) is the same as placing this element inside the cell (X + Xo, Y + Yo, Wo, Ho). See Fig. 1. Usually used together with theon
element (see below).-- Rectangle is a Block local element = ui.Rectangle { w = 100, h = 100, offset = { x = 10, y = 20, w = 200, h = 200, } } element:place(10, 10, 300, 300) -- actually placed in (20, 30, 200, 200)
on
: a block. Special property which value must be a block. Let us explain the meaning of theon
property looking at the following example:local UI = ui.Rectangle { id = 'A' w = 100, h = 50, on = { ui.Rectangle { -- the enclosing cell of this element is A itself id = 'B', w = 60, h = 30, } } } UI:place(20, 20, 200, 200)
which produces the following diagram:
Fig.3 Enclosing cell of A ┆ ┆ ┆ Enclosing cell of B (0, 0) ┆ ┆ ■──────────────────────────┆───────┆─────────────────╮ │ ▲ ┆ ┆ │ │ 20 ┆ ┆ │ │ ▼ ▼ ┆ │ │ ■──────────────────────────┆──────────╮ │ │ │ ┆ │ │ │◀︎ 20 ▶︎│ ┆ │ │ │ │ ▼ │ │ │ │ ╭────────────────────╮ │ │ │ │ │A │ │ │ │ │ │ ╭────────────╮ │ │ │ │ │ │ │B │ 50 │ │ │ │ │ │ 30 │ 200 H (screen) │ │ │ ╰─────60─────╯ │ │ │ │ │ │ │ │ │ │ │ ╰────────100─────────╯ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────200───────────────╯ │ │ │ │ │ │ │ ╰───────────────────────────W (screen)───────────────╯
as you can see now the enclosing cell of B is A itself, namely, (A.x, A.y, A.w, A.h). In other words B is automatically placed inside A. This property together with the
offset
one makes it possible to integrate elements (usually Label) into images. F.e., suppose you drew a health bar image:╭─────────────────╮ │■───────────────■│◀︎- cool health bar image ││ area for text ││ │■───────────────■│ ╰─────────────────╯
and you want the text showing the health is always centered inside this image wherever the bar is placed. All you need is to place a Label inside this image with the right offset:
local hp = ui.Image { path = 'path/to/image.png', on = { ui.Label { text = '100', offset = { -- area for text } } } }
Now wherever
hp
is placed the text inside it is placed correctly automatically.
- block:size (includePad)
-
Returns the size of the block.
The size of the block is the minimum rectangle which the block can be placed in,
in the form of a table
{ x = width, y = height }
.Parameters:
- includePad
If
true
then the pads of the block are included (AxB in Fig. 1). Ifnil
(default) orfalse
the pads are not included (axb in the figure above)
Returns:
-
A table of the form
{ x = width, y = height }
- includePad
If
- block:cell ()
-
Enclosing cell.
Returns the enclosing cell of the block.
Returns:
-
The enclosing cell rectangle i.e. a table of the form
{ x = cell.x, y = cell.y, w = cell.w, h = cell.h }
, ornil
. NOTE: the enclosing cell is initialized only after the block was placed by calling its place method. Otherwise it staysnil
.Usage:
local r = ui.Rectangle { w = 100, h = 200, } local c = r:cell() --> c == nil r:place(10, 10, 300, 300) local c = r:cell() --> c = { x = 10, y = 10, w = 300, h = 300 }
- block:place (x, y, w, h)
-
Places the block within specified enclosing cell.
Parameters:
- x x coordinate of the cell (X in the Fig. 1)
- y y coordinate of the cell (Y in the Fig. 1)
- w width of the cell (W in the Fig. 1)
- h height of the cell (H in the Fig. 1)
- block:draw ()
- Draws the block. Used to draw the block. Usually you don't want to call it on every block in your GUI - only once for each root element.
- block:traverse ()
-
Block iterator.
Used to iterate over blocks in the hierarchy.
Usage:
local UI = ui.Layout { rows = 1, columns = 2, ui.Label { text = 'foo' }, ui.Label { text = 'bar' } } for i, b in UI:traverse() do b.border = true end
User defined blocks
- block:doDraw ()
- A block must override this function to be drawn. Does nothing by default.
- block:doPlace (x, y, w, h)
-
Placement.
A block should override this function to be placed.
Used to place the block within specified enclosing cell.
Parameters:
- x x coordinate of the window
- y y coordinate of the window
- w width of the window
- h height of the window