28
GUI API
Smok1e edited this page 2025-07-06 19:20:23 +03:00

This is a multifunctional user interface library, especially designed and optimized for low-performance computers. You can realize all of your most perverted fantasies using it: from regular buttons, sliders and charts to complex animated interfaces. Extreme performance of the library is achieved by using double buffering and complex color grouping and processing algorithms.

MineOS and every one of its applications are created using this library:

Let the abundance of text below not frighten you: this documentation has many illustrated examples with fully working code.

Objects

The library is divided into three main concepts: workspaces, containers and widgets. Workspaces are designed for launching your cool interfaces and handling computer events when user touches the screen, presses some keys, etc. Containers are designed for grouping widgets and processing their positions. Widgets are designed for displaying different stuff: they can represent buttons, scrollbars, sliders, etc.

First let's talk about objects. Every GUI.object is a rectangular entity with it's own width and height, it's used as a template for any other stuff like containers and workspaces. You can easily create object via this method:

GUI.object(x, y, width, height)

Type Parameter Description
int x Object coordinate by x-axis
int y Object coordinate by y-axis
int width Object width
int height Object height

Every created object and it's extended classes have several universal properties:

Type Property Description
int .x Current screen rendering coordinate of object by x-axis
int .y Current screen rendering coordinate of object by y-axis
int .width Object width
int .height Object height
boolean .hidden Whether the object is hidden. If the object is hidden, then its rendering and analysis of system events are ignored
boolean .disabled Whether the object is disabled. If the object is disabled, then it can be rendered, but all system events are ignored
boolean .blockScreenEvents Whether screen events like touch/drag should be blocked or pass through object to deeper levels of the hierarchy. Default value is true
function :draw() Main method that is called to render this object to the screen buffer. It can be defined by the user in any convenient way

Example

Let's implement the simple rectangle object. Don't worry if you don't understand what "workspace" or "container" means, it's just a template for copy-pasting, you can read two sections below and everything will be OK:

-- Import this library
local GUI = require("GUI")
-- We will also need downloaded double buffering library to render rectangles
local screen = require("Screen")

--------------------------------------------------------------------------------

-- Create new workspace
local workspace = GUI.workspace()

-- Create and add template object to workspace
local object = workspace:addChild(GUI.object(3, 2, 50, 10))
-- Create own :draw() method and make it render green rectangle
object.draw = function(object)
	screen.drawRectangle(object.x, object.y, object.width, object.height, 0x33FF80, 0x0, " ")
end

--------------------------------------------------------------------------------

-- Draw workspace content once on screen when program starts
workspace:draw()
-- Start processing events for workspace
workspace:start()

As a result, we will get a nice green rectangle:

Containers

Now let's talk about containers. Container is a object that can store some other objects inside itself, its behavior is very similar to a folder that contains a some nested files and other folders. You can easily create empty container via this method:

GUI.container(x, y, width, height) extends GUI.object

Type Parameter Description
int x Container's coordinate by x-axis
int y Container's coordinate by y-axis
int width Container's width
int height Container's height

All container child objects are stored in container.children table. To add an object to the container, use the following method:

container:addChild(<Object>)

After adding any object to container using :addChild() method, the object will acquire additional properties:

Type Property Description
table .workspace Reference to current workspace which have topmost position in hierarchy of objects tree
table .parent Reference to parent container of the object
int .localX Local position on the x-axis in the parent container
int .localY Local position on the y-axis in the parent container
function :indexOf() Get the index of this object in the parent container (iterative method)
function :moveForward() Move the object "back" in the container children hierarchy
function :moveBackward() Move the object "forward" in the container children hierarchy
function :moveToFront() Move the object to the end of the container children hierarchy
function :moveToBack() Move the object to the beginning of the container children hierarchy
function :remove() Remove this object from the parent container. Roughly speaking, this is a convenient way of self-destruction
function :addAnimation(function frameHandler, function onFinish): table animation Add an animation to this object, see below
[callback-function .eventHandler(container workspace, object object, ... varargs eventData) ] An optional method for handling system events, called by the parent container handler. If it exists in the object under consideration, it will be called with the appropriate arguments

Let's pay attention on object.localX and object.localY properties. They're represents local position of child object inside parent container. There's a difference from object.x and object.y: they're represents current position of object on screen and are being calculated automatically for performing drawing operations. But most of the time the developer will work with local position. The hierarchy and positioning of container's children is well presented in the following image:

Containers also have an important feature: any child that extends beyond the bounds of the container will be rendered only within the size of this container:

Of course, you can add to container another container, add a new ones to the added ones, creating complex hierarchy and grouping the child objects at your will.

Every container has following properties:

Type Property Description
table .children Table that contains all child objects of this container
function :addChild(table child[, int atIndex]): table child Add specified object to the container as a child. When you do this, the object's global coordinates will become local. If the optional parameter atIndex is specified, then the element will be added to the corresponding position in container.children table
function :removeChildren([int fromIndex, int toIndex]) Remove all child elements of the container. If the optional parameters of the element indices are specified, the deletion will be performed in the appropriate range

Workspaces

Finally, let's talk about workspaces. Every workspace is full screen container that can automatically handle computer events. For example, it allows buttons to be pressed when user interacts with screen and it allows text fields receive data from the keyboard.

GUI.workspace([x, y, width, height]) extends GUI.container

Type Parameter Description
[int x] Optional coordinate by x-axis
[int y] Optional coordinate by y-axis
[int width] Optional width
[int height] Optional height

Every workspace can be launched and start processing events. To do this, use the following:

workspace:start([delay])

If event handling should be terminated, use the following:

workspace:stop()

Once the workspace receives the event, it recursively analyzes itself and all child objects for the .eventHandler function. If the child object has this function, then it runs with following parameters:

object.eventHandler = function(workspace, object, ...eventData)
    ...
end

The first parameter is a pointer to the workspace, the second is a pointer to the child object event handler belongs to, and the rest is the set of event parameters just like computer.pullSignal() do. For example, the "touch" event will call .eventHandler with 8 arguments:

Argument Description
table Pointer to workspace object
table Pointer to event handler object
"touch" Event type
"7842f8..." Screen address
13 Screen x
21 Screen y
0 Mouse button
"ECS" Username

The important detail of the event handlers is that if the event belongs to screen (touch, drag, drop, scroll), then the child object event handler will be called, and event processing for the remaining unprocessed child objects will be finished.

And if the event does not belong to the screen, or the object does not have event handler method, the processing of the remaining child elements will continue as always. You can see the logic and the order of event processing in the following image:

Every workspace has following properties:

Type Property Description
table | nil :capturedObject reference to currenlty captured object. Used for exclusive event processing, see [Capturing][#Capturing-objects] section for more details
table | nil :focusedObject A reference to currenlty focused object. Used mostly in windows and text fields, when only one of many displayed object must react to a particular event. See the Focusing section for more details
function :start([float delay]) Run the event processing for this container and analyse events for all it's child objects. The delay parameter is similar to computer.pullSignal one
function :stop() Stop processing events for this container
function :draw([boolean force = false]) This method works like similar to container:draw(). First, it will draw every child object of workspace to screen buffer. Then it will render changed pixels from buffer to physical computer screen. In short words, call it every time when you need to see visual updates. The optional force argument tells screen buffer to draw all pixels, not just changed pixels. It is useful for work with legacy applications that uses GPU directly without screen buffer concept, but in 99% cases you won't need it

Below is a classic example of the implementation of the workspace:

-- Import the library
local GUI = require("GUI")

--------------------------------------------------------------------------------

-- Create new workspace
local workspace = GUI.workspace()

-- Create simple event handler for it
workspace.eventHandler = function(workspace, object, ...)
    GUI.alert("It works! The event data was: ", ...)
end

--------------------------------------------------------------------------------

-- Start processing events for workspace
workspace:start()

As a result, you will get an amusing output of event data to the terminal:

Animations

The next feature of objects is animations: every object can be animated. For more information about creating animations, see practical examples in end of this documentation. By the way, here is field of animated GUI.switch objects:

As described above, to add an animation to object, call