Event Window
Event Window
Section titled “Event Window”Wails mengirim event siklus hidup dan perubahan state window melalui satu API: OnWindowEvent untuk listener pasif, dan RegisterHook untuk hook yang dapat dibatalkan yang mencegah aksi default.
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.
Event Siklus Hidup
Section titled “Event Siklus Hidup”Pembuatan Window
Section titled “Pembuatan Window”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)})Event Fokus
Section titled “Event Fokus”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") }}Event Perubahan State
Section titled “Event Perubahan State”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.
Event Posisi dan Ukuran
Section titled “Event Posisi dan Ukuran”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)})Contoh Lengkap
Section titled “Contoh Lengkap”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")}Koordinasi Event
Section titled “Koordinasi Event”Event Lintas Window
Section titled “Event Lintas Window”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()})Rantai Event
Section titled “Rantai Event”window.OnWindowEvent(events.Common.WindowMaximise, func(e *application.WindowEvent) { saveWindowState() window.EmitEvent("layout-changed", "maximised") app.Event.Emit("window-maximised", window.ID())})Event Debounced
Section titled “Event Debounced”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) })})Praktik Terbaik
Section titled “Praktik Terbaik”✅ Lakukan
Section titled “✅ Lakukan”- 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().
❌ Jangan
Section titled “❌ Jangan”- 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})
// ✅ Dapat dibatalkan — 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()Langkah Selanjutnya
Section titled “Langkah Selanjutnya”Window Basics - Learn the fundamentals of window management Pelajari Selengkapnya →
Multiple Windows - Patterns for multi-window applications Pelajari Selengkapnya →
Events System - Deep dive into the event system Pelajari Selengkapnya →
Application Lifecycle - Understand the application lifecycle Pelajari Selengkapnya →