Window Events
Window Events
Section titled “Window Events”Wails provides comprehensive event hooks for window lifecycle and state changes: creation, focus/blur, resize/move, minimise/maximise, and close events. Register callbacks, handle events, and coordinate between windows with simple, consistent APIs.
Lifecycle Events
Section titled “Lifecycle Events”OnCreate
Section titled “OnCreate”Called when a window is created:
app.OnWindowCreation(func(window *application.WebviewWindow) { fmt.Printf("Window created: %s (ID: %d)\n", window.Name(), window.ID())
// Configure all new windows window.SetMinSize(400, 300)
// Register window-specific handlers window.OnClose(func() bool { return confirmClose() })})Use cases:
- Configure all windows consistently
- Register event handlers
- Track window creation
- Initialise window-specific resources
OnClose
Section titled “OnClose”Called when user attempts to close window:
window.OnClose(func() bool { // Return false to cancel close // Return true to allow close
if hasUnsavedChanges() { result := showConfirmdialog("Unsaved changes. Close anyway?") return result == "yes" } return true})Important:
- Only triggered by user actions (clicking X button)
- NOT triggered by
window.Destroy() - Can cancel the close by returning
false
Use cases:
- Confirm before closing
- Save state
- Prevent accidental closure
- Cleanup before close
Example with dialog:
window.OnClose(func() bool { if !hasUnsavedChanges() { return true }
// Show confirmation dialog dialog := app.Window.NewWithOptions(application.WebviewWindowOptions{ Title: "Confirm Close", Width: 400, Height: 150, Parent: window, AlwaysOnTop: true, })
// Wait for user response result := waitFordialogResult(dialog)
return result == "yes"})OnDestroy
Section titled “OnDestroy”Called when window is destroyed:
window.OnDestroy(func() { fmt.Printf("Window destroyed: %s\n", window.Name())
// Cleanup resources closeDatabase()
// Remove from tracking removeWindowFromRegistry(window.ID())
// Update application state updateWindowCount()})Important:
- Always called when window is destroyed
- Cannot be cancelled
- Last chance to cleanup
Use cases:
- Release resources
- Close connections
- Update application state
- Remove from tracking
Example with resource cleanup:
type ManagedWindow struct { window *application.WebviewWindow db *sql.DB listeners []func()}
func (mw *ManagedWindow) Setup() { mw.window.OnDestroy(func() { // Close database if mw.db != nil { mw.db.Close() }
// Remove event listeners for _, listener := range mw.listeners { listener() }
// Clear references mw.db = nil mw.listeners = nil })}Focus Events
Section titled “Focus Events”OnFocus
Section titled “OnFocus”Called when window gains focus:
window.OnFocus(func() { fmt.Println("Window gained focus")
// Update UI updateTitleBar(true)
// Refresh data refreshContent()
// Notify other windows app.Event.Emit("window-focused", window.ID())})Use cases:
- Update UI appearance
- Refresh data
- Resume operations
- Coordinate with other windows
OnBlur
Section titled “OnBlur”Called when window loses focus:
window.OnBlur(func() { fmt.Println("Window lost focus")
// Update UI updateTitleBar(false)
// Pause operations pauseAnimations()
// Save state saveCurrentState()})Use cases:
- Update UI appearance
- Pause operations
- Save state
- Reduce resource usage
Example: Focus-aware UI:
type FocusAwareWindow struct { window *application.WebviewWindow focused bool}
func (fw *FocusAwareWindow) Setup() { fw.window.OnFocus(func() { fw.focused = true fw.updateAppearance() })
fw.window.OnBlur(func() { 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”OnMinimise / OnUnMinimise
Section titled “OnMinimise / OnUnMinimise”Called when window is minimised or restored:
window.OnMinimise(func() { fmt.Println("Window minimised")
// Pause expensive operations pauseRendering()
// Save state saveWindowState()})
window.OnUnMinimise(func() { fmt.Println("Window restored from minimised")
// Resume operations resumeRendering()
// Refresh data refreshContent()})Use cases:
- Pause/resume operations
- Save/restore state
- Reduce resource usage
- Update UI
OnMaximise / OnUnMaximise
Section titled “OnMaximise / OnUnMaximise”Called when window is maximised or restored:
window.OnMaximise(func() { fmt.Println("Window maximised")
// Adjust layout window.EmitEvent("layout-mode", "maximised")
// Update button icon updateMaximiseButton("restore")})
window.OnUnMaximise(func() { fmt.Println("Window restored from maximised")
// Adjust layout window.EmitEvent("layout-mode", "normal")
// Update button icon updateMaximiseButton("maximise")})Use cases:
- Adjust layout
- Update UI
- Save window state
- Coordinate with other windows
OnFullscreen / OnUnFullscreen
Section titled “OnFullscreen / OnUnFullscreen”Called when window enters or exits fullscreen:
window.OnFullscreen(func() { fmt.Println("Window entered fullscreen")
// Hide UI chrome window.EmitEvent("chrome-visibility", false)
// Adjust layout window.EmitEvent("layout-mode", "fullscreen")})
window.OnUnFullscreen(func() { fmt.Println("Window exited fullscreen")
// Show UI chrome window.EmitEvent("chrome-visibility", true)
// Restore layout window.EmitEvent("layout-mode", "normal")})Use cases:
- Show/hide UI elements
- Adjust layout
- Update controls
- Save preferences
Position and Size Events
Section titled “Position and Size Events”OnMove
Section titled “OnMove”Called when window is moved:
window.OnMove(func(x, y int) { fmt.Printf("Window moved to: %d, %d\n", x, y)
// Save position saveWindowPosition(x, y)
// Update related windows updateRelatedWindowPositions(x, y)})Use cases:
- Save window position
- Update related windows
- Snap to edges
- Multi-monitor handling
OnResize
Section titled “OnResize”Called when window is resized:
window.OnResize(func(width, height int) { fmt.Printf("Window resized to: %dx%d\n", width, height)
// Save size saveWindowSize(width, height)
// Adjust layout window.EmitEvent("window-size", map[string]int{ "width": width, "height": height, })})Use cases:
- Save window size
- Adjust layout
- Update UI
- Responsive design
Example: Responsive layout:
window.OnResize(func(width, height int) { var layout string
if width < 600 { layout = "compact" } else if width < 1200 { layout = "normal" } else { layout = "wide" }
window.EmitEvent("layout-changed", layout)})Complete Example
Section titled “Complete Example”Here’s a production-ready window with full event handling:
package main
import ( "encoding/json" "fmt" "os" "github.com/wailsapp/wails/v3/pkg/application")
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.Application 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.OnFocus(func() { fmt.Println("Window focused") mw.window.EmitEvent("focus-state", true) })
mw.window.OnBlur(func() { fmt.Println("Window blurred") mw.window.EmitEvent("focus-state", false) })
// State change events mw.window.OnMinimise(func() { fmt.Println("Window minimised") mw.SaveState() })
mw.window.OnUnMinimise(func() { fmt.Println("Window restored") })
mw.window.OnMaximise(func() { fmt.Println("Window maximised") mw.state.Maximised = true mw.dirty = true })
mw.window.OnUnMaximise(func() { fmt.Println("Window restored from maximised") mw.state.Maximised = false mw.dirty = true })
mw.window.OnFullscreen(func() { fmt.Println("Window fullscreen") mw.state.Fullscreen = true mw.dirty = true })
mw.window.OnUnFullscreen(func() { fmt.Println("Window exited fullscreen") mw.state.Fullscreen = false mw.dirty = true })
// Position and size events mw.window.OnMove(func(x, y int) { mw.state.X = x mw.state.Y = y mw.dirty = true })
mw.window.OnResize(func(width, height int) { mw.state.Width = width mw.state.Height = height mw.dirty = true })
// Lifecycle events mw.window.OnClose(func() bool { if mw.dirty { mw.SaveState() } return true })
mw.window.OnDestroy(func() { fmt.Println("Window destroyed") 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:
// In main windowmainWindow.OnFocus(func() { // Notify all windows app.Event.Emit("main-window-focused", nil)})
// In other windowsapp.Event.On("main-window-focused", func(event *application.WailsEvent) { // Update UI updateRelativeToMain()})Event Chains
Section titled “Event Chains”Chain events together:
window.OnMaximise(func() { // Save state saveWindowState()
// Update layout window.EmitEvent("layout-changed", "maximised")
// Notify other windows app.Event.Emit("window-maximised", window.ID())})Debounced Events
Section titled “Debounced Events”Debounce frequent events:
var resizeTimer *time.Timer
window.OnResize(func(width, height int) { if resizeTimer != nil { resizeTimer.Stop() }
resizeTimer = time.AfterFunc(500*time.Millisecond, func() { // Save after resize stops saveWindowSize(width, height) })})Best Practices
Section titled “Best Practices”- Save state on close - Restore window position/size
- Cleanup on destroy - Release resources
- Debounce frequent events - Resize, move
- Handle focus changes - Update UI appropriately
- Coordinate windows - Use events for communication
- Test all events - Ensure handlers work correctly
❌ Don’t
Section titled “❌ Don’t”- Don’t block event handlers - Keep them fast
- Don’t forget cleanup - Memory leaks
- Don’t ignore errors - Log or handle them
- Don’t save on every event - Debounce first
- Don’t create circular events - Infinite loops
- Don’t forget platform differences - Test thoroughly
Troubleshooting
Section titled “Troubleshooting”OnClose Not Firing
Section titled “OnClose Not Firing”Cause: Using window.Destroy() instead of window.Close()
Solution:
// ✅ Triggers OnClosewindow.Close()
// ❌ Doesn't trigger OnClosewindow.Destroy()Events Not Firing
Section titled “Events Not Firing”Cause: Handler registered after event occurred
Solution:
// Register handlers immediately after creationwindow := app.Window.New()window.OnClose(func() bool { return true })Memory Leaks
Section titled “Memory Leaks”Cause: Not cleaning up in OnDestroy
Solution:
window.OnDestroy(func() { // Always cleanup closeResources() removeReferences()})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 →