Skip to content

Changes for v3#

Note

This is currently an unsorted brain dump of changes. It will be organised into a more readable format soon.

Events#

In v3, there are 3 types of events:

  • Application Events
  • Window Events
  • Custom Events

Application Events#

Application events are events that are emitted by the application. These events include native events such as ApplicationDidFinishLaunching on macOS.

Window Events#

Window events are events that are emitted by a window. These events include native events such as WindowDidBecomeMain on macOS. Common events are also defined, so they work cross-platform, e.g. WindowClosing.

Custom Events#

Events that the user defines are called WailsEvents. This is to differentiate them from the Event object that is used to communicate with the browser. WailsEvents are now objects that encapsulate all the details of an event. This includes the event name, the data, and the source of the event.

The data associated with a WailsEvent is now a single value. If multiple values are required, then a struct can be used.

Event callbacks and Emit function signature#

The signatures events callbacks (as used by On, Once & OnMultiple) have changed. In v2, the callback function received optional data. In v3, the callback function receives a WailsEvent object that contains all data related to the event.

Similarly, the Emit function has changed. Instead of taking a name and optional data, it now takes a single WailsEvent object that it will emit.

Off and OffAll#

In v2, Off and OffAll calls would remove events in both JS and Go. Due to the multi-window nature of v3, this has been changed so that these methods only apply to the context they are called in. For example, if you call Off in a window, it will only remove events for that window. If you use Off in Go, it will only remove events for Go.

Hooks#

Event Hooks are a new feature in v3. They allow you to hook into the event system and perform actions when certain events are emitted. For example, you can hook into the WindowClosing event and perform some cleanup before the window closes. Hooks can be registered at the application level or at the window level using RegisterHook. Application level are for application events. Window level hooks will only be called for the window they are registered with.

Developer notes#

When emitting an event in Go, it will dispatch the event to local Go listeners and also each window in the application. When emitting an event in JS, it now sends the event to the application. This will be processed as if it was emitted in Go, however the sender ID will be that of the window.

Window#

The Window API has largely remained the same, however the methods are now on an instance of a window rather than the runtime. Some notable differences are:

  • Windows now have a Name that identifies them. This is used to identify the window when emitting events.
  • Windows have even more methods that were previously unavailable, such as SetFrameless and ToggleDevTools.
  • Windows can now accept files via native drag and drop. See the Drag and Drop section for more details.

BackgroundColour#

In v2, this was a pointer to an RGBA struct. In v3, this is an RGBA struct value.

WindowIsTranslucent#

This flag has been removed. Now there is a BackgroundType flag that can be used to set the type of background the window should have. This flag can be set to any of the following values:

  • BackgroundTypeSolid - The window will have a solid background
  • BackgroundTypeTransparent - The window will have a transparent background
  • BackgroundTypeTranslucent - The window will have a translucent background

On Windows, if the BackgroundType is set to BackgroundTypeTranslucent, the type of translucency can be set using the BackdropType flag in the WindowsWindow options. This can be set to any of the following values:

  • Auto - The window will use an effect determined by the system
  • None - The window will have no background
  • Mica - The window will use the Mica effect
  • Acrylic - The window will use the acrylic effect
  • Tabbed - The window will use the tabbed effect

Systray#

Wails 3 comes with a built-in systray. This is a fully featured systray that has been designed to be as simple as possible to use. It is possible to set the icon, tooltip and menu of the systray. It is possible to also "attach" a window to the systray. Doing this will provide the following functionality:

  • Clicking the systray icon with toggle the window visibility
  • Right-clicking the systray will open the menu, if there is one

On macOS, if there is no attached window, the systray will use the default method of displaying the menu (any button). If there is an attached window but no menu, the systray will toggle the window regardless of the button pressed.

Bindings#

Bindings work in a similar way to v2, by providing a means to bind struct methods to the frontend. These can be called in the frontend using the binding wrappers generated by the wails3 generate bindings command:

// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT

import { main } from "./models";

window.go = window.go || {};
window.go.main = {
  GreetService: {
    /**
     * GreetService.Greet
     * Greet greets a person
     * @param name {string}
     * @returns {Promise<string>}
     **/
    Greet: function (name) {
      wails.CallByID(1411160069, ...Array.prototype.slice.call(arguments, 0));
    },

    /**
     * GreetService.GreetPerson
     * GreetPerson greets a person
     * @param person {main.Person}
     * @returns {Promise<string>}
     **/
    GreetPerson: function (person) {
      wails.CallByID(4021313248, ...Array.prototype.slice.call(arguments, 0));
    },
  },
};

Bound methods are obfuscated by default, and are identified using uint32 IDs, calculated using the FNV hashing algorithm. This is to prevent the method name from being exposed in production builds. In debug mode, the method IDs are logged along with the calculated ID of the method to aid in debugging. If you wish to add an extra layer of obfuscation, you can use the BindAliases option. This allows you to specify a map of alias IDs to method IDs. When the frontend calls a method using an ID, the method ID will be looked up in the alias map first for a match. If it does not find it, it assumes it's a standard method ID and tries to find the method in the usual way.

Example:

    app := application.New(application.Options{
        Bind: []any{
            &GreetService{},
        },
        BindAliases: map[uint32]uint32{
            1: 1411160069,
            2: 4021313248,
        },
        Assets: application.AssetOptions{
            Handler: application.AssetFileServerFS(assets),
        },
        Mac: application.MacOptions{
            ApplicationShouldTerminateAfterLastWindowClosed: true,
        },
    })

We can now call using this alias in the frontend: wails.Call(1, "world!").

Insecure calls#

If you don't mind your calls being available in plain text in your binary and have no intention of using garble, then you can use the insecure wails.CallByName() method. This method takes the fully qualified name of the method to call and the arguments to pass to it. Example:

```go
wails.CallByName("main.GreetService.Greet", "world!")
```

Danger

This is only provided as a convenience method for development. It is not recommended to use this in production.

Drag and Drop#

Native drag and drop can be enabled per-window. Simply set the EnableDragAndDrop window config option to true and the window will allow files to be dragged onto it. When this happens, the events.FilesDropped event will be emitted. The filenames can then be retrieved from the WindowEvent.Context() using the DroppedFiles() method. This returns a slice of strings containing the filenames.

Context Menus#

Context menus are contextual menus that are shown when the user right-clicks on an element. Creating a context menu is the same as creating a standard menu , by using app.NewMenu(). To make the context menu available to a window, call window.RegisterContextMenu(name, menu). The name will be the id of the context menu and used by the frontend.

To indicate that an element has a context menu, add the data-contextmenu attribute to the element. The value of this attribute should be the name of a context menu previously registered with the window.

It is possible to register a context menu at the application level, making it available to all windows. This can be done using app.RegisterContextMenu(name, menu). If a context menu cannot be found at the window level, the application context menus will be checked. A demo of this can be found in v3/examples/contextmenus.

Dialogs#

Dialogs are now available in JavaScript!

Windows#

Dialog buttons in Windows are not configurable and are constant depending on the type of dialog. To trigger a callback when a button is pressed, create a button with the same name as the button you wish to have the callback attached to. Example: Create a button with the label Ok and use OnClick() to set the callback method:

        dialog := app.QuestionDialog().
            SetTitle("Update").
            SetMessage("The cancel button is selected when pressing escape")
        ok := dialog.AddButton("Ok")
        ok.OnClick(func() {
            // Do something
        })
        no := dialog.AddButton("Cancel")
        dialog.SetDefaultButton(ok)
        dialog.SetCancelButton(no)
        dialog.Show()

ClipBoard#

The clipboard API has been simplified. There is now a single Clipboard object that can be used to read and write to the clipboard. The Clipboard object is available in both Go and JS. SetText() to set the text and Text() to get the text.

Wails Markup Language (WML)#

The Wails Markup Language is a simple markup language that allows you to add functionality to standard HTML elements without the use of Javascript.

The following tags are currently supported:

data-wml-event#

This specifies that a Wails event will be emitted when the element is clicked. The value of the attribute should be the name of the event to emit.

Example:

<button data-wml-event="myevent">Click Me</button>

Sometimes you need the user to confirm an action. This can be done by adding the data-wml-confirm attribute to the element. The value of this attribute will be the message to display to the user.

Example:

<button data-wml-event="delete-all-items" data-wml-confirm="Are you sure?">
  Delete All Items
</button>

data-wml-window#

Any wails.window method can be called by adding the data-wml-window attribute to an element. The value of the attribute should be the name of the method to call. The method name should be in the same case as the method.

<button data-wml-window="Close">Close Window</button>

data-wml-trigger#

This attribute specifies which javascript event should trigger the action. The default is click.

<button data-wml-event="hover-box" data-wml-trigger="mouseover">
  Hover over me!
</button>

Plugins#

Plugins are a way to extend the functionality of your Wails application.

Creating a plugin#

Plugins are standard Go structure that adhere to the following interface:

type Plugin interface {
    Name() string
    Init(*application.App) error
    Shutdown()
    CallableByJS() []string
    InjectJS() string
}

The Name() method returns the name of the plugin. This is used for logging purposes.

The Init(*application.App) error method is called when the plugin is loaded. The *application.App parameter is the application that the plugin is being loaded into. Any errors will prevent the application from starting.

The Shutdown() method is called when the application is shutting down.

The CallableByJS() method returns a list of exported functions that can be called from the frontend. These method names must exactly match the names of the methods exported by the plugin.

The InjectJS() method returns JavaScript that should be injected into all windows as they are created. This is useful for adding custom JavaScript functions that complement the plugin.

Enums#

In Go, enums are often defined as a type and a set of constants. For example:

type MyEnum int

const (
    MyEnumOne MyEnum = iota
    MyEnumTwo
    MyEnumThree
)

Due to incompatibility between Go and JavaScript, custom types cannot be used in this way. The best strategy is to use a type alias for float64:

type MyEnum = float64

const (
    MyEnumOne MyEnum = iota
    MyEnumTwo
    MyEnumThree
)

In Javascript, you can then use the following:

const MyEnum = {
  MyEnumOne: 0,
  MyEnumTwo: 1,
  MyEnumThree: 2,
};
  • Why use float64? Can't we use int?
    • Because JavaScript doesn't have a concept of int. Everything is a number, which translates to float64 in Go. There are also restrictions on casting types in Go's reflection package, which means using int doesn't work.

Logging#

Logging in v2 was confusing as both application logs and system (internal) logs were using the same logger. We have simplified this as follows:

  • Internal logs are now handled using the standard Go slog logger. This is configured using the logger option in the application options. By default, this uses the tint logger.
  • Application logs can now be achieved through the new log plugin which utilises slog under the hood. This plugin provides a simple API for logging to the console. It is available in both Go and JS.

Misc#

Windows Application Options#

WndProcInterceptor#

If this is set, the WndProc will be intercepted and the function will be called. This allows you to handle Windows messages directly. The function should have the following signature:

func(hwnd uintptr, msg uint32, wParam, lParam uintptr) (returnValue uintptr, shouldReturn)

The shouldReturn value should be set to true if the returnValue should be returned by the main wndProc method. If it is set to false, the return value will be ignored and the message will continue to be processed by the main wndProc method.

Hide Window on Close + OnBeforeClose#

In v2, there was the HideWindowOnClose flag to hide the window when it closed. There was a logical overlap between this flag and the OnBeforeClose callback. In v3, the HideWindowOnClose flag has been removed and the OnBeforeClose callback has been renamed to ShouldClose. The ShouldClose callback is called when the user attempts to close a window. If the callback returns true, the window will close. If it returns false, the window will not close. This can be used to hide the window instead of closing it.

Window Drag#

In v2, the --wails-drag attribute was used to indicate that an element could be used to drag the window. In v3, this has been replaced with --webkit-app-region to be more in line with the way other frameworks handle this. The --webkit-app-region attribute can be set to any of the following values:

  • drag - The element can be used to drag the window
  • no-drag - The element cannot be used to drag the window

We would have ideally liked to use app-region, however this is not supported by the getComputedStyle call on webkit on macOS.