Introduction

Welcome to the tophat documentation. This documentation contains some guides for using tophat and reference for tophat's Umka API.

What is tophat?

Tophat is a 2d game library for the Umka programming language. It is small and simple to use. You can visit the homepage at tophat2d.dev.

Getting started

This chapter contains guides for getting started with tophat.

Installation

The first step on the road to using tophat is to install it. If you download the prebuilt binaries, it is a very simple process.

Installing tophat on Linux

All commands are prefixed either using $ or #. The former is used for commands that can be run as a normal user, latter is used for commands requiring root privileges.

First you have to download tophat from this page. You can do that manually or using this curl command.

$ curl https://tophat2d.dev/dl/tophat-linux -o tophat

When you successfully download the binary, the next step is to install it to a directory, which is in the system path. For example /usr/bin.

# install tophat /usr/bin

To check if tophat was installed correctly, run tophat version.

Installing tophat on Windows

Download the tophat exe from the downloads page. Then move it to a folder of your liking. Whenever you want to make a tophat project, it is recommended to make a shortcut to tophat in the project directory. This way you won't have to use the file dialog every time you run your game.

Running the examples

To run the examples, clone the tophat repository. The examples are in the examples/ folder. On linux, cd to an example you want to run and type tophat. On windows, launch tophat and choose the folder of the example you want to run.

Text editors with umka support

No text editors support umka out-of-the-box, but there are available extensions for the most popular ones.

Hello Tophat!

This chapter shows you how to make a simple hello world program in tophat. Firstly make a new folder. In that folder create a file called main.um, open it in your favorite text editor and write the following in:

import (
	"canvas.um"
	"th.um"
	"window.um"
)

fn init*() {
	window.setup("Hello tophat!", 600, 600)

    window.onFrame.register({
		canvas.drawText("Hello tophat!", { 1, 1 }, th.black, 2)
	})
}

Now run the file. On linux you would cd into the directory and run tophat. On windows, open tophat and choose the directory or make a shortcut to tophat in the folder, so you can run the game by just double-clicking it.

Does everything work? Great! Is there a problem? Contact me at marek@mrms.cz.

Now let's explain all the parts of the example.

import (
	"canvas.um"
	"th.um"
	"window.um"
)

The import statement imports all the modules you will need. The strings in the import statements are file-paths, but all the modules included in the example are tophat builtin modules. This means you can import them even though they aren't on the file system. All modules have their documentation available here.

fn main() {

This declares the init function and exports it. This function your game's entry point.

	window.setup("Hello tophat!", 600, 600)

This function call creates a window. It will set the title to "Hello tophat!" and the dimensions to 600x600.

    window.onFrame.register({

This registers a callback to the onFrame signal. What this means is that the code in the brackets will be called on every frame. This is you game's main loop.

		canvas.drawText("Hello tophat!", { 1, 1 }, th.black, 2)

This function draws the text Hello tophat! at position 1, 1, with black color and scaled 2 times.

Tophat tips

The API reference is your best friend.

Rotations are in degrees.

Assets paths are prefixed with the dir. That is ./ by default, but you can specify it using the dir flag. If you prefix a path with raw://, it will not be changed by tophat.

You can get last frame length from th.delta. This is useful for making your game FPS independant. A good rule of thumb is to multiply any kind of movement by the th.delta value.

Resizing the window won't change how the game is displayed - the viewport stays the same. The viewport size is in the window.wp variable. You can change it to your liking.

Dimensions of one canvas.drawText character is 5x5 px, and the character spacing is 1px.

input.getMouseDelta works even if the cursor is frozen using window.freezeCursor

Tutorials

This chapter contains various tophat tutorials. Please suggest tutorial topics at marek@mrms.cz.

Program structure

Your every project has to have a main file. By default that is main.um, but that can be changed using the main flag. In your main file you need to declare an init function, which needs to be exported. If it isn't exported, you will receive this error:

error: <PWD>/tophat_main.um (4, 11): Unknown identifier init

In this function, you can set up the window, load resources and register to the window.onFrame signal. This signal is emitted on every frame. The following script will print "init" when it is launched and then follow up by printing "frame" on every frame.

import (
    "signal.um"
    "window.um"
)

fn init*() {
    window.setup("Main file structure example")

    printf("init\n")

    window.onFrame.register({
        printf("frame\n")
    })
}

Loading and drawing images

First you need to import the image.um module, which houses all sorts of image functions. We are interested in image.load. It takes a path to an image, which it loads and then returns. Example:

apple := image.load("gfx/apple.png")

You can check the image using the validate method.

if !apple.validate() {
	error("Could not load gfx/apple.png")
}

That's all. The image is now ready to be drawn. You can do that using it's draw method. You need to pass it a transform.

apple.draw({
	p: {5, 20},
	s: {1, 1},
	r: 45 })

This draws the image at position [5, 20] rotated by 45 degrees.

Full example:

import (
	"image.um"
    "signal.um"
	"th.um"
	"window.um"
)

var (
    apple: image.Image
)

fn init*() {
	window.setup()

	apple = image.load("gfx/apple.png")
	if !apple.validate() {
		error("Could not load gfx/apple.png")
	}

	window.onFrame.register({
		apple.draw({
			p: {5, 20},
			s: {1, 1},
			r: 45 })
	})
}

Keyboard input

Getting keyboard and mouse input in tophat is very simple. It is done using the input.um module.

There are two ways to get keyboard input in tophat. The is* functions and the getStr function. The former returns info about a physical key on the keyboard. getStr returns a string typed by the user last cycle.

Example:

for i:=0; i < 256; i++ {
	if input.isPressed(char(i)) {
		printf("%d is pressed\n", i)
	}
}

printf("the user wrote: %s\n", input.getStr())

Getting mouse input is also simple. Mouse buttons act as normal keys, so you can use the is* fuctions. Mouse position can be retrieved using the getMousePos function.

import (
	"input.um"
	"signal.um"
	"th.um"
	"window.um"
)

fn init*() {
	window.setup()

	window.onFrame.register({
		for i:=0; i < 256; i++ {
			if input.isPressed(char(i)) {
				printf("%d is pressed\n", i)
			}
		}

		printf("the user wrote: %s\n", input.getStr())
		
		printf("the cursor is at %s\n", repr(input.getMousePos()))
	})
}

Drawing text

There are two main ways of drawing text on the screen. The first one is the drawText function from canvas.um. It draws a simple 5*5 pixel text. It is useful for debugging, since it's easy to use, but doesn't produce good looking results and only supports ASCII. This makes it unfit for production use.

The second one uses tophat's font.um module, which provides functions for operating with TrueType fonts. You can obtain a Font like this:

f := font.load("unifont.ttf", 32)

Then you can just use it's draw method to draw text. Example:

f.draw("Hello tophat", th.Vf2{20, 20}, th.green)
import (
	"canvas.um"
	"font.um"
	"th.um"
	"window.um"
)

var (
    f: font.Font
)

fn init*() {
	window.setup()

	f := font.load("unifont.ttf", 32, font.filteringNearest)

	window.onFrame.register({
		canvas.drawText("Hello tophat using canvas.um", th.Vf2{1, 1}, th.black, 1)
		f.draw("Hello tophat using unifont", th.Vf2{1, 7}, th.black, 1)
	})
}

Playing sounds

All of the sound stuff is in the audio.um. First step is loading a sound from a file.

mySound := audio.load("sfx/music.mp3")

Supported formats are mp3, flac and wave. You can modify some properties of the sound.

mySound.setLooping(true)
mySound.setVol(0.5)

If you want to play it, use the play method.

mySound.play()

Keep in mind that you can't play a sound multiple times at once. A solution to this is to make copies of a sound using the copy method.

mySound.copy().play()
import (
	"audio.um"
	"th.um"
	"window.um"
)

fn init*() {
	window.setup()

	mySound := audio.load("sfx/music.mp3")
	mySound.setLooping(true)
	mySound.setVol(0.5)
	mySound.play()
}

Making web builds

Tophat games can be exported to run on the web.

Linux

On Linux you can use the th_emscripten_link script. When passed files, the script will make a web build of your game and save it to the wasm-out directory. Example:

Before using the script, you need to install emscripten. On Ubuntu, you can do that using apt install emscripten.

$ ls
main.um image_the_game_needs.png another_resource.txt
$ th_emscripten_link main.um image_the_game_needs.png another_resource.txt
$ ls
main.um image_the_game_needs.png another_resource.txt wasm-out

If you run the script for the first time, it will download some resources from the internet. The script can take some time to run. After it finished, the result is in the wasm-out directory.

Windows

Currently there is no way of making web builds directly on windows. It is recommended to make them using WSL2.

Sprite sheet animations

This tutorial shows how to turn your spritesheets into animation. This is a builtin tophat feature, which makes this a very easy task.

We will need to declare these global variables:

var (
	spriteSheet: image.Image
	atl: atlas.Atlas
	anm: anim.Anim
)

First you have to load the image with your spritesheet:

	spriteSheet = image.load("spritesheet.png")

We then use the image to create an atlas. An atlas is used to operate with images that store subimages in a grid. The first argument to the mk function is the image we will create the atlas from. The second argument are the dimensions of the atlas.

	atl = atlas.mk(spriteSheet, th.Vf2{ 6, 1 })

The atlas is then used to create an animation. The first argument is the atlas with the spritesheet. The second argument is the fps count. You can also add optional arguments specifying start and end frame and the start offset.

	anm = anim.mk(atl, 1)

We now have the animation ready. Now we just have to call it's animate method. The only argument it the time. We can use just th.time in this case.

		anm.animate(th.time)

The animate method uses the the base image's crop method to select the current frame. This means we can now just draw the image.

		spriteSheet.draw(th.mkTransform(th.vf2f(1)))

Full program:

import (
	"anim.um"
	"atlas.um"
	"image.um"
	"signal.um"
	"th.um"
	"window.um"
)

var (
	spriteSheet: image.Image
	atl: atlas.Atlas
	anm: anim.Anim
)

fn init*() {
	window.setup("animation example", 500, 500)
	window.setViewport(th.Vf2{ 5, 5 })

	spriteSheet = image.load("spritesheet.png")
	atl = atlas.mk(spriteSheet, th.Vf2{ 6, 1 })
	anm = anim.mk(atl, 1)

	window.onFrame.register({
		anm.animate(th.time)
		spriteSheet.draw(th.mkTransform(th.vf2f(1)))
	})
}

ui.um tutorial

var gui: ui.Gui
var tbName, tbPwd: ui.TextBox

Here we declare three variables. gui is of the type ui.Gui, which holds the whole UI instance. tbName and tbPwd are both textboxes, which will be used later to get input from the user.

	gui = ui.mk({ 0, 0, 200, 200 }, ui.getDefaultStyle())

This initializes the UI instance. The first argument is the rect the gui will occupy, the second is the style used. For now you can use tophat's default style available using ui.getDefaultStyle().

		layout := ui.LayoutFn{
            ...
		}

This is the layout function, which builds the UI by calling methods on the gui struct. It is ran two times - first time to evaluate user input and second time to draw the user interface.

			gui.stack({ padding: 10 })

First we use the stack container. It puts all the other elements on top of each other. You can also apply padding to it.

			gui.box({
				dimension: 10,
				dir: ui.BoxDirectionDown,
				growType: ui.BoxGrowDimension })

Then we initialize a box container. This container puts all the elements next to each other. The configuration specifies three things:

  • dir - the direction the elements are put in, in this case they will go from up to down
  • growType - specifies the way box will move the elements
  • dimension - this specifies the number of pixels to grow by. This is needed as ui.BoxGrowDimension is specified
				gui.label("User name:", { centerY: true })
				gui.textBox(&tbName)

				gui.label("Password:", { centerY: true })
				gui.textBox(&tbPwd)

Now we can add the textboxes into the container.

			gui.dupStyle()
			gui.getStyle().containerBox.color = 0

			gui.box({
				dimension: 10,
				dir: ui.BoxDirectionUp,
				growType: ui.BoxGrowDimension })
			gui.box({
				subdivisions: 2,
				dir: ui.BoxDirectionRight,
				growType: ui.BoxGrowSubdivision })

This initializes the container for the buttons. Since we want the buttons to be on the bottom, a box with ui.BoxDirectionUp is put onto the stack. However the style dictates, that a box shall have a gray background, which needs to be removed as not to cover the textboxes. Then we push another box. It grows to the right and uses the subdivision grow type. We specify that there will be two elements in it.

				if gui.qbutton("Cancel") {
					window.quit()
				}

				if gui.qbutton("Login") {
					printf("Login: %s %s\n",
						tbName.buffer, tbPwd.buffer)
				}

Now we can just add the two buttons. We use the method qbutton, which adds a button with a label.

			gui.popContainer()
			gui.popContainer()

			gui.popStyle()

			gui.popContainer()
		}

Now we have to pop all the styles and containers. At the end of the layout function, len(gui.container) should equal 1.

		gui.eval(layout)
		gui.draw(layout)

This applies the layout with our UI instance gui.


Full code:

import (
	"th.um"
	"ui.um"
	"window.um"
)

var gui: ui.Gui
var tbName, tbPwd: ui.TextBox

fn init*() {
	window.setup("ui.um tutorial", 600, 600)
	window.setViewport({ 200, 200 })

	gui = ui.mk({ 0, 0, 200, 200 }, ui.getDefaultStyle())

	window.onFrame.register({
		layout := ui.LayoutFn{
			gui.stack({ padding: 10 })

			gui.box({
				dimension: 10,
				dir: ui.BoxDirectionDown,
				growType: ui.BoxGrowDimension })

				gui.label("User name:", { centerY: true })
				gui.textBox(&tbName)

				gui.label("Password:", { centerY: true })
				gui.textBox(&tbPwd)
			gui.popContainer()

			gui.dupStyle()
			gui.getStyle().containerBox.color = 0

			gui.box({
				dimension: 10,
				dir: ui.BoxDirectionUp,
				growType: ui.BoxGrowDimension })
			gui.box({
				subdivisions: 2,
				dir: ui.BoxDirectionRight,
				growType: ui.BoxGrowSubdivision })

				if gui.qbutton("Cancel") {
					window.quit()
				}

				if gui.qbutton("Login") {
					printf("Login: %s %s\n",
						tbName.buffer, tbPwd.buffer)
				}

			gui.popContainer()
			gui.popContainer()

			gui.popStyle()

			gui.popContainer()
		}
		
		gui.eval(layout)
		gui.draw(layout)
	})
}

Umka API reference

Documentation for various builtin tophat modules. They are categorized into multiple groups.

struct Anim

type Anim* = struct {
	// the source atlas
	atl: atlas::Atlas
	// the first cell of the animation
	min: int
	// the last cell of the animation
	max: int
	fps: real32
	// offset in time
	offset: int
}

Anim allows you to animate between individual frames of an atlas.

fn mk

fn mk*(atl: atlas::Atlas, fps: int, min: int = 0, max: int = -1 /* len(atl) - 1 */, offset: int = -1 /* th.time */): Anim {

Anim constructor

fn Anim.animate

fn (anm: ^Anim) animate*(time: int) {

Crops the base atlas to the cell that should be visible at time.

fn Anim.framesPlayed

fn (anm: ^Anim) framesPlayed*(time: int): int {

Returns how many frames were played at time.

struct Atlas

type Atlas* = struct {
	i: image::Image // source image
	cs: th::Vf2 // size of a cell in pixels
	dm: th::Vf2 // amount of cells in image
}

Atlas is an image containing tiles in a square grid.

fn mk

fn mk*(i: image::Image, dm: th::Vf2): Atlas {

i: source image dm: amount of cells

fn Atlas.coords

fn (a: ^Atlas) coords*(n: int): (th::Vf2, std::Err) {

returns the coordinates of the nth tile

fn Atlas.cropSource

fn (a: ^Atlas) cropSource*(at: th::Vf2): std::Err {

Crops the sourse image to only show a wanted tile

enum PackStrategy

const (
	PackSquare* = 0
	PackRow*
	PackColumn*
)

fn pack

fn pack*(images: []image::Image, strategy: int): (Atlas, std::Err) {

Packs an array of images into an atlas

fn Atlas.draw

fn (a: ^Atlas) draw*(at: th::Vf2, t: th::Transform): std::Err {

Draws the tile at at

Module for audio loading and playback.

opaque Sound

type Sound* = struct { _: ^struct{} }

Represents an instance of a playable sound. It is an opaque structure.

enum LoadFlag

type LoadFlag* = enum (uint32) {
	none = 0
	// Streams the audio, only saving 2 seconds into memory.
	stream = 1
	// Loads the sound in async. Use `audio.waitForLoad` to wait for the
	// sounds to load.
	async = 4
}

fn load

fn load*(path: str, flags: LoadFlag = LoadFlag.none): (Sound, std::Err) {

Loads a sounds at path, if there is an error, the underlying pointer will be NULL and validate will return false.

fn Sound.validate

fn (s: ^Sound) validate*(): bool {

Returns true if s loaded correctly.

fn Sound.copy

fn (s: ^Sound) copy*(): (Sound, std::Err) {

Copies the sound. This will create another sound which can be configured and played independently from the original sound.

fn Sound.isPlaying

fn (s: ^Sound) isPlaying*(): bool {

Returns true if the sound is still playing.

fn Sound.play

fn (s: ^Sound) play*() {

Plays the sound.

fn Sound.start

fn (s: ^Sound) start*(): (^Sound, std::Err) {

The start function allows you to play a single sound multiple times. It will create a copy and return a pointer to it, so you can controll it while it is playing. The returned pointer can be discarded.

fn Sound.stop

fn (s: ^Sound) stop*() {

Stops the sound, but keeps the progress. If you want to start from the begginning, use audio.Sound.seekToFrame(0).

fn Sound.setVol

fn (s: ^Sound) setVol*(vol: real32) {

Sets the volume as a multiplier of the base volume.

fn Sound.setPan

fn (s: ^Sound) setPan*(pan: real32) {

Sets the pan of the sound.

fn Sound.setPitch

fn (s: ^Sound) setPitch*(pitch: real32) {

Sets the pitch of the sound.

fn Sound.setLooping

fn (s: ^Sound) setLooping*(looping: bool) {

Sets whether the sound will loop upon finishing.

fn Sound.seekToFrame

fn (s: ^Sound) seekToFrame*(frame: uint) {

Seeks to a specified PCM frame.

fn Sound.frameCount

fn (s: ^Sound) frameCount*(): uint {

Returns length of the sound in PCM frames.

fn Sound.lengthMs

fn (s: ^Sound) lengthMs*(): uint {

Returns length of the sound in ms.

fn Sound.setStartTimeMs

fn (s: ^Sound) setStartTimeMs*(t: uint) {

fn Sound.setStopTimeMs

fn (s: ^Sound) setStopTimeMs*(t: uint) {

Canvas library allowing for drawing basic shapes. Coordinates are based on the screen.

fn drawText

fn drawText*(text: str, pos: th::Vf2, color: uint32, size: th::fu) {

Draws a basic pixel text. Only ascii is supported.

fn textSize

fn textSize*(text: str, scale: th::fu): th::Vf2 {

Returns the size of text taken by an equivalent drawText call.

fn drawRect

fn drawRect*(color: uint32, r: rect::Rect) {

Draws a Rectangle.

fn drawLine

fn drawLine*(color: uint32, b, e: th::Vf2, thickness: th::fu) {

Draws a line.

fn drawRectLines

fn drawRectLines*(color: uint32, r: rect::Rect, thickness: real32 = 1.0) {

Draws rect border.

fn drawQuad

fn drawQuad*(color: uint32, q: th::Quad) {

Draws a convex quad.

fn beginScissorRect

fn beginScissorRect*(r: rect::Rect, debug: bool = false) {

Disable rendering outside of rect r

fn endScissor

fn endScissor*() {

Stops cropping

Color operations. Operate on RGBA uint32 values.

fn hsv

fn hsv*(h, s, v: th::fu, a: th::fu = 1.0): uint32 {

Converts HSV values into RGBA uint32 color. NOTE: Hue is between 0 and 1

fn alpha

fn alpha*(c: uint32, to: th::fu): uint32 {

Sets alpha of the color c to a value in to.

fn rgb

fn rgb*(r, g, b: th::fu, a: th::fu = 1.0): uint32 {

Constructs RGBA uint32 from RGBA of reals.

A CSV parser, which also works for similar formats. It doesn't support quotes, but you can escape characters using a backslash.

fn parse

fn parse*(inp: str, sep: char = ','): [][]str {

Parses input into a 2d string array.

fn encode

fn encode*(inp: [][]str, sep: char = ','): str {

Converts 2d array to csv string.

Module for font rendering. Unicode is supported, but only left to right.

Filtering constants

type Filter* = enum {
	nearest = 0
	linear = 1
}

opaque Font

type Font* = struct { _: ^struct{} }

fn load

fn load*(path: str, size: th::fu, filter: Filter = Filter.linear): (Font, std::Err) {

fn Font.validate

fn (f: ^Font) validate*(): bool {

fn Font.draw

fn (f: ^Font) draw*(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0) {

fn Font.measure

fn (f: ^Font) measure*(text: str): th::Vf2 {

opaque Image

type Image* = struct{ _: ^struct{} }

Represents a drawable image. It is an opaque structure. Images support a color filter. It is applied by multiplying the color of each pixel with the filter.

opaque RenderTarget

type RenderTarget* = struct { _: ^struct{} }

An image you can render to.

fn createRenderTarget

fn createRenderTarget*(size: th::Vf2, filter: int): (RenderTarget, std::Err) {

Creates a render target you can draw to, like to a window. Filter specifies specfifies filtering for resulting image. Image can be retrieved via toImage.

fn RenderTarget.end

fn (rt: ^RenderTarget) begin*(): std::Err {

Begins the render target rendering pass.

fn RenderTarget.end

fn (rt: ^RenderTarget) end*(wp: th::Vf2): std::Err {

Ends the render target rendering pass.

fn RenderTarget.toImage

fn (rt: ^RenderTarget) toImage*(): Image {

Returns the image of the render target. The resulting image has the same lifetime as the base RenderTarget. If you need to use it past the lifetime of the RenderTarget, use the copy method. Do not call setfilter on the resulting image.

fn load

fn load*(path: str): (Image, std::Err) {

Loads an image at path.

fn Image.validate

fn (i: ^Image) validate*(): bool {

Returns true, if i's handle points to an image.

fn Image.flipv

fn (i: ^Image) flipv*(flip: bool) {

Flips image on it's vertical axis.

fn Image.fliph

fn (i: ^Image) fliph*(flip: bool) {

Flips image on it's horizontal axis.

fn Image.draw

fn (i: ^Image) draw*(t: th::Transform, color: uint32 = th::white) {

Draws the image in screen coordinates. It transforms it with t and applies color as a color filter.

fn Image.drawNinepatch

fn (i: ^Image) drawNinepatch*(outer, inner, dest: rect::Rect, color: uint32 = th::white, scale: real = 1.0) {

Draws "nine-patch" image. outer specifies the texture coordinates of the outer rect of source image, inner specifies coordinates of inner rect of source image, positioned relative to outer. You can tint with color.

fn Image.drawOnQuad

fn (i: ^Image) drawOnQuad*(q: th::Quad, color: uint32 = th::white) {

Draws the image on top of a quad with corners of the image positioned on the verticies of the quad.

fn Image.getDims

fn (i: ^Image) getDims*(): th::Vf2 {

Returns width and heigth.

fn Image.crop

fn (i: ^Image) crop*(tl, br: th::Vf2) {

Crops an image. Coordinates are between 0, 0 (top left) and 1, 1 (bottom right)

fn Image.cropPx

fn (i: ^Image) cropPx*(tr, br: th::Vf2) {

Same as Image.crop, but the positions are in pixels.

fn Image.cropRect

fn (i: ^Image) cropRect*(r: rect::Rect) {

Same as Image.crop, but uses a rect instead of two positions.

fn Image.cropQuad

fn (i: ^Image) cropQuad*(q: th::Quad) {

Crop an image using a quad.

fn Image.getCropQuad

fn (i: ^Image) getCropQuad*(): th::Quad {

Crop an image using a quad.

fn mk

fn mk*(data: []uint32, dm: th::Vf2): (Image, std::Err) {

Creates an image from raw data.

fn Image.copy

fn (i: ^Image) copy*(): (Image, std::Err) {

Copies image into a new one.

fn Image.setfilter

fn (i: ^Image) setfilter*(filter: int): std::Err {

Sets a mag/min filter. 0 is nearest, others are linear. This function will regenerate the texture. This means it shouldn't be used in a loop. https://learnopengl.com/img/getting-started/texture_filtering.png left is nearest, right is linear.

fn Image.setData

fn (i: ^Image) setData*(data: []uint32, dm: th::Vf2): std::Err {

Updates the image data. dm are the dimensions of the new image. The new image doesn't have to be the same size as the old one.

fn Image.getData

fn (i: ^Image) getData*(): []uint32 {

Gets the image data. This downloads the data from the GPU on each call. Don't use in performance critical sections.

Module for getting keyboard and mouse input. is* functions return info based on a us QWERTY layout. They are supposed to be used for game controls. For text input use getStr.

Keycode constants

type Key* = enum {
	mouse1         = 1   // NOTE: mouse 2 and 3 key codes are swapped
	mouse2         = 3   //       because sokol uses 3 for middle mouse
	mouse3         = 2   //       button.

	ctrl           = 16
	shift          = 17
	alt            = 18

	space          = 32
	apostrophe     = 39  /* ' */
	comma          = 44  /* , */
	minus          = 45  /* - */
	dot            = 46  /* . */
	slash          = 47  /* / */
	num0           = 48
	num1           = 49
	num2           = 50
	num3           = 51
	num4           = 52
	num5           = 53
	num6           = 54
	num7           = 55
	num8           = 56
	num9           = 57
	semicolon      = 59  /* ; */
	equal          = 61  /* = */
	a              = 65
	b              = 66
	c              = 67
	d              = 68
	e              = 69
	f              = 70
	g              = 71
	h              = 72
	i              = 73
	j              = 74
	k              = 75
	l              = 76
	m              = 77
	n              = 78
	o              = 79
	p              = 80
	q              = 81
	r              = 82
	s              = 83
	t              = 84
	u              = 85
	v              = 86
	w              = 87
	x              = 88
	y              = 89
	z              = 90
	leftBracket    = 91  /* [ */
	backslash      = 92  /* \ */
	rightBracket   = 93  /* ] */
	graveAccent    = 96  /* ` */
	world1         = 161 /* non-US #1 */
	world2         = 162 /* non-US #2 */
	escape         = 256
	enter          = 257
	tab            = 258
	backspace      = 259
	insert         = 260
	delete         = 261
	right          = 262
	left           = 263
	down           = 264
	up             = 265
	pageUp         = 266
	pageDown       = 267
	home           = 268
	end            = 269
	capsLock       = 280
	scrollLock     = 281
	numLock        = 282
	printScreen    = 283
	pause          = 284
	fn0            = 289
	fn1            = 290
	fn2            = 291
	fn3            = 292
	fn4            = 293
	fn5            = 294
	fn6            = 295
	fn7            = 296
	fn8            = 297
	fn9            = 298
	fn10           = 299
	fn11           = 300
	fn12           = 301
	fn13           = 302
	fn14           = 303
	fn15           = 304
	fn16           = 305
	fn17           = 306
	fn18           = 307
	fn19           = 308
	fn20           = 309
	fn21           = 310
	fn22           = 311
	fn23           = 312
	fn24           = 313
	fn25           = 314
	kp0            = 320
	kp1            = 321
	kp2            = 322
	kp3            = 323
	kp4            = 324
	kp5            = 325
	kp6            = 326
	kp7            = 327
	kp8            = 328
	kp9            = 329
	kpDecimal      = 330
	kpDivide       = 331
	kpMultiply     = 332
	kpSubtract     = 333
	kpAdd          = 334
	kpEnter        = 335
	kpEqual        = 336
	leftShift      = 340
	leftControl    = 341
	leftAlt        = 342
	leftSuper      = 343
	rightShift     = 344
	rightControl   = 345
	rightAlt       = 346
	rightSuper     = 347
	menu           = 348
}

Gamepad button number

type GamepadButton* = enum {
	a
	b
	x
	y
	lt
	rt
	lb
	rb
	select
	start
	up
	down
	left
	right
	lstick
	rstick
	_count
}

Gamepad stick number

type GamepadStick* = enum {
	left
	right
}

fn getMousePos

fn getMousePos*(): th::Vf2 {

Returns the position of mouse cursor in relation to the screen.

fn getGlobalMousePos

fn getGlobalMousePos*(): th::Vf2 {

Returns the position of mouse cursor in relation to cam.

fn isPressed

fn isPressed*(code: Key): bool {

Returns true if key is pressed. Either use codes defined in the file above, or pass lower case char/number.

fn isPressedc

fn isPressedc*(code: char): bool {

Like isPressed, but you can pass char as the code.

fn isJustPressed

fn isJustPressed*(code: Key): bool {

Returns, whether code was just pressed this loop.

fn isJustPressedc

fn isJustPressedc*(code: char): bool {

Like isJustPressed, but you can pass char as the code.

fn isPressedRepeat

fn isPressedRepeat*(code: Key): bool {

Returns, whether code was just pressed this loop, with key repeat.

fn isPressedRepeatc

fn isPressedRepeatc*(code: char): bool {

Like isPressedRepeat, but you can pass char as the code.

fn isJustReleased

fn isJustReleased*(code: Key): bool {

Returns true if a key was just released.

fn isJustReleasedc

fn isJustReleasedc*(code: char): bool {

Like isJustReleased, but you can pass char as the code.

fn clear

fn clear*(code: Key) {

Clears both the pressed and justPressed state of a code.

fn clearc

fn clearc*(code: char) {

Like clear, but you can pass char as the code.

fn getStr

fn getStr*(): str {

Returns a string entered by the user in the last cycle.

fn getMouseDelta

fn getMouseDelta*(): th::Vf2 {

Returns the difference between mouse positions in the last cycle. Will work even if window.freezeCursor is enabled.

fn getMouseScroll

fn getMouseScroll*(): th::Vf2 {

Returns the scroll wheel value

fn gamepads

fn gamepads*(): []Gamepad {

Returns a list of gamepads that are currently connected.

fn gamepad

fn gamepad*(): Gamepad {

Returns the connected gamepad.

fn isPressed

fn (g: ^Gamepad) isPressed*(button: GamepadButton): bool {

Returns true if the gamepad button is pressed.

fn isJustPressed

fn (g: ^Gamepad) isJustPressed*(button: GamepadButton): bool {

Returns true if the gamepad button was just pressed.

fn isJustReleased

fn (g: ^Gamepad) isJustReleased*(button: GamepadButton): bool {

Returns true if the gamepad button was just released.

fn pressure

fn (g: ^Gamepad) pressure*(button: GamepadButton): th::fu {

Returns the pressure of the gamepad button in the range [0, 1].

fn stick

fn (g: ^Gamepad) stick*(stick: GamepadStick): th::Vf2 {

Returns the movement of the gamepad stick in the range [-1, 1] for each axis.

fn rumble

fn (g: ^Gamepad) rumble*(left, right: th::fu) {

Rumbles/vibrates the gamepad.

Particles allow for performant and random particle systems.

struct Particle

type Particle* = struct {

Particle struct. You can tweak the start_time for godot-like explossivness.

struct Emitter

type Emitter* = struct {
	pos: th::Vf2 // position
	dm: th::Vf2 // size of the emittion area
	gravity: th::Vf2 // gravity
	repeat: bool // if false, particle won't be renewed
	active: bool // false, if there aren't any active particles anymore

	angle: th::Vf2 // angle in which particles are emitted

	lifetime: th::uu // lifetime of particles
	lifetimeRandomness: th::fu // randomness in %/100

	velocity: th::fu // velocity
	velocityRandomness: th::fu // randomness in %/100

	size: th::fu // size
	sizeRandomness: th::fu // randomness in %/100
	maxSize: th::fu // size at the end of particles lifetime

	rotation: th::fu
	rotationRandomness: th::fu
	maxRotation: th::fu

	colors: []uint32 // array of colors, which are interpolated between

	particles: []Particle // list of particles
}

Emitter. This is where everything is configured.

fn Emitter.draw

fn (e: ^Emitter) draw*(t: int32) {

Draws and updates the particles.

fn Emitter.genParticles

fn (e: ^Emitter) genParticles*(time, count: uint, explosiveness: th::fu = 0.0) {

Generates particles for an emitter. The time specifies the time the first particles is emitted. The explosiveness argument specifies the interval at which particles are emitted using this formula: / umka / e.lifetime / count * explosiveness /

Builtin collision functions. The ic argument stores the collision position.

fn lineToLine

fn lineToLine*(b1, e1, b2, e2: th::Vf2, ic: ^th::Vf2): bool {

Checks for a collision between 2 lines specified by their end points.

fn vf2ToQuad

fn vf2ToQuad*(p: th::Vf2, q: th::Quad, ic: ^th::Vf2): bool {

Checks for a collision between a vf2 and a quad.

fn lineToQuad

fn lineToQuad*(b, e: th::Vf2, q: th::Quad, ic1, ic2: ^th::Vf2): bool {

Check for a collision between a line and quad edges.

fn quadToQuad

fn quadToQuad*(q1, q2: th::Quad, ic: ^th::Vf2): bool {

Check for a collision between two quads.

fn vf2ToRect

fn vf2ToRect*(p: th::Vf2, r: rect::Rect): bool {

Check for a collision between a vf2 and a rectangle.

fn rectToRect

fn rectToRect*(r1, r2: rect::Rect): bool {

Check for a collision between two rects

struct Ent

type Ent* = struct {
	// used as a collider, used as backup when invalid image is supplied
	r: rect::Rect
	// used in drawing
	i: image::Image
	// used to transform and translate the image and rect
	t: th::Transform
	// used as a color of the rect and a color filter for the image
	c: uint32
}

Entity is the main game object. It features drawing and collisions. Every entity has an image used for drawing and a rectangle used for collisions. It also has a transform used for transforming it's image and rectangle.

struct Coll

type Coll* = struct {
	index: th::uu
	pos: th::Vf2
}

Value returned by get coll. It contains a position where the collision happened and the index of the entity involved in said collision.

fn Ent.draw

fn (e: ^Ent) draw*() {

Draws the entity onto the screen.

fn mk

fn mk*(img: image::Image = image::Image{}, t: th::Transform = th::Transform{ s: {1, 1} }): Ent {

ent's constructor

fn Ent.getColl

fn (e: ^Ent) getColl*(s: []^Ent, maxColls: th::uu): []Coll {

Checks collisions of e with entities in s. Checks at max maxColl collisions. If s contains e, the collision won't be returned.

fn Ent.animate

fn (e: ^Ent) animate*(fps: int, frames: ^[]image::Image, t: int) {

Animates the entity's image with one of the anim array. Won't begin on the first frame. If you want that, use anim.Anim.

Misc functions.

fn readall DEPRECATED

fn readall*(path: str): str {

Use std.freadall instaed.

fn stepify

fn stepify*(val, step: th::fu): th::fu {

Snaps a value to step.

fn maxf

fn maxf*(a, b: th::fu): th::fu {

fn minf

fn minf*(a, b: th::fu): th::fu {

struct Mesh

type Mesh* = struct {
	// The mesh data.
	d: []bool
	// The dimensions and position of the mesh, r.w == w * s
	r: rect::Rect
	// Width of the mesh.  Used to adress the mesh data.
	w: th::uu
	// Scale of one cell (cell is a square)
	s: th::fu
}

Mesh is a 2d array of bool cells. If a cell is true, the cell can be used in a path. The mesh is located in a world using the r field. A cell can have an arbitrary size as specified by s.

The mesh can be edited using the addQuad method, but it should be trivial to add your own, similar methods.

Please use the mk constructor to construct a Mesh, unless you really know what you're doing.

fn mk

fn mk*(r: rect::Rect, s: th::fu): Mesh {

Creates a new nav mesh. r : the rectangle of the mask 's' : the scale of the mask

fn Mesh.addQuad

fn (m: ^Mesh) addQuad*(q: th::Quad) {

Sets mask's fields overlapping q to false.

fn Mesh.nav

fn (m: ^Mesh) nav*(p1, p2: th::Vf2): []th::Vf2 {

Navigates between p1 and p2. Returns the path as an array of th.Vf2s. If it doesn't find any path, or one of the points is outside of the mask, returns an empty array.

fn Mesh.draw

fn (m: ^Mesh) draw*(alpha: real32 = 1.0) {

Draws the mesh for debugging purposes.

struct Ray

type Ray* = struct {
	pos: th.Vf2
	l: th.fu // length
	r: th.fu // rotation
}

Ray is a line specified by an origin, length and a rotation.

fn mk

fn mk*(pos: th.Vf2, l: th.fu, r: th.fu = 0.0): Ray {

Ray constructor

fn Ray.getColl

fn (r: ^Ray) getColl*(s: []^ent.Ent, maxColls: th.uu): []ent.Coll {

Checks the ray's collisions with a scene of ents. Similar to ent.Ent.getColl

fn Ray.getTilemapColl

fn (r: ^Ray) getTilemapColl*(t: tilemap.Tilemap, ic: ^th.Vf2): bool {

Gets ray's collision to a tilemap.

fn Ray.getEnd

fn (r: ^Ray) getEnd*(): th.Vf2 {

Returns the other point of the ray.

fn Ray.draw

fn (r: ^Ray) draw*(color: uint32, thickness: th.fu) {

Draws the ray to the screen.

struct Rect

type Rect* = struct {
	x, y, w, h: th::fu
}

A set of points representing a rectangle.

fn mk

fn mk*(x, y, w, h: th::fu): Rect {

fn mk

fn fromVf2*(p: th::Vf2, dm: th::Vf2): Rect {

Creates a rect from two Vf2s - the position and the dimensions.

fn Rect.getPos

fn (r: ^Rect) getPos*(): th::Vf2 {

fn Rect.getDims

fn (r: ^Rect) getDims*(): th::Vf2 {

fn Rect.getEnd

fn (r: ^Rect) getEnd*(): th::Vf2 {

returns where the second point of the rectangle lies.

fn Rect.transformed

fn (r: ^Rect) transformed*(t: th::Transform): th::Quad {

Transforms a rect into a quad. Order:

  1. scale
  2. rotation
  3. position

fn Rect.shrink

fn (r: ^Rect) shrink*(p: th::Vf2): Rect {

Shrink the rectangle by p pixels from all sides.

fn Rect.shift

fn (r: ^Rect) shift*(p: th::Vf2): Rect {

Shift the rectangle by p pixels.

fn Rect.scale

fn (r: ^Rect) scale*(p: th::Vf2): Rect {

Multiply the dimensions by p

fn Rect.center

fn (r: ^Rect) center*(): th::Vf2 {

Returns the position, which is the center of the rect.

fn Rect.centerWithinRect

fn (r: ^Rect) centerWithinRect*(child: Rect): Rect {

Centers child with the rect r.

A module for importless communication between modules. A signal is a set of callbacks. You can use signals directly in your own structs if you want them to be instance specific, of you can use global signals which are adressed by a string name.

type Callback

type Callback* = fn(args: []any)

args is a list of arguments passed to the emit method.

type Id

type Id* = uint

type Signal

type Signal* = map[Id]Callback

fn mk

fn mk*(): Signal {

Signal constructor

fn Signal.register

fn (this: ^Signal) register*(callback: Callback): Id {

Registers a callback to a signal and returns the callback id.

fn Signal.remove

fn (this: ^Signal) remove*(id: Id) {

Removes a callback by id.

fn Signal.emit

fn (this: ^Signal) emit*(args: ..any) {

Emits a signal.

fn Signal.clear

fn (this: ^Signal) clear*() {

Removes all signal handlers.

fn register

fn register*(name: str, callback: Callback): Id {

Registers a callback to a global signal. There is no need to explicitly create global signals. Returns id of the added callback

fn remove

fn remove*(name: str, id: Id) {

Removes a callback from a global signal by id.

fn remove

fn clear*(name: str) {

Removes all signal handlers from a global signal.

fn emit

fn emit*(name: str, args: ..any): std::Err {

Calls all callbacks associated with the passed global signal name.

Module with useful variables and types. Variables: time, delta, platform Constants: black, white, red, green, blue, yellow, magenta, cyan.

Tophat type aliases

type fu* = real32
// standard type for integer values
type iu* = int32
// standard type for unsigned values
type uu* = uint32

standard type for real values

enum ErrCode

type ErrCode* = enum (int32) {
	ok = 0
	failure = 1
	io
	bad_enum
	bad_action
	bad_input
	alloc
	already
	out_of_bounds
}

struct Vf2

type Vf2* = struct {
	x, y: fu
}

vector 2

fn mkVf2

fn mkVf2*(x: fu = 0, y: fu = 0): Vf2 {
	return Vf2{x, y}
}

Vf2 constructor

fn Vf2.rotated

fn (p: ^Vf2) rotated*(origin: Vf2, rot: fu): Vf2 {

rotates p around origin with rot in degrees

fn Vf2.distanceTo

fn (p1: ^Vf2) distanceTo*(p2: Vf2): fu {

distance between p1 and p2

fn Vf2.angleTo

fn (p1: ^Vf2) angleTo*(p2: Vf2): real {

Angle between p1 and p2

fn Vf2.abs

fn (p: ^Vf2) abs*(): Vf2 {

Absolute value of a vector.

fn Vf2.round

fn (p: ^Vf2) round*(): Vf2 {

Rounds a vector.

fn Vf2.trunc

fn (p: ^Vf2) trunc*(): Vf2 {

Truncates a vector.

fn Vf2.floor

fn (p: ^Vf2) floor*(): Vf2 {

Floors a vector.

fn Vf2.ceil

fn (p: ^Vf2) ceil*(): Vf2 {

Ceils a vector.

fn vf2f

fn vf2f*(f: fu): Vf2 {

Creates a vector with both x and y set to f

fn Vf2.sub

fn (p: ^Vf2) sub*(p2: Vf2): Vf2 {

Subtracts a vector from another one.

fn Vf2.subf

fn (p: ^Vf2) subf*(f: fu): Vf2 {

Subtracts a fu from a vector.

fn Vf2.add

fn (p: ^Vf2) add*(p2: Vf2): Vf2 {

Adds a vector to another one.

fn Vf2.addf

fn (p: ^Vf2) addf*(f: fu): Vf2 {

Adds a fu to a vector.

fn Vf2.div

fn (p: ^Vf2) div*(p2: Vf2): Vf2 {

Divides a vector by another one.

fn Vf2.divf

fn (p: ^Vf2) divf*(f: fu): Vf2 {

Divides a vector by a fu.

fn Vf2.mul

fn (p: ^Vf2) mul*(p2: Vf2): Vf2 {

Multiplies a vector by another one.

fn Vf2.mulf

fn (p: ^Vf2) mulf*(f: fu): Vf2 {

Multiplies a vector by a fu.

fn Vf2.mag

fn (p: ^Vf2) mag*(): fu {

Returns the magnitude of a vector p.

fn Vf2.norm

fn (p: ^Vf2) norm*(): Vf2 {

Normalizes a vector.

fn Vf2.dot

fn (p: ^Vf2) dot*(q: Vf2): fu {

Calculates dot product between 2 vectors.

struct Transform

type Transform* = struct {
	p: Vf2 // position
	s: Vf2 // scale
	o: Vf2 // origin
	r: fu  // rotation
}

Struct defining transformation. Used for example by entities.

fn mkTransform

fn mkTransform*(p: Vf2, s: Vf2 = Vf2{1, 1}, o: Vf2 = Vf2{0, 0}, r: fu = 0.0): Transform {

Transform constructor

fn Transform.transformed

fn (o: ^Transform) transformed*(t: Transform): Transform {

Transforms a transform with another transform.

fn Vf2.transformed

fn (v: ^Vf2) transformed*(t: Transform): Vf2 {

Transforms a vf2 to another vf2. Order:

  1. scale
  2. rotation
  3. position

This allows conversion from a relative to an absolute vf2.

type Quad

type Quad* = [4]Vf2

fn Quad.getMax

fn (q: ^Quad) getMax*(): Vf2 {

Gets the maximum coordinate.

fn Quad.getMin

fn (q: ^Quad) getMin*(): Vf2 {

Gets the minimum coordinate.

fn Quad.getDims

fn (q: ^Quad) getDims*(): Vf2 {

Returns the dimensions of the quad's bounding box

fn getGlobal

fn getGlobal*(): ^struct{} {

returns a pointer to the th_global. Set this as your extensions thg.

fn getFuncs

fn getFuncs*(): ^struct{} {

returns pointer to tophat functions. Pass this to th_ext_set.

var enableErrrors

var enableErrors*: bool = true

If true, errors will result in a call to exit(255), otherwise printf is used.

Color constants

const (
	black* = 0xff
	white* = 0xffffffff
	red* = 0xff0000ff
	green* = 0x00ff00ff
	blue* = 0x0000ffff
	yellow* = 0xffff00ff
	magenta* = 0xff00ffff
	cyan* = 0x00ffffff
)

enum Platform

type Platform* = enum {
	unknown
	linux
	windows
	macOs
	web
}

Misc variables

var (
	// time in ms from start of the game
	time*: uint
	// length of the last frame in ms
	delta*: int
	// platform tophat is running on
	platform*: Platform
)

Tilemaps allow for easy level construction and fast collisions. You can even use them for some games instead of entities (tetris comes to mind)

Direction constants used for autotile

const (
	top* = 1
	right* = 2
	bot* = 4
	left* = 8
)

struct Tilemap

type Tilemap* = struct {
	atlas: atlas::Atlas
	pos: th::Vf2
	w: th::uu // width of tilemap
	cells: []th::uu // all cells (this will draw the tile in tiles with number in cells - 1)
	collMask: []bool // if true, the tile collides
	scale: th::fu
}

Tilemap struct

fn mk

fn mk*(cells: []th::uu, w: th::uu, at: atlas::Atlas, scale: th::fu = 1): Tilemap {

fn Tilemap.edit

fn (t: ^Tilemap) edit*(x, y, tile: int) {

Sets tile at [x, y] to tile.

fn Tilemap.draw

fn (t: ^Tilemap) draw*() {

Draws the tilemap.

fn Tilemap.getColl

fn (t: ^Tilemap) getColl*(e: ent::Ent, ic: ^th::Vf2, pos: ^th::Vf2): bool {

Checks whether e collides with any of the tiles in t, which are in the collmask.

  • ic[out] - the position where a collision occured
  • pos[out] - coordinates of a tile where a collision occured

Note: While there may be multiple collisions with a tilemap, this function will only return one.

fn Tilemap.getCollLine

fn (t: ^Tilemap) getCollLine*(b, e: th::Vf2, ic: ^th::Vf2): bool {

Check for a collision between a tilemap and a line. ic will be a point of an intersection, if there is one.

fn Tilemap.autotile

fn (t: ^Tilemap) autotile*(src, tileCfg: []th::uu, tile: th::uu) {

Autotile turns all tile tiles in src into tiles in tileCfg, so they follow up correctly. tileCfg is an array of 16 tiles. They are placed in a way where OR of all the places where the tile continues (top, right bot, right). The constants for them are defined in this file. Example: tileCfg[top | bot] = 21 top | bot would look something like this: |

ui.um offers an immediate GUI library suitable both for simple game menus and more complex applications. See the tutorial for example usage.

struct BoxStyle

type BoxStyle* = struct {
	img: image::Image
	outer, inner: rect::Rect
	scale: th::fu
	color: uint32
}

BoxStyle describes how a box within the GUI is styled. In this case box can be anything, ranging from a button to a container. By default the box is drawn using the image.Image.drawNinepatch method. However if the image is invalid, a rectangle with color color is drawn.

interface Font

type Font* = interface {
	draw(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0)
	measure(test: str): th::Vf2
}

This interface is used by all elements that draw text. A font.Font implements this interface.

struct PixelFont

type PixelFont* = struct { }

This struct implement the Font interface using the canvas.um pixel font.

struct Style

type Style* = struct {
	// current font
	ft: Font
	// font scale
	ftScale: th::fu
	// text color
	ftColor: uint32

	// Positive box - i. e. unpressed button
	posBox: BoxStyle
	// Negative box - i. e. pressed button, text box
	negBox: BoxStyle
	// Used to draw containers
	containerBox: BoxStyle
}

Style is used as a global state for styling the GUI.

interface Container

type Container* = interface {
	// This adds a rectangle to the container, and returns the rectangle
	// which was actually added (the container can modify the rectangle).
	// See individual containers for further documentation.
	pushRect(r: rect::Rect): rect::Rect
	getDims(): rect::Rect
}

Containers are used to layout elements or other containers.

struct Gui

type Gui* = struct {
	// user context passed to layout functions
	ctx: any

	// the index of the current selection. TODO implement properly
	selection: int
	// true, if the layout is being evaluated, not drawn
	isEval: bool
	// contains more unexported fields

This is the main struct of any UI. Styles and containers are in a stack.

fn mk

fn mk*(r: rect::Rect, s: Style): Gui

Creates a new gui spanning r, with style s.

type LayoutFn

type LayoutFn* = fn(gui: ^Gui)

The layout function calls different element or container methods to create the user interface itself. It is called in the eval and draw.

fn BoxStyle.draw

fn (this: ^BoxStyle) draw*(r: rect::Rect) {

Draws a rectangle using a BoxStyle

fn Gui.pushStyle

fn (this: ^Gui) pushStyle*(s: Style) {

Pushes a style onto the style stack.

fn Gui.popStyle

fn (this: ^Gui) popStyle*() {

Pops a style from the style stack.

fn Gui.getStyle

fn (this: ^Gui) getStyle*(): ^Style {

Returns a pointer to the style atop the style stack.

fn Gui.getContainer

fn (this: ^Gui) getContainer*(): Container {

Returns the container atop the container stack.

fn Gui.pushRect

fn (this: ^Gui) pushRect*(r: rect::Rect): rect::Rect {

Shortcut to this.getContainer().pushRect(r)

fn Gui.getDims

fn (this: ^Gui) getDims*(): rect::Rect {

Shortcut to this.getContainer().getDims(r)

fn Gui.dupStyle

fn (this: ^Gui) dupStyle*() {

Duplicates the current style.

fn Gui.pushContainer

fn (this: ^Gui) pushContainer*(c: Container) {

Pushes a container onto the container stack.

fn Gui.popContainer

fn (this: ^Gui) popContainer*() {

Pops a container from the container stack.

fn Gui.eval

fn (this: ^Gui) eval*(layout: LayoutFn) {

Runs the evaluation phase on layout.

fn Gui.draw

fn (this: ^Gui) draw*(layout: LayoutFn) {

Runs the draw phase on layout.

enum BoxGrow

type BoxGrow* = enum {
	dimension
	subdiv
	span
	pxSpan
}

The different types of "growing" the box can do.

enum BoxDirection

type BoxDir* = enum {
	down
	right
	up
	left
}

Direction in which the box will grow.

struct BoxConfig

type BoxConfig* = struct {
	// dimension to grow by if `BoxGrowDimension` is used
	dimension: th::fu
	// number of subdivisions if `BoxGrowSubdivisions` is used
	subdivisions: uint
	// the grow type
	growType: BoxGrow
	// the grow direction
	dir: BoxDir
	// Specifies the values used with BoxGrowSpan nad BoxGrowPxSpan.
	// If BoxGrowSpan is used, 1 equals the size of the box divided by the sum
	// of all spans.
	// If BoxGrowPxSpan is used, 1 equals one pixel.
	span: []th::fu
	// rect passed to the current container
	rect: rect::Rect
	// padding inserted after each element
	padding: th::fu
}

Configuration of the Box container.

struct Box

type Box* = struct {
	grow: th::fu
	spanCursor: int
	dm: rect::Rect
	cfg: BoxConfig
}

Box is the main layout. It puts the elements next to each other, according to the config.

If the dimensions of the rect passed to pushRect are non zero, they will be kept. Position is always forced.

fn Gui.box

fn (gui: ^Gui) box*(cfg: BoxConfig = {
	dimension: 30,
	growType: BoxGrow.dimension,
	dir: BoxDir.down }) {

Adds the Box container to the gui.

struct StackConfig

type StackConfig* = struct {
	rect: rect::Rect
	padding: th::fu
}

Configuration for the Stack container.

struct Stack

type Stack* = struct {
	dm: rect::Rect
	cfg: StackConfig
}

The stack container puts elements on top of each other. If a property of the rect passed to pushRect is zero, it will be changed to an equivalent property of the containers' dimensions (minus the padding), else it will stay the same. This means stack can be used either to put multiple elements/containers on top of each other, or for absolutely positioned elements.

fn Gui.stack

fn (gui: ^Gui) stack*(cfg: StackConfig = {}) {

Adds the Stack container to the gui.

struct ScrollAreaConfig

type ScrollAreaConfig* = struct {
	rect: rect::Rect
	// scroll speed. Default is 1
	speed: real32
	// if true, scrolling will be horizontal
	horizontal: bool
}

Configuration for the scroll area.

struct ScrollArea

type ScrollArea* = struct {
	dm: rect::Rect
	cfg: ScrollAreaConfig
	scroll: ^real32
	maxScroll: real32
}

Scroll area is a container which allows the user to scroll. It acts as a stack container, but all the elements are shifted by the scroll.

fn Gui.scrollArea

fn (gui: ^Gui) scrollArea*(scroll: ^real32, maxScroll: real32, cfg: ScrollAreaConfig = {}) {

Pushes a scroll area. scroll is both input and output value. Both scroll and maxScroll are in pixels.

struct ButtonConfig

type ButtonConfig* = struct {
	rect: rect::Rect
}

Configuration for the button.

fn Gui.button

fn (gui: ^Gui) button*(cfg: ButtonConfig = {}): bool {

Adds a button to the gui. The button acts like a Stack container, but it is drawn using the pos/nexBox styles and handles clicks. If the button is pressed and the gui is in the eval phase, the return value will be true.

struct LabelConfig

type LabelConfig* = struct {
	// centers the label along the X axis, enables `stretchX`
	centerX: bool
	// centers the label along the Y axis, enables `stretchY`
	centerY: bool
	// if false, the rect passed to `pushRect` will have the width of
	// the text, else it will be 0
	stretchX: bool
	// if false, the rect passed to `pushRect` will have the height of
	// the text, else it will be 0
	stretchY: bool
	// forces the rectangle the label will use
	rect: rect::Rect
}

fn Gui.label

fn (gui: ^Gui) label*(text: str, cfg: LabelConfig = {

Draws a label using the current font style.

fn Gui.qbutton

fn (gui: ^Gui) qbutton*(text: str, cfg: ButtonConfig = {}): bool {

Adds a button with a label to gui.

struct TextBoxConfig

type TextBoxConfig* = struct {
	// force the rect of the text box
	rect: rect::Rect
}

struct TextBox

type TextBox* = struct {
	// index of the cursor
	cursor: int
	// contains other unexported rules...

fn TextBox.clear

fn (this: ^TextBox) clear*() {

Clears the textbox

fn TextBox.getBuf()

fn (this: ^TextBox) getBuf*(): str {

Get the content of the textbox.

fn TextBox.setBuf()

fn (this: ^TextBox) setBuf*(s: str) {

Get the content of the textbox.

fn Gui.textBox

fn (gui: ^Gui) textBox*(tb: ^TextBox, cfg: TextBoxConfig = {}) {

Adds a single line textbox to the gui. TODO:

  • right-to-left unicode is not supported.
  • no selection
  • multiline
  • copy paste (now implemented but in a limited way due to the lack of selection)
  • common input shortcuts
  • ctrl+delete / ctrl+backspace (delete word)

struct ImageConfig

type ImageConfig* = struct {
	stretchX, stretchY: bool
	centerX, centerY: bool
	color: uint32
	scale: th::Vf2
	rect: rect::Rect
}

Configuration for the images element. Behaves similarly to labels.

fn Gui.image

fn (gui: ^Gui) image*(i: image::Image, cfg: ImageConfig = {
	stretchX: true,
	stretchY: true,
	color: th::white,
	scale: { 1, 1 } }) {

Draws an image.

fn getDefaultStyle

fn getDefaultStyle*(): Style {

Returns the default tophat ui style.

fn mk

fn mk*(r: rect::Rect, s: Style): Gui {

Creates a GUI instance.

Cursor types

type Cursor* = enum {
	system = 0		// Default system cursor
	arrow			// Normal cursor; Arrow cursor
	iBeam			// 'I' text cursor; I-Beam
	crosshair		// '+' cursor; Select region cursor
	finger			// Index finger pointing cursor; Click cursor
	sizeEW			// '<->' cursor; Resize width cursor; Resize horizontally cursor; East-West resize cursor
	sizeNS			// Resize height cursor; Resize vertically cursor; North-South resize cursor
	sizeNWSE			// Resize width and height from the right side cursor; Northwest-Southeast resize cursor
	sizeSWNE			// Resize width and height from the left side cursor; Southwest-Northeast resize cursor
	sizeAll			// Resize all cursor; Move cursor
	no			// '(/)' cursor; Disabled cursor; Disallowed cursor
	count_
}

Window dimensions

var (
	w*, h*: int32
)

Viewport size

var wp*: th::Vf2

signal OnFrame

var onFrame*: signal::Signal

signal OnDestroy

var onDestroy*: signal::Signal

fn setViewport

fn setViewport*(dm: th::Vf2) {

Sets the dimensions of the viewport. The dimensions are saved in the wp variable.

dm : dimension of the viewport

fn isDpiEnabled

fn isDpiEnabled*(): bool {

Returns true if DPI awareness was enabled

fn getDpiScaleFactor

fn getDpiScaleFactor*(): th::fu {

Returns the DPI scaling of the current window. If dpiAware was not enabled in window setup, this function will return 1.0 (default scaling).

fn setup

fn setup*(title: str = "tophat game", width: int = 400, height: int32 = 400) {

Sets up the engine and opens a window.

fn cycle

fn cycle(delta: real) {

Cycle needs to be called every cycle for the window to work. If the window was closed, it returns false.

fn setFullscreen

fn setFullscreen*(fullscreen: bool) {

Makes window go full screen

fn isFullscreen

fn isFullscreen*(): bool {

Returns true if window is fullscreen

fn getDims

fn getDims*(): th::Vf2 {

Returns dimensions of the window in screen pixels.

fn setTargetFps

fn setTargetFps*(fps: int) {

Sets the fps limit.

fps : amount of fps the limit should be set to

fn setDims

fn setDims*(dm: th::Vf2) {

Sets the dimensions of the window.

dm : the target dimensions in screen pixels

fn setIcon

fn setIcon*(img: image::Image) {

Sets the window icon.

fn showCursor

fn showCursor*(show: bool) {

Show or hide the cursor, linux only.

fn freezeCursor

fn freezeCursor*(freeze: bool) {

Freezes the cursor in place. input.getMouseDelta will still report mouse movements. The cursor will be automatically hidden.

fn setCursor

fn setCursor*(cursor: Cursor) {

Allows you to set the displaying cursor. Refer to the cursors section for available cursors.

fn requestExit

fn requestExit*() {

Requests the window to close.

fn setClipboard

fn setClipboard*(s: str) {

Puts a string to the system clipboard.

fn getClipboard

fn getClipboard*(): str {

Gets a string from the system clipboard.

fn setViewportOffset

fn setViewportOffset*(s: th::Vf2) {

Sets the offset of the viewport.

fn getViewportOffset

fn getViewportOffset*(): th::Vf2 {

Gets the offset of the viewport (as set by setViewportShift)