Desktop App
The same App can run in a desktop window instead of a browser, through Wails v2. Only the executor changes: pages, components and state work exactly as they do on the web.
package main
import (
"log"
"github.com/voilelab/toolgui/toolgui/tgcomp"
"github.com/voilelab/toolgui/toolgui/tgframe"
tgwails "github.com/voilelab/toolgui/toolgui-wails"
)
func main() {
app := tgframe.NewApp()
app.AddPage("index", "Index", func(p *tgframe.Params) error {
tgcomp.Text(p.Main, "Hello world")
return nil
})
e := tgwails.NewExecutor(app, &tgwails.Conf{Title: "ToolGUI Hello"})
if err := e.Run(); err != nil {
log.Fatal(err)
}
}
Run opens the window and blocks until the user closes it.
The example app
toolgui-wails/example/hello
is a runnable version of that program, with a sidebar textbox and a button to
show the state round trip working in a window:
func Main(p *tgframe.Params) error {
name := tgcomp.Textbox(p.Sidebar, "What's your name?")
if name != "" {
tgcomp.Text(p.Sidebar, "Hi "+name+"~")
}
tgcomp.Text(p.Main, "hello ")
if tgcomp.Button(p.Main, "keep going") {
tgcomp.Text(p.Main, "world")
}
return nil
}
func main() {
app := tgframe.NewApp()
app.AddPage("index", "Index", Main)
e := tgwails.NewExecutor(app, &tgwails.Conf{Title: "ToolGUI Hello"})
err := e.Run()
if err != nil {
log.Fatal(err)
}
}
The whole app is two files:
toolgui-wails/example/hello/
├── main.go the app
└── wails.json what the wails CLI builds
{
"$schema": "https://wails.io/schemas/config.v2.json",
"name": "hello",
"outputfilename": "hello",
"frontend:dir": "../../../toolgui-web/wails",
"frontend:install": "yarn",
"frontend:build": "yarn build",
"assetdir": "../../frontend/dist",
"wailsjsdir": "./build"
}
frontend:diris the desktop frontend workspace. The CLI runs yarn there, which builds intotoolgui-wails/frontend/dist.assetdiris that same directory, the oneassets.goembeds and Wails serves the window from.wailsjsdiris where the generated JavaScript bindings land. The example does not import them — the frontend calls the bound methods throughwindow.go— so they are build output, not source.
Running it
Building a desktop app needs Go, Node with yarn for the frontend build, and the webview development packages. On Debian and Ubuntu:
sudo apt-get install libgtk-3-dev libwebkit2gtk-4.1-dev
Then, from the repository root:
task run_wails_hello # dev mode: frontend from disk, Go files watched
task build_wails_hello # packaged binary, in example/hello/build/bin
The wails CLI itself is pinned as a tool dependency of the toolgui-wails
module, so there is nothing to install: both tasks run go tool wails in
example/hello. They also stub the embedded assets first, because the CLI
generates the bindings — which compiles the package holding the //go:embed —
before it builds the frontend the embed points at.
Build tags
webkit2_41asks for WebKit2GTK 4.1, on Linux only. The tasks add it there; passTAGS=to drop it on a distribution that still ships 4.0, which is what Wails asks for by default.productionpicks the real app over a stub that refuses to run. The CLI adds it, anddevin dev mode.
A plain go build works too, it just has to supply what the CLI would. See
toolgui-wails/README.md
for that and the other platform notes.
Conf
type Conf struct {
// Title shows in the title bar.
Title string
// Width and Height are the window size in pixels.
Width, Height int
// Background is the window background colour.
Background *RGBA
// DisableResize stops the user resizing the window.
DisableResize bool
// Frameless drops the window decorations.
Frameless bool
}
Empty fields fall back to DefaultConf: a 1024x768 white window titled
ToolGUI. An app title names the window ahead of that
default, so a Conf only sets Title to override it.
A separate module
github.com/voilelab/toolgui/toolgui-wails is its own Go module, because Wails
needs cgo, GTK and WebKit. Apps that only target the web never pull any of that
in.