docs: Migrate /learn content and reorganize documentation structure

Completes the documentation reorganization by migrating all /learn content
to proper locations and removing the legacy /learn directory.

Changes:
- Migrated 10 unique files from /learn to appropriate sections:
  - features/notifications, keyboard, environment, browser, platform
  - concepts/manager-api
  - contributing/architecture/bindings
  - reference/runtime
  - guides/build/customization
- Removed /learn directory and 22 legacy files
- Updated sidebar navigation:
  - Removed "Learn" section
  - Added Manager API to Core Concepts
  - Reorganized Features with new subdirectories
  - Added Build Customization to Guides
  - Added Binding System to Contributing/Architecture
- Fixed missing Card/CardGrid imports in 12 feature files
- Cleaned up stale d2 SVG files

All /features content verified to be more comprehensive than /learn versions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Lea Anthony 2025-11-22 16:37:37 +11:00
commit 232c39abaa
48 changed files with 78 additions and 6301 deletions

View file

@ -92,19 +92,13 @@ export default defineConfig({
autogenerate: { directory: "tutorials" },
},
// Learn - Discrete features
{
label: "Learn",
collapsed: true,
autogenerate: { directory: "learn" },
},
// Core Concepts
{
label: "Core Concepts",
collapsed: true,
items: [
{ label: "How Wails Works", link: "/concepts/architecture" },
{ label: "Manager API", link: "/concepts/manager-api" },
{ label: "Application Lifecycle", link: "/concepts/lifecycle" },
{ label: "Go-Frontend Bridge", link: "/concepts/bridge" },
{ label: "Build System", link: "/concepts/build-system" },
@ -165,21 +159,41 @@ export default defineConfig({
{ label: "Custom Dialogs", link: "/features/dialogs/custom" },
],
},
{ label: "Clipboard", link: "/features/clipboard" },
{
label: "Clipboard",
collapsed: true,
autogenerate: { directory: "features/clipboard" },
},
{
label: "Browser",
collapsed: true,
autogenerate: { directory: "features/browser" },
},
{ label: "Drag & Drop", link: "/features/drag-drop" },
{ label: "Keyboard Shortcuts", link: "/features/keyboard" },
{ label: "Notifications", link: "/features/notifications" },
{ label: "Screens API", link: "/features/screens" },
{ label: "Environment", link: "/features/environment" },
{
label: "Keyboard",
collapsed: true,
autogenerate: { directory: "features/keyboard" },
},
{
label: "Notifications",
collapsed: true,
autogenerate: { directory: "features/notifications" },
},
{
label: "Screens",
collapsed: true,
autogenerate: { directory: "features/screens" },
},
{
label: "Environment",
collapsed: true,
autogenerate: { directory: "features/environment" },
},
{
label: "Platform-Specific",
collapsed: true,
items: [
{ label: "macOS Dock", link: "/features/platform/macos-dock" },
{ label: "macOS Toolbar", link: "/features/platform/macos-toolbar" },
{ label: "Windows UAC", link: "/features/platform/windows-uac" },
{ label: "Linux Desktop", link: "/features/platform/linux" },
],
autogenerate: { directory: "features/platform" },
},
],
},
@ -204,6 +218,7 @@ export default defineConfig({
collapsed: true,
items: [
{ label: "Building Applications", link: "/guides/build/building" },
{ label: "Build Customization", link: "/guides/build/customization" },
{ label: "Cross-Platform Builds", link: "/guides/build/cross-platform" },
{ label: "Code Signing", link: "/guides/build/signing" },
{ label: "Windows Packaging", link: "/guides/build/windows" },
@ -305,6 +320,7 @@ export default defineConfig({
{ label: "Runtime Layer", link: "/contributing/architecture/runtime" },
{ label: "Platform Layer", link: "/contributing/architecture/platform" },
{ label: "Build System", link: "/contributing/architecture/build" },
{ label: "Binding System", link: "/contributing/architecture/bindings" },
],
},
{

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 40 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 40 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 37 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 30 KiB

View file

@ -1,7 +1,8 @@
---
title: Manager API
description: Organized API structure with focused manager interfaces
sidebar:
order: 25
order: 2
---
import { Tabs, TabItem } from "@astrojs/starlight/components";

View file

@ -1,7 +1,8 @@
---
title: Binding System Internals
title: Binding System
description: How the binding system collects, processes, and generates JavaScript/TypeScript code
sidebar:
order: 21
order: 1
---
import { FileTree } from "@astrojs/starlight/components";

View file

@ -1,7 +1,8 @@
---
title: Advanced Binding Techniques
title: Advanced Binding
description: Advanced binding techniques including directives, code injection, and custom IDs
sidebar:
order: 22
order: 3
---
import { FileTree } from "@astrojs/starlight/components";

View file

@ -5,7 +5,7 @@ sidebar:
order: 1
---
import { FileTree } from "@astrojs/starlight/components";
import { FileTree, Card, CardGrid } from "@astrojs/starlight/components";
## Type-Safe Go-JavaScript Bindings

View file

@ -5,6 +5,8 @@ sidebar:
order: 3
---
import { Card, CardGrid } from "@astrojs/starlight/components";
## Data Model Bindings
Wails **automatically generates JavaScript/TypeScript classes** from Go structs, providing full type safety when passing complex data between backend and frontend. Write Go structs, generate bindings, and get fully-typed frontend models complete with constructors, type annotations, and JSDoc comments.

View file

@ -1,7 +1,8 @@
---
title: Browser Integration
description: Open URLs and files in the user's default web browser
sidebar:
order: 58
order: 1
---
import { Tabs, TabItem } from "@astrojs/starlight/components";

View file

@ -5,6 +5,8 @@ sidebar:
order: 1
---
import { Card, CardGrid } from "@astrojs/starlight/components";
## Clipboard Operations
Wails provides a **unified clipboard API** that works across all platforms. Copy and paste text with simple, consistent methods on Windows, macOS, and Linux.

View file

@ -4,6 +4,8 @@ sidebar:
order: 4
---
import { Card, CardGrid } from "@astrojs/starlight/components";
## Custom dialogs
Create **custom dialog windows** using regular Wails windows with dialog-like behaviour. Build custom forms, complex input validation, branded appearance, and rich content (images, videos) whilst maintaining familiar dialog patterns.

View file

@ -5,6 +5,8 @@ sidebar:
order: 3
---
import { Card, CardGrid } from "@astrojs/starlight/components";
## File dialogs
Wails provides **native file dialogs** with platform-appropriate appearance for opening files, saving files, and selecting folders. Simple API with file type filtering, multiple selection support, and default locations.

View file

@ -5,6 +5,8 @@ sidebar:
order: 2
---
import { Card, CardGrid } from "@astrojs/starlight/components";
## Message dialogs
Wails provides **native message dialogs** with platform-appropriate appearance: info, warning, error, and question dialogs with customisable titles, messages, and buttons. Simple API, native behaviour, accessible by default.

View file

@ -5,7 +5,7 @@ sidebar:
order: 1
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
import { Tabs, TabItem, Card, CardGrid } from "@astrojs/starlight/components";
## Native dialogs

View file

@ -5,7 +5,7 @@ sidebar:
order: 1
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
import { Tabs, TabItem, Card, CardGrid } from "@astrojs/starlight/components";
## Event System

View file

@ -1,7 +1,8 @@
---
title: Key Bindings
title: Keyboard Shortcuts
description: Register global keyboard shortcuts for quick access to functionality
sidebar:
order: 56
order: 1
---
import { Tabs, TabItem } from "@astrojs/starlight/components";

View file

@ -5,7 +5,7 @@ sidebar:
order: 3
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
import { Tabs, TabItem, Card, CardGrid } from "@astrojs/starlight/components";
## System Tray Menus

View file

@ -1,5 +1,8 @@
---
title: Notifications
description: Display native system notifications with action buttons and text input
sidebar:
order: 1
---
import { Tabs, TabItem } from "@astrojs/starlight/components";

View file

@ -1,5 +1,8 @@
---
title: Dock
title: Dock & Taskbar
description: Manage dock icon visibility and display badges on macOS and Windows
sidebar:
order: 1
---
import { Tabs, TabItem } from "@astrojs/starlight/components";

View file

@ -5,6 +5,8 @@ sidebar:
order: 1
---
import { Card, CardGrid } from "@astrojs/starlight/components";
## Screen Information
Wails provides a **unified screen API** that works across all platforms. Get screen information, detect multiple monitors, query screen properties (size, position, DPI), identify the primary display, and handle DPI scaling with consistent code.

View file

@ -5,7 +5,7 @@ sidebar:
order: 1
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
import { Tabs, TabItem, Card, CardGrid } from "@astrojs/starlight/components";
## Window Management

View file

@ -5,7 +5,7 @@ sidebar:
order: 4
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
import { Tabs, TabItem, Card, CardGrid } from "@astrojs/starlight/components";
## Frameless Windows

View file

@ -1,7 +1,8 @@
---
title: Build System
title: Build Customization
description: Customize your build process using Task and Taskfile.yml
sidebar:
order: 40
order: 1
---
import { FileTree } from "@astrojs/starlight/components";

View file

@ -1,261 +0,0 @@
---
title: Application Menu
sidebar:
order: 53
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
## Application Menus
Wails provides native application menus with full support for menu bars, submenus, separators, keyboard shortcuts, and dynamic updates. Create professional menus that respect platform conventions across macOS, Windows, and Linux.
Create a new application menu using the `New` method from the Menu manager:
```go
menu := app.Menu.New()
```
## Setting the Menu
The way to set the menu varies on the platform:
<Tabs>
<TabItem label="macOS" icon="fa-brands:apple">
On macOS, there is only one menu bar per application. Set the menu using the `Set` method of the Menu manager:
```go
app.Menu.Set(menu)
```
</TabItem>
<TabItem label="Windows" icon="fa-brands:windows">
On Windows, there is a menu bar per window. Set the menu using the `SetMenu` method of the window:
```go
app.Window.Current().SetMenu(menu)
```
</TabItem>
<TabItem label="Linux" icon="fa-brands:linux">
On Linux, the menu bar is typically per window. Set the menu using the `SetMenu` method of the window:
```go
app.Window.Current().SetMenu(menu)
```
</TabItem>
</Tabs>
## Menu Roles
Wails provides predefined menu roles that automatically create platform-appropriate menu structures:
```go
// Add standard application menu on macOS
if runtime.GOOS == "darwin" {
menu.AddRole(application.AppMenu)
}
// Add standard menus
menu.AddRole(application.FileMenu)
menu.AddRole(application.EditMenu)
menu.AddRole(application.WindowMenu)
menu.AddRole(application.HelpMenu)
```
:::note[Platform Behaviour]
The AppMenu role is specific to macOS and provides the standard application menu containing About, Preferences, and Quit items.
:::
### Available Roles
| Role | Description | Platform Notes |
|------|-------------|----------------|
| `AppMenu` | Standard application menu | macOS only |
| `FileMenu` | File operations menu | All platforms |
| `EditMenu` | Text editing operations | All platforms |
| `WindowMenu` | Window management | All platforms |
| `HelpMenu` | Help and information | All platforms |
## Custom Menus
Create custom menus by adding items directly:
```go
// Add a custom menu
customMenu := menu.AddSubmenu("Tools")
customMenu.Add("Settings").OnClick(func(ctx *application.Context) {
// Show settings dialog
})
```
:::tip[Menu Items]
For detailed information about available menu item types and properties, refer to the [Menu Reference](./menu-reference) documentation.
:::
## Window Control
Menu items can control the application windows:
```go
viewMenu := menu.AddSubmenu("View")
viewMenu.Add("Toggle Fullscreen").OnClick(func(ctx *application.Context) {
window := app.Window.Current()
if window.Fullscreen() {
window.SetFullscreen(false)
} else {
window.SetFullscreen(true)
}
})
```
## Dynamic Menus
Menus can be updated dynamically based on application state:
```go
projectMenu := menu.AddSubmenu("Project")
saveItem := projectMenu.Add("Save Project")
// Update based on state
saveItem.OnClick(func(ctx *application.Context) {
if projectSaved {
saveItem.SetEnabled(false)
saveItem.SetLabel("Project Saved")
}
menu.Update()
})
```
## Platform-Specific Considerations
<Tabs>
<TabItem label="macOS" icon="fa-brands:apple">
On macOS, menus are deeply integrated with the system:
- Menus appear in the system menu bar at the top of the screen
- The application menu (⌘) is required and should be added using `menu.AddRole(application.AppMenu)`
- Standard keyboard shortcuts are automatically handled
- Menu styling follows system appearance
- The "About" menu item appears in the application menu
- Preferences are typically placed in the application menu
</TabItem>
<TabItem label="Windows" icon="fa-brands:windows">
On Windows, menus follow the traditional Windows UI guidelines:
- Menus appear in the application window's title bar
- Standard keyboard shortcuts should be explicitly set using `SetAccelerator`
- Menu styling matches the Windows theme
- The "About" menu item typically appears in the Help menu
- Settings/Preferences are typically placed in the Tools menu
</TabItem>
<TabItem label="Linux" icon="fa-brands:linux">
On Linux, menu behaviour depends on the desktop environment:
- Menu appearance adapts to the desktop environment's theme
- Some desktop environments (like Unity) support global menu bars
- Menu placement follows the desktop environment's conventions
- Keyboard shortcuts should be explicitly set
- Settings are typically placed in the Edit menu
</TabItem>
</Tabs>
## Best Practices
1. Use standard menu roles where appropriate
2. Follow platform-specific menu conventions
3. Provide keyboard shortcuts for common actions
4. Keep menu structures shallow and organised
5. Update menu items to reflect application state
6. Use clear, concise menu labels
7. Group related items logically
:::danger[Warning]
Always test menu functionality across all target platforms to ensure consistent behaviour and appearance.
:::
:::tip[Pro Tip]
Consider using the `app.Window.Current()` method in menu handlers to affect the active window, rather than storing window references.
:::
## Complete Example
Here's a comprehensive example demonstrating various menu features:
```go
package main
import (
"runtime"
"github.com/wailsapp/wails/v3/pkg/application"
)
func main() {
app := application.New(application.Options{
Name: "Menu Demo",
})
// Create main menu
menu := app.Menu.New()
// Add platform-specific application menu
if runtime.GOOS == "darwin" {
menu.AddRole(application.AppMenu)
}
// Add standard menus
fileMenu := menu.AddRole(application.FileMenu)
menu.AddRole(application.EditMenu)
menu.AddRole(application.WindowMenu)
menu.AddRole(application.HelpMenu)
// Add custom menu
toolsMenu := menu.AddSubmenu("Tools")
// Add checkbox item
toolsMenu.AddCheckbox("Dark Mode", false).OnClick(func(ctx *application.Context) {
isDark := ctx.ClickedMenuItem().Checked()
// Toggle theme
})
// Add radio group
toolsMenu.AddRadio("Small Text", true).OnClick(handleFontSize)
toolsMenu.AddRadio("Medium Text", false).OnClick(handleFontSize)
toolsMenu.AddRadio("Large Text", false).OnClick(handleFontSize)
// Add submenu
advancedMenu := toolsMenu.AddSubmenu("Advanced")
advancedMenu.Add("Configure...").OnClick(func(ctx *application.Context) {
// Show configuration
})
// Set the menu
app.Menu.Set(menu)
// Create main window
app.Window.New()
err := app.Run()
if err != nil {
panic(err)
}
}
func handleFontSize(ctx *application.Context) {
size := ctx.ClickedMenuItem().Label()
// Update font size
}

View file

@ -1,115 +0,0 @@
---
title: Binding Best Practices
sidebar:
order: 23
---
import { FileTree } from "@astrojs/starlight/components";
This guide provides best practices and patterns for using the Wails binding system effectively in your applications.
## Service Design Patterns
### Service Organization
Organize your services based on functionality rather than technical concerns. For example, instead of having a single large service, split it into smaller, focused services:
```go
// Instead of this:
type AppService struct {}
func (s *AppService) GetUser() User { /* ... */ }
func (s *AppService) UpdateUser(user User) error { /* ... */ }
func (s *AppService) GetProducts() []Product { /* ... */ }
func (s *AppService) AddProduct(product Product) error { /* ... */ }
// Do this:
type UserService struct {}
func (s *UserService) GetUser() User { /* ... */ }
func (s *UserService) UpdateUser(user User) error { /* ... */ }
type ProductService struct {}
func (s *ProductService) GetProducts() []Product { /* ... */ }
func (s *ProductService) AddProduct(product Product) error { /* ... */ }
```
This makes your code more maintainable and easier to understand.
### Use JSON Tags
Use JSON tags to control how your models are serialized:
```go
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"-"` // Exclude from JSON
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
```
### Separate Frontend and Backend Models
Consider using different models for the frontend and backend:
```go
// Backend model
type User struct {
ID int
Name string
Email string
Password string // Sensitive data
CreatedAt time.Time
UpdatedAt time.Time
}
// Frontend model
type UserDTO struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
func (s *UserService) GetUser() UserDTO {
user := getUserFromDatabase()
return UserDTO{
ID: user.ID,
Name: user.Name,
Email: user.Email,
CreatedAt: user.CreatedAt,
}
}
```
This gives you more control over what data is exposed to the frontend.
### Use Context for Cancellation
Use context for cancellation to avoid wasting resources on abandoned requests:
```go
func (s *ProductService) GetProducts(ctx context.Context, req ProductsRequest) (ProductsResponse, error) {
// Check if the request has been cancelled
select {
case <-ctx.Done():
return ProductsResponse{}, ctx.Err()
default:
// Continue processing
}
products, total, err := getProductsFromDatabase(ctx, req.Page, req.PageSize, req.Filter)
if err != nil {
return ProductsResponse{}, err
}
return ProductsResponse{
Products: products,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}, nil
}
```

View file

@ -1,674 +0,0 @@
---
title: Bindings
sidebar:
order: 20
---
import { FileTree } from "@astrojs/starlight/components";
## Introduction
One of the key features of Wails is the ability to seamlessly integrate backend
Go code with the frontend, enabling efficient communication between the two.
This can be done manually by sending messages between the frontend and backend,
but this can be cumbersome and error-prone, especially when dealing with complex
data types.
The bindings generator in Wails v3 simplifies this process by automatically
generating JavaScript or TypeScript functions and models that reflect the
methods and data structures defined in your Go code. This means you can write
your backend logic in Go and easily expose it to the frontend without the need
for manual binding or complex integration.
This guide is designed to help you understand and utilize this powerful binding
tool.
## Core Concepts
In Wails v3, services can be added to your application. These services act as a
bridge between the backend and frontend, allowing you to define methods and
state that can be accessed and manipulated from the frontend.
### Services
1. Services can hold state and expose methods that operate on that state.
2. Services can be used similar to controllers in HTTP web applications or as
services.
3. Only public methods on the service are bound, following Go's convention.
Here's a simple example of how you can define a service and add it to your Wails
application:
```go
package main
import (
"log"
"github.com/wailsapp/wails/v3/pkg/application"
)
type GreetService struct {}
func (g *GreetService) Greet(name string) string {
return "Hello " + name
}
func main() {
app := application.New(application.Options{
Services: []application.Service{
application.NewService(&GreetService{}),
},
})
// ....
err := app.Run()
if err != nil {
log.Fatal(err)
}
}
```
In this example, we define a `GreetService` services with a public `Greet`
method. The `Greet` method takes a `name` parameter and returns a greeting
string.
We then create a new Wails application using `application.New` and add the
`GreetService` service to the application using the `Services` option in the
`application.Options`. The `application.NewService` method must always be given
an _instance_ of the service struct, not the service struct type itself.
### Generating the Bindings
By binding the struct, Wails is able to generate the necessary JavaScript or
TypeScript code by running the following command in the project directory:
```bash
wails3 generate bindings
```
The bindings generator will scan the project and dependencies for anything that
needs generating. Note: It will take longer the very first time you run the
bindings generator, as it will be building up a cache of packages to scan. You
should see output similar to the following:
```bash
% wails3 generate bindings
INFO 347 Packages, 1 Service, 1 Method, 0 Enums, 0 Models in 1.981036s.
INFO Output directory: /Users/me/myproject/frontend/bindings
```
If we look in the `frontend/bindings` directory, we should see the following
files:
<FileTree>
- frontend/bindings
- changeme
- greetservice.js
- index.js
</FileTree>
NOTE: The `changeme` directory is the name of the module defined in `go.mod` and
is used to namespace the generated files.
The generated `greetservice.js` file contains the JavaScript code that mirrors
the Go struct and its methods:
```javascript title="greetservice.js"
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import { Call as $Call, Create as $Create } from "@wailsio/runtime";
/**
* @param {string} name
* @returns {Promise<string> & { cancel(): void }}
*/
export function Greet(name) {
let $resultPromise = /** @type {any} */ ($Call.ByID(1411160069, name));
return $resultPromise;
}
```
As you can see, it also generates all the necessary JSDoc type information to
ensure type safety in your frontend code.
### Using the Bindings
You can import and use this file in your frontend code to interact with the
backend.
```javascript
import { Greet } from "./bindings/changeme/greetservice.js";
console.log(Greet("Alice")); // Output: Hello Alice
```
### Binding Models
In addition to binding methods, you can also use structs as input or output
parameters in your bound methods. When structs are used as parameters, Wails
generates corresponding JavaScript versions of those types.
Let's extend the previous example to use a `Person` type that has a `Name`
field:
```go
package main
import (
"github.com/wailsapp/wails/v3/pkg/application"
"log"
)
// Person defines a person
type Person struct {
// Name of the person
Name string
}
type GreetService struct{}
func (g *GreetService) Greet(person Person) string {
return "Hello " + person.Name
}
func main() {
app := application.New(application.Options{
Services: []application.Service{
application.NewService(&GreetService{}),
},
})
// ....
err := app.Run()
if err != nil {
log.Fatal(err)
}
}
```
In this updated example, we define a `Person` struct with a `Name` field. The
`Greet` method in the `GreetService` service now takes a `Person` as an input
parameter.
When you run the bindings generator, Wails will generate a corresponding
JavaScript `Person` type that mirrors the Go struct. This allows you to create
instances of the `Person` type in your frontend code and pass them to the bound
`Greet` method.
If we run the bindings generator again, we should see the following output:
```bash
% wails3 generate bindings
INFO Processed: 347 Packages, 1 Service, 1 Method, 0 Enums, 1 Model in 1.9943997s.
INFO Output directory: /Users/me/myproject/frontend/bindings
```
In the `frontend/bindings/changeme` directory, you should see a new `models.js`
file containing the following code:
```javascript title="models.js"
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import { Create as $Create } from "@wailsio/runtime";
/**
* Person defines a person
*/
export class Person {
/**
* Creates a new Person instance.
* @param {Partial<Person>} [$$source = {}] - The source object to create the Person.
*/
constructor($$source = {}) {
if (!("Name" in $$source)) {
/**
* Name of the person
* @member
* @type {string}
*/
this["Name"] = "";
}
Object.assign(this, $$source);
}
/**
* Creates a new Person instance from a string or object.
* @param {any} [$$source = {}]
* @returns {Person}
*/
static createFrom($$source = {}) {
let $$parsedSource =
typeof $$source === "string" ? JSON.parse($$source) : $$source;
return new Person(/** @type {Partial<Person>} */ ($$parsedSource));
}
}
```
The `Person` class is generated with a constructor that takes an optional
`source` parameter, which allows you to create a new `Person` instance from an
object. It also has a static `createFrom` method that can create a `Person`
instance from a string or object.
You may also notice that comments in the Go struct are kept in the generated
JavaScript code! This can be helpful for understanding the purpose of the fields
and methods in the generated models and should be picked up by your IDE.
### Using Bound Models
Here's an example of how you can use the generated JavaScript `Person` type in
your frontend code:
```javascript
import { Greet } from "./bindings/changeme/greetservice.js";
import { Person } from "./bindings/changeme/models.js";
const resultElement = document.getElementById("result");
async function doGreet() {
let person = new Person({ Name: document.getElementById("name").value });
if (!person.Name) {
person.Name = "anonymous";
}
resultElement.innerText = await Greet(person);
}
```
In this example, we import the generated `Person` type from the `models` module.
We create a new instance of `Person`, set its `Name` property, and pass it to
the `Greet` method.
Using bound models allows you to work with complex data structures and
seamlessly pass them between the frontend and backend of your Wails application.
### Index files
The generator outputs an additional `index.js` file that re-exports all services and models:
```javascript title="index.js"
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import * as GreetService from "./greetservice.js";
export {
GreetService
};
export {
Person
} from "./models.js";
```
You can take advantage of this feature
to aggregate import statements for multiple services and models.
If you are building your frontend with a bundler,
which is the default for most project templates,
you can also simplify the import path:
```javascript
import { GreetService, Person } from "./bindings/changeme";
await GreetService.Greet(new Person(/* ... */));
```
### Using Typescript
To generate TypeScript bindings instead of JavaScript, you can use the `-ts`
flag:
```bash
% wails3 generate bindings -ts
```
This will generate TypeScript files in the `frontend/bindings` directory:
<FileTree>
- frontend/bindings
- main
- greetservice.ts
- index.ts
- models.ts
</FileTree>
The generated files include `greetservice.ts`, which contains the TypeScript
code for the bound struct and its methods, and `models.ts`, which contains the
TypeScript types for the bound models:
```typescript title="GreetService.ts"
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import { Call as $Call, Create as $Create } from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as $models from "./models.js";
export function Greet(
person: $models.Person,
): Promise<string> & { cancel(): void } {
let $resultPromise = $Call.ByID(1411160069, person) as any;
return $resultPromise;
}
```
```typescript title="models.ts"
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
/**
* Person defines a person
*/
export class Person {
/**
* Name of the person
*/
"Name": string;
/** Creates a new Person instance. */
constructor(source: Partial<Person> = {}) {
if (!("Name" in source)) {
this["Name"] = "";
}
Object.assign(this, source);
}
/** Creates a new Person instance from a string or object. */
static createFrom(source: string | object = {}): Person {
let parsedSource = typeof source === "string" ? JSON.parse(source) : source;
return new Person(parsedSource as Partial<Person>);
}
}
```
Using TypeScript bindings provides type safety and improved IDE support when
working with the generated code in your frontend.
### Using `context.Context`
When defining service methods in Go, you can include `context.Context` as the
first parameter. The runtime will automatically provide a context when the
method is called from the frontend.
The context provides several powerful features:
1. **Cancellation Support**: Long-running operations can be cancelled from the
frontend, which will raise an error through the Promise chain.
2. **Window Information**: You can determine which window made the call using
the context key `application.WindowKey`.
Here are some examples:
```go
// Basic context usage with cancellation
func (s *MyService) LongRunningTask(ctx context.Context, input string) (string, error) {
select {
// Check if the context has been cancelled from the frontend
case <-ctx.Done():
return "", ctx.Err()
default:
// Process task
return "completed", nil
}
}
// Getting caller window information
func (s *MyService) WindowAwareMethod(ctx context.Context) (string, error) {
window := ctx.Value(application.WindowKey).(application.Window)
return fmt.Sprintf("Called from window: %s (ID: %s)", window.Name(), window.ID()), nil
}
```
From the frontend, these methods can be called normally. If you need to cancel a
long-running operation, you can call the special `cancel` method on the promise
and it will reject immediately with a special cancellation error;
the Go context will be cancelled and the actual result of the call will be discarded:
```javascript
// Call the method
const promise = MyService.LongRunningTask("input");
// Cancel it later if needed
// This will cause the context to be cancelled in the Go method
promise.cancel();
```
In fact, the runtime returns a special promise wrapper
that provides cancellation support for arbitrarily long promise chains.
For example:
```javascript
import { CancelError } from "@wailsio/runtime";
// Call the method and process its output
const promise = MyService.LongRunningTask("input").then((result) => {
console.log(result);
}).catch((err) => {
if (err instanceof CancelError) {
console.log("Cancelled.", err.cause);
} else {
console.error("Failed.", err);
}
});
// Later...
// cancel() accepts an optional cause parameter
// that will be attached to the cancellation error:
promise.cancel("I'm tired of waiting!").then(() => {
// Cancellation has been requested successfully
// and all handlers attached above have run.
console.log("Ready for the next adventure!");
});
```
The `cancel` method returns a promise that fulfills always (and never rejects)
after the cancellation request has been submitted successfully
and all previously attached handlers have run.
:::note
Calling the `cancel` method on a settled promise is safe and has no effect;
if the task completes before the call to `cancel`, the code above is going to log:
```
completed
Ready for the next adventure!
```
However, if `cancel` is called before the task finishes, the output will be:
```
Cancelled. I'm tired of waiting!
Ready for the next adventure!
```
:::
The approach discussed above requires storing and chaining promises manually,
which can be cumbersome for code written in `async`/`await` style.
If you target plaforms that support the `AbortController`/`AbortSignal` idiom,
you can call the `cancelOn` method and tie call cancellation to an `AbortSignal` instead:
```javascript
async function callBinding(signal) {
try {
await MyService.LongRunningTask("input").cancelOn(signal);
} catch (err) {
if (err instanceof CancelError) {
console.log("Cancelled! Cause: ", err.cause);
} else {
console.error("Failed! Error: ", err);
}
}
}
let controller = new AbortController();
callBinding(controller.signal);
// Later...
controller.abort("I'm tired of waiting!");
```
:::caution
On the macOS platform, `AbortSignal` is only supported from macOS 10.15 Catalina onwards.
:::
### Handling errors
As you may have noticed above, bound methods can return errors, which are handled specially.
When a result field has type `error`, it is omitted by default from the values returned to JS.
When such a field is _non-nil_, the promise rejects with a `RuntimeError` exception
that wraps the Go error message:
```go
func (*MyService) FailingMethod(name string) error {
return fmt.Errorf("Welcome to an imperfect world, %s", name)
}
```
```js
import { MyService } from './bindings/changeme';
try {
await MyService.FailingMethod("CLU")
} catch (err) {
if (err.name === 'RuntimeError') {
console.log(err.message); // Prints 'Welcome to an imperfect world, CLU'
}
}
```
The exception will be an instance of the `Call.RuntimeError` class from the wails runtime,
hence you can also test its type like this:
```js
import { Call } from '@wailsio/runtime';
try {
// ...
} catch (err) {
if (err instanceof Call.RuntimeError) {
// ...
}
}
```
If the Go error value supports JSON marshaling, the exception's `cause` property
will hold the marshaled version of the error:
```go
type ImperfectWorldError struct {
Name string `json:"name"`
}
func (err *ImperfectWorldError) Error() {
return fmt.Sprintf("Welcome to an imperfect world, %s", err.Name)
}
func (*MyService) FailingMethod(name string) error {
return &ImperfectWorldError{
Name: name,
}
}
```
```js
import { MyService } from './bindings/changeme';
try {
await MyService.FailingMethod("CLU")
} catch (err) {
if (err.name === 'RuntimeError') {
console.log(err.cause.name); // Prints 'CLU'
}
}
```
Generally, many Go error values will only have limited or no support for marshaling to JSON.
If you so wish, you can customise the value provided as cause
by specifying either a global or per-service error marshaling function:
```go
app := application.New(application.Options{
MarshalError: func(err error) []byte {
// ...
},
Services: []application.Service{
application.NewServiceWithOptions(&MyService{}, application.ServiceOptions{
MarshalError: func(err error) []byte {
// ...
},
}),
},
})
```
Per-service functions override the global function,
which in turn overrides the default behaviour of using `json.Marshal`.
If a marshaling function returns `nil`, it falls back to the outer function:
per-service functions fall back to the global function,
which in turn falls back to the default behaviour.
:::tip
If you wish to omit the `cause` property on the resulting exception,
let the marshaling function return a falsy JSON value like `[]byte("null")`.
:::
Here's an example marshaling function that unwraps path errors and reports the file path:
```go
app := application.New(application.Options{
MarshalError: func(err error) []byte {
var perr *fs.PathError
if !errors.As(err, &perr) {
// Not a path error, fall back to default handling.
return nil
}
// Marshal path string
path, err := json.Marshal(&perr.Path)
if err != nil {
// String marshaling failed, fall back to default handling.
return nil
}
return []byte(fmt.Sprintf(`{"path":%s}`, path))
},
})
```
:::note
Error marshaling functions are not allowed to fail.
If they are not able to process a given error and return valid JSON,
they should return `nil` and fall back to a more generic handler.
If no strategy succeeds, the exception will not have a `cause` property.
:::
Binding call promises may also reject with a `TypeError`
when the method has been passed the wrong number of arguments,
when the conversion of arguments from JSON to their Go types fails,
or when the conversion of results to JSON fails.
These problems will usually be caught early by the type system.
If your code typechecks but you still get type errors,
it might be that some of your Go types are not supported by the `encoding/json` package:
look for warnings from the binding generator to catch these.
:::caution
If you see a `ReferenceError` complaining about unknown methods,
it could mean that your JS bindings have gotten out of sync with Go code
and must be regenerated.
:::

View file

@ -1,135 +0,0 @@
---
title: Clipboard
sidebar:
order: 50
---
The Wails Clipboard API provides a simple interface for interacting with the system clipboard. It allows you to read from and write to the clipboard, whilst supporting text data.
## Accessing the Clipboard
The clipboard can be accessed through the application instance:
```go
clipboard := app.Clipboard
```
## Setting Text
To set text to the clipboard, utilise the `SetText` method:
```go
success := app.Clipboard.SetText("Hello World")
if !success {
// Handle error
}
```
The `SetText` method returns a boolean indicating whether the operation was successful.
:::tip[Empty Text]
Setting an empty string (`""`) effectively clears the text content from the clipboard.
:::
## Getting Text
To retrieve text from the clipboard, utilise the `Text` method:
```go
text, ok := app.Clipboard.Text()
if !ok {
// Handle error
} else {
// Use the text
}
```
The `Text` method returns two values:
- The text content from the clipboard (string)
- A boolean indicating whether the operation was successful
:::note[Platform Behaviour]
The clipboard behaviour might vary slightly amongst operating systems. Always check the return values to ensure operations were successful.
:::
## Example
Here's a complete example showing how to create a menu-driven application that demonstrates clipboard operations:
```go
package main
import (
"log"
"runtime"
"time"
"github.com/wailsapp/wails/v3/pkg/application"
)
func main() {
app := application.New(application.Options{
Name: "Clipboard Demo",
Description: "A demo of the clipboard API",
Assets: application.AlphaAssets,
})
// Create a custom menu
menu := app.Menu.New()
if runtime.GOOS == "darwin" {
menu.AddRole(application.AppMenu)
}
// Add clipboard operations to menu
setClipboardMenu := menu.AddSubmenu("Set Clipboard")
setClipboardMenu.Add("Set Text 'Hello'").OnClick(func(ctx *application.Context) {
success := app.Clipboard.SetText("Hello")
if !success {
app.Dialog.Info().SetMessage("Failed to set clipboard text").Show()
}
})
getClipboardMenu := menu.AddSubmenu("Get Clipboard")
getClipboardMenu.Add("Get Text").OnClick(func(ctx *application.Context) {
result, ok := app.Clipboard.Text()
if !ok {
app.Dialog.Info().SetMessage("Failed to get clipboard text").Show()
} else {
app.Dialog.Info().SetMessage("Got:\n\n" + result).Show()
}
})
clearClipboardMenu := menu.AddSubmenu("Clear Clipboard")
clearClipboardMenu.Add("Clear Text").OnClick(func(ctx *application.Context) {
success := app.Clipboard.SetText("")
if success {
app.Dialog.Info().SetMessage("Clipboard text cleared").Show()
} else {
app.Dialog.Info().SetMessage("Clipboard text not cleared").Show()
}
})
app.Menu.Set(menu)
app.Window.New()
err := app.Run()
if err != nil {
log.Fatal(err.Error())
}
}
```
:::danger[Warning]
Always handle clipboard operation failures gracefully, as they can fail due to various system-level reasons such as permissions or resource constraints.
:::
## Best Practices
1. Always check the return values of clipboard operations
2. Handle failures gracefully with appropriate user feedback
3. Clear sensitive data from the clipboard when your application exits if it was responsible for putting it there
4. Consider implementing a timeout mechanism for clipboard operations in critical sections of your application
:::tip[Pro Tip]
Whilst working with the clipboard in a production environment, consider implementing retry logic for critical clipboard operations, as they can occasionally fail due to temporary system conditions.
:::

View file

@ -1,231 +0,0 @@
---
title: Context Menus
sidebar:
order: 51
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
## Context Menus
Wails provides context menus (right-click menus) with full support for context-specific actions, custom data passing, and default menu overrides. Create different menus for text, images, links, and custom elements.()
The `menu-id` parameter is a unique identifier for the menu that will be used to associate it with HTML elements.
## Adding Menu Items
You can add items to your context menu using the same methods as application menus. Here's a simple example:
```go
contextMenu := application.NewContextMenu()
contextMenu.Add("Cut").OnClick(func(ctx *application.Context) {
// Handle cut action
})
contextMenu.Add("Copy").OnClick(func(ctx *application.Context) {
// Handle copy action
})
contextMenu.Add("Paste").OnClick(func(ctx *application.Context) {
// Handle paste action
})
// Register the context menu with the manager
app.ContextMenu.Add("editor-menu", contextMenu)
```
:::tip[Menu Items]
For detailed information about available menu item types and properties, refer to the [Menu Reference](./menu-reference) documentation.
:::
## Context Data
Context menus can receive data from the HTML element that triggered them. This data can be accessed in the click handlers:
```go
contextMenu := app.ContextMenu.New()
menuItem := contextMenu.Add("Process Image")
menuItem.OnClick(func(ctx *application.Context) {
imageID := ctx.ContextMenuData()
// Process the image using the ID
})
// Register the context menu with the manager
app.ContextMenu.Add("image-menu", contextMenu)
```
## Associating with HTML Elements
To associate a context menu with an HTML element, use the `--custom-contextmenu` and `--custom-contextmenu-data` CSS properties:
```html
<div style="--custom-contextmenu: menu-id; --custom-contextmenu-data: some-data">
Right click me!
</div>
```
- `--custom-contextmenu`: Specifies the menu ID (must match the ID used in `NewContextMenu`)
- `--custom-contextmenu-data`: Optional data that will be passed to the click handlers
:::note
This feature will only work as expected after the runtime [has been initialised](../runtime#initialisation).
:::
## Default Context Menu
The default context menu is the webview's built-in context menu that provides system-level operations. You can control its visibility using the `--default-contextmenu` CSS property:
```html
<!-- Hide the default context menu -->
<div style="--default-contextmenu: hide">
No default menu here
</div>
<!-- Show the default context menu -->
<div style="--default-contextmenu: show">
Default menu always shown
</div>
<!-- Smart context menu (default behaviour) -->
<div style="--default-contextmenu: auto">
Shows menu when appropriate
</div>
```
:::note[Smart Context Menu]
The `auto` setting enables "smart" context menu behaviour:
- Shows when text is selected
- Shows in text input fields
- Shows in editable content
- Hides in other contexts
:::
## Updating Menu Items
Menu items can be updated dynamically using the `SetLabel` method and other property setters. After making changes, call `Update` on the menu to apply them:
```go
contextMenu := application.NewContextMenu()
menuItem := contextMenu.Add("Initial Label")
// Register the context menu with the manager
app.ContextMenu.Add("dynamic-menu", contextMenu)
// Later, update the menu item
menuItem.SetLabel("New Label")
contextMenu.Update()
```
## Platform Considerations
<Tabs>
<TabItem label="macOS" icon="fa-brands:apple">
On macOS, context menus follow system conventions:
- Menus use native system animations and transitions
- Right-click is automatically mapped to Control+Click
- Menu styling automatically adapts to system appearance
- Standard text operations appear in the default context menu
- Context menus support native macOS scrolling behaviour
</TabItem>
<TabItem label="Windows" icon="fa-brands:windows">
On Windows, context menus integrate with the Windows UI:
- Menus use the Windows native context menu style
- Right-click handling is native
- Menu appearance follows the Windows theme
- Default context menu includes standard Windows operations
- Context menus support Windows touch and pen input
</TabItem>
<TabItem label="Linux" icon="fa-brands:linux">
On Linux, context menu behaviour varies by desktop environment:
- Menu styling adapts to the current desktop theme
- Right-click behaviour follows system settings
- Default context menu content may vary by environment
- Menu positioning follows desktop environment conventions
- GTK/Qt integration depends on the environment
</TabItem>
</Tabs>
:::tip[Pro Tip]
Consider using different context menus for different types of elements in your application. This allows you to provide context-specific actions that make sense for each element type.
:::
## Best Practices
1. Keep context menus focused and relevant to the clicked element
2. Use clear, concise labels for menu items
3. Group related items together
4. Consider using separators to organise menu items
5. Provide keyboard shortcuts for common actions
6. Update menu items dynamically based on application state
7. Handle errors gracefully when processing context data
:::danger[Warning]
Always validate context data received from the frontend before using it in your application logic, as it could be manipulated by users.
:::
## Example
Here's a complete example demonstrating context menu features:
```go
package main
import (
"github.com/wailsapp/wails/v3/pkg/application"
)
func main() {
app := application.New(application.Options{
Name: "Context Menu Demo",
})
// Create a context menu
contextMenu := app.ContextMenu.New()
// Add items that respond to context data
clickMe := contextMenu.Add("Click to show context data")
dataLabel := contextMenu.Add("Current data: None")
clickMe.OnClick(func(ctx *application.Context) {
data := ctx.ContextMenuData()
dataLabel.SetLabel("Current data: " + data)
contextMenu.Update()
})
// Register the context menu with the manager
app.ContextMenu.Add("test", contextMenu)
window := app.Window.New()
window.SetTitle("Context Menu Demo")
err := app.Run()
if err != nil {
panic(err)
}
}
```
Associated HTML:
```html
<div class="region" style="--custom-contextmenu: test; --custom-contextmenu-data: item-123">
Right click me to see the custom menu!
</div>
<div style="--default-contextmenu: hide">
No context menu here
</div>
<div style="--default-contextmenu: auto">
<p style="user-select: text">Select this text to see the default menu</p>
<input type="text" placeholder="Type here to see the default menu"/>
</div>

View file

@ -1,232 +0,0 @@
---
title: Dialogs
sidebar:
order: 54
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
Wails provides a comprehensive dialog system for displaying native system dialogs. These include informational messages, questions, file selection, and more.
## Dialog Types
### Information Dialog
Display simple informational messages to users:
```go
dialog := app.Dialog.Info()
dialog.SetTitle("Welcome")
dialog.SetMessage("Welcome to our application!")
dialog.Show()
```
### Question Dialog
Present users with questions and customisable buttons:
```go
dialog := app.Dialog.Question()
dialog.SetTitle("Save Changes")
dialog.SetMessage("Do you want to save your changes?")
dialog.AddButton("Save").OnClick(func() {
// Handle save
})
saveButton := dialog.AddButton("Don't Save")
dialog.SetDefaultButton(saveButton)
dialog.Show()
```
### Error Dialog
Display error messages:
```go
dialog := app.Dialog.Error()
dialog.SetTitle("Error")
dialog.SetMessage("Failed to save file")
dialog.Show()
```
### File Dialogs
#### Open File Dialog
Allow users to select files to open:
```go
dialog := app.Dialog.OpenFile()
dialog.SetTitle("Select Image")
dialog.SetFilters([]*application.FileFilter{
{
DisplayName: "Images (*.png;*.jpg)",
Pattern: "*.png;*.jpg",
},
})
// Single file selection
if path, err := dialog.PromptForSingleSelection(); err == nil {
// Use selected file path
}
// Multiple file selection
if paths, err := dialog.PromptForMultipleSelection(); err == nil {
// Use selected file paths
}
```
#### Save File Dialog
Allow users to choose where to save files:
```go
dialog := app.Dialog.SaveFile()
dialog.SetTitle("Save Document")
dialog.SetDefaultFilename("document.txt")
dialog.SetFilters([]*application.FileFilter{
{
DisplayName: "Text Files (*.txt)",
Pattern: "*.txt",
},
})
if path, err := dialog.PromptForSingleSelection(); err == nil {
// Save file to selected path
}
```
## Dialog Customisation
### Setting Icons
Dialogs can use custom icons from the built-in icon set:
```go
dialog := app.Dialog.Info()
dialog.SetIcon(icons.ApplicationDarkMode256)
```
### Window Attachment
Dialogs can be attached to specific windows:
```go
dialog := app.Dialog.Question()
dialog.AttachToWindow(app.Window.Current())
dialog.Show()
```
### Button Customisation
Create buttons with custom labels and actions:
```go
dialog := app.Dialog.Question()
dialog.SetMessage("Choose an action")
// Add buttons with custom handlers
dialog.AddButton("Save").OnClick(func() {
// Handle save
})
dialog.AddButton("Don't Save").OnClick(func() {
// Handle don't save
})
cancelButton := dialog.AddButton("Cancel")
dialog.SetDefaultButton(cancelButton) // Set default button
```
## Platform Considerations
<Tabs>
<TabItem label="macOS" icon="fa-brands:apple">
On macOS, dialogs follow system conventions:
- Use system-standard dialog appearances
- Support keyboard navigation (Tab, Space, Return)
- Support standard keyboard shortcuts (⌘+.)
- Automatically handle dark/light mode
- Support system accessibility features
- Position relative to parent window
</TabItem>
<TabItem label="Windows" icon="fa-brands:windows">
On Windows, dialogs integrate with the Windows UI:
- Use Windows system dialog styles
- Support keyboard navigation (Tab, Space, Enter)
- Support Windows accessibility features
- Follow Windows dialog positioning rules
- Adapt to Windows theme settings
- Support high DPI displays
</TabItem>
<TabItem label="Linux" icon="fa-brands:linux">
On Linux, dialog behaviour depends on the desktop environment:
- Use native dialog widgets when available
- Follow desktop environment theme
- Support keyboard navigation
- Adapt to desktop environment settings
- Position according to window manager rules
- Support desktop environment accessibility
</TabItem>
</Tabs>
## Directory Selection
Allow users to select directories:
```go
dialog := app.Dialog.OpenFile()
dialog.CanChooseDirectories(true)
dialog.CanChooseFiles(false)
dialog.SetTitle("Select Project Directory")
if path, err := dialog.PromptForSingleSelection(); err == nil {
// Use selected directory path
}
```
## About Dialog
Display application information:
```go
app.Menu.ShowAbout()
```
## Best Practices
1. Use appropriate dialog types for different scenarios:
- InfoDialog for general messages
- QuestionDialog for user decisions
- ErrorDialog for error messages
- FileDialog for file operations
2. Provide clear and concise messages:
- Use descriptive titles
- Keep messages brief but informative
- Clearly state any required user action
3. Handle dialog responses appropriately:
- Check for errors in file dialogs
- Provide feedback for user actions
- Handle cancellation gracefully
4. Consider platform conventions:
- Follow platform-specific dialog patterns
- Use appropriate button ordering
- Respect system settings
:::tip[Pro Tip]
When using file dialogs, always set appropriate filters to help users select the correct file types for your application.
:::
:::danger[Warning]
Always handle potential errors from file and directory dialogs, as they may fail due to permissions or other system issues.
:::

View file

@ -1,676 +0,0 @@
---
title: Events
sidebar:
order: 55
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
Wails provides a flexible event system that enables communication between different parts of your application. This includes both application-level and window-level events.
## Application Events
Application events are triggered by application-level state changes such as application startup, theme changes, and power events. You can listen for these events using the `OnApplicationEvent` method:
```go
app.Event.OnApplicationEvent(events.Mac.ApplicationDidBecomeActive, func(event *application.ApplicationEvent) {
app.Logger.Info("Application started!")
})
app.Event.OnApplicationEvent(events.Windows.SystemThemeChanged, func(event *application.ApplicationEvent) {
app.Logger.Info("System theme changed!")
if event.Context().IsDarkMode() {
app.Logger.Info("System is now using dark mode!")
} else {
app.Logger.Info("System is now using light mode!")
}
})
```
### Common Application Events
Common application events are aliases for platform-specific application events. These events are triggered by application-level state
changes such as application startup, theme changes, and power events.
Here is the same example as above, but using common application events to make it work across all platforms:
```go
app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(event *application.ApplicationEvent) {
app.Logger.Info("Application started!")
})
app.Event.OnApplicationEvent(events.Common.ThemeChanged, func(event *application.ApplicationEvent) {
if event.Context().IsDarkMode() {
app.Logger.Info("System is now using dark mode!")
} else {
app.Logger.Info("System is now using light mode!")
}
})
```
#### Common Application Event List
| Event Name | Description |
|---------------------------|----------------------------------------------------------------------------------------------------------|
| ApplicationOpenedWithFile | Application opened with a file. See [File Associations](/guides/file-associations) for more information. |
| ApplicationStarted | Application has started |
| ThemeChanged | System theme changed |
### Platform-Specific Application Events
Below is a list of all platform-specific application events.
<Tabs>
<TabItem label="macOS" icon="fa-brands:apple">
| Event Name | Common Event | Description |
|------------|--------------|-------------|
| ApplicationDidBecomeActive | - | Application became active |
| ApplicationDidChangeBackingProperties | - | Application backing properties changed |
| ApplicationDidChangeEffectiveAppearance | ThemeChanged | Application appearance changed |
| ApplicationDidChangeIcon | - | Application icon changed |
| ApplicationDidChangeOcclusionState | - | Application occlusion state changed |
| ApplicationDidChangeScreenParameters | - | Screen parameters changed |
| ApplicationDidChangeStatusBarFrame | - | Status bar frame changed |
| ApplicationDidChangeStatusBarOrientation | - | Status bar orientation changed |
| ApplicationDidChangeTheme | ThemeChanged | System theme changed |
| ApplicationDidFinishLaunching | ApplicationStarted | Application finished launching |
| ApplicationDidHide | - | Application hidden |
| ApplicationDidResignActiveNotification | - | Application resigned active state |
| ApplicationDidUnhide | - | Application unhidden |
| ApplicationDidUpdate | - | Application updated |
| ApplicationShouldHandleReopen | - | Application should handle reopen |
| ApplicationWillBecomeActive | - | Application will become active |
| ApplicationWillFinishLaunching | - | Application will finish launching |
| ApplicationWillHide | - | Application will hide |
| ApplicationWillResignActiveNotification | - | Application will resign active state |
| ApplicationWillTerminate | - | Application will terminate |
| ApplicationWillUnhide | - | Application will unhide |
| ApplicationWillUpdate | - | Application will update |
</TabItem>
<TabItem label="Windows" icon="fa-brands:windows">
| Event Name | Common Event | Description |
|------------|--------------|-------------|
| APMPowerSettingChange | - | Power settings changed |
| APMPowerStatusChange | - | Power status changed |
| APMResumeAutomatic | - | System resuming automatically |
| APMResumeSuspend | - | System resuming from suspend |
| APMSuspend | - | System suspending |
| ApplicationStarted | ApplicationStarted | Application started |
| SystemThemeChanged | ThemeChanged | System theme changed |
</TabItem>
<TabItem label="Linux" icon="fa-brands:linux">
| Event Name | Common Event | Description |
|------------|--------------|-------------|
| ApplicationStartup | ApplicationStarted | Application started |
| SystemThemeChanged | ThemeChanged | System theme changed |
</TabItem>
</Tabs>
## Window Events
Window events are triggered by window-specific actions such as resizing, moving, or changing focus state. You can listen for these events using the `OnWindowEvent` method:
```go
window.OnWindowEvent(events.Common.WindowClosing, func(e *application.WindowEvent) {
app.Logger.Info("Window is closing!")
})
window.OnWindowEvent(events.Common.WindowFocus, func(e *application.WindowEvent) {
app.Logger.Info("Window gained focus!")
})
```
### Hooks vs Standard Listeners
Wails provides two ways to handle window events: standard listeners (OnWindowEvent) and hooks (RegisterHook). The key differences are:
1. **Execution Order**: Hooks are executed first and in the order they are registered, while standard listeners execute after Hooks and have no guaranteed order.
2. **Blocking**: Hooks are blocking and must complete before the next hook is executed. Standard listeners are non-blocking.
3. **Event Cancellation**: When cancelling an event in a Hook, it prevents it from propagating further. This is useful to prevent
default behaviour, such as closing a window. Cancelling an event in a standard listener will only prevent it from being emitted
from that point in time.
In this example, the window will only close after the close button has been clicked three times, demonstrating how hooks can be used to control event flow.
```go
// Hook - runs synchronously. The window will not close until the countdown reaches zero.
var countdown = 3
window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) {
countdown--
if countdown == 0 {
app.Logger.Info("Window closing - countdown reached zero!")
return
}
app.Logger.Info("Preventing window from closing - countdown:", countdown)
e.Cancel()
})
```
This next example demonstrates the execution order of hooks vs standard listeners.
```go
window.OnWindowEvent(events.Common.WindowFocus, func(e *application.WindowEvent) {
app.Logger.Info("I always run after hooks!")
})
// Multiple hooks are executed in order
window.RegisterHook(events.Common.WindowFocus, func(e *application.WindowEvent) {
app.Logger.Info("First focus hook - will always run first!")
})
window.RegisterHook(events.Common.WindowFocus, func(e *application.WindowEvent) {
app.Logger.Info("Second focus hook - will always run second!")
})
```
This produces the following output:
```
INF First focus hook - will always run first!
INF Second focus hook - will always run second!
INF I always run after hooks!
```
### Common Window Events
| Event Name | Description |
|--------------------|---------------------------|
| WindowClosing | Window is closing |
| WindowDidMove | Window moved |
| WindowDidResize | Window resized |
| WindowDPIChanged | Window DPI changed |
| WindowFilesDropped | Files dropped on window |
| WindowFocus | Window gained focus |
| WindowFullscreen | Window entered fullscreen |
| WindowHide | Window hidden |
| WindowLostFocus | Window lost focus |
| WindowMaximise | Window maximised |
| WindowMinimise | Window minimised |
| WindowRestore | Window restored |
| WindowRuntimeReady | Window runtime is ready |
| WindowShow | Window shown |
| WindowUnFullscreen | Window exited fullscreen |
| WindowUnMaximise | Window unmaximised |
| WindowUnMinimise | Window unminimised |
| WindowZoom | Window zoomed |
| WindowZoomIn | Window zoomed in |
| WindowZoomOut | Window zoomed out |
| WindowZoomReset | Window zoom reset |
### Enhanced Drag and Drop with Targeted Dropzones
Wails v3 introduces an enhanced drag-and-drop system that allows you to define specific "dropzones" within your application's HTML. This provides finer control over where files can be dropped and offers automatic visual feedback managed by the Wails runtime.
#### 1. Defining Dropzones in HTML
To designate an HTML element as a dropzone, add the `data-wails-dropzone` attribute to it. Any element with this attribute will become a valid target for file drops.
**Example:**
```html
<div id="myDropArea" class="my-styles" data-wails-dropzone>
<p>Drop files here!</p>
</div>
<div id="anotherZone" data-wails-dropzone style="width: 300px; height: 100px; border: 1px solid grey;">
Another drop target
</div>
<!-- Advanced example with custom data attributes -->
<div class="tree-node folder folder-dropzone"
data-wails-dropzone
data-folder-id="documents"
data-folder-name="Documents"
data-path="/home/user/Documents">
<span>📁 Documents</span>
</div>
```
#### 2. Visual Feedback
When files are dragged over an element marked with `data-wails-dropzone`, the Wails JavaScript runtime automatically adds the `wails-dropzone-hover` CSS class to that element. You can define styles for this class to provide visual feedback:
**Example CSS:**
```css
/* Base style for all dropzones (resting state) */
.dropzone {
border: 2px dashed #888;
background-color: #303030;
padding: 20px;
text-align: center;
}
/* Default hover effect applied by the runtime */
.dropzone.wails-dropzone-hover {
background-color: #3c3c3e;
border-style: dotted;
border-color: #007bff;
box-shadow: 0 0 8px rgba(0, 123, 255, 0.4);
}
/* Example: Customizing hover for a specific dropzone to override the default */
#myDropArea.wails-dropzone-hover {
background-color: lightgreen;
outline: 2px solid green;
}
```
The runtime handles adding and removing the `wails-dropzone-hover` class as files are dragged in and out of the dropzone or the window.
#### 3. Handling Drops in Go
On the Go side, listen for the `events.Common.WindowDropZoneFilesDropped` event. This event will be emitted when files are dropped onto an element that has the `data-wails-dropzone` attribute.
**Example Go Handler:**
```go
import (
"fmt"
"log"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/events"
)
// Assuming 'win' is your *application.WebviewWindow instance
win.OnWindowEvent(events.Common.WindowDropZoneFilesDropped, func(event *application.WindowEvent) {
droppedFiles := event.Context().DroppedFiles()
log.Printf("Files dropped: %v", droppedFiles)
details := event.Context().DropZoneDetails()
if details != nil {
log.Printf("Dropped on Element ID: '%s'", details.ElementID)
log.Printf("Element Classes: %v", details.ClassList)
log.Printf("Drop Coordinates (relative to window): X=%d, Y=%d", details.X, details.Y)
// Access custom data attributes from the HTML element
if folderName, exists := details.Attributes["data-folder-name"]; exists {
log.Printf("Folder name: %s", folderName)
}
if folderPath, exists := details.Attributes["data-path"]; exists {
log.Printf("Target path: %s", folderPath)
}
// Example: Handle different dropzone types based on ElementID
switch details.ElementID {
case "documents":
log.Printf("Files dropped on Documents folder")
// Handle document uploads
case "downloads":
log.Printf("Files dropped on Downloads folder")
// Handle download folder drops
case "trash":
log.Printf("Files dropped on Trash")
// Handle file deletion
default:
log.Printf("Files dropped on unknown target: %s", details.ElementID)
}
payload := map[string]interface{}{
"files": droppedFiles,
"targetID": details.ElementID,
"targetClasses": details.ClassList,
"dropX": details.X,
"dropY": details.Y,
"attributes": details.Attributes,
}
application.Get().EmitEvent("frontend:FileDropInfo", payload) // Emits globally
// or win.EmitEvent("frontend:FileDropInfoForWindow", payload) // Emits to this specific window
} else {
log.Println("Drop occurred, but DropZoneDetails were nil.")
}
})
```
The `event.Context().DropZoneDetails()` method returns a pointer to an `application.DropZoneDetails` struct (or `nil` if details aren't available), containing:
- `ElementID string`: The id of the element dropped onto
- `ClassList []string`: The list of CSS classes of the HTML element that received the drop.
- `X int`: The X-coordinate of the drop, relative to the window's content area.
- `Y int`: The Y-coordinate of the drop, relative to the window's content area.
- `Attributes map[string]string`: A map containing all HTML attributes of the target element, allowing access to custom data attributes like `data-path`, `data-folder-name`, etc.
The `event.Context().DroppedFiles()` method returns a `[]string` of file paths.
For a fully runnable demonstration of these features, including multiple styled dropzones, please refer to the example located in the `v3/examples/drag-n-drop` directory within the Wails repository.
### Platform-Specific Window Events
<Tabs>
<TabItem label="macOS" icon="fa-brands:apple">
| Event Name | Common Event | Description |
|------------|--------------|-------------|
| WindowDidBecomeKey | WindowFocus | Window became key window |
| WindowDidBecomeMain | - | Window became main window |
| WindowDidBeginSheet | - | Sheet began |
| WindowDidChangeAlpha | - | Window alpha changed |
| WindowDidChangeBackingLocation | - | Window backing location changed |
| WindowDidChangeBackingProperties | - | Window backing properties changed |
| WindowDidChangeCollectionBehavior | - | Window collection behaviour changed |
| WindowDidChangeEffectiveAppearance | - | Window appearance changed |
| WindowDidChangeOcclusionState | - | Window occlusion state changed |
| WindowDidChangeOrderingMode | - | Window ordering mode changed |
| WindowDidChangeScreen | - | Window screen changed |
| WindowDidChangeScreenParameters | - | Window screen parameters changed |
| WindowDidChangeScreenProfile | - | Window screen profile changed |
| WindowDidChangeScreenSpace | - | Window screen space changed |
| WindowDidChangeScreenSpaceProperties | - | Window screen space properties changed |
| WindowDidChangeSharingType | - | Window sharing type changed |
| WindowDidChangeSpace | - | Window space changed |
| WindowDidChangeSpaceOrderingMode | - | Window space ordering mode changed |
| WindowDidChangeTitle | - | Window title changed |
| WindowDidChangeToolbar | - | Window toolbar changed |
| WindowDidDeminiaturize | WindowUnMinimise | Window unminimised |
| WindowDidEndSheet | - | Sheet ended |
| WindowDidEnterFullScreen | WindowFullscreen | Window entered fullscreen |
| WindowDidEnterVersionBrowser | - | Window entered version browser |
| WindowDidExitFullScreen | WindowUnFullscreen | Window exited fullscreen |
| WindowDidExitVersionBrowser | - | Window exited version browser |
| WindowDidExpose | - | Window exposed |
| WindowDidFocus | WindowFocus | Window gained focus |
| WindowDidMiniaturize | WindowMinimise | Window minimised |
| WindowDidMove | WindowDidMove | Window moved |
| WindowDidOrderOffScreen | - | Window ordered off screen |
| WindowDidOrderOnScreen | - | Window ordered on screen |
| WindowDidResignKey | - | Window resigned key window |
| WindowDidResignMain | - | Window resigned main window |
| WindowDidResize | WindowDidResize | Window resized |
| WindowDidUpdate | - | Window updated |
| WindowDidUpdateAlpha | - | Window alpha updated |
| WindowDidUpdateCollectionBehavior | - | Window collection behaviour updated |
| WindowDidUpdateCollectionProperties | - | Window collection properties updated |
| WindowDidUpdateShadow | - | Window shadow updated |
| WindowDidUpdateTitle | - | Window title updated |
| WindowDidUpdateToolbar | - | Window toolbar updated |
| WindowDidZoom | WindowZoom | Window zoomed |
| WindowFileDraggingEntered | - | File dragging entered window |
| WindowFileDraggingExited | - | File dragging exited window |
| WindowFileDraggingPerformed | - | File dragging performed |
| WindowHide | WindowHide | Window hidden |
| WindowMaximise | WindowMaximise | Window maximised |
| WindowShouldClose | WindowClosing | Window should close |
| WindowShow | WindowShow | Window shown |
| WindowUnMaximize | WindowUnMaximise | Window unmaximised |
| WindowZoomIn | WindowZoomIn | Window zoomed in |
| WindowZoomOut | WindowZoomOut | Window zoomed out |
| WindowZoomReset | WindowZoomReset | Window zoom reset |
|------------|--------------|-------------|
</TabItem>
<TabItem label="Windows" icon="fa-brands:windows">
| Event Name | Common Event | Description |
|------------|--------------|-------------|
| WebViewNavigationCompleted | - | WebView navigation completed |
| WindowActive | - | Window became active |
| WindowBackgroundErase | - | Window background needs erasing |
| WindowClickActive | - | Window clicked whilst active |
| WindowClosing | WindowClosing | Window closing |
| WindowDidMove | WindowDidMove | Window moved |
| WindowDidResize | WindowDidResize | Window resized |
| WindowEndMove | - | Window finished moving |
| WindowEndResize | - | Window finished resising |
| WindowFullscreen | WindowFullscreen | Window entered fullscreen |
| WindowHide | WindowHide | Window hidden |
| WindowInactive | - | Window became inactive |
| WindowKillFocus | WindowLostFocus | Window lost focus |
| WindowMaximise | WindowMaximise | Window maximised |
| WindowMinimise | WindowMinimise | Window minimised |
| WindowPaint | - | Window needs painting |
| WindowRestore | WindowRestore | Window restored |
| WindowSetFocus | WindowFocus | Window gained focus |
| WindowShow | WindowShow | Window shown |
| WindowStartMove | - | Window started moving |
| WindowStartResize | - | Window started resising |
| WindowUnFullscreen | WindowUnFullscreen | Window exited fullscreen |
| WindowUnMaximise | WindowUnMaximise | Window unmaximised |
| WindowUnMinimise | WindowUnMinimise | Window unminimised |
| WindowZOrderChanged | - | Window z-order changed |
#### Input Events
| Event Name | Description |
|------------|-------------|
| WindowDragDrop | Files dragged and dropped |
| WindowDragEnter | Drag entered window |
| WindowDragLeave | Drag left window |
| WindowDragOver | Drag over window |
| WindowKeyDown | Key pressed |
| WindowKeyUp | Key released |
| WindowNonClientHit | Mouse hit in non-client area |
| WindowNonClientMouseDown | Mouse down in non-client area |
| WindowNonClientMouseLeave | Mouse left non-client area |
| WindowNonClientMouseMove | Mouse move in non-client area |
| WindowNonClientMouseUp | Mouse up in non-client area |
</TabItem>
<TabItem label="Linux" icon="fa-brands:linux">
| Event Name | Common Event | Description |
|------------|--------------|-------------|
| WindowDeleteEvent | WindowClosing | Window delete requested |
| WindowDidMove | WindowDidMove | Window moved |
| WindowDidResize | WindowDidResize | Window resized |
| WindowFocusIn | WindowFocus | Window gained focus |
| WindowFocusOut | WindowLostFocus | Window lost focus |
| WindowLoadChanged | WindowShow | Window load state changed |
</TabItem>
</Tabs>
## Menu Events
Menu events are triggered by menu-specific actions such as opening, closing, and interacting with menu items. These events are useful for creating dynamic menus and responding to menu interactions.
```go
// Listen for menu events
app.Event.OnApplicationEvent(events.Mac.MenuDidOpen, func(event *application.ApplicationEvent) {
app.Logger.Info("Menu opened!")
})
app.Event.OnApplicationEvent(events.Mac.MenuWillSendAction, func(event *application.ApplicationEvent) {
app.Logger.Info("Menu about to send action!")
})
```
For more information about menus, see the [Application Menu](/learn/application-menu) and [Context Menu](/learn/context-menu) documentation.
### Platform-Specific Menu Events
<Tabs>
<TabItem label="macOS" icon="fa-brands:apple">
| Event Name | Description |
|------------|-------------|
| MenuDidAddItem | Menu item added |
| MenuDidBeginTracking | Menu began tracking |
| MenuDidClose | Menu closed |
| MenuDidDisplayItem | Menu item displayed |
| MenuDidEndTracking | Menu ended tracking |
| MenuDidHighlightItem | Menu item highlighted |
| MenuDidOpen | Menu opened |
| MenuDidPopUp | Menu popped up |
| MenuDidRemoveItem | Menu item removed |
| MenuDidSendAction | Menu sent action |
| MenuDidSendActionToItem | Menu sent action to item |
| MenuDidUpdate | Menu updated |
| MenuWillAddItem | Menu will add item |
| MenuWillBeginTracking | Menu will begin tracking |
| MenuWillDisplayItem | Menu will display item |
| MenuWillEndTracking | Menu will end tracking |
| MenuWillHighlightItem | Menu will highlight item |
| MenuWillOpen | Menu will open |
| MenuWillPopUp | Menu will pop up |
| MenuWillRemoveItem | Menu will remove item |
| MenuWillSendAction | Menu will send action |
| MenuWillSendActionToItem | Menu will send action to item |
| MenuWillUpdate | Menu will update |
</TabItem>
<TabItem label="Windows" icon="fa-brands:windows">
Windows does not currently provide specific menu events.
</TabItem>
<TabItem label="Linux" icon="fa-brands:linux">
Linux does not currently provide specific menu events.
</TabItem>
</Tabs>
## WebView Events
WebView events are triggered by navigation and loading state changes in the WebView component. These events are useful for tracking page loads and navigation state.
```go
// Listen for WebView navigation events
app.Event.OnApplicationEvent(events.Mac.WebViewDidStartProvisionalNavigation, func(event *application.ApplicationEvent) {
app.Logger.Info("WebView started loading a page!")
})
app.Event.OnApplicationEvent(events.Mac.WebViewDidFinishNavigation, func(event *application.ApplicationEvent) {
app.Logger.Info("WebView finished loading the page!")
})
// On Windows
app.Event.OnApplicationEvent(events.Windows.WebViewNavigationCompleted, func(event *application.ApplicationEvent) {
app.Logger.Info("WebView navigation completed!")
})
```
### Platform-Specific WebView Events
<Tabs>
<TabItem label="macOS" icon="fa-brands:apple">
| Event Name | Description |
|------------|-------------|
| WebViewDidCommitNavigation | Navigation committed |
| WebViewDidFinishNavigation | Navigation finished |
| WebViewDidReceiveServerRedirectForProvisionalNavigation | Server redirect received |
| WebViewDidStartProvisionalNavigation | Provisional navigation started |
</TabItem>
<TabItem label="Windows" icon="fa-brands:windows">
| Event Name | Description |
|------------|-------------|
| WebViewNavigationCompleted | Navigation completed |
</TabItem>
<TabItem label="Linux" icon="fa-brands:linux">
Linux does not currently provide specific WebView events.
</TabItem>
</Tabs>
## Custom Events
You can emit and listen for custom events to enable communication between different parts of your application. Wails v3 provides both a traditional API and a new [Manager API](/learn/manager-api) for better organization.
### Emitting Events
You can emit custom events from anywhere in your application:
<Tabs>
<TabItem label="Manager API (Recommended)">
```go
// NEW: Using the Event Manager (recommended)
app.Event.Emit("myevent", "hello")
// Emit from a specific window
window.EmitEvent("windowevent", "window specific data")
```
</TabItem>
<TabItem label="Traditional API">
```go
// Traditional API (no longer works)
app.EmitEvent("myevent", "hello")
// Emit from a specific window
window.EmitEvent("windowevent", "window specific data")
```
</TabItem>
</Tabs>
### Handling Custom Events
Listen for custom events using the event management methods:
<Tabs>
<TabItem label="Manager API (Recommended)">
```go
// NEW: Using the Event Manager (recommended)
cancelFunc := app.Event.On("myevent", func(e *application.CustomEvent) {
// Access event information
name := e.Name // Event name
data := e.Data // Event data
sender := e.Sender // Name of the window sending the event, or "" if sent from application
cancelled := e.IsCancelled() // Event cancellation status
})
// Remove specific event listener
app.Event.Off("myevent")
// Remove all event listeners
app.Event.Reset()
// Listen for events a limited number of times
app.Event.OnMultiple("myevent", func(e *application.CustomEvent) {
// This will only be called 3 times
}, 3)
```
</TabItem>
<TabItem label="Traditional API">
```go
// Traditional API (still supported)
cancelFunc := app.OnEvent("myevent", func(e *application.CustomEvent) {
// Access event information
name := e.Name // Event name
data := e.Data // Event data
sender := e.Sender // Name of the window sending the event, or "" if sent from application
cancelled := e.IsCancelled() // Event cancellation status
})
// Remove specific event listener
app.OffEvent("myevent")
// Remove all event listeners
app.ResetEvents()
// Listen for events a limited number of times
app.OnMultipleEvent("myevent", func(e *application.CustomEvent) {
// This will only be called 3 times
}, 3)
```
</TabItem>
</Tabs>
## Event Cancellation
Events can be cancelled to prevent their default behaviour or stop propagation to other listeners. This is particularly useful for hooks that need to control window closing, menu actions, or other system events.
### Cancelling Events
To cancel an event, call the `Cancel()` method on the event object:
```go
window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) {
// Prevent the window from closing
e.Cancel()
})
```
### Checking Event Cancellation
You can check if an event has been cancelled using the `IsCancelled()` method:
```go
window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) {
if e.IsCancelled() {
app.Logger.Info("Window closing was cancelled by another hook")
return
}
// Process event
})
// For custom events
app.Event.On("myevent", func(e *application.CustomEvent) {
if e.IsCancelled() {
app.Logger.Info("Event was cancelled")
return
}
// Process event
})
```
:::tip[Pro Tip]
Remember that event cancellation in hooks affects all subsequent hooks and listeners, whilst cancellation in standard listeners only affects listeners that haven't yet been called.
:::

View file

@ -1,188 +0,0 @@
---
title: Menu Reference
sidebar:
order: 52
---
This reference document covers the common menu item types and properties available in Wails v3. These features are shared between application menus and context menus.
## Menu Item Types
### Regular Menu Items
The most basic type of menu item displays text and triggers an action when clicked:
```go
menuItem := menu.Add("Click Me")
menuItem.OnClick(func(ctx *application.Context) {
// Handle click
})
```
### Checkboxes
Checkbox menu items provide a toggleable state:
```go
checkbox := menu.AddCheckbox("Enable Feature", true) // true = initially checked
checkbox.OnClick(func(ctx *application.Context) {
isChecked := ctx.ClickedMenuItem().Checked()
// Handle state change
})
```
### Radio Groups
Radio items create mutually exclusive options. Items are grouped automatically when placed adjacently:
```go
menu.AddRadio("Option 1", true) // true = initially selected
menu.AddRadio("Option 2", false)
menu.AddRadio("Option 3", false)
```
### Submenus
Submenus allow you to create nested menu structures:
```go
submenu := menu.AddSubmenu("More Options")
submenu.Add("Submenu Item 1")
submenu.Add("Submenu Item 2")
```
### Separators
Separators add visual dividers between menu items:
```go
menu.Add("Item 1")
menu.AddSeparator()
menu.Add("Item 2")
```
## Menu Item Properties
### Label
The text displayed for the menu item:
```go
menuItem := menu.Add("Initial Label")
menuItem.SetLabel("New Label")
```
### Enabled State
Control whether the menu item can be interacted with:
```go
menuItem := menu.Add("Disabled Item")
menuItem.SetEnabled(false)
```
### Checked State
For checkbox and radio items, control or query their checked state:
```go
checkbox := menu.AddCheckbox("Feature", false)
checkbox.SetChecked(true)
isChecked := checkbox.Checked()
```
### Accelerators
Add keyboard shortcuts to menu items:
```go
menuItem := menu.Add("Save")
menuItem.SetAccelerator("CmdOrCtrl+S")
```
Common accelerator modifiers:
- `CmdOrCtrl`: Command on macOS, Control on Windows/Linux
- `Shift`
- `Alt`: Option on macOS
- `Ctrl`: Control key on all platforms
## Event Handling
### Click Events
Handle menu item clicks using the `OnClick` method:
```go
menuItem.OnClick(func(ctx *application.Context) {
// Access the clicked item
clickedItem := ctx.ClickedMenuItem()
// Get current state
label := clickedItem.Label()
isChecked := clickedItem.Checked()
// Update the item
clickedItem.SetLabel("New Label")
})
```
### Shared Event Handlers
Event handlers can be shared amongst multiple menu items:
```go
handleClick := func(ctx *application.Context) {
item := ctx.ClickedMenuItem()
// Common handling logic
}
menu.Add("Item 1").OnClick(handleClick)
menu.Add("Item 2").OnClick(handleClick)
```
## Dynamic Updates
Menu items can be updated dynamically during runtime:
```go
menuItem := menu.Add("Initial State")
// Later, update the item
menuItem.SetLabel("New Label")
menuItem.SetEnabled(false)
menuItem.SetChecked(true)
// Apply changes
menu.Update()
```
:::note[Update Required]
After modifying menu items, call `Update()` on the parent menu to apply the changes.
:::
## Best Practices
1. Use clear, concise labels that describe the action
2. Group related items together using separators
3. Limit submenu depth to maintain usability
4. Provide keyboard shortcuts for common actions
5. Keep radio groups focused on a single choice
6. Update menu items to reflect application state
7. Handle all possible states in click handlers
:::tip[Pro Tip]
When sharing event handlers, use the `ctx.ClickedMenuItem()` method to determine which item triggered the event and handle it accordingly.
:::
## Platform Considerations
:::note[Platform Behaviour]
Menu appearance and behaviour varies by platform:
- macOS: Uses native menu styling and supports system roles
- Windows: Follows Windows menu conventions
- Linux: Adapts to the desktop environment's theme
:::
:::danger[Warning]
Always test menu functionality across all supported platforms, as behaviour and appearance may vary significantly.
:::

View file

@ -1,505 +0,0 @@
---
title: Screen Management
sidebar:
order: 57
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
Wails provides comprehensive screen management capabilities for multi-monitor setups and high-DPI displays. The ScreenManager API allows you to query screen information, handle coordinate transformations, and manage window placement across multiple displays.
## Accessing the Screen Manager
The screen manager is accessed through the `Screens` property on your application instance:
```go
app := application.New(application.Options{
Name: "Multi-Monitor App",
})
// Access the screen manager
screens := app.Screen
```
## Getting Screen Information
### Get All Screens
Retrieve information about all connected displays:
```go
allScreens := app.Screen.GetAll()
for i, screen := range allScreens {
app.Logger.Info("Screen info",
"index", i,
"name", screen.Name,
"id", screen.ID,
"primary", screen.IsPrimary,
"width", screen.Size.Width,
"height", screen.Size.Height,
"scaleFactor", screen.ScaleFactor,
)
}
```
### Get Primary Screen
Get the primary (main) display:
```go
primaryScreen := app.Screen.GetPrimary()
if primaryScreen != nil {
app.Logger.Info("Primary screen",
"name", primaryScreen.Name,
"resolution", fmt.Sprintf("%dx%d", primaryScreen.Size.Width, primaryScreen.Size.Height),
"workArea", primaryScreen.WorkArea,
)
}
```
## Screen Properties
### Screen Structure
Each screen provides comprehensive information about the display:
```go
type Screen struct {
ID string // Unique identifier for the display
Name string // Display name (e.g., "Built-in Retina Display")
ScaleFactor float32 // DPI scale factor (1.0 = 96 DPI, 2.0 = 192 DPI)
X int // X coordinate of top-left corner
Y int // Y coordinate of top-left corner
Size Size // Logical size of the display
Bounds Rect // Display bounds in logical coordinates
PhysicalBounds Rect // Physical bounds before scaling
WorkArea Rect // Available area (excluding taskbars/docks)
PhysicalWorkArea Rect // Physical work area
IsPrimary bool // Whether this is the primary display
Rotation float32 // Display rotation in degrees
}
```
### Coordinate Systems
Wails handles two coordinate systems:
- **Logical (DIP)**: Device-independent pixels, scaled for readability
- **Physical**: Actual pixel coordinates on the hardware
```go
screen := app.Screen.GetPrimary()
// Logical coordinates (what you typically work with)
logicalWidth := screen.Size.Width
logicalHeight := screen.Size.Height
// Physical coordinates (actual hardware pixels)
physicalWidth := screen.PhysicalBounds.Width
physicalHeight := screen.PhysicalBounds.Height
// Scale factor relationship
scaleFactor := screen.ScaleFactor // e.g., 2.0 for Retina displays
// physicalWidth = logicalWidth * scaleFactor
```
## Coordinate Transformations
### Point Transformations
Convert coordinates between logical and physical systems:
```go
// Convert logical point to physical coordinates
logicalPoint := application.Point{X: 100, Y: 50}
physicalPoint := app.Screen.DipToPhysicalPoint(logicalPoint)
// Convert physical point to logical coordinates
physicalPoint2 := application.Point{X: 200, Y: 100}
logicalPoint2 := app.Screen.PhysicalToDipPoint(physicalPoint2)
app.Logger.Info("Coordinate conversion",
"logical", logicalPoint,
"physical", physicalPoint,
"backToLogical", logicalPoint2,
)
```
### Rectangle Transformations
Convert rectangular areas between coordinate systems:
```go
// Define a logical rectangle
logicalRect := application.Rect{
X: 100, Y: 100,
Width: 400, Height: 300,
}
// Convert to physical coordinates
physicalRect := app.Screen.DipToPhysicalRect(logicalRect)
// Convert back to logical coordinates
logicalRect2 := app.Screen.PhysicalToDipRect(physicalRect)
app.Logger.Info("Rectangle conversion",
"original", logicalRect,
"physical", physicalRect,
"converted", logicalRect2,
)
```
## Screen Detection and Positioning
### Find Screen by Point
Determine which screen contains a specific point:
```go
// Find screen containing a logical point
point := application.Point{X: 500, Y: 300}
screen := app.Screen.ScreenNearestDipPoint(point)
// Find screen containing a physical point
physicalPoint := application.Point{X: 1000, Y: 600}
screenPhys := app.Screen.ScreenNearestPhysicalPoint(physicalPoint)
app.Logger.Info("Screen detection",
"point", point,
"screen", screen.Name,
"physicalPoint", physicalPoint,
"physicalScreen", screenPhys.Name,
)
```
### Find Screen by Rectangle
Determine which screen best contains or overlaps with a rectangle:
```go
// Find screen for a logical rectangle
rect := application.Rect{X: 200, Y: 150, Width: 800, Height: 600}
screen := app.Screen.ScreenNearestDipRect(rect)
// Find screen for a physical rectangle
physicalRect := application.Rect{X: 400, Y: 300, Width: 1600, Height: 1200}
screenPhys := app.Screen.ScreenNearestPhysicalRect(physicalRect)
app.Logger.Info("Screen detection by area",
"rect", rect,
"screen", screen.Name,
"physicalRect", physicalRect,
"physicalScreen", screenPhys.Name,
)
```
## Window Positioning
### Position Window on Specific Screen
Place a window on a particular screen:
```go
func positionWindowOnScreen(window *application.WebviewWindow, targetScreen *application.Screen) {
// Calculate center position on target screen
centerX := targetScreen.WorkArea.X + (targetScreen.WorkArea.Width-800)/2
centerY := targetScreen.WorkArea.Y + (targetScreen.WorkArea.Height-600)/2
// Position the window
window.SetPosition(centerX, centerY)
window.SetSize(800, 600)
}
// Usage
primaryScreen := app.Screen.GetPrimary()
if primaryScreen != nil {
window := app.Window.New()
positionWindowOnScreen(window, primaryScreen)
}
```
### Multi-Monitor Window Management
Manage windows across multiple screens:
```go
func distributeWindowsAcrossScreens(app *application.App) {
screens := app.Screen.GetAll()
if len(screens) == 0 {
return
}
// Create one window per screen
for i, screen := range screens {
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
Name: fmt.Sprintf("window-%d", i),
Title: fmt.Sprintf("Window on %s", screen.Name),
Width: 800,
Height: 600,
})
// Position window in center of screen's work area
x := screen.WorkArea.X + (screen.WorkArea.Width-800)/2
y := screen.WorkArea.Y + (screen.WorkArea.Height-600)/2
window.SetPosition(x, y)
}
}
```
## High-DPI Support
### Handling Different Scale Factors
Properly handle displays with different DPI settings:
```go
func analyzeDisplayScaling(app *application.App) {
screens := app.Screen.GetAll()
for _, screen := range screens {
scaleFactor := screen.ScaleFactor
switch {
case scaleFactor == 1.0:
app.Logger.Info("Standard DPI display", "screen", screen.Name)
case scaleFactor == 1.25:
app.Logger.Info("125% scaled display", "screen", screen.Name)
case scaleFactor == 1.5:
app.Logger.Info("150% scaled display", "screen", screen.Name)
case scaleFactor == 2.0:
app.Logger.Info("High DPI display (200%)", "screen", screen.Name)
default:
app.Logger.Info("Custom scale display",
"screen", screen.Name,
"scale", scaleFactor,
)
}
}
}
```
### DPI-Aware Measurements
Convert measurements appropriately for different displays:
```go
func getDPIAwareSizes(screen *application.Screen, logicalSize int) (int, int) {
// Physical size in actual pixels
physicalSize := int(float32(logicalSize) * screen.ScaleFactor)
return logicalSize, physicalSize
}
// Usage
screen := app.Screen.GetPrimary()
logicalWidth, physicalWidth := getDPIAwareSizes(screen, 400)
app.Logger.Info("Size conversion",
"logical", logicalWidth,
"physical", physicalWidth,
"scaleFactor", screen.ScaleFactor,
)
```
## Platform Considerations
<Tabs>
<TabItem label="macOS" icon="fa-brands:apple">
On macOS:
- Screen coordinates start from bottom-left (different from Windows/Linux)
- Built-in Retina displays typically have 2.0 scale factor
- External displays may have different scale factors
- Mission Control and Spaces affect screen management
- Screen arrangement can be configured in System Preferences
```go
// macOS-specific screen handling
if runtime.GOOS == "darwin" {
// Handle coordinate system differences
screen := app.Screen.GetPrimary()
// Y coordinates are flipped on macOS
}
```
</TabItem>
<TabItem label="Windows" icon="fa-brands:windows">
On Windows:
- Screen coordinates start from top-left
- Scale factors vary: 100%, 125%, 150%, 175%, 200%, etc.
- Multiple monitor configurations are common
- Taskbar affects work area calculations
- Windows display settings control scaling
```go
// Windows-specific screen handling
if runtime.GOOS == "windows" {
screen := app.Screen.GetPrimary()
// Account for taskbar in work area
workArea := screen.WorkArea
}
```
</TabItem>
<TabItem label="Linux" icon="fa-brands:linux">
On Linux:
- Screen coordinates start from top-left
- Scale factors depend on desktop environment
- X11 and Wayland have different behaviors
- Panel/dock configurations affect work areas
- Multi-monitor support varies by environment
```go
// Linux-specific screen handling
if runtime.GOOS == "linux" {
screen := app.Screen.GetPrimary()
// Handle different desktop environments
}
```
</TabItem>
</Tabs>
## Best Practices
1. **Always Check for Screens**: Ensure screens are available before using them:
```go
screens := app.Screen.GetAll()
if len(screens) == 0 {
app.Logger.Warn("No screens detected")
return
}
```
2. **Use Work Areas**: Position windows within work areas to avoid taskbars/docks:
```go
screen := app.Screen.GetPrimary()
workArea := screen.WorkArea
// Position within workArea, not full screen bounds
```
3. **Handle Scale Factors**: Always consider DPI scaling for proper sizing:
```go
screen := app.Screen.GetPrimary()
if screen.ScaleFactor > 1.0 {
// Adjust UI elements for high-DPI displays
}
```
4. **Monitor Screen Changes**: Listen for screen configuration changes:
```go
app.Event.OnApplicationEvent(events.Common.SystemDisplayChanged, func(event *application.ApplicationEvent) {
// Refresh screen information
screens := app.Screen.GetAll()
// Reposition windows if needed
})
```
## Complete Example
Here's a complete example demonstrating screen management:
```go
package main
import (
"fmt"
"runtime"
"github.com/wailsapp/wails/v3/pkg/application"
)
func main() {
app := application.New(application.Options{
Name: "Screen Management Demo",
})
// Analyze connected screens
analyzeScreens(app)
// Create windows strategically positioned
createPositionedWindows(app)
// Setup screen change monitoring
monitorScreenChanges(app)
err := app.Run()
if err != nil {
panic(err)
}
}
func analyzeScreens(app *application.App) {
screens := app.Screen.GetAll()
app.Logger.Info("Screen analysis", "count", len(screens))
primary := app.Screen.GetPrimary()
if primary != nil {
app.Logger.Info("Primary screen",
"name", primary.Name,
"size", fmt.Sprintf("%dx%d", primary.Size.Width, primary.Size.Height),
"scaleFactor", primary.ScaleFactor,
"workArea", primary.WorkArea,
)
}
for i, screen := range screens {
app.Logger.Info("Screen details",
"index", i,
"name", screen.Name,
"primary", screen.IsPrimary,
"bounds", screen.Bounds,
"scaleFactor", screen.ScaleFactor,
)
}
}
func createPositionedWindows(app *application.App) {
screens := app.Screen.GetAll()
for i, screen := range screens {
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
Name: fmt.Sprintf("screen-%d", i),
Title: fmt.Sprintf("Window on %s", screen.Name),
Width: 600,
Height: 400,
})
// Center window in screen's work area
x := screen.WorkArea.X + (screen.WorkArea.Width-600)/2
y := screen.WorkArea.Y + (screen.WorkArea.Height-400)/2
window.SetPosition(x, y)
app.Logger.Info("Created window",
"screen", screen.Name,
"position", fmt.Sprintf("%d,%d", x, y),
)
}
}
func monitorScreenChanges(app *application.App) {
// Monitor for screen configuration changes
app.Event.OnApplicationEvent(events.Common.SystemDisplayChanged, func(event *application.ApplicationEvent) {
app.Logger.Info("Screen configuration changed")
// Re-analyze screens
screens := app.Screen.GetAll()
app.Logger.Info("Updated screen count", "count", len(screens))
// Could reposition windows here if needed
})
}
```
:::tip[Pro Tip]
When developing multi-monitor applications, test on various configurations including different DPI settings and monitor arrangements to ensure proper behavior.
:::
:::danger[Warning]
Screen configurations can change during application runtime (monitors connected/disconnected, resolution changes). Always handle these changes gracefully and avoid storing screen references long-term.
:::

View file

@ -1,212 +0,0 @@
---
title: Services
sidebar:
order: 10
---
Services in Wails v3 provide a powerful way to extend the functionality of your
application. They allow you to create modular, reusable components that can be
easily integrated into your Wails application.
## Overview
Services are designed to encapsulate specific functionality and can be
registered with the application at startup. They can handle various tasks such
as file serving, database operations, logging, and more. Services can also
interact with the application lifecycle and respond to HTTP requests.
## Creating a Service
To create a service, you simply define a struct. Here's a basic structure of a
service:
```go
type MyService struct {
// Your service fields
}
func NewMyService() *MyService {
// Initialize and return your service
}
func (s *MyService) Greet(name string) string {
return fmt.Sprintf("Hello, %s!", name)
}
```
This service has a single method, `Greet`, which accepts a name and returns a
greeting.
## Registering a Service
To register a service with the application, you need to provide an instance of
the service to the `Services` field of the `application.Options` struct.
All services need to be wrapped by an `application.NewService` call.
Here's an example:
```go
app := application.New(application.Options{
Services: []application.Service{
application.NewService(NewMyService()),
},
})
```
This registers the `NewMyService` function as a service with the application.
Services may also be registered with additional options:
```go
app := application.New(application.Options{
Services: []application.Service{
application.NewServiceWithOptions(NewMyService(), application.ServiceOptions{
// ...
})
}
})
```
ServiceOptions has the following fields:
- Name - Specify a custom name for the Service
- Route - A route to bind the Service to the frontend (more on this below)
After the application has been created but not yet started,
you can register more services using the `RegisterService` method.
This is useful when you need to feed a service some value
that is only available after the application has been created.
For example, let's wire application's logger into your own service:
```go
app := application.New(application.Options{})
app.RegisterService(application.NewService(NewMyService(app.Logger)))
// ...
err := app.Run()
```
Services may only be registered before running the application:
`RegisterService` will panic if called after the `Run` method.
## Optional Methods
Services can implement optional methods to hook into the application lifecycle.
:::note
The `ServiceStartup`, `ServiceShutdown`, `ServiceName` and `ServeHTTP` methods are not included in the
bindings generated for a service, so they are not exposed to your frontend.
:::
### ServiceName
```go
func (s *Service) ServiceName() string
```
This method returns the name of the service. By default, it will the struct name of the Service but can be
overridden with the `Name` field of the `ServiceOptions`.
It is used for logging purposes only.
### ServiceStartup
```go
func (s *Service) ServiceStartup(ctx context.Context, options application.ServiceOptions) error
```
This method is called when the application is starting up. You can use it to
initialize resources, set up connections, or perform any necessary setup tasks.
The context is the application context that will be canceled upon shutdown,
and the `options` parameter provides additional information about the service.
Services are initialised in the exact order of registration:
first those listed in the `Services` field of the `application.Options` struct,
then those added through the `RegisterService` method.
### ServiceShutdown
```go
func (s *Service) ServiceShutdown() error
```
This method is called when the application is shutting down. Use it to clean up
resources, close connections, or perform any necessary cleanup tasks.
Services are shut down in reverse registration order.
The application context will be canceled before `ServiceShutdown` is called.
### ServeHTTP
```go
func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request)
```
If your service needs to handle HTTP requests, implement this method. It allows
your service to act as an HTTP handler. The route of the handler is defined in
the service options:
```go
application.NewServiceWithOptions(fileserver.New(&fileserver.Config{
RootPath: rootPath,
}), application.ServiceOptions{
Route: "/files",
})
```
## Example: File Server Service
Let's look at a simplified version of the `fileserver` service as an example:
```go
type Config struct {
RootPath string
}
type Service struct {
fs http.Handler
}
func NewWithConfig(config *Config) *Service {
return &Service{
fs: http.FileServer(http.Dir(config.RootPath)),
}
}
func (s *Service) ServiceName() string {
return "github.com/wailsapp/wails/v3/services/fileserver"
}
func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.fs.ServeHTTP(w, r)
}
```
We can now use this service in our application:
```go
app := application.New(application.Options{
Services: []application.Service{
application.NewServiceWithOptions(fileserver.New(&fileserver.Config{
RootPath: rootPath,
}), application.ServiceOptions{
Route: "/files",
}),
},
})
```
All requests to `/files` will be handled by the `fileserver` service.
## Application Lifecycle and Services
1. During application initialization, services are registered with the
application.
2. When the application starts (`app.Run()`), the `ServiceStartup` method of each
service is called with the application context and service options.
3. Throughout the application's lifetime, services can perform their specific
tasks.
4. If a service implements `ServeHTTP`, it can handle HTTP requests at the
specified path.
5. When the application is shutting down, the `ServiceShutdown` method of each
service is called as well as the context being cancelled.

View file

@ -1,224 +0,0 @@
---
title: System Tray
description: Learn how to create and use system tray icons in Wails
---
import {Badge} from "@astrojs/starlight/components";
## Introduction
The system tray (also known as the notification area) is a section of the desktop environment where applications can display icons and menus. In Wails, you can easily add a system tray icon to your application with full control over its appearance and behavior.
## Basic Usage
To create a basic system tray icon:
```go
app := application.New(options)
systray := app.SystemTray.New()
systray.SetLabel("My App")
systray.SetIcon(iconBytes)
app.Run()
```
## Setting the Icon
The system tray icon can be set using embedded image files. First, import the `embed` package and declare your icon files:
```go
import "embed"
//go:embed assets/icon.png assets/icon-dark.png
var iconFS embed.FS
```
Then read and set the icons:
```go
// Read icon data
iconBytes, _ := iconFS.ReadFile("assets/icon.png")
darkModeIconBytes, _ := iconFS.ReadFile("assets/icon-dark.png")
// Set icons
systray.SetIcon(iconBytes)
systray.SetDarkModeIcon(darkModeIconBytes)
```
Supported image formats include PNG and JPEG. For best results, use icons with appropriate sizes:
- Windows: 16x16 or 32x32 pixels
- macOS: 18x18 to 128x128 pixels
- Linux: Varies by desktop environment
On macOS, you can mark the icon as a template image for automatic dark/light mode adaptation:
```go
systray.SetTemplateIcon(iconBytes)
```
For more details on creating template icons, read this [great article](https://bjango.com/articles/designingmenubarextras/).
## Setting the Tooltip <Badge text="Windows" variant="note" />
You can set a tooltip for your system tray icon that appears when hovering over the icon:
```go
systray.SetTooltip("My Application Tooltip")
```
## Setting the Label <Badge text="macOS" variant="success" />
You can set a text label for your system tray icon:
```go
systray.SetLabel("My App")
```
The label will appear next to the icon in the system tray. On some platforms, this text may be truncated if it's too long.
:::note
On Windows, setting the label is the equivalent of setting the tooltip.
:::
## Adding a Menu
You can add a menu to your system tray icon:
```go
menu := application.NewMenu()
menu.Add("Open").OnClick(func(ctx *application.Context) {
// Handle click
})
menu.Add("Quit").OnClick(func(ctx *application.Context) {
app.Quit()
})
systray.SetMenu(menu)
```
## Attaching a Window
You can attach a window to a system tray icon to gain a number of desirable features:
- The attached window will start hidden
- Left-clicking on the system tray icon will toggle the visibility of the attached window
- Right-clicking on the system tray icon will show the system tray menu, if given
Here's a complete example:
```go
app := application.New()
// Create system tray
systray := app.SystemTray.New()
systray.SetLabel("My App")
// Create a window
window := app.Window.New()
// Attach the window to the system tray
systray.AttachWindow(window)
// Optional: Set window offset from tray icon
systray.WindowOffset(10)
// Optional: Set debounce time for window show/hide
systray.WindowDebounce(200 * time.Millisecond)
// Add a menu (optional)
menu := app.Menu.New()
menu.Add("Open").OnClick(func() {
window.Show()
})
menu.Add("Quit").OnClick(func() {
app.Quit()
})
systray.SetMenu(menu)
app.Run()
```
## Icon Position <Badge text="macOS" variant="success" />
On macOS, you can control the position of the system tray icon relative to other icons:
```go
systray.SetIconPosition(application.IconPositionRight)
```
Available positions:
- `NSImageNone`
- `NSImageOnly`
- `NSImageLeft`
- `NSImageRight`
- `NSImageBelow`
- `NSImageAbove`
- `NSImageOverlaps`
- `NSImageLeading`
- `NSImageTrailing`
## Destroying the System Tray
When you're done with the system tray, you should destroy it to release resources:
```go
systray.Destroy()
```
## Platform Considerations
- **macOS**: Icons support template images for automatic dark/light mode
- **Windows**: Icons should be 16x16 or 32x32 pixels
- **Linux**: Uses the StatusNotifierItem specification
## Examples
Explore these examples for more advanced usage:
- [Basic System Tray](/examples/systray-basic)
- [System Tray with Menu](/examples/systray-menu)
- [Custom System Tray](/examples/systray-custom)
## API Reference
### Core Methods
| Method | Description |
|------------------------------------------|--------------------------------------------|
| `app.SystemTray.New()` | Creates a new system tray instance |
| `Run()` | Starts the system tray |
| `SetLabel(label string)` | Sets the text label |
| `SetTooltip(tooltip string)` | Sets the tooltip text (Windows) |
| `SetIcon(icon []byte)` | Sets the icon image |
| `SetDarkModeIcon(icon []byte)` | Sets the dark mode variant of the icon |
| `SetTemplateIcon(icon []byte)` | Marks the icon as a template image (macOS) |
| `SetIconPosition(position IconPosition)` | Sets the icon position (macOS) |
| `Destroy()` | Destroys the system tray |
### Menu Management
| Method | Description |
|-----------------------|----------------------------------|
| `SetMenu(menu *Menu)` | Attaches a menu to the tray icon |
| `OpenMenu()` | Programmatically opens the menu |
### Event Handlers
| Method | Description |
|--------------------------------------|-----------------------------------|
| `OnClick(handler func())` | Handles left-click events |
| `OnRightClick(handler func())` | Handles right-click events |
| `OnDoubleClick(handler func())` | Handles left-double-click events |
| `OnRightDoubleClick(handler func())` | Handles right-double-click events |
| `OnMouseEnter(handler func())` | Handles mouse enter events |
| `OnMouseLeave(handler func())` | Handles mouse leave events |
### Window Attachment
| Method | Description |
|------------------------------------------|---------------------------------------------|
| `AttachWindow(window *WebviewWindow)` | Associates a window with the tray icon |
| `WindowOffset(offset int)` | Sets the offset between the tray and window |
| `WindowDebounce(debounce time.Duration)` | Sets the debounce time for window show/hide |
### Visibility Control
| Method | Description |
|----------|-----------------------------|
| `Show()` | Makes the tray icon visible |
| `Hide()` | Hides the tray icon |

View file

@ -1,444 +0,0 @@
---
title: Windows
sidebar:
order: 52
---
import { Tabs, TabItem } from "@astrojs/starlight/components";
Wails provides a comprehensive window management system through the WindowManager API. This allows you to create, manage, and control multiple windows in your application.
## Accessing the Window Manager
The window manager is accessed through the `Windows` property on your application instance:
```go
app := application.New(application.Options{
Name: "Multi-Window App",
})
// Access the window manager
windowManager := app.Window
```
## Creating Windows
### Basic Window Creation
Create a new window with default settings:
```go
window := app.Window.New()
```
### Window Creation with Options
Create a window with custom configuration:
```go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Custom Window",
Width: 800,
Height: 600,
Mac: application.MacOptions{
TitleBarAppearsTransparent: true,
},
Windows: application.WindowsOptions{
DisableWindowIcon: true,
},
Linux: application.LinuxOptions{
Icon: []byte{/* icon data */},
},
})
```
## Finding Windows
### Get Window by Name
Retrieve a window using its name:
```go
window, exists := app.Window.GetByName("main-window")
if exists {
// Work with the window
window.SetTitle("Found Window")
}
```
### Get Window by ID
Retrieve a window using its unique ID:
```go
window, exists := app.Window.GetByID(123)
if exists {
// Work with the window
}
```
### Get Current Window
Get the currently active/focused window:
```go
// Get current window (may be nil)
currentWindow := app.Window.Current()
if currentWindow != nil {
currentWindow.SetTitle("Active Window")
}
// Get current window with existence check
currentWindow, exists := app.Window.GetCurrent()
if exists {
currentWindow.SetTitle("Active Window")
}
```
### Get All Windows
Retrieve all windows managed by the application:
```go
allWindows := app.Window.GetAll()
for i, window := range allWindows {
window.SetTitle(fmt.Sprintf("Window %d", i+1))
}
```
## Window Lifecycle Management
### Window Creation Callbacks
Register callbacks to be notified when new windows are created:
```go
app.Window.OnCreate(func(window application.Window) {
app.Logger.Info("New window created", "name", window.Name())
// Configure the window
if webviewWindow, ok := window.(*application.WebviewWindow); ok {
webviewWindow.SetMinSize(400, 300)
}
})
```
### Removing Windows
Remove windows from the manager:
```go
// Remove by ID
app.Window.Remove(windowID)
// Remove by name
removed := app.Window.RemoveByName("window-name")
if removed {
app.Logger.Info("Window removed successfully")
}
```
## Window Types
### WebView Windows
The primary window type in Wails v3 is the WebView window, which embeds a web browser:
```go
window := app.Window.New()
// WebView-specific operations
window.SetURL("https://example.com")
window.SetHTML("<h1>Hello World</h1>")
window.Reload()
```
### Window Interface
All windows implement the `Window` interface, providing common functionality:
```go
type Window interface {
ID() uint
Name() string
SetTitle(title string)
SetSize(width, height int)
SetPosition(x, y int)
Show()
Hide()
Close()
// ... other methods
}
```
### Common Window Methods
Windows provide many methods for controlling their appearance and behavior:
#### Display Control
- `Show()` - Shows the window
- `Hide()` - Hides the window
- `SetTitle(title string)` - Sets the window title
- `SetSize(width, height int)` - Sets the window size
- `SetPosition(x, y int)` - Sets the window position
- `Center()` - Centers the window on screen
- `SetAlwaysOnTop(bool)` - Sets whether window stays on top
#### Window State
- `Minimise()` - Minimizes the window
- `Maximise()` - Maximizes the window
- `UnMinimise()` - Restores from minimized state
- `UnMaximise()` - Restores from maximized state
- `Fullscreen()` - Enters fullscreen mode
- `UnFullscreen()` - Exits fullscreen mode
- `Restore()` - Restores window to normal state
- `IsMinimised() bool` - Checks if window is minimized
- `IsMaximised() bool` - Checks if window is maximized
- `IsFullscreen() bool` - Checks if window is fullscreen
#### Security and Privacy
- `SetContentProtection(bool)` - Enables/disables content protection to prevent screen capture
#### Platform-Specific Methods
**Windows Only:**
- `Flash(bool)` - Flashes the taskbar button
- `SnapAssist()` - Triggers Windows 11 Snap Assist (Win+Z)
## Content Protection
Content Protection prevents window contents from being captured by screen recording or screen sharing software. This is useful for applications that display sensitive information.
### Enabling Content Protection
You can enable content protection when creating a window:
```go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Secure Window",
ContentProtectionEnabled: true,
})
```
Or enable/disable it dynamically at runtime:
```go
// Enable content protection
window.SetContentProtection(true)
// Disable content protection
window.SetContentProtection(false)
```
### Platform Support
- **Windows**: Uses `WDA_EXCLUDEFROMCAPTURE` on Windows 10 version 2004+ (build 19041+). Falls back to `WDA_MONITOR` on older versions (legacy mode providing partial protection that may not block all capture paths).
- **macOS**: Sets `NSWindow.sharingType` to `NSWindowSharingNone` when enabled (prevents capture) and `NSWindowSharingReadOnly` when disabled (allows capture).
- **Linux**: Currently not implemented (no-op).
### Use Cases
Content protection is ideal for:
- Banking and financial applications
- Password managers
- Medical record systems
- Document viewers with confidential information
- Communication apps with private messages
### Important Notes
1. Content protection does not prevent physical photography of the screen
2. On Windows, the fallback for older versions provides partial protection
3. Some screen capture tools may still be able to capture protected content using low-level APIs
4. This feature should be part of a comprehensive security strategy, not the only protection
5. Detached DevTools or inspector windows are separate instances and will not inherit content protection automatically
## Advanced Window Management
### Multi-Window Applications
Create and manage multiple windows for complex applications:
```go
// Create main window
mainWindow := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Main Application",
Width: 1200,
Height: 800,
})
// Create settings window
settingsWindow := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Settings",
Width: 600,
Height: 400,
WindowState: application.WindowStateHidden, // Start hidden
})
// Show settings when needed
settingsWindow.Show()
```
### Window Communication
Windows can communicate through events:
```go
// In window A
app.Event.Emit("data-updated", newData)
// In window B
app.Event.On("data-updated", func(event *application.CustomEvent) {
// Update UI with new data
data := event.Data
// Process data...
})
```
## Platform-Specific Considerations
<Tabs>
<TabItem label="macOS" icon="fa-brands:apple">
On macOS, windows:
- Support native title bar customization
- Can be configured with transparent title bars
- Follow macOS window management conventions
- Support native fullscreen mode
- Integrate with Mission Control and Spaces
</TabItem>
<TabItem label="Windows" icon="fa-brands:windows">
On Windows, windows:
- Support custom window icons
- Can be configured without system title bars
- Follow Windows window management conventions
- Support Windows 11 snap layouts via `SnapAssist()` method
- Integrate with Windows taskbar features
**Windows 11 Snap Assist Example:**
```go
// Trigger Windows 11 Snap Assist
window.SnapAssist()
// This is equivalent to pressing Win+Z
// Shows snap layout options for the current window
```
</TabItem>
<TabItem label="Linux" icon="fa-brands:linux">
On Linux, windows:
- Support custom icons
- Follow desktop environment conventions
- Work with various window managers
- Support X11 and Wayland protocols
- Integrate with desktop environment features
</TabItem>
</Tabs>
## Best Practices
1. **Window Naming**: Always provide meaningful names for windows to make them easy to find:
```go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
Name: "settings-window",
Title: "Application Settings",
})
```
2. **Resource Management**: Properly clean up windows when they're no longer needed:
```go
window.OnEvent(events.Common.WindowClosing, func(e *application.WindowEvent) {
app.Window.Remove(window.ID())
})
```
3. **Window State**: Consider starting secondary windows as hidden and showing them when needed:
```go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
WindowState: application.WindowStateHidden,
})
```
4. **Error Handling**: Always check if windows exist before using them:
```go
if window, exists := app.Window.GetByName("main"); exists {
window.SetTitle("New Title")
}
```
## Complete Example
Here's a complete example of a multi-window application:
```go
package main
import (
"github.com/wailsapp/wails/v3/pkg/application"
)
func main() {
app := application.New(application.Options{
Name: "Multi-Window Demo",
})
// Create main window
mainWindow := app.Window.NewWithOptions(application.WebviewWindowOptions{
Name: "main",
Title: "Main Window",
Width: 800,
Height: 600,
})
// Create hidden settings window
settingsWindow := app.Window.NewWithOptions(application.WebviewWindowOptions{
Name: "settings",
Title: "Settings",
Width: 400,
Height: 300,
WindowState: application.WindowStateHidden,
})
// Setup window creation callback
app.Window.OnCreate(func(window application.Window) {
app.Logger.Info("Window created", "name", window.Name())
})
// Setup menu to show settings
menu := app.Menu.New()
menu.AddRole(application.AppMenu)
settingsMenu := menu.AddSubmenu("Settings")
settingsMenu.Add("Open Settings").OnClick(func(ctx *application.Context) {
if window, exists := app.Window.GetByName("settings"); exists {
window.Show()
window.Focus()
}
})
app.Menu.Set(menu)
err := app.Run()
if err != nil {
panic(err)
}
}
```
:::tip[Pro Tip]
Use the window manager's callback system to implement application-wide window policies, such as automatically setting minimum sizes or configuring window behaviors.
:::
:::danger[Warning]
Always check if windows exist before performing operations on them, as they may have been closed or removed by the user or system.
:::

View file

@ -1,7 +1,8 @@
---
title: Runtime
title: Runtime Overview
description: The Wails JavaScript runtime package for frontend integration
sidebar:
order: 30
order: 1
---
The Wails runtime is the standard library for Wails applications. It provides a