Window Events
Este conteúdo não está disponível em sua língua ainda.
Window Events
Section titled “Window Events”Wails dispatches window lifecycle and state-change events through a single API: OnWindowEvent for passive listeners, and RegisterHook for cancellable hooks that can prevent the default action.
import "github.com/wailsapp/wails/v3/pkg/events"
// Listener — observes the event, cannot cancel it.window.OnWindowEvent(events.Common.WindowDidResize, func(e *application.WindowEvent) { /* ... */ })
// Hook — runs before listeners; can call e.Cancel() to suppress the default action.window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { if hasUnsavedChanges() { e.Cancel() // prevent the window from closing }})Both calls return an unsubscribe func() you can call to remove the handler.
Cross-platform events live in events.Common.*. Platform-specific events live in events.Mac.*, events.Windows.*, and events.Linux.*. The full list is generated in v3/pkg/events/events.go.
Lifecycle Events
Section titled “Lifecycle Events”Window Creation
Section titled “Window Creation”Run a callback every time a window is created using app.Window.OnCreate:
app.Window.OnCreate(func(window application.Window) { fmt.Printf("Window created: %s (ID: %d)\n", window.Name(), window.ID())
// Configure all new windows window.SetMinSize(400, 300)
// Register a hook for this window window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { if !confirmClose() { e.Cancel() } })})The callback receives application.Window (an interface). The OnCreate callback is invoked once per window, after the window’s runtime is initialised.
WindowClosing
Section titled “WindowClosing”Fires when the user attempts to close the window (clicking X, ⌘W, Alt+F4, etc.).
Use a hook (RegisterHook) — only hooks can cancel the close. Listeners observe the event but cannot prevent it.
window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { if hasUnsavedChanges() { // Cancel the close — the window stays open. e.Cancel() }})Important:
WindowClosingis dispatched on user-initiated close attempts.- A
RegisterHookcallback can calle.Cancel()to keep the window open. OnWindowEventcallbacks forWindowClosingare observers — they fire but cannot cancel.
Programmatic close: call window.Close(). There is no window.Destroy().
WindowRuntimeReady
Section titled “WindowRuntimeReady”Fires once the in-window runtime has finished initialising — safe point to call into the window’s JS context:
window.OnWindowEvent(events.Common.WindowRuntimeReady, func(e *application.WindowEvent) { window.EmitEvent("app-ready", nil)})Focus Events
Section titled “Focus Events”WindowFocus
Section titled “WindowFocus”Called when the window gains focus:
window.OnWindowEvent(events.Common.WindowFocus, func(e *application.WindowEvent) { fmt.Println("Window gained focus") updateTitleBar(true) app.Event.Emit("window-focused", window.ID())})WindowLostFocus
Section titled “WindowLostFocus”Called when the window loses focus:
window.OnWindowEvent(events.Common.WindowLostFocus, func(e *application.WindowEvent) { fmt.Println("Window lost focus") updateTitleBar(false) saveCurrentState()})Example: Focus-aware UI:
type FocusAwareWindow struct { window *application.WebviewWindow focused bool}
func (fw *FocusAwareWindow) Setup() { fw.window.OnWindowEvent(events.Common.WindowFocus, func(e *application.WindowEvent) { fw.focused = true fw.updateAppearance() })
fw.window.OnWindowEvent(events.Common.WindowLostFocus, func(e *application.WindowEvent) { fw.focused = false fw.updateAppearance() })}
func (fw *FocusAwareWindow) updateAppearance() { if fw.focused { fw.window.EmitEvent("update-theme", "active") } else { fw.window.EmitEvent("update-theme", "inactive") }}State Change Events
Section titled “State Change Events”WindowMinimise / WindowUnMinimise
Section titled “WindowMinimise / WindowUnMinimise”window.OnWindowEvent(events.Common.WindowMinimise, func(e *application.WindowEvent) { pauseRendering() saveWindowState()})
window.OnWindowEvent(events.Common.WindowUnMinimise, func(e *application.WindowEvent) { resumeRendering() refreshContent()})WindowMaximise / WindowUnMaximise
Section titled “WindowMaximise / WindowUnMaximise”window.OnWindowEvent(events.Common.WindowMaximise, func(e *application.WindowEvent) { window.EmitEvent("layout-mode", "maximised")})
window.OnWindowEvent(events.Common.WindowUnMaximise, func(e *application.WindowEvent) { window.EmitEvent("layout-mode", "normal")})WindowFullscreen / WindowUnFullscreen
Section titled “WindowFullscreen / WindowUnFullscreen”window.OnWindowEvent(events.Common.WindowFullscreen, func(e *application.WindowEvent) { window.EmitEvent("chrome-visibility", false) window.EmitEvent("layout-mode", "fullscreen")})
window.OnWindowEvent(events.Common.WindowUnFullscreen, func(e *application.WindowEvent) { window.EmitEvent("chrome-visibility", true) window.EmitEvent("layout-mode", "normal")})Enter/exit fullscreen programmatically with window.Fullscreen() / window.UnFullscreen() / window.ToggleFullscreen() — there is no SetFullscreen(bool). Query state with window.IsFullscreen() bool.
Position and Size Events
Section titled “Position and Size Events”WindowDidMove
Section titled “WindowDidMove”window.OnWindowEvent(events.Common.WindowDidMove, func(e *application.WindowEvent) { x, y := window.Position() fmt.Printf("Window moved to: %d, %d\n", x, y) saveWindowPosition(x, y)})WindowDidResize
Section titled “WindowDidResize”window.OnWindowEvent(events.Common.WindowDidResize, func(e *application.WindowEvent) { width, height := window.Size() fmt.Printf("Window resized to: %dx%d\n", width, height) saveWindowSize(width, height) window.EmitEvent("window-size", map[string]int{ "width": width, "height": height, })})WindowDidResize and WindowDidMove do not carry coordinates on the event itself — query the window via window.Size() / window.Position() inside the callback.
Example: Responsive layout:
window.OnWindowEvent(events.Common.WindowDidResize, func(e *application.WindowEvent) { width, _ := window.Size() var layout string switch { case width < 600: layout = "compact" case width < 1200: layout = "normal" default: layout = "wide" } window.EmitEvent("layout-changed", layout)})Complete Example
Section titled “Complete Example”A production-ready window with full event handling:
package main
import ( "encoding/json" "fmt" "os"
"github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/events")
type WindowState struct { X int `json:"x"` Y int `json:"y"` Width int `json:"width"` Height int `json:"height"` Maximised bool `json:"maximised"` Fullscreen bool `json:"fullscreen"`}
type ManagedWindow struct { app *application.App window *application.WebviewWindow state WindowState dirty bool}
func main() { app := application.New(application.Options{ Name: "Event Demo", })
mw := &ManagedWindow{app: app} mw.CreateWindow() mw.LoadState() mw.SetupEventHandlers()
app.Run()}
func (mw *ManagedWindow) CreateWindow() { mw.window = mw.app.Window.NewWithOptions(application.WebviewWindowOptions{ Name: "main", Title: "Event Demo", Width: 800, Height: 600, })}
func (mw *ManagedWindow) SetupEventHandlers() { // Focus events mw.window.OnWindowEvent(events.Common.WindowFocus, func(e *application.WindowEvent) { mw.window.EmitEvent("focus-state", true) })
mw.window.OnWindowEvent(events.Common.WindowLostFocus, func(e *application.WindowEvent) { mw.window.EmitEvent("focus-state", false) })
// State change events mw.window.OnWindowEvent(events.Common.WindowMinimise, func(e *application.WindowEvent) { mw.SaveState() })
mw.window.OnWindowEvent(events.Common.WindowMaximise, func(e *application.WindowEvent) { mw.state.Maximised = true mw.dirty = true })
mw.window.OnWindowEvent(events.Common.WindowUnMaximise, func(e *application.WindowEvent) { mw.state.Maximised = false mw.dirty = true })
mw.window.OnWindowEvent(events.Common.WindowFullscreen, func(e *application.WindowEvent) { mw.state.Fullscreen = true mw.dirty = true })
mw.window.OnWindowEvent(events.Common.WindowUnFullscreen, func(e *application.WindowEvent) { mw.state.Fullscreen = false mw.dirty = true })
// Position and size events mw.window.OnWindowEvent(events.Common.WindowDidMove, func(e *application.WindowEvent) { mw.state.X, mw.state.Y = mw.window.Position() mw.dirty = true })
mw.window.OnWindowEvent(events.Common.WindowDidResize, func(e *application.WindowEvent) { mw.state.Width, mw.state.Height = mw.window.Size() mw.dirty = true })
// Cancellable close — use a hook, not a listener. mw.window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { if mw.dirty { mw.SaveState() } })}
func (mw *ManagedWindow) LoadState() { data, err := os.ReadFile("window-state.json") if err != nil { return }
if err := json.Unmarshal(data, &mw.state); err != nil { return }
// Restore window state mw.window.SetPosition(mw.state.X, mw.state.Y) mw.window.SetSize(mw.state.Width, mw.state.Height)
if mw.state.Maximised { mw.window.Maximise() }
if mw.state.Fullscreen { mw.window.Fullscreen() }}
func (mw *ManagedWindow) SaveState() { data, err := json.Marshal(mw.state) if err != nil { return }
os.WriteFile("window-state.json", data, 0644) mw.dirty = false
fmt.Println("Window state saved")}Event Coordination
Section titled “Event Coordination”Cross-Window Events
Section titled “Cross-Window Events”Coordinate between multiple windows using the application event bus:
// In main windowmainWindow.OnWindowEvent(events.Common.WindowFocus, func(e *application.WindowEvent) { app.Event.Emit("main-window-focused", nil)})
// In other windowsapp.Event.On("main-window-focused", func(event *application.CustomEvent) { updateRelativeToMain()})Event Chains
Section titled “Event Chains”window.OnWindowEvent(events.Common.WindowMaximise, func(e *application.WindowEvent) { saveWindowState() window.EmitEvent("layout-changed", "maximised") app.Event.Emit("window-maximised", window.ID())})Debounced Events
Section titled “Debounced Events”var resizeTimer *time.Timer
window.OnWindowEvent(events.Common.WindowDidResize, func(e *application.WindowEvent) { if resizeTimer != nil { resizeTimer.Stop() }
resizeTimer = time.AfterFunc(500*time.Millisecond, func() { w, h := window.Size() saveWindowSize(w, h) })})Best Practices
Section titled “Best Practices”- Use hooks for cancellation — only
RegisterHookcallbacks can calle.Cancel(). - Save state on close — restore window position/size next launch.
- Debounce frequent events —
WindowDidResizeandWindowDidMovefire rapidly. - Handle focus changes — update UI appropriately.
- Coordinate via events —
app.Event.Emitfor cross-window messaging. - Unsubscribe when handlers are no longer needed —
OnWindowEvent/RegisterHookboth return an unsubscribefunc().
❌ Don’t
Section titled “❌ Don’t”- Don’t block event handlers — keep them fast.
- Don’t try to cancel from
OnWindowEvent— useRegisterHook. - Don’t use
window.Destroy()— it doesn’t exist; usewindow.Close(). - Don’t save on every event — debounce first.
- Don’t assume coordinates on the event — call
window.Position()/window.Size().
Troubleshooting
Section titled “Troubleshooting”WindowClosing Hook Doesn’t Prevent Close
Section titled “WindowClosing Hook Doesn’t Prevent Close”Cause: You used OnWindowEvent instead of RegisterHook.
Solution: Only RegisterHook callbacks can call e.Cancel(). OnWindowEvent callbacks observe; they cannot cancel.
// ❌ Cannot cancel — this is a listener.window.OnWindowEvent(events.Common.WindowClosing, func(e *application.WindowEvent) { e.Cancel() // no effect})
// ✅ Can cancel — this is a hook.window.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { e.Cancel()})Events Not Firing
Section titled “Events Not Firing”Cause: Handler registered after the event occurred.
Solution: Register handlers immediately after window creation (or in the app.Window.OnCreate callback).
window := app.Window.New()window.OnWindowEvent(events.Common.WindowFocus, func(e *application.WindowEvent) { /* ... */ })Memory Leaks
Section titled “Memory Leaks”Cause: Long-lived handlers held by short-lived state.
Solution: Keep the unsubscribe func() returned by OnWindowEvent/RegisterHook and call it during cleanup.
unsub := window.OnWindowEvent(events.Common.WindowDidResize, handler)// ...later:unsub()Next Steps
Section titled “Next Steps”Window Basics - Learn the fundamentals of window management Learn More →
Multiple Windows - Patterns for multi-window applications Learn More →
Events System - Deep dive into the event system Learn More →
Application Lifecycle - Understand the application lifecycle Learn More →