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
// WindowStartState sets the initial window state. The zero value is Normal.
WindowStartState WindowStartState
// FullscreenToggle adds View > Toggle Fullscreen to the native menu.
// The shortcut is F11, or Ctrl+Cmd+F on macOS. Disabled by default.
FullscreenToggle bool
// 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.
Set WindowStartState to tgwails.Maximised to open maximised, or
tgwails.Fullscreen to open fullscreen. tgwails.Normal is the zero value,
so omitting the field keeps the normal window size from Width and Height.
tgwails.Minimised is also exported. The type aliases Wails' window start state;
apps do not need to import Wails' options package.
With DisableResize: true, Wails v2.15.0 ignores Maximised on Windows and
uses the normal size; Fullscreen still takes the fullscreen path. macOS
passes both requests to AppKit. See the
desktop README
for the platform details and outstanding manual GUI checks.
Set FullscreenToggle: true to add View > Toggle Fullscreen, using F11 on
Windows/Linux and Ctrl+Cmd+F on macOS. It works without an app menu and does
not rerun the page or change session state. An existing View submenu is
reused. App shortcuts take precedence: if the fullscreen shortcut is already
assigned, the built-in command remains clickable without an accelerator.
The default is false. On macOS,
enabling it replaces the standard Window role (including its Minimize and
Zoom menu entries) to avoid a duplicate fullscreen command. See the
menu notes.
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.