ToolGUI

Go Reference

This Go package provides a framework for rapidly building interactive data dashboards and web applications. It aims to offer a similar development experience to Streamlit for Python users.

⚠️ Under Development:

The API for this package is still under development, and may be subject to changes in the future.

Demo page: https://toolgui-demo.fly.dev/

screen-shot

Server-Client

Step by step Hello World

  1. Create main.go:
package main

import (
	"github.com/VoileLab/toolgui/toolgui/tgcomp"
	"github.com/VoileLab/toolgui/toolgui/tgexec"
	"github.com/VoileLab/toolgui/toolgui/tgframe"
)

func main() {
	app := tgframe.NewApp()
	app.AddPage("index", "Index", func(p *tgframe.Params) error {
		tgcomp.Text(p.Main, "Hello world")
		return nil
	})

	tgexec.NewWebExecutor(app).StartService(":3001")
}
  1. Create go.mod and download toolgui:
go mod init toolgui-helloworld
go mod tidy
  1. Run helloworld
go run main.go

Explain

  • Create a ToolGUI App: The App intance include the info that app needs.
app := tgframe.NewApp()
  • Register a page in App: Tell App instance, we will have a page in the App.
    • index is the name.
    • Index is the title.
app.AddPage("index", "Index", ...)
  • The Page Func: Draw a text component in the Main container.
func(p *tgframe.Params) error {
	tgcomp.Text(p.Main, "Hello world")
	return nil
}
  • WebExecuter: The App only includes the logic of app, but not includes GUI. The we executer provide web server GUI interface for App.
tgexec.NewWebExecutor(app).StartService(":3001")

How it works?

Basic

ui-state-pagefunc

The key concept is that in the Page Function, the UI component interact immediately with the running logic.

For example:

if tgcomp.Button(p.State, p.Main, "Click me") {
    tgcomp.Text(p.Main, "Hi")
}

In the first call (For example: When the web page is entering.), The Click me button will render, but the Hi will not render. Since the button is not clicked in the first round.

When the user clicks the button, the Page Function will be called again. In this round, tgcomp.Button will return true and Hi will render.

How?

Since we delcare a button which its id is Click me, and when the button clicked, we store true with key Click me into p.State.

When entering tccinput.Button, it will check if there is any data store in p.State with key Click me.

Server-Client Architecture

server arch

Server-Client need to handle a more complex part: multiple state for multiple users. Hence we need a state_id for each state.

The server part is responsible for the state pool.

App

The App includes the info of

  1. Navbar: The navbar is the topmost part of the app.
  2. Pages: The App hold a ordered map from page name to page's data.

layout

Navbar

Here is the navbar of toolgui-demo.

navbar

The left part will be buttons which nav to the pages in App. The right part will be two button:

  1. Rerun: Rerun the Page Func without changing any state.
  2. Dark/Light Mode Switch: Switch the theme of the app.

The button of current page will be highlighted with a specific style.

Page

Parameters

We can config the page's:

  1. Name: Or ID. The name of the page should be unique. In the web GUI provider, the name of a page will be used as the path of the page.

  2. Title: In the web GUI provider, The title of a page will be used as the title of the page and text of button on the navbar.

  3. Emoji: Optional. The emoji will be used as a icon on navbar and browser favicon.

The config type in package is:

type PageConfig struct {
	Name  string `json:"name"`
	Title string `json:"title"`
	Emoji string `json:"emoji"`
}

Page Function

The function signature of Page Function is defined as:

type RunFunc func(p *Params) error

Where Params contains these parameters to operate the page:

type Params struct {
	State   *State
	Main    *Container
	Sidebar *Container
}

Main and Sidebar is the root container component of the main and sidebar part shown in the layout image.

We can show a text in the Main container by:

tgcomp.Text(p.Main, "Hello")

The State in Params provided for

  1. The component that need to pass state. For example: the checked state of checkbox.
  2. The state that user need to store. For example: The todo items in the Todo App.

Example for adding a page

  • No emoji icon
app.AddPage("index", "Index", Main)
  • With a emoji icon
app.AddPageByConfig(&tgframe.PageConfig{
	Name:  "page2",
	Title: "Page2",
	Emoji: "🔄",
}, Page2)

State Storage

  • Faster access: Frequently used data can be retrieved from the cache much faster than recalculating it or fetching it from an external source every time. This improves the application's overall performance.

  • Reduced resource usage: By avoiding redundant calculations and external data fetching, the app can conserve resources like CPU and network bandwidth.

App Cache

An app-level cache stores data for the entire duration of the application's execution, from launch to termination.

This cache is not provided by ToolGUI and needs to be implemented by the developer.

For example:

type App1 struct {
	sync.Map data
}

func (app *App) QueryData(key string) {
	if v, ok := data.Load(key); ok{
		return v
	}

	// ...
	v, _ := data.LoadOrStore(key, calVal)
	return v
}

func (app *App) Page1(...) {
	tgcomp.text(app.QueryData(...))
}

Additional Considerations:

  • Cache Invalidation: As the application runs, the underlying data sources might change. It's crucial to have a strategy to invalidate cached data when necessary to ensure consistency. This could involve periodically refreshing the cache or implementing mechanisms to detect changes in the source data.

  • Memory Usage: App-level caches can consume memory. It's essential to choose appropriate data structures and cache eviction policies to balance performance gains with memory constraints.

State Cache

State-level cache stores data specific to the current view or "page" displayed to the user. This data lost when the user navigates away from the page or refreshes it.

Example of variable

Here we provide a state-level TODO App example. It stores list of todos in the state:

type TODOList struct {
	items []string
}

func (t *TODOList) add(item string) {
	t.items = append(t.items, item)
}

func Main(p *tgframe.Params) error {
	tgcomp.Title(p.Main, "Example for Todo App")

	todoList := p.State.Default("todoList", &TODOList{}).(*TODOList)

	inp := tgcomp.Textbox(p.State, p.Main, "Add todo")
	if tgcomp.Button(p.State, p.Main, "Add") {
		todoList.add(inp)
	}

	for i, todo := range todoList.items {
		tgcomp.TextWithID(p.Main,
			fmt.Sprintf("%d: %s", i, todo),
			fmt.Sprintf("todo_%d", i))
	}

	return nil
}

Example of function

Here we provide a state-level cache for a function.

// getFiles unarchive cbz file and return list of file names,
// since unarchive is time-consuming, we use state-level cache to store the result
func getFiles(p *tgframe.Params, f *tcinput.FileObject) ([]string, error) {
	key := fmt.Sprintf("%s_%s_%x", f.Name, f.Type, md5.Sum(f.Bytes))

	v := p.State.GetFuncCache(key)
	if v != nil {
		slog.Debug("cache found")
		return v.([]string), nil
	}

	buf := bytes.NewReader(f.Bytes)

	cbzFp, err := zip.NewReader(buf, buf.Size())
	if err != nil {
		// don't store nil to cache
		return nil, err
	}

	ret := []string{}
	for _, f := range cbzFp.File {
		ret = append(ret, f.Name)
	}

	// ok, we can store to cache
	p.State.SetFuncCache(key, ret)
	return ret, nil
}

func FuncCachePage(p *tgframe.Params) error {
	cbzfile := tgcomp.Fileupload(p.State, p.Sidebar, "CBZ File", "application/x-cbz")

	if cbzfile == nil {
		return nil
	}

	files, err := getFiles(p, cbzfile)
	if err != nil {
		return err
	}

	for i, f := range files {
		tgcomp.Text(p.Main, fmt.Sprintf("%d: %s", i, f))
	}

	return nil
}

Components

Component Tree / Forest

When every component created, we need to assign where it should generate. The root will be Main Container or Sidebar Container. Hence the relation between components is trees.

For example, if a page function implements as:

tgcomp.Text(p.Main, "Text")
tgcomp.Button(p.State, p.Main, "Button")
box := tgcomp.Box("box")
tgcomp.Text(box, "Text1")
tgcomp.Text(box, "Text2")

Then the Component Tree will be:

component tree example

Content Components

The content components show basic content. It doesn't return any value to user.

import "github.com/VoileLab/toolgui/toolgui/tgcomp"

The demo page can be found here: https://toolgui-demo.fly.dev/content

Title

Title component display a title.

API

func Title(c *tgframe.Container, text string)
func TitleWithID(c *tgframe.Container, text string, id string)
  • c is Parent container.
  • text is the title text.
  • id is a user specific element id.

Example

tgcomp.Title(p.Main, "Title")

title component

Subtitle

Subtitle component display a subtitle.

API

func Subtitle(c *tgframe.Container, text string)
func SubtitleWithID(c *tgframe.Container, text string, id string)
  • c is Parent container.
  • text is the subtitle text.
  • id is a user specific element id.

Example

tgcomp.Subtitle(p.Main, "Subtitle")

subtitle component

Text

Text component display a text.

API

func Text(c *tgframe.Container, text string)
func TextWithID(c *tgframe.Container, text string, id string)
  • c is Parent container.
  • text is the text.
  • id is a user specific element id.

Example

tgcomp.Text(p.Main, "Text")

text component

Image

Image component display an image.

API

Interface

func Image(c *tgframe.Container, img any)
func ImageWithConf(c *tgframe.Container, img any, conf *tgcomp.ImageConf)

Parameters

  • c is Parent container.
  • img is the image.
    • image.Image: an image.Image
    • []byte: a byte array
    • string: a url or base64 encoded image
      • example:
        • url: https://http.cat/100
        • base64 uri: data:image/png;base64,...
  • conf is the configuration of the image.
// ImageConf is the configuration for the Image component
type ImageConf struct {
	// Width is the width of the image (e.g. "100px", "50%")
	Width string

	// Format is the format of the image, default is "png"
	Format ImageFormat

	// ID is the unique identifier for this image component
	ID string
}

Example

tgcomp.Image(p.Main, "https://http.cat/100")

image component

Code

API

Interface

func Code(c *tgframe.Container, code string)
func CodeWithConf(c *tgframe.Container, code string, conf *CodeConf)

Parameters

  • c: Parent container.
  • code: Code to display.
  • conf: Configuration for the code component.
// CodeConf provide extra config for Code Component.
type CodeConf struct {
	// Language is language of code block, leave empty to use `go`
	Language string

	// ID is id of the component
	ID string
}

Example

tgcomp.Code(c, "package main\n\nfunc main() {\n\tprintln(\"Hello, World!\")")
tgcomp.CodeWithConf(c, "package main\n\nfunc main() {\n\tprintln(\"Hello, World!\")",
    &tgcomp.CodeConf{
        Language: "go",
        ID:       "mycode",
    })

Markdown

API

Interface

func Markdown(c *tgframe.Container, markdown string)
func MarkdownWithID(c *tgframe.Container, markdown string, id string)

Parameters

  • c: Parent container.
  • markdown: Markdown content to display.
  • id: Unique component ID.

Example

tgcomp.Markdown(c, "* Hello, World!")
tgcomp.MarkdownWithID(c, "* Hello, World!", "my-markdown")

Divider

Divider component display a horizontal line.

API

func Divider(c *tgframe.Container)
func DividerWithID(c *tgframe.Container, id string)
  • c is Parent container.
  • id is a user specific element id.

Example

tgcomp.Divider(p.Main)

divider component

Link

Link component display a link.

API

func Link(c *tgframe.Container, text, url string)
func LinkWithID(c *tgframe.Container, text, url, id string)
  • c is Parent container.
  • text is the link text.
  • url is the link url.
  • id is a user specific element id.

Example

tgcomp.Link(p.Main, "Link", "https://www.example.com/")

link component

Latex

Latex component is used to display LaTeX content.

API

func Latex(c *tgframe.Container, latex string)
func LatexWithID(c *tgframe.Container, latex string, id string)
  • c is the container to add the LaTeX component to.
  • latex is the LaTeX content to display.

Example

tgcomp.Latex(c, "E = mc^2")

Data Components

The data components display data in some special form.

import "github.com/VoileLab/toolgui/toolgui/tgcomp"

The demo page can be found here: https://toolgui-demo.fly.dev/data

JSON

JSON component display the JSON representation of an object.

API

func JSON(c *tgframe.Container, v any)
  • c is Parent container.
  • v is the object.
    • string: assume to be a serialized JSON string.
    • other: assume to be a struct and will be converted to a JSON string.

Example

type DemoJSONHeader struct {
	Type int
}

type DemoJSON struct {
	Header   DemoJSONHeader
	IntValue int
	URL      string
	IsOk     bool
}

tgcomp.JSON(p.Main, &DemoJSON{})

JSON component

Table

Table component display a table.

API

func Table(c *tgframe.Container, head []string, table [][]string)
  • c is Parent container.
  • head is the head of table.
  • body is the body of table.

Example

tgcomp.Table(p.Main, []string{"a", "b"}, [][]string{{"1", "2"}, {"3", "4"}})

table component

Input Components

The input components provide UI for app-user to input their data.

import "github.com/VoileLab/toolgui/toolgui/tgcomp"

The demo page can be found here: https://toolgui-demo.fly.dev/input

Textarea

Textarea create a textarea and return its value.

API

Interface

func Textarea(s *tgframe.State, c *tgframe.Container, label string) string
func TextareaWithConf(s *tgframe.State, c *tgframe.Container, label string, conf *tgcomp.TextareaConfig) string

Parameters

  • s is State.
  • c is Parent container.
  • label is the label for textbox.
  • height is heigh of the textarea.
// TextareaConf is the configuration for a textarea.
type TextareaConf struct {
	// Height is the height of the textarea. default value is 3.
	Height int `json:"height"`

	// Default is the default value of the textarea.
	Default string `json:"default"`

	// Color defines the color of the textarea
	Color tcutil.Color

	// ID is the unique identifier for this textarea component
	ID string
}

Example

textareaValue := tgcomp.Textarea(p.State, p.Main, "Textarea", 5)
tgcomp.TextWithID(p.Main, "Value: "+textareaValue, "textarea_result")

textarea component

Textbox

Textbox create a textbox and return its value.

API

Interface

func Textbox(s *tgframe.State, c *tgframe.Container, label string) string
func TextboxWithConf(s *tgframe.State, c *tgframe.Container, conf *tgcomp.TextboxConf) string

Parameters

  • s is State.
  • c is Parent container.
  • label is the label for textbox.
  • conf is the configuration of the textbox.
// TextboxConf is the configuration for the Textbox component
type TextboxConf struct {
	// Placeholder text to display in the textbox.
	Placeholder string

	// Maximum number of characters allowed in the textbox.
	// If 0, there is no character limit.
	MaxLength int

	// Indicates whether the textbox should mask input as asterisks.
	Password bool

	// Indicates whether the textbox should be disabled.
	Disabled bool

	// Default value of the textbox.
	Default string

	// Color defines the color of the textbox
	Color tcutil.Color

	// ID is the unique identifier for this textbox component
	ID string
}

Example

textboxValue := tgcomp.Textbox(p.State, p.Main, "Textbox")
tgcomp.TextWithID(p.Main, "Value: "+textboxValue, "textbox_result")

textbox component

Fileupload

Fileupload create a fileupload and return its selected file.

API

type FileObject struct {
	Name string `json:"name"`
	Type string `json:"type"`
	Size int    `json:"size"`

	Bytes []byte `json:"_"`
}

func Fileupload(s *tgframe.State, c *tgframe.Container, label, accept string) *FileObject
  • s is State.
  • c is Parent container.
  • label is the label for options group.
  • accept is the file type to accept.
  • Return the selected file object. nil if no file is selected.

Example

fileObj := tgcomp.Fileupload(p.State, p.Main, "Fileupload", ".jpg,.png")
if fileObj != nil {
    tgcomp.Text(p.Main, "Fileupload filename: "+fileObj.Name)
    tgcomp.Text(p.Main, fmt.Sprintf("Fileupload bytes length: %d", len(fileObj.Bytes)))
}

fileupload component

Download Button

DownloadButton create a download button component.

API

Interface

func DownloadButton(s *tgframe.State, c *tgframe.Container, text string, body []byte) bool
func DownloadButtonWithConf(s *tgframe.State, c *tgframe.Container, text string, body []byte, conf *DownloadButtonConf) bool

Parameters

  • c is Parent container.
  • text is the link text.
  • body is the bytes of file.
  • filename is the file name.
  • conf is the configuration of the download button.
type DownloadButtonConf struct {
	// MIME specifies the Multipurpose Internet Mail Extension (MIME) type of the downloaded content.
	// Defaults to "application/octet-stream" if not provided.
	MIME string

	// Color defines the color of the download button.
	Color tcutil.Color

	// Disabled indicates whether the download button should be initially disabled.
	Disabled bool

	// Filename sets the suggested filename for the downloaded content when clicked.
	Filename string

	ID string
}

Example

tgcomp.DownloadButton(p.State, p.Main,
    "Download", []byte("123"), "123.txt")

download button component

Checkbox

Checkbox create a checkbox and return true if it's checked.

API

Interface

func Checkbox(s *tgframe.State, c *tgframe.Container, label string) bool
func CheckboxWithConf(s *tgframe.State, c *tgframe.Container, label string, conf *CheckboxConf) bool

Parameters

  • s is State.
  • c is Parent container.
  • label is the text on checkbox.
// CheckboxConf is the configuration for a checkbox.
type CheckboxConf struct {
	// Default is true if the checkbox is default checked.
	Default bool

	// Disabled is true if the checkbox is disabled.
	Disabled bool

	// ID is the ID of the checkbox.
	ID string
}

Example

checkboxValue := tgcomp.Checkbox(p.State, p.Main, "Checkbox")
if checkboxValue {
	tgcomp.TextWithID(p.Main, "Value: true", "checkbox_result")
} else {
	tgcomp.TextWithID(p.Main, "Value: false", "checkbox_result")
}

checkbox component

Button

Button create a button and return true if it's clicked.

API

func Button(s *tgframe.State, c *tgframe.Container, label string) bool
func ButtonWithConf(s *tgframe.State, c *tgframe.Container, conf *tgcomp.ButtonConf) bool
  • s is State.
  • c is Parent container.
  • label is the text on button.
  • conf is the configuration of the button.
// ButtonConf is the configuration for the Button component
type ButtonConf struct {
	// Color defines the color of the button
	Color tcutil.Color

	// Disabled indicates whether the button should be initially disabled
	Disabled bool

	// ID is the unique identifier for this button component
	ID string
}

Example

btnClicked := tgcomp.Button(p.State, p.Main, "button")
if btnClicked {
	tgcomp.TextWithID(p.Main, "Value: true", "button_result")
} else {
	tgcomp.TextWithID(p.Main, "Value: false", "button_result")
}

button component

Select

Select create a select dropdown list and return its selected value.

API

func Select(s *tgframe.State, c *tgframe.Container, label string, items []string) *int
  • s is State.
  • c is Parent container.
  • label is the label for select.
  • items is the list of options.
  • Return the index of the selected item, 0-indexed. nil if no item is selected.

Example

selIndex := tgcomp.Select(p.State, p.Main, "Select", []string{"Value1", "Value2"})
if selIndex != nil {
	tgcomp.TextWithID(p.Main, fmt.Sprintf("Value: Value%d", *selIndex + 1), "select_result")
}

select component

Radio

Radio create a group of radio items and return its selected value.

API

func Radio(s *tgframe.State, c *tgframe.Container, label string, items []string) *int
  • s is State.
  • c is Parent container.
  • label is the label for options group.
  • items is the list of options.
  • Return the index of the selected item, 0-indexed. nil if no item is selected.

Example

radioIndex := tgcomp.Radio(p.State, p.Main, "Radio", []string{"Value3", "Value4"})
if radioIndex != nil {
	tgcomp.TextWithID(p.Main, fmt.Sprintf("Value: Value%d", *radioIndex + 3), "radio_result")
}

options component

Datepicker

Datepicker create a datepicker and return its selected date.

API

type Date struct {
	Year  int
	Month int
	Day   int
}

func Datepicker(s *tgframe.State, c *tgframe.Container, label string) *Date
  • s is State.
  • c is Parent container.
  • label is the label for datepicker.
  • Return the selected date. nil if no date is selected.

Example

dateValue := tgcomp.Datepicker(p.State, p.Main, "Datepicker")
if dateValue != nil {
	text := fmt.Sprintf("Value: %04d-%02d-%02d", dateValue.Year, dateValue.Month, dateValue.Day)
	tgcomp.TextWithID(p.Main, text, "datepicker_result")
}

datepicker component

Timepicker

Timepicker create a timepicker and return its selected time.

API

type Time struct {
	Hour int
	Minute int
}

func Timepicker(s *tgframe.State, c *tgframe.Container, label string) *Time
  • s is State.
  • c is Parent container.
  • label is the label for timepicker.
  • Return the selected time. nil if no time is selected.

Example

timeValue := tgcomp.Timepicker(p.State, p.Main, "Timepicker")
if timeValue != nil {
	text := fmt.Sprintf("Value: %02d:%02d", timeValue.Hour, timeValue.Minute)
	tgcomp.TextWithID(p.Main, text, "timepicker_result")
}

timepicker component

Datetimepicker

Datetimepicker create a datetimepicker and return its selected datetime.

API

func Datetimepicker(s *tgframe.State, c *tgframe.Container, label string) *time.Time
  • s is State.
  • c is Parent container.
  • label is the label for datetimepicker.
  • Return the selected datetime. nil if no datetime is selected.

Example

dateValue := tgcomp.Datetimepicker(p.State, p.Main, "Datetimepicker")
if dateValue != nil {
	tgcomp.TextWithID(p.Main, "Value: "+dateValue.Format("2006-01-02 15:04"), "datetimepicker_result")
}

datetimepicker component

Number Input

NumberInput create a number input and return its value.

API

Interface

func NumberFloat64(s *tgframe.State, c *tgframe.Container, label string) *float64
func NumberWithConfFloat64(s *tgframe.State, c *tgframe.Container, label string, conf *NumberConf[float64]) *float64

func NumberInt64(s *tgframe.State, c *tgframe.Container, label string) *int64
func NumberWithConfInt64(s *tgframe.State, c *tgframe.Container, label string, conf *NumberConf[int64]) *int64

Parameters

  • s is State.
  • c is Parent container.
  • label is the label of the number input.
// NumberConf is the configuration for a number component.
type NumberConf[T float64 | int64] struct {
	// Default is the default value of the number component.
	Default *T

	// Min is the minimum value of the number component.
	Min *T

	// Max is the maximum value of the number component.
	Max *T

	// Step is the step of the number component.
	Step *T

	// Color is the color of the number component.
	Color tcutil.Color

	// Placeholder is the placeholder of the number component.
	Placeholder string

	// Disabled is the disabled state of the number component.
	Disabled bool

	// ID is the ID of the number component.
	ID string
}

func (c *NumberConf[T]) SetDefault(default T) *NumberConf[T] {
	c.Default = &default
	return c
}

func (c *NumberConf[T]) SetMin(min T) *NumberConf[T] {
	c.Min = &min
	return c
}

func (c *NumberConf[T]) SetMax(max T) *NumberConf[T] {
	c.Max = &max
	return c
}

func (c *NumberConf[T]) SetStep(step T) *NumberConf[T] {
	c.Step = &step
	return c
}

Example

numberValue := tgcomp.NumberWithConfFloat64(numberCompCol, p.State, "Number",
	(&tgcomp.NumberConf[float64]{
		Placeholder: "input the value here",
		Color:       tcutil.ColorSuccess,
	}).SetMin(10).SetMax(20).SetStep(2))

valStr := ""
if numberValue != nil {
	valStr = fmt.Sprint(*numberValue)
}

tgcomp.TextWithID(numberCompCol, "Value: "+valStr, "number_result")

Form

Form create a form component. The input components in this component will not auto trigger script execution. The user need to click the submit button to trigger the script execution.

API

Interface

func Form(c *tgframe.Container, id string) *tgframe.Container

Parameters

  • c is Parent container.
  • id is the ID of the form.

Example

tgcomp.Form(formCompCol, "form").With(func(c *tgframe.Container) {
	a = tgcomp.Number(c, p.State, "a")
	b = tgcomp.Number(c, p.State, "b")
})

if a != nil && b != nil {
	tgcomp.Text(formCompCol, fmt.Sprintf("int(a) + int(b) = %d", int(*a)+int(*b)))
}

Layout Components

The layout components display control component layout and position.

import "github.com/VoileLab/toolgui/toolgui/tgcomp"

The demo page can be found here: https://toolgui-demo.fly.dev/layout

Container

Container is the most basic layout component. Their definition are in tgframe. The Main "container" and Sidebar "container" are Containers.

Usage

To create a container under a container. Just call the function:

func (c *Container) AddContainer(id string) *Container
  • ID should be unique.

Box

Box provide a simple container that show box style.

Usage

Box create a box container.

func Box(c *tgframe.Container, id string) *tgframe.Container
  • c: Parent container.
  • id: Unique component ID.

Example

box := tgcomp.Box(boxCompCol, "box")
tgcomp.Text(box, "A box!")

Column

Column provides columns layout.

Usage

  • Column create N columns.
  • Column2 create 2 columns.
  • Column3 create 3 columns.
  • EqColumn create N columns with equal width.
  • EqColumn1 create 1 column with equal width.
  • EqColumn2 create 2 columns with equal width.
  • EqColumn3 create 3 columns with equal width.
  • EqColumn4 create 4 columns with equal width.
  • EqColumn5 create 5 columns with equal width.
func Column(c *tgframe.Container, id string, n uint) []*tgframe.Container
func Column2(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container)
func Column3(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container, *tgframe.Container)

func EqColumn(c *tgframe.Container, id string, n uint) []*tgframe.Container
func EqColumn1(c *tgframe.Container, id string) *tgframe.Container
func EqColumn2(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container)
func EqColumn3(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container, *tgframe.Container)
func EqColumn4(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container)
func EqColumn5(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container)
  • c: Parent container.
  • id: Unique component ID.
  • n: Number of column.

Example

cols := tgcomp.Column(colCompCol, "cols", 3)
for i, col := range cols {
	tgcomp.Text(col, fmt.Sprintf("col-%d", i))
}

Expand

Expand provides expandable layout.

API

func Expand(c *tgframe.Container, title string, expanded bool) *tgframe.Container
  • c is the container to add the expandable component to.
  • title is the title of the expandable component.
  • expanded is the initial expanded state of the expandable component.

Example

tgcomp.Expand(c, "Expand", true)

Tab

Tab component is used to create a tabbed interface.

API

func Tab(c *tgframe.Container, tabs []string) []*tgframe.Container
func Tab2(c *tgframe.Container, tab1, tab2 string) (*tgframe.Container, *tgframe.Container)
func Tab3(c *tgframe.Container, tab1, tab2, tab3 string) (*tgframe.Container, *tgframe.Container, *tgframe.Container)
func Tab4(c *tgframe.Container, tab1, tab2, tab3, tab4 string) (*tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container)
func Tab5(c *tgframe.Container, tab1, tab2, tab3, tab4, tab5 string) (*tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container)
  • c is the container to add the tab component to.
  • tabs is the list of tab titles.
  • tab1, tab2, tab3, tab4, tab5 are the tab titles.

Example

tabs := tgcomp.Tab(c, []string{"Tab 1", "Tab 2", "Tab 3"})
for _, tab := range tabs {
    tgcomp.Text(tab, "Hello World")
}

Misc Components

The misc components provide various UI components that are not classified under other categories.

import "github.com/VoileLab/toolgui/toolgui/tgcomp"

The demo page can be found here: https://toolgui-demo.fly.dev/misc

Echo

Echo is a component that execute the code of lambda function and display the code of lambda function.

API

func Echo(c *tgframe.Container, code string, lambda func())

Parameters

  • c: Parent container.
  • code: Code of current file. The user should use go embed syntax to embed the code.
  • lambda: Lambda function to execute.

Example

//go:embed main.go
var code string

tgcomp.Echo(c, code, func() {
	println("Hello, World!")
})

Message

Message is a component that displays a message.

API

Interface

func Message(c *tgframe.Container, text string)
func MessageInfo(c *tgframe.Container, text string)
func MessageSuccess(c *tgframe.Container, text string)
func MessageWarning(c *tgframe.Container, text string)
func MessageDanger(c *tgframe.Container, text string)

func MessageWithConf(c *tgframe.Container, text string, conf *MessageConf)

Parameters

  • c: Parent container.

  • text: Text to display.

  • conf: Configuration for the message component.

  • Message[Info|Success|Warning|Danger]: Create a message with a specific color.

type MessageConf struct {
	// Title is the title of the message. Optional.
	Title string

	// Color is the color of the message. Default is tcutil.ColorNull.
	Color tcutil.Color

	// ID is the unique identifier of the component.
	ID string
}

Example

tgcomp.Message(c, "Hello, World!")
tgcomp.MessageInfo(c, "Hello, World!")
tgcomp.MessageWithConf(c, "Hello, World!", &tgcomp.MessageConf{
	Title: "Info",
	Color: tcutil.ColorInfo,
})

Progress Bar

ProgressBar is a component that displays a progress bar.

API

Interface

func ProgressBar(c *tgframe.Container, value int, label string) *ProgressBarComponent

Parameters

  • c: Parent container.
  • value: Value of the progress bar.
  • label: Label of the progress bar.

Example

bar := tgcomp.ProgressBar(c, 50, "Progress")
for i := 0; i <= 100; i++ {
	bar.SetValue(i)
	time.Sleep(100 * time.Millisecond)
}

bar.SetLabel("Completed")

Iframe (Experimental)

Iframe is used to show a html in a iframe.

Warning: This component is experimental 🧪 and may not work as expected.

API

func Iframe(c *tgframe.Container, html string, script bool)
func IframeWithID(c *tgframe.Container, html string, script bool, id string)
  • c is the container to add the iframe to.
  • html is the html to show in the iframe.
  • script is used to allow the iframe to run javascript. (notice that this is not secure)
  • id is the user specific id.

Examples

Simple

Show h1 element in the iframe.

tgcomp.Iframe(p.Main, "<h1>Hello World</h1>", true)

Script

Show a button that updates the iframe content.

htmlWithScript := `
<b id="test">Hello world not changed</b>
<script>
	const element = document.getElementById('test');
	element.innerText = 'Hello world gen by script';
</script>`

tgcomp.IframeWithID(
	p.Main,
	htmlWithScript,
	true,
	"iframe_with_script")

Interactive

Show a button that updates the iframe content.

In the iframe, there are four functions injected:

  • window.update(data) - update the iframe content with the data.
  • window.theme - current theme
  • window.upload(data) - upload a file to the server
  • window.props - props of the iframe component
tgcomp.IframeWithID(
	p.Main,
	`<button id="btn">Click me to update</button>
	<script>
		const btn = document.getElementById('btn');
		btn.addEventListener('click', (event) => {
			window.update({type: "click", id: "iframe_with_interactive_btn"});
		});
	</script>`,
	true,
	"iframe_with_interactive")
clickStatus := p.State.GetClickID() == "iframe_with_interactive_btn"
tgcomp.Text(p.Main, fmt.Sprintf("Status: %v", clickStatus))

Html

Html component is used to display html content.

API

func Html(c *tgframe.Container, html string)
func HtmlWithID(c *tgframe.Container, html string, id string)

Parameters

  • c: Parent container.
  • html: Html content to display.
  • id: Id of the component.

Example

tgcomp.Html(p.Main, "<b>Hello world gen by html component</b>")