feat(stubs): expand Wails v3 stub bridge — 16 files, 316 functions
Some checks failed
Security Scan / security (push) Failing after 24s

New stub files:
- browser_manager.go, browser_window.go (95 methods, full Window interface)
- clipboard.go, context_menu.go, dialog.go (33 dialog methods)
- environment.go, events.go, keybinding.go
- menuitem.go, screen.go, services.go
- webview_window_options.go (574 lines, all platform types)
- window.go (Window interface ~50 methods)
- window_manager_expanded.go (Get, GetByID)
- application_options.go (Options, platform options, iOS/Android)

App struct expanded with all manager fields.
WebviewWindow and BrowserWindow both satisfy Window interface.
GetAll() returns []Window (was []any).

All stubs compile clean: GOWORK=off go build ./...

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude 2026-03-31 14:43:17 +01:00
parent 82f427dc12
commit fc94b4d2a5
No known key found for this signature in database
GPG key ID: AF404715446AEB41
17 changed files with 3364 additions and 15 deletions

View file

@ -2,6 +2,7 @@ package application
import (
"sync"
"unsafe"
"github.com/wailsapp/wails/v3/pkg/events"
)
@ -238,10 +239,11 @@ func (w *WebviewWindow) IsFocused() bool {
return w.focused
}
func (w *WebviewWindow) SetTitle(title string) {
func (w *WebviewWindow) SetTitle(title string) Window {
w.mu.Lock()
w.title = title
w.mu.Unlock()
return w
}
func (w *WebviewWindow) SetPosition(x, y int) {
@ -251,25 +253,28 @@ func (w *WebviewWindow) SetPosition(x, y int) {
w.mu.Unlock()
}
func (w *WebviewWindow) SetSize(width, height int) {
func (w *WebviewWindow) SetSize(width, height int) Window {
w.mu.Lock()
w.width = width
w.height = height
w.mu.Unlock()
return w
}
func (w *WebviewWindow) SetBackgroundColour(colour RGBA) {}
func (w *WebviewWindow) SetBackgroundColour(colour RGBA) Window { return w }
func (w *WebviewWindow) SetAlwaysOnTop(alwaysOnTop bool) {
func (w *WebviewWindow) SetAlwaysOnTop(alwaysOnTop bool) Window {
w.mu.Lock()
w.alwaysOnTop = alwaysOnTop
w.mu.Unlock()
return w
}
func (w *WebviewWindow) Maximise() {
func (w *WebviewWindow) Maximise() Window {
w.mu.Lock()
w.maximised = true
w.mu.Unlock()
return w
}
func (w *WebviewWindow) Restore() {
@ -279,7 +284,7 @@ func (w *WebviewWindow) Restore() {
w.mu.Unlock()
}
func (w *WebviewWindow) Minimise() {}
func (w *WebviewWindow) Minimise() Window { return w }
func (w *WebviewWindow) Focus() {
w.mu.Lock()
@ -293,22 +298,25 @@ func (w *WebviewWindow) Close() {
w.mu.Unlock()
}
func (w *WebviewWindow) Show() {
func (w *WebviewWindow) Show() Window {
w.mu.Lock()
w.visible = true
w.mu.Unlock()
return w
}
func (w *WebviewWindow) Hide() {
func (w *WebviewWindow) Hide() Window {
w.mu.Lock()
w.visible = false
w.mu.Unlock()
return w
}
func (w *WebviewWindow) Fullscreen() {
func (w *WebviewWindow) Fullscreen() Window {
w.mu.Lock()
w.fullscreen = true
w.mu.Unlock()
return w
}
func (w *WebviewWindow) UnFullscreen() {
@ -324,6 +332,376 @@ func (w *WebviewWindow) OnWindowEvent(eventType events.WindowEventType, callback
return func() {}
}
// ID returns a stable numeric identifier for this window.
//
// id := w.ID()
func (w *WebviewWindow) ID() uint { return 0 }
// ClientID returns the client identifier (empty for native windows).
//
// cid := w.ClientID()
func (w *WebviewWindow) ClientID() string { return "" }
// Width returns the current window width in logical pixels.
//
// px := w.Width()
func (w *WebviewWindow) Width() int {
w.mu.RLock()
defer w.mu.RUnlock()
return w.width
}
// Height returns the current window height in logical pixels.
//
// px := w.Height()
func (w *WebviewWindow) Height() int {
w.mu.RLock()
defer w.mu.RUnlock()
return w.height
}
// IsVisible reports whether the window is currently shown.
//
// if w.IsVisible() { ... }
func (w *WebviewWindow) IsVisible() bool {
w.mu.RLock()
defer w.mu.RUnlock()
return w.visible
}
// IsFullscreen reports whether the window is in fullscreen mode.
//
// if w.IsFullscreen() { ... }
func (w *WebviewWindow) IsFullscreen() bool {
w.mu.RLock()
defer w.mu.RUnlock()
return w.fullscreen
}
// IsMinimised reports whether the window is minimised.
//
// if w.IsMinimised() { ... }
func (w *WebviewWindow) IsMinimised() bool { return false }
// IsIgnoreMouseEvents reports whether mouse events are being suppressed.
//
// if w.IsIgnoreMouseEvents() { ... }
func (w *WebviewWindow) IsIgnoreMouseEvents() bool { return false }
// Resizable reports whether the window can be resized by the user.
//
// if w.Resizable() { ... }
func (w *WebviewWindow) Resizable() bool { return true }
// Bounds returns the current position and size as a Rect.
//
// r := w.Bounds()
func (w *WebviewWindow) Bounds() Rect {
w.mu.RLock()
defer w.mu.RUnlock()
return Rect{X: w.x, Y: w.y, Width: w.width, Height: w.height}
}
// SetBounds sets position and size simultaneously.
//
// w.SetBounds(Rect{X: 100, Y: 100, Width: 1280, Height: 800})
func (w *WebviewWindow) SetBounds(bounds Rect) {
w.mu.Lock()
w.x, w.y, w.width, w.height = bounds.X, bounds.Y, bounds.Width, bounds.Height
w.mu.Unlock()
}
// RelativePosition returns the position relative to the screen origin.
//
// rx, ry := w.RelativePosition()
func (w *WebviewWindow) RelativePosition() (int, int) {
w.mu.RLock()
defer w.mu.RUnlock()
return w.x, w.y
}
// SetRelativePosition sets the position relative to the screen.
//
// w.SetRelativePosition(0, 0)
func (w *WebviewWindow) SetRelativePosition(x, y int) Window {
w.mu.Lock()
w.x = x
w.y = y
w.mu.Unlock()
return w
}
// SetMinSize sets the minimum window dimensions.
//
// w.SetMinSize(640, 480)
func (w *WebviewWindow) SetMinSize(minWidth, minHeight int) Window { return w }
// SetMaxSize sets the maximum window dimensions.
//
// w.SetMaxSize(3840, 2160)
func (w *WebviewWindow) SetMaxSize(maxWidth, maxHeight int) Window { return w }
// Center positions the window at the centre of the screen.
//
// w.Center()
func (w *WebviewWindow) Center() {}
// SetURL navigates the webview to the given URL.
//
// w.SetURL("https://example.com")
func (w *WebviewWindow) SetURL(url string) Window { return w }
// SetHTML replaces the webview content with the given HTML string.
//
// w.SetHTML("<h1>Hello</h1>")
func (w *WebviewWindow) SetHTML(html string) Window { return w }
// SetFrameless toggles the window frame.
//
// w.SetFrameless(true)
func (w *WebviewWindow) SetFrameless(frameless bool) Window { return w }
// SetResizable controls whether the user can resize the window.
//
// w.SetResizable(false)
func (w *WebviewWindow) SetResizable(b bool) Window { return w }
// SetIgnoreMouseEvents suppresses or restores mouse event delivery.
//
// w.SetIgnoreMouseEvents(true)
func (w *WebviewWindow) SetIgnoreMouseEvents(ignore bool) Window { return w }
// SetMinimiseButtonState controls the minimise button appearance.
//
// w.SetMinimiseButtonState(ButtonHidden)
func (w *WebviewWindow) SetMinimiseButtonState(state ButtonState) Window { return w }
// SetMaximiseButtonState controls the maximise button appearance.
//
// w.SetMaximiseButtonState(ButtonDisabled)
func (w *WebviewWindow) SetMaximiseButtonState(state ButtonState) Window { return w }
// SetCloseButtonState controls the close button appearance.
//
// w.SetCloseButtonState(ButtonEnabled)
func (w *WebviewWindow) SetCloseButtonState(state ButtonState) Window { return w }
// SetEnabled enables or disables user interaction with the window.
//
// w.SetEnabled(false)
func (w *WebviewWindow) SetEnabled(enabled bool) {}
// SetContentProtection prevents the window contents from being captured.
//
// w.SetContentProtection(true)
func (w *WebviewWindow) SetContentProtection(protection bool) Window { return w }
// SetMenu attaches a menu to the window.
//
// w.SetMenu(myMenu)
func (w *WebviewWindow) SetMenu(menu *Menu) {}
// ShowMenuBar makes the menu bar visible.
//
// w.ShowMenuBar()
func (w *WebviewWindow) ShowMenuBar() {}
// HideMenuBar hides the menu bar.
//
// w.HideMenuBar()
func (w *WebviewWindow) HideMenuBar() {}
// ToggleMenuBar toggles menu bar visibility.
//
// w.ToggleMenuBar()
func (w *WebviewWindow) ToggleMenuBar() {}
// ToggleFrameless toggles the window frame.
//
// w.ToggleFrameless()
func (w *WebviewWindow) ToggleFrameless() {}
// ExecJS executes a JavaScript string in the webview.
//
// w.ExecJS("document.title = 'Ready'")
func (w *WebviewWindow) ExecJS(js string) {}
// Reload reloads the current page.
//
// w.Reload()
func (w *WebviewWindow) Reload() {}
// ForceReload bypasses the cache and reloads.
//
// w.ForceReload()
func (w *WebviewWindow) ForceReload() {}
// OpenDevTools opens the browser developer tools panel.
//
// w.OpenDevTools()
func (w *WebviewWindow) OpenDevTools() {}
// OpenContextMenu triggers a named context menu at the given position.
//
// w.OpenContextMenu(&ContextMenuData{Name: "edit", X: 100, Y: 200})
func (w *WebviewWindow) OpenContextMenu(data *ContextMenuData) {}
// Zoom applies the default zoom level.
//
// w.Zoom()
func (w *WebviewWindow) Zoom() {}
// ZoomIn increases the zoom level by one step.
//
// w.ZoomIn()
func (w *WebviewWindow) ZoomIn() {}
// ZoomOut decreases the zoom level by one step.
//
// w.ZoomOut()
func (w *WebviewWindow) ZoomOut() {}
// ZoomReset returns the zoom level to 1.0.
//
// w.ZoomReset()
func (w *WebviewWindow) ZoomReset() Window { return w }
// GetZoom returns the current zoom magnification factor.
//
// z := w.GetZoom()
func (w *WebviewWindow) GetZoom() float64 { return 1.0 }
// SetZoom sets the zoom magnification factor.
//
// w.SetZoom(1.5)
func (w *WebviewWindow) SetZoom(magnification float64) Window { return w }
// RegisterHook registers a pre-event hook for the given window event type.
//
// cancel := w.RegisterHook(events.Common.WindowClose, func(e *WindowEvent) { saveState() })
// defer cancel()
func (w *WebviewWindow) RegisterHook(eventType events.WindowEventType, callback func(event *WindowEvent)) func() {
return func() {}
}
// EmitEvent fires a named event from this window.
//
// w.EmitEvent("user:login", payload)
func (w *WebviewWindow) EmitEvent(name string, data ...any) bool { return false }
// DispatchWailsEvent sends a custom event through the Wails event bus.
//
// w.DispatchWailsEvent(&CustomEvent{Name: "init"})
func (w *WebviewWindow) DispatchWailsEvent(event *CustomEvent) {}
// GetScreen returns the screen on which this window is currently displayed.
//
// screen, err := w.GetScreen()
func (w *WebviewWindow) GetScreen() (*Screen, error) { return nil, nil }
// GetBorderSizes returns the platform-specific window border dimensions.
//
// borders := w.GetBorderSizes()
func (w *WebviewWindow) GetBorderSizes() *LRTB { return nil }
// EnableSizeConstraints activates the min/max size limits.
//
// w.EnableSizeConstraints()
func (w *WebviewWindow) EnableSizeConstraints() {}
// DisableSizeConstraints removes the min/max size limits.
//
// w.DisableSizeConstraints()
func (w *WebviewWindow) DisableSizeConstraints() {}
// AttachModal registers a modal window that blocks this window.
//
// w.AttachModal(confirmDialog)
func (w *WebviewWindow) AttachModal(modalWindow Window) {}
// Flash requests the window manager to flash or bounce this window.
//
// w.Flash(true)
func (w *WebviewWindow) Flash(enabled bool) {}
// Print opens the system print dialog for the webview contents.
//
// err := w.Print()
func (w *WebviewWindow) Print() error { return nil }
// Error logs an error-level message on behalf of this window.
//
// w.Error("load failed: %s", err)
func (w *WebviewWindow) Error(message string, args ...any) {}
// Info logs an info-level message on behalf of this window.
//
// w.Info("window ready")
func (w *WebviewWindow) Info(message string, args ...any) {}
// NativeWindow returns the platform-specific window handle (nil in stub).
//
// ptr := w.NativeWindow()
func (w *WebviewWindow) NativeWindow() unsafe.Pointer { return nil }
// Run starts the window event loop.
//
// w.Run()
func (w *WebviewWindow) Run() {}
// UnMaximise restores the window from maximised state.
//
// w.UnMaximise()
func (w *WebviewWindow) UnMaximise() {
w.mu.Lock()
w.maximised = false
w.mu.Unlock()
}
// UnMinimise restores the window from minimised state.
//
// w.UnMinimise()
func (w *WebviewWindow) UnMinimise() {}
// ToggleFullscreen switches between fullscreen and windowed mode.
//
// w.ToggleFullscreen()
func (w *WebviewWindow) ToggleFullscreen() {
w.mu.Lock()
w.fullscreen = !w.fullscreen
w.mu.Unlock()
}
// ToggleMaximise switches between maximised and restored state.
//
// w.ToggleMaximise()
func (w *WebviewWindow) ToggleMaximise() {
w.mu.Lock()
w.maximised = !w.maximised
w.mu.Unlock()
}
// SnapAssist triggers the platform snap-assist feature.
//
// w.SnapAssist()
func (w *WebviewWindow) SnapAssist() {}
// Internal platform hooks — no-ops in the stub.
func (w *WebviewWindow) handleDragAndDropMessage(filenames []string, dropTarget *DropTargetDetails) {}
func (w *WebviewWindow) InitiateFrontendDropProcessing(filenames []string, x int, y int) {}
func (w *WebviewWindow) HandleMessage(message string) {}
func (w *WebviewWindow) HandleWindowEvent(id uint) {}
func (w *WebviewWindow) HandleKeyEvent(acceleratorString string) {}
func (w *WebviewWindow) shouldUnconditionallyClose() bool { return false }
func (w *WebviewWindow) cut() {}
func (w *WebviewWindow) copy() {}
func (w *WebviewWindow) paste() {}
func (w *WebviewWindow) undo() {}
func (w *WebviewWindow) redo() {}
func (w *WebviewWindow) delete() {}
func (w *WebviewWindow) selectAll() {}
// WindowManager manages in-memory windows.
type WindowManager struct {
mu sync.RWMutex
@ -338,10 +716,13 @@ func (wm *WindowManager) NewWithOptions(options WebviewWindowOptions) *WebviewWi
return window
}
func (wm *WindowManager) GetAll() []any {
// GetAll returns all windows managed by this manager.
//
// for _, w := range wm.GetAll() { w.Show() }
func (wm *WindowManager) GetAll() []Window {
wm.mu.RLock()
defer wm.mu.RUnlock()
out := make([]any, 0, len(wm.windows))
out := make([]Window, 0, len(wm.windows))
for _, window := range wm.windows {
out = append(out, window)
}
@ -349,11 +730,23 @@ func (wm *WindowManager) GetAll() []any {
}
// App is the top-level application object used by the GUI packages.
//
// app := &application.App{}
// app.Dialog.Info().SetTitle("Done").SetMessage("Saved.").Show()
// app.Event.Emit("user:login", payload)
type App struct {
Logger Logger
Window WindowManager
Menu MenuManager
SystemTray SystemTrayManager
Logger Logger
Window WindowManager
Menu MenuManager
SystemTray SystemTrayManager
Dialog DialogManager
Event EventManager
Browser BrowserManager
Clipboard ClipboardManager
ContextMenu ContextMenuManager
Environment EnvironmentManager
Screen ScreenManager
KeyBinding KeyBindingManager
}
func (a *App) Quit() {}

View file

@ -0,0 +1,382 @@
package application
// Handler is a stub for net/http.Handler.
// In the real Wails runtime this is http.Handler; the stub replaces it with
// an interface so the application package compiles without importing net/http.
//
// var h Handler = myHTTPHandler
type Handler interface {
ServeHTTP(w ResponseWriter, r *Request)
}
// ResponseWriter is a minimal stub for http.ResponseWriter.
type ResponseWriter interface {
Header() map[string][]string
Write([]byte) (int, error)
WriteHeader(statusCode int)
}
// Request is a minimal stub for *http.Request.
type Request struct {
Method string
URL string
Header map[string][]string
Body []byte
}
// FS is a stub for fs.FS (filesystem abstraction).
// In the real Wails runtime this is io/fs.FS.
type FS interface {
Open(name string) (interface{ Read([]byte) (int, error) }, error)
}
// Duration is a stub for time.Duration (nanoseconds).
type Duration = int64
// LogLevel is a stub for slog.Level.
type LogLevel = int
// Logger is a stub for *slog.Logger.
// In production this is the standard library structured logger.
type SlogLogger struct{}
// Middleware defines HTTP middleware applied to the AssetServer.
// The handler passed as next is the next handler in the chain.
//
// Middleware: func(next application.Handler) application.Handler { return myHandler }
type Middleware func(next Handler) Handler
// ChainMiddleware chains multiple middlewares into one.
//
// chained := application.ChainMiddleware(auth, logging, cors)
func ChainMiddleware(middleware ...Middleware) Middleware {
return func(h Handler) Handler {
for i := len(middleware) - 1; i >= 0; i-- {
h = middleware[i](h)
}
return h
}
}
// AssetFileServerFS returns a handler serving assets from an FS.
// In the stub this returns nil — no real file serving occurs.
//
// opts.Assets.Handler = application.AssetFileServerFS(embedFS)
func AssetFileServerFS(assets FS) Handler { return nil }
// BundledAssetFileServer returns a handler serving bundled assets.
// In the stub this returns nil — no real file serving occurs.
//
// opts.Assets.Handler = application.BundledAssetFileServer(embedFS)
func BundledAssetFileServer(assets FS) Handler { return nil }
// ActivationPolicy controls the macOS application activation policy.
//
// Mac: MacOptions{ActivationPolicy: ActivationPolicyAccessory}
type ActivationPolicy int
const (
// ActivationPolicyRegular is for applications with a user interface.
ActivationPolicyRegular ActivationPolicy = iota
// ActivationPolicyAccessory is for menu-bar or background applications.
ActivationPolicyAccessory
// ActivationPolicyProhibited disables activation entirely.
ActivationPolicyProhibited
)
// NativeTabIcon is an SF Symbols name string used for iOS tab bar icons.
//
// NativeTabsItems: []NativeTabItem{{Title: "Home", SystemImage: NativeTabIconHouse}}
type NativeTabIcon string
const (
NativeTabIconNone NativeTabIcon = ""
NativeTabIconHouse NativeTabIcon = "house"
NativeTabIconGear NativeTabIcon = "gear"
NativeTabIconStar NativeTabIcon = "star"
NativeTabIconPerson NativeTabIcon = "person"
NativeTabIconBell NativeTabIcon = "bell"
NativeTabIconMagnify NativeTabIcon = "magnifyingglass"
NativeTabIconList NativeTabIcon = "list.bullet"
NativeTabIconFolder NativeTabIcon = "folder"
)
// NativeTabItem describes a single iOS UITabBar item.
//
// NativeTabsItems: []NativeTabItem{{Title: "Settings", SystemImage: NativeTabIconGear}}
type NativeTabItem struct {
Title string `json:"Title"`
SystemImage NativeTabIcon `json:"SystemImage"`
}
// PanicDetails carries the information delivered to the panic handler.
//
// opts.PanicHandler = func(details *application.PanicDetails) { log(details.Message) }
type PanicDetails struct {
// Message is the string form of the recovered panic value.
Message string
// Stack is the formatted goroutine stack trace.
Stack string
}
// Transport is the interface for custom IPC transport layers.
// Implement this to replace the default HTTP fetch + js.Exec transport.
//
// opts.Transport = myWebSocketTransport
type Transport interface{}
// SingleInstanceOptions configures the single-instance lock behaviour.
//
// opts.SingleInstance = &application.SingleInstanceOptions{UniqueID: "com.example.myapp"}
type SingleInstanceOptions struct {
// UniqueID is the identifier used to detect duplicate instances.
UniqueID string
// OnSecondInstanceLaunch is called in the first instance when a second
// one starts. Receives the arguments passed to the second instance.
OnSecondInstanceLaunch func(secondInstanceData SecondInstanceData)
}
// SecondInstanceData carries data from a second application instance launch.
//
// func handler(data application.SecondInstanceData) { openFile(data.Args[0]) }
type SecondInstanceData struct {
// Args are the command-line arguments of the second instance.
Args []string
// WorkingDirectory is the cwd of the second instance.
WorkingDirectory string
}
// OriginInfo carries the origin details of a frontend message.
//
// opts.RawMessageHandler = func(w Window, msg string, info *application.OriginInfo) {}
type OriginInfo struct {
// Origin is the origin of the frame that sent the message.
Origin string
// TopOrigin is the origin of the top-level frame.
TopOrigin string
// IsMainFrame is true when the message came from the main frame.
IsMainFrame bool
}
// AssetOptions configures the embedded asset server.
//
// opts.Assets = application.AssetOptions{Handler: application.AssetFileServerFS(embedFS)}
type AssetOptions struct {
// Handler serves all content to the WebView.
Handler Handler
// Middleware hooks into the AssetServer request chain.
// Multiple middlewares can be composed with ChainMiddleware.
Middleware Middleware
// DisableLogging suppresses per-request AssetServer log output.
DisableLogging bool
}
// TLSOptions configures HTTPS for the headless server.
//
// opts.Server.TLS = &application.TLSOptions{CertFile: "cert.pem", KeyFile: "key.pem"}
type TLSOptions struct {
// CertFile is the path to the TLS certificate file.
CertFile string
// KeyFile is the path to the TLS private key file.
KeyFile string
}
// ServerOptions configures the HTTP server used in headless (server) mode.
// Enable server mode by building with: go build -tags server
//
// opts.Server = application.ServerOptions{Host: "0.0.0.0", Port: 8080}
type ServerOptions struct {
// Host is the address to bind to. Defaults to "localhost".
Host string
// Port is the port to listen on. Defaults to 8080.
Port int
// ReadTimeout is the maximum duration for reading a request (nanoseconds).
ReadTimeout Duration
// WriteTimeout is the maximum duration for writing a response (nanoseconds).
WriteTimeout Duration
// IdleTimeout is the maximum idle connection duration (nanoseconds).
IdleTimeout Duration
// ShutdownTimeout is the maximum time to wait for graceful shutdown (nanoseconds).
ShutdownTimeout Duration
// TLS configures HTTPS. If nil, plain HTTP is used.
TLS *TLSOptions
}
// MacOptions contains macOS-specific application configuration.
//
// opts.Mac = application.MacOptions{ActivationPolicy: application.ActivationPolicyRegular}
type MacOptions struct {
// ActivationPolicy controls how the app interacts with the Dock and menu bar.
ActivationPolicy ActivationPolicy
// ApplicationShouldTerminateAfterLastWindowClosed quits the app when the
// last window closes (matches NSApplicationDelegate behaviour).
ApplicationShouldTerminateAfterLastWindowClosed bool
}
// WindowsOptions contains Windows-specific application configuration.
//
// opts.Windows = application.WindowsOptions{WndClass: "MyAppWindow"}
type WindowsOptions struct {
// WndClass is the Win32 window class name. Default: WailsWebviewWindow.
WndClass string
// WndProcInterceptor intercepts all Win32 messages for the application.
WndProcInterceptor func(hwnd uintptr, msg uint32, wParam, lParam uintptr) (returnCode uintptr, shouldReturn bool)
// DisableQuitOnLastWindowClosed prevents auto-quit when the last window closes.
DisableQuitOnLastWindowClosed bool
// WebviewUserDataPath is the directory for WebView2 user data.
WebviewUserDataPath string
// WebviewBrowserPath is the directory containing WebView2 executables.
WebviewBrowserPath string
// EnabledFeatures lists WebView2 feature flags to enable.
EnabledFeatures []string
// DisabledFeatures lists WebView2 feature flags to disable.
DisabledFeatures []string
// AdditionalBrowserArgs are extra browser arguments (must include "--" prefix).
AdditionalBrowserArgs []string
}
// LinuxOptions contains Linux-specific application configuration.
//
// opts.Linux = application.LinuxOptions{ProgramName: "myapp"}
type LinuxOptions struct {
// DisableQuitOnLastWindowClosed prevents auto-quit when the last window closes.
DisableQuitOnLastWindowClosed bool
// ProgramName sets g_set_prgname() for the window manager.
ProgramName string
}
// IOSOptions contains iOS-specific application configuration.
//
// opts.IOS = application.IOSOptions{EnableInlineMediaPlayback: true}
type IOSOptions struct {
// DisableInputAccessoryView hides the iOS keyboard accessory bar.
DisableInputAccessoryView bool
// DisableScroll disables WebView scrolling.
DisableScroll bool
// DisableBounce disables the WebView bounce effect.
DisableBounce bool
// DisableScrollIndicators hides scroll indicators.
DisableScrollIndicators bool
// EnableBackForwardNavigationGestures enables swipe navigation.
EnableBackForwardNavigationGestures bool
// DisableLinkPreview disables link long-press previews.
DisableLinkPreview bool
// EnableInlineMediaPlayback allows media to play inline.
EnableInlineMediaPlayback bool
// EnableAutoplayWithoutUserAction allows media autoplay without a gesture.
EnableAutoplayWithoutUserAction bool
// DisableInspectable disables the Safari Web Inspector.
DisableInspectable bool
// UserAgent overrides the WebView user agent string.
UserAgent string
// ApplicationNameForUserAgent is appended to the user agent. Default: "wails.io".
ApplicationNameForUserAgent string
// AppBackgroundColourSet enables the custom BackgroundColour below.
AppBackgroundColourSet bool
// BackgroundColour is the app window background before WebView creation.
BackgroundColour RGBA
// EnableNativeTabs shows a native iOS UITabBar.
EnableNativeTabs bool
// NativeTabsItems configures the UITabBar items. Auto-enables tabs when non-empty.
NativeTabsItems []NativeTabItem
}
// AndroidOptions contains Android-specific application configuration.
//
// opts.Android = application.AndroidOptions{EnableZoom: true}
type AndroidOptions struct {
// DisableScroll disables WebView scrolling.
DisableScroll bool
// DisableOverscroll disables the overscroll bounce effect.
DisableOverscroll bool
// EnableZoom allows pinch-to-zoom in the WebView.
EnableZoom bool
// UserAgent sets a custom user agent string.
UserAgent string
// BackgroundColour sets the WebView background colour.
BackgroundColour RGBA
// DisableHardwareAcceleration disables hardware acceleration.
DisableHardwareAcceleration bool
}
// Options is the top-level application configuration passed to New().
//
// app := application.New(application.Options{
// Name: "MyApp",
// Assets: application.AssetOptions{Handler: application.AssetFileServerFS(embedFS)},
// Services: []application.Service{application.NewService(&myService{})},
// })
type Options struct {
// Name is the application name shown in the default about box.
Name string
// Description is shown in the default about box.
Description string
// Icon is the application icon bytes used in the about box.
Icon []byte
// Mac is the macOS-specific configuration.
Mac MacOptions
// Windows is the Windows-specific configuration.
Windows WindowsOptions
// Linux is the Linux-specific configuration.
Linux LinuxOptions
// IOS is the iOS-specific configuration.
IOS IOSOptions
// Android is the Android-specific configuration.
Android AndroidOptions
// Services are the bound Go service instances exposed to the frontend.
Services []Service
// MarshalError serialises error values from service methods to JSON.
// A nil return falls back to the default error handler.
MarshalError func(error) []byte
// BindAliases maps alias IDs to bound method IDs.
// Example: map[uint32]uint32{1: 1411160069}
BindAliases map[uint32]uint32
// Logger is the structured logger for Wails system messages.
// If nil, a default logger is used.
Logger *SlogLogger
// LogLevel sets the log level for the Wails system logger.
LogLevel LogLevel
// Assets configures the embedded asset server.
Assets AssetOptions
// Flags are key-value pairs exposed to the frontend.
Flags map[string]any
// PanicHandler is called when a panic occurs in a service method.
PanicHandler func(*PanicDetails)
// DisableDefaultSignalHandler disables the built-in SIGINT/SIGTERM handler.
DisableDefaultSignalHandler bool
// KeyBindings maps accelerator strings to window callbacks.
KeyBindings map[string]func(window Window)
// OnShutdown is called before the application terminates.
OnShutdown func()
// PostShutdown is called after shutdown completes, just before process exit.
PostShutdown func()
// ShouldQuit is called when the user requests quit. Return false to cancel.
ShouldQuit func() bool
// RawMessageHandler handles raw messages sent from the frontend.
RawMessageHandler func(window Window, message string, originInfo *OriginInfo)
// WarningHandler is called when a non-fatal warning occurs.
WarningHandler func(string)
// ErrorHandler is called when an error occurs.
ErrorHandler func(err error)
// FileAssociations lists file extensions associated with this application.
// Example: []string{".txt", ".md"} — the leading dot is required.
FileAssociations []string
// SingleInstance configures single-instance enforcement.
SingleInstance *SingleInstanceOptions
// Transport provides a custom IPC transport layer.
// When nil, the default HTTP fetch transport is used.
Transport Transport
// Server configures the headless HTTP server (requires -tags server).
Server ServerOptions
}

View file

@ -0,0 +1,36 @@
package application
import "sync"
// BrowserManager manages browser-related operations in-memory.
//
// manager := &BrowserManager{}
// manager.OpenURL("https://example.com")
// last := manager.LastURL // "https://example.com"
type BrowserManager struct {
mu sync.RWMutex
LastURL string
LastFile string
}
// OpenURL stores the URL as the last opened URL.
//
// manager.OpenURL("https://lthn.io")
// _ = manager.LastURL // "https://lthn.io"
func (bm *BrowserManager) OpenURL(url string) error {
bm.mu.Lock()
bm.LastURL = url
bm.mu.Unlock()
return nil
}
// OpenFile stores the path as the last opened file.
//
// manager.OpenFile("/home/user/report.pdf")
// _ = manager.LastFile // "/home/user/report.pdf"
func (bm *BrowserManager) OpenFile(path string) error {
bm.mu.Lock()
bm.LastFile = path
bm.mu.Unlock()
return nil
}

View file

@ -0,0 +1,192 @@
package application
import (
"sync"
"unsafe"
"github.com/wailsapp/wails/v3/pkg/events"
)
// ButtonState controls window button appearance.
type ButtonState int
const (
ButtonEnabled ButtonState = 0
ButtonDisabled ButtonState = 1
ButtonHidden ButtonState = 2
)
// LRTB represents Left, Right, Top, Bottom border sizes.
type LRTB struct {
Left, Right, Top, Bottom int
}
// ContextMenuData carries context menu trigger details.
type ContextMenuData struct {
Name string
X, Y int
Data any
}
// BrowserWindow represents a browser client connection in server mode.
// Implements the Window interface — most methods are no-ops since browser
// clients are controlled via WebSocket, not native APIs.
//
// browserWindow := application.NewBrowserWindow(1, "client-abc123")
type BrowserWindow struct {
mu sync.RWMutex
id uint
name string
clientID string
}
// NewBrowserWindow creates a browser window with the given ID and client ID.
//
// browserWindow := application.NewBrowserWindow(1, "nanoid-abc123")
func NewBrowserWindow(id uint, clientID string) *BrowserWindow {
return &BrowserWindow{
id: id,
name: "browser-" + string(rune('0'+id%10)),
clientID: clientID,
}
}
func (browserWindow *BrowserWindow) ID() uint { return browserWindow.id }
func (browserWindow *BrowserWindow) Name() string { return browserWindow.name }
func (browserWindow *BrowserWindow) ClientID() string { return browserWindow.clientID }
func (browserWindow *BrowserWindow) DispatchWailsEvent(event *CustomEvent) {}
func (browserWindow *BrowserWindow) EmitEvent(name string, data ...any) bool {
return true
}
func (browserWindow *BrowserWindow) Error(message string, arguments ...any) {}
func (browserWindow *BrowserWindow) Info(message string, arguments ...any) {}
// No-op methods — browser windows are controlled via WebSocket, not native APIs.
func (browserWindow *BrowserWindow) Center() {}
func (browserWindow *BrowserWindow) Close() {}
func (browserWindow *BrowserWindow) DisableSizeConstraints() {}
func (browserWindow *BrowserWindow) EnableSizeConstraints() {}
func (browserWindow *BrowserWindow) ExecJS(javascript string) {}
func (browserWindow *BrowserWindow) Focus() {}
func (browserWindow *BrowserWindow) ForceReload() {}
func (browserWindow *BrowserWindow) Fullscreen() Window { return browserWindow }
func (browserWindow *BrowserWindow) GetBorderSizes() *LRTB { return nil }
func (browserWindow *BrowserWindow) GetScreen() (*Screen, error) {
return nil, nil
}
func (browserWindow *BrowserWindow) GetZoom() float64 { return 1.0 }
func (browserWindow *BrowserWindow) handleDragAndDropMessage(filenames []string, dropTarget *DropTargetDetails) {
}
func (browserWindow *BrowserWindow) HandleMessage(message string) {}
func (browserWindow *BrowserWindow) HandleWindowEvent(identifier uint) {}
func (browserWindow *BrowserWindow) Height() int { return 0 }
func (browserWindow *BrowserWindow) Hide() Window { return browserWindow }
func (browserWindow *BrowserWindow) HideMenuBar() {}
func (browserWindow *BrowserWindow) IsFocused() bool { return false }
func (browserWindow *BrowserWindow) IsFullscreen() bool { return false }
func (browserWindow *BrowserWindow) IsIgnoreMouseEvents() bool { return false }
func (browserWindow *BrowserWindow) IsMaximised() bool { return false }
func (browserWindow *BrowserWindow) IsMinimised() bool { return false }
func (browserWindow *BrowserWindow) HandleKeyEvent(accelerator string) {}
func (browserWindow *BrowserWindow) Maximise() Window { return browserWindow }
func (browserWindow *BrowserWindow) Minimise() Window { return browserWindow }
func (browserWindow *BrowserWindow) OnWindowEvent(eventType events.WindowEventType, callback func(event *WindowEvent)) func() {
return func() {}
}
func (browserWindow *BrowserWindow) OpenContextMenu(data *ContextMenuData) {}
func (browserWindow *BrowserWindow) Position() (int, int) { return 0, 0 }
func (browserWindow *BrowserWindow) RelativePosition() (int, int) { return 0, 0 }
func (browserWindow *BrowserWindow) Reload() {}
func (browserWindow *BrowserWindow) Resizable() bool { return false }
func (browserWindow *BrowserWindow) Restore() {}
func (browserWindow *BrowserWindow) Run() {}
func (browserWindow *BrowserWindow) SetPosition(x, y int) {}
func (browserWindow *BrowserWindow) SetAlwaysOnTop(alwaysOnTop bool) Window { return browserWindow }
func (browserWindow *BrowserWindow) SetBackgroundColour(colour RGBA) Window { return browserWindow }
func (browserWindow *BrowserWindow) SetFrameless(frameless bool) Window { return browserWindow }
func (browserWindow *BrowserWindow) SetHTML(html string) Window { return browserWindow }
func (browserWindow *BrowserWindow) SetMinimiseButtonState(state ButtonState) Window {
return browserWindow
}
func (browserWindow *BrowserWindow) SetMaximiseButtonState(state ButtonState) Window {
return browserWindow
}
func (browserWindow *BrowserWindow) SetCloseButtonState(state ButtonState) Window {
return browserWindow
}
func (browserWindow *BrowserWindow) SetMaxSize(maxWidth, maxHeight int) Window {
return browserWindow
}
func (browserWindow *BrowserWindow) SetMinSize(minWidth, minHeight int) Window {
return browserWindow
}
func (browserWindow *BrowserWindow) SetRelativePosition(x, y int) Window {
return browserWindow
}
func (browserWindow *BrowserWindow) SetResizable(resizable bool) Window {
return browserWindow
}
func (browserWindow *BrowserWindow) SetIgnoreMouseEvents(ignore bool) Window {
return browserWindow
}
func (browserWindow *BrowserWindow) SetSize(width, height int) Window { return browserWindow }
func (browserWindow *BrowserWindow) SetTitle(title string) Window { return browserWindow }
func (browserWindow *BrowserWindow) SetURL(url string) Window { return browserWindow }
func (browserWindow *BrowserWindow) SetZoom(magnification float64) Window {
return browserWindow
}
func (browserWindow *BrowserWindow) Show() Window { return browserWindow }
func (browserWindow *BrowserWindow) ShowMenuBar() {}
func (browserWindow *BrowserWindow) Size() (int, int) { return 0, 0 }
func (browserWindow *BrowserWindow) OpenDevTools() {}
func (browserWindow *BrowserWindow) ToggleFullscreen() {}
func (browserWindow *BrowserWindow) ToggleMaximise() {}
func (browserWindow *BrowserWindow) ToggleMenuBar() {}
func (browserWindow *BrowserWindow) ToggleFrameless() {}
func (browserWindow *BrowserWindow) UnFullscreen() {}
func (browserWindow *BrowserWindow) UnMaximise() {}
func (browserWindow *BrowserWindow) UnMinimise() {}
func (browserWindow *BrowserWindow) Width() int { return 0 }
func (browserWindow *BrowserWindow) IsVisible() bool { return true }
func (browserWindow *BrowserWindow) Bounds() Rect { return Rect{} }
func (browserWindow *BrowserWindow) SetBounds(bounds Rect) {}
func (browserWindow *BrowserWindow) Zoom() {}
func (browserWindow *BrowserWindow) ZoomIn() {}
func (browserWindow *BrowserWindow) ZoomOut() {}
func (browserWindow *BrowserWindow) ZoomReset() Window { return browserWindow }
func (browserWindow *BrowserWindow) SetMenu(menu *Menu) {}
func (browserWindow *BrowserWindow) SnapAssist() {}
func (browserWindow *BrowserWindow) SetContentProtection(protection bool) Window {
return browserWindow
}
func (browserWindow *BrowserWindow) SetEnabled(enabled bool) {}
func (browserWindow *BrowserWindow) Flash(enabled bool) {}
func (browserWindow *BrowserWindow) Print() error { return nil }
func (browserWindow *BrowserWindow) RegisterHook(eventType events.WindowEventType, callback func(event *WindowEvent)) func() {
return func() {}
}
// Internal platform hooks — no-ops for browser windows.
func (browserWindow *BrowserWindow) InitiateFrontendDropProcessing(filenames []string, x int, y int) {
}
func (browserWindow *BrowserWindow) shouldUnconditionallyClose() bool { return false }
func (browserWindow *BrowserWindow) cut() {}
func (browserWindow *BrowserWindow) copy() {}
func (browserWindow *BrowserWindow) paste() {}
func (browserWindow *BrowserWindow) undo() {}
func (browserWindow *BrowserWindow) redo() {}
func (browserWindow *BrowserWindow) delete() {}
func (browserWindow *BrowserWindow) selectAll() {}
// NativeWindow returns nil — browser windows have no native handle.
//
// ptr := w.NativeWindow()
func (browserWindow *BrowserWindow) NativeWindow() unsafe.Pointer { return nil }
// AttachModal registers a modal window that blocks this window.
//
// w.AttachModal(confirmDialog)
func (browserWindow *BrowserWindow) AttachModal(modalWindow Window) {}

View file

@ -0,0 +1,69 @@
package application
import "sync"
// Clipboard stores and retrieves text in-memory.
//
// cb := &Clipboard{}
// cb.SetText("hello")
// text, ok := cb.Text() // "hello", true
type Clipboard struct {
mu sync.RWMutex
text string
set bool
}
// SetText stores the given text in the in-memory clipboard.
//
// cb.SetText("copied content")
func (c *Clipboard) SetText(text string) bool {
c.mu.Lock()
c.text = text
c.set = true
c.mu.Unlock()
return true
}
// Text returns the stored clipboard text and whether any text has been set.
//
// text, ok := cb.Text()
// if !ok { text = "" }
func (c *Clipboard) Text() (string, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
return c.text, c.set
}
// ClipboardManager manages clipboard operations via a lazily created Clipboard.
//
// manager := &ClipboardManager{}
// manager.SetText("hello")
// text, ok := manager.Text() // "hello", true
type ClipboardManager struct {
mu sync.Mutex
clipboard *Clipboard
}
// SetText sets text in the clipboard.
//
// manager.SetText("some text")
func (cm *ClipboardManager) SetText(text string) bool {
return cm.getClipboard().SetText(text)
}
// Text gets text from the clipboard.
//
// text, ok := manager.Text()
func (cm *ClipboardManager) Text() (string, bool) {
return cm.getClipboard().Text()
}
// getClipboard returns the clipboard instance, creating it if needed.
func (cm *ClipboardManager) getClipboard() *Clipboard {
cm.mu.Lock()
defer cm.mu.Unlock()
if cm.clipboard == nil {
cm.clipboard = &Clipboard{}
}
return cm.clipboard
}

View file

@ -0,0 +1,84 @@
package application
import "sync"
// ContextMenu is a named menu used for right-click context menus.
//
// menu := &ContextMenu{name: "file-list", Menu: NewMenu()}
// menu.Add("Open")
type ContextMenu struct {
*Menu
name string
}
// ContextMenuManager manages all context menu operations in-memory.
//
// manager := &ContextMenuManager{}
// menu := manager.New()
// manager.Add("file-list", menu)
// retrieved, ok := manager.Get("file-list")
type ContextMenuManager struct {
mu sync.RWMutex
contextMenus map[string]*ContextMenu
}
// New creates a new context menu.
//
// menu := manager.New()
// menu.Add("Delete")
func (cmm *ContextMenuManager) New() *ContextMenu {
return &ContextMenu{
Menu: NewMenu(),
}
}
// Add registers a context menu under the given name.
//
// manager.Add("item-actions", menu)
func (cmm *ContextMenuManager) Add(name string, menu *ContextMenu) {
cmm.mu.Lock()
defer cmm.mu.Unlock()
if cmm.contextMenus == nil {
cmm.contextMenus = make(map[string]*ContextMenu)
}
cmm.contextMenus[name] = menu
}
// Remove removes a context menu by name.
//
// manager.Remove("item-actions")
func (cmm *ContextMenuManager) Remove(name string) {
cmm.mu.Lock()
defer cmm.mu.Unlock()
if cmm.contextMenus != nil {
delete(cmm.contextMenus, name)
}
}
// Get retrieves a context menu by name.
//
// menu, ok := manager.Get("item-actions")
// if !ok { return nil }
func (cmm *ContextMenuManager) Get(name string) (*ContextMenu, bool) {
cmm.mu.RLock()
defer cmm.mu.RUnlock()
if cmm.contextMenus == nil {
return nil, false
}
menu, exists := cmm.contextMenus[name]
return menu, exists
}
// GetAll returns all registered context menus as a slice.
//
// menus := manager.GetAll()
// for _, m := range menus { _ = m }
func (cmm *ContextMenuManager) GetAll() []*ContextMenu {
cmm.mu.RLock()
defer cmm.mu.RUnlock()
result := make([]*ContextMenu, 0, len(cmm.contextMenus))
for _, menu := range cmm.contextMenus {
result = append(result, menu)
}
return result
}

View file

@ -0,0 +1,421 @@
package application
import "sync"
// DialogType identifies the visual style of a message dialog.
type DialogType int
const (
InfoDialogType DialogType = iota
QuestionDialogType
WarningDialogType
ErrorDialogType
)
// FileFilter restricts which files are shown in a file dialog.
//
// filter := FileFilter{DisplayName: "Images", Pattern: "*.png;*.jpg"}
type FileFilter struct {
DisplayName string
Pattern string
}
// OpenFileDialogOptions configures an open-file dialog.
//
// opts := &OpenFileDialogOptions{Title: "Choose file", AllowsMultipleSelection: true}
type OpenFileDialogOptions struct {
Title string
Directory string
Filters []FileFilter
AllowsMultipleSelection bool
CanChooseDirectories bool
CanChooseFiles bool
ShowHiddenFiles bool
}
// SaveFileDialogOptions configures a save-file dialog.
//
// opts := &SaveFileDialogOptions{Title: "Save report", Directory: "/tmp"}
type SaveFileDialogOptions struct {
Title string
Directory string
Filename string
Filters []FileFilter
ShowHiddenFiles bool
}
// OpenFileDialogStruct is an in-memory open-file dialog.
//
// dialog := &OpenFileDialogStruct{}
// dialog.SetTitle("Pick a file")
// path, _ := dialog.PromptForSingleSelection()
type OpenFileDialogStruct struct {
mu sync.RWMutex
title string
directory string
filters []FileFilter
multipleAllowed bool
canChooseDirs bool
canChooseFiles bool
showHidden bool
selectedFiles []string
}
func newOpenFileDialog() *OpenFileDialogStruct {
return &OpenFileDialogStruct{canChooseFiles: true}
}
// SetOptions applies OpenFileDialogOptions to the dialog.
//
// dialog.SetOptions(&OpenFileDialogOptions{Title: "Open log", ShowHiddenFiles: true})
func (d *OpenFileDialogStruct) SetOptions(options *OpenFileDialogOptions) {
if options == nil {
return
}
d.mu.Lock()
d.title = options.Title
d.directory = options.Directory
d.filters = append([]FileFilter(nil), options.Filters...)
d.multipleAllowed = options.AllowsMultipleSelection
d.canChooseDirs = options.CanChooseDirectories
d.canChooseFiles = options.CanChooseFiles
d.showHidden = options.ShowHiddenFiles
d.mu.Unlock()
}
// SetTitle sets the dialog window title.
//
// dialog.SetTitle("Select configuration file")
func (d *OpenFileDialogStruct) SetTitle(title string) *OpenFileDialogStruct {
d.mu.Lock()
d.title = title
d.mu.Unlock()
return d
}
// SetDirectory sets the initial directory shown in the dialog.
//
// dialog.SetDirectory("/home/user/documents")
func (d *OpenFileDialogStruct) SetDirectory(directory string) *OpenFileDialogStruct {
d.mu.Lock()
d.directory = directory
d.mu.Unlock()
return d
}
// AddFilter appends a file filter to the dialog.
//
// dialog.AddFilter("Go source", "*.go")
func (d *OpenFileDialogStruct) AddFilter(displayName, pattern string) *OpenFileDialogStruct {
d.mu.Lock()
d.filters = append(d.filters, FileFilter{DisplayName: displayName, Pattern: pattern})
d.mu.Unlock()
return d
}
// SetAllowsMultipleSelection controls whether multiple files can be selected.
//
// dialog.SetAllowsMultipleSelection(true)
func (d *OpenFileDialogStruct) SetAllowsMultipleSelection(allow bool) *OpenFileDialogStruct {
d.mu.Lock()
d.multipleAllowed = allow
d.mu.Unlock()
return d
}
// SetSelectedFiles injects pre-selected files for stub testing.
//
// dialog.SetSelectedFiles([]string{"/tmp/a.txt", "/tmp/b.txt"})
func (d *OpenFileDialogStruct) SetSelectedFiles(paths []string) {
d.mu.Lock()
d.selectedFiles = append([]string(nil), paths...)
d.mu.Unlock()
}
// PromptForSingleSelection returns the first injected file path, or "" if none.
//
// path, err := dialog.PromptForSingleSelection()
// if err != nil { return err }
func (d *OpenFileDialogStruct) PromptForSingleSelection() (string, error) {
d.mu.RLock()
defer d.mu.RUnlock()
if len(d.selectedFiles) > 0 {
return d.selectedFiles[0], nil
}
return "", nil
}
// PromptForMultipleSelection returns all injected file paths.
//
// paths, err := dialog.PromptForMultipleSelection()
// for _, p := range paths { process(p) }
func (d *OpenFileDialogStruct) PromptForMultipleSelection() ([]string, error) {
d.mu.RLock()
defer d.mu.RUnlock()
return append([]string(nil), d.selectedFiles...), nil
}
// SaveFileDialogStruct is an in-memory save-file dialog.
//
// dialog := &SaveFileDialogStruct{}
// dialog.SetFilename("report.pdf")
// path, _ := dialog.PromptForSingleSelection()
type SaveFileDialogStruct struct {
mu sync.RWMutex
title string
directory string
filename string
filters []FileFilter
showHidden bool
selectedPath string
}
func newSaveFileDialog() *SaveFileDialogStruct {
return &SaveFileDialogStruct{}
}
// SetOptions applies SaveFileDialogOptions to the dialog.
//
// dialog.SetOptions(&SaveFileDialogOptions{Title: "Export", Filename: "data.json"})
func (d *SaveFileDialogStruct) SetOptions(options *SaveFileDialogOptions) {
if options == nil {
return
}
d.mu.Lock()
d.title = options.Title
d.directory = options.Directory
d.filename = options.Filename
d.filters = append([]FileFilter(nil), options.Filters...)
d.showHidden = options.ShowHiddenFiles
d.mu.Unlock()
}
// SetTitle sets the dialog window title.
//
// dialog.SetTitle("Export configuration")
func (d *SaveFileDialogStruct) SetTitle(title string) *SaveFileDialogStruct {
d.mu.Lock()
d.title = title
d.mu.Unlock()
return d
}
// SetDirectory sets the initial directory shown in the dialog.
//
// dialog.SetDirectory("/home/user/exports")
func (d *SaveFileDialogStruct) SetDirectory(directory string) *SaveFileDialogStruct {
d.mu.Lock()
d.directory = directory
d.mu.Unlock()
return d
}
// SetFilename sets the default filename shown in the dialog.
//
// dialog.SetFilename("backup-2026.tar.gz")
func (d *SaveFileDialogStruct) SetFilename(filename string) *SaveFileDialogStruct {
d.mu.Lock()
d.filename = filename
d.mu.Unlock()
return d
}
// AddFilter appends a file filter to the dialog.
//
// dialog.AddFilter("JSON files", "*.json")
func (d *SaveFileDialogStruct) AddFilter(displayName, pattern string) *SaveFileDialogStruct {
d.mu.Lock()
d.filters = append(d.filters, FileFilter{DisplayName: displayName, Pattern: pattern})
d.mu.Unlock()
return d
}
// SetSelectedPath injects the path returned by PromptForSingleSelection for stub testing.
//
// dialog.SetSelectedPath("/tmp/output.csv")
func (d *SaveFileDialogStruct) SetSelectedPath(path string) {
d.mu.Lock()
d.selectedPath = path
d.mu.Unlock()
}
// PromptForSingleSelection returns the injected save path, or "" if none.
//
// path, err := dialog.PromptForSingleSelection()
// if err != nil { return err }
func (d *SaveFileDialogStruct) PromptForSingleSelection() (string, error) {
d.mu.RLock()
defer d.mu.RUnlock()
return d.selectedPath, nil
}
// MessageButton represents a button in a message dialog.
type MessageButton struct {
Label string
IsDefault bool
IsCancel bool
}
// MessageDialog is an in-memory message dialog (info, question, warning, error).
//
// dialog := &MessageDialog{dialogType: InfoDialogType}
// dialog.SetTitle("Done").SetMessage("File saved successfully.")
// _ = dialog.Show()
type MessageDialog struct {
mu sync.RWMutex
dialogType DialogType
title string
message string
buttons []MessageButton
clickedButton string
}
func newMessageDialog(dialogType DialogType) *MessageDialog {
return &MessageDialog{dialogType: dialogType}
}
// SetTitle sets the dialog window title.
//
// dialog.SetTitle("Confirm deletion")
func (d *MessageDialog) SetTitle(title string) *MessageDialog {
d.mu.Lock()
d.title = title
d.mu.Unlock()
return d
}
// SetMessage sets the body text of the dialog.
//
// dialog.SetMessage("Are you sure you want to delete this file?")
func (d *MessageDialog) SetMessage(message string) *MessageDialog {
d.mu.Lock()
d.message = message
d.mu.Unlock()
return d
}
// AddButton appends a button to the dialog.
//
// dialog.AddButton("Yes").AddButton("No")
func (d *MessageDialog) AddButton(label string) *MessageDialog {
d.mu.Lock()
d.buttons = append(d.buttons, MessageButton{Label: label})
d.mu.Unlock()
return d
}
// SetDefaultButton marks the named button as the default action.
//
// dialog.SetDefaultButton("OK")
func (d *MessageDialog) SetDefaultButton(label string) *MessageDialog {
d.mu.Lock()
for index := range d.buttons {
d.buttons[index].IsDefault = d.buttons[index].Label == label
}
d.mu.Unlock()
return d
}
// SetCancelButton marks the named button as the cancel action.
//
// dialog.SetCancelButton("Cancel")
func (d *MessageDialog) SetCancelButton(label string) *MessageDialog {
d.mu.Lock()
for index := range d.buttons {
d.buttons[index].IsCancel = d.buttons[index].Label == label
}
d.mu.Unlock()
return d
}
// SetButtonClickedForStub injects which button was clicked for stub testing.
//
// dialog.SetButtonClickedForStub("Yes")
// result, _ := dialog.Show()
func (d *MessageDialog) SetButtonClickedForStub(label string) {
d.mu.Lock()
d.clickedButton = label
d.mu.Unlock()
}
// Show displays the dialog and returns the label of the clicked button.
//
// clicked, err := dialog.Show()
// if clicked == "Yes" { deleteFile() }
func (d *MessageDialog) Show() (string, error) {
d.mu.RLock()
defer d.mu.RUnlock()
return d.clickedButton, nil
}
// DialogManager manages dialog operations in-memory.
//
// manager := &DialogManager{}
// dialog := manager.Info().SetTitle("Done").SetMessage("Saved.")
type DialogManager struct {
mu sync.RWMutex
}
// OpenFile creates an open-file dialog.
//
// dialog := manager.OpenFile()
// dialog.SetTitle("Choose config")
// path, _ := dialog.PromptForSingleSelection()
func (dm *DialogManager) OpenFile() *OpenFileDialogStruct {
return newOpenFileDialog()
}
// OpenFileWithOptions creates an open-file dialog pre-configured from options.
//
// dialog := manager.OpenFileWithOptions(&OpenFileDialogOptions{Title: "Select log"})
func (dm *DialogManager) OpenFileWithOptions(options *OpenFileDialogOptions) *OpenFileDialogStruct {
dialog := newOpenFileDialog()
dialog.SetOptions(options)
return dialog
}
// SaveFile creates a save-file dialog.
//
// dialog := manager.SaveFile()
// dialog.SetFilename("export.csv")
// path, _ := dialog.PromptForSingleSelection()
func (dm *DialogManager) SaveFile() *SaveFileDialogStruct {
return newSaveFileDialog()
}
// SaveFileWithOptions creates a save-file dialog pre-configured from options.
//
// dialog := manager.SaveFileWithOptions(&SaveFileDialogOptions{Title: "Export data"})
func (dm *DialogManager) SaveFileWithOptions(options *SaveFileDialogOptions) *SaveFileDialogStruct {
dialog := newSaveFileDialog()
dialog.SetOptions(options)
return dialog
}
// Info creates an information message dialog.
//
// manager.Info().SetTitle("Done").SetMessage("File saved.").Show()
func (dm *DialogManager) Info() *MessageDialog {
return newMessageDialog(InfoDialogType)
}
// Question creates a question message dialog.
//
// manager.Question().SetTitle("Confirm").SetMessage("Delete file?").AddButton("Yes").AddButton("No").Show()
func (dm *DialogManager) Question() *MessageDialog {
return newMessageDialog(QuestionDialogType)
}
// Warning creates a warning message dialog.
//
// manager.Warning().SetTitle("Warning").SetMessage("Disk almost full.").Show()
func (dm *DialogManager) Warning() *MessageDialog {
return newMessageDialog(WarningDialogType)
}
// Error creates an error message dialog.
//
// manager.Error().SetTitle("Error").SetMessage("Operation failed.").Show()
func (dm *DialogManager) Error() *MessageDialog {
return newMessageDialog(ErrorDialogType)
}

View file

@ -0,0 +1,85 @@
package application
import "sync"
// EnvironmentInfo holds information about the host environment.
//
// info := manager.Info()
// if info.IsDarkMode { applyDarkTheme() }
type EnvironmentInfo struct {
OS string
Arch string
Debug bool
IsDarkMode bool
AccentColour string
PlatformInfo map[string]any
}
// EnvironmentManager tracks environment state in-memory.
//
// manager := &EnvironmentManager{}
// manager.SetDarkMode(true)
// dark := manager.IsDarkMode() // true
type EnvironmentManager struct {
mu sync.RWMutex
darkMode bool
accentColour string
operatingSystem string
architecture string
debugMode bool
}
// SetDarkMode sets the dark mode state used by IsDarkMode.
//
// manager.SetDarkMode(true)
func (em *EnvironmentManager) SetDarkMode(darkMode bool) {
em.mu.Lock()
em.darkMode = darkMode
em.mu.Unlock()
}
// IsDarkMode returns true when the environment is in dark mode.
//
// if manager.IsDarkMode() { applyDarkTheme() }
func (em *EnvironmentManager) IsDarkMode() bool {
em.mu.RLock()
defer em.mu.RUnlock()
return em.darkMode
}
// SetAccentColour sets the accent colour returned by GetAccentColor.
//
// manager.SetAccentColour("rgb(0,122,255)")
func (em *EnvironmentManager) SetAccentColour(colour string) {
em.mu.Lock()
em.accentColour = colour
em.mu.Unlock()
}
// GetAccentColor returns the stored accent colour, or the default blue if unset.
//
// colour := manager.GetAccentColor() // "rgb(0,122,255)"
func (em *EnvironmentManager) GetAccentColor() string {
em.mu.RLock()
defer em.mu.RUnlock()
if em.accentColour == "" {
return "rgb(0,122,255)"
}
return em.accentColour
}
// Info returns a snapshot of the current environment state.
//
// info := manager.Info()
// _ = info.OS // e.g. "linux"
func (em *EnvironmentManager) Info() EnvironmentInfo {
em.mu.RLock()
defer em.mu.RUnlock()
return EnvironmentInfo{
OS: em.operatingSystem,
Arch: em.architecture,
Debug: em.debugMode,
IsDarkMode: em.darkMode,
AccentColour: em.accentColour,
}
}

View file

@ -0,0 +1,212 @@
package application
import (
"sync"
"sync/atomic"
"github.com/wailsapp/wails/v3/pkg/events"
)
// ApplicationEventContext carries optional context for an application event.
//
// ctx := event.Context()
// _ = ctx // reserved for future platform-specific fields
type ApplicationEventContext struct{}
func newApplicationEventContext() *ApplicationEventContext {
return &ApplicationEventContext{}
}
// ApplicationEvent is emitted by the application layer for system-level events.
//
// manager.OnApplicationEvent(events.Mac.ApplicationShouldTerminate, func(e *ApplicationEvent) {
// e.Cancel()
// })
type ApplicationEvent struct {
Id uint
ctx *ApplicationEventContext
cancelled atomic.Bool
}
// Context returns the context attached to this application event.
//
// ctx := event.Context()
func (e *ApplicationEvent) Context() *ApplicationEventContext {
return e.ctx
}
// Cancel prevents further processing of this application event.
//
// event.Cancel()
func (e *ApplicationEvent) Cancel() {
e.cancelled.Store(true)
}
// IsCancelled reports whether Cancel has been called on this event.
//
// if event.IsCancelled() { return }
func (e *ApplicationEvent) IsCancelled() bool {
return e.cancelled.Load()
}
// CustomEvent is a named application-level event carrying arbitrary data.
//
// manager.Emit("user:login", userPayload)
type CustomEvent struct {
Name string `json:"name"`
Data any `json:"data"`
Sender string `json:"sender,omitempty"`
cancelled atomic.Bool
}
// Cancel prevents further processing of this custom event.
//
// event.Cancel()
func (e *CustomEvent) Cancel() {
e.cancelled.Store(true)
}
// IsCancelled reports whether Cancel has been called on this event.
//
// if event.IsCancelled() { return }
func (e *CustomEvent) IsCancelled() bool {
return e.cancelled.Load()
}
// customEventListener holds a callback and its remaining invocation count.
type customEventListener struct {
callback func(*CustomEvent)
counter int
}
// applicationEventListener holds a callback for an application event.
type applicationEventListener struct {
callback func(*ApplicationEvent)
}
// EventManager manages custom and application events in-memory.
//
// manager := &EventManager{}
// cancel := manager.On("data:ready", func(e *CustomEvent) { process(e.Data) })
// defer cancel()
type EventManager struct {
mu sync.RWMutex
customListeners map[string][]*customEventListener
appListeners map[uint][]*applicationEventListener
}
func newEventManager() *EventManager {
return &EventManager{
customListeners: make(map[string][]*customEventListener),
appListeners: make(map[uint][]*applicationEventListener),
}
}
// Emit fires a named custom event with optional data to all registered listeners.
// Returns true if the event was cancelled by a listener.
//
// cancelled := manager.Emit("file:saved", "/home/user/doc.txt")
// if cancelled { log("event cancelled") }
func (em *EventManager) Emit(name string, data ...any) bool {
event := &CustomEvent{Name: name}
switch len(data) {
case 0:
// no data
case 1:
event.Data = data[0]
default:
event.Data = data
}
em.mu.Lock()
listeners := append([]*customEventListener(nil), em.customListeners[name]...)
remaining := em.customListeners[name][:0]
for _, listener := range em.customListeners[name] {
if listener.counter < 0 {
remaining = append(remaining, listener)
} else {
listener.counter--
if listener.counter > 0 {
remaining = append(remaining, listener)
}
}
}
em.customListeners[name] = remaining
em.mu.Unlock()
for _, listener := range listeners {
if event.IsCancelled() {
break
}
listener.callback(event)
}
return event.IsCancelled()
}
// On registers a persistent listener for the named custom event.
// Returns a cancellation function that removes the listener.
//
// cancel := manager.On("theme:changed", func(e *CustomEvent) { applyTheme(e.Data) })
// defer cancel()
func (em *EventManager) On(name string, callback func(*CustomEvent)) func() {
listener := &customEventListener{callback: callback, counter: -1}
em.mu.Lock()
em.customListeners[name] = append(em.customListeners[name], listener)
em.mu.Unlock()
return func() {
em.mu.Lock()
defer em.mu.Unlock()
updated := em.customListeners[name][:0]
for _, existing := range em.customListeners[name] {
if existing != listener {
updated = append(updated, existing)
}
}
em.customListeners[name] = updated
}
}
// Off removes all listeners for the named custom event.
//
// manager.Off("theme:changed")
func (em *EventManager) Off(name string) {
em.mu.Lock()
delete(em.customListeners, name)
em.mu.Unlock()
}
// OnMultiple registers a listener for the named custom event that fires at most counter times.
//
// manager.OnMultiple("startup:phase", onPhase, 3)
func (em *EventManager) OnMultiple(name string, callback func(*CustomEvent), counter int) {
listener := &customEventListener{callback: callback, counter: counter}
em.mu.Lock()
em.customListeners[name] = append(em.customListeners[name], listener)
em.mu.Unlock()
}
// OnApplicationEvent registers a listener for application-level events.
// Returns a cancellation function that removes the listener.
//
// cancel := manager.OnApplicationEvent(events.Mac.ApplicationShouldTerminate, func(e *ApplicationEvent) {
// saveState()
// })
// defer cancel()
func (em *EventManager) OnApplicationEvent(eventType events.ApplicationEventType, callback func(*ApplicationEvent)) func() {
eventID := uint(eventType)
listener := &applicationEventListener{callback: callback}
em.mu.Lock()
em.appListeners[eventID] = append(em.appListeners[eventID], listener)
em.mu.Unlock()
return func() {
em.mu.Lock()
defer em.mu.Unlock()
updated := em.appListeners[eventID][:0]
for _, existing := range em.appListeners[eventID] {
if existing != listener {
updated = append(updated, existing)
}
}
em.appListeners[eventID] = updated
}
}

View file

@ -0,0 +1,71 @@
package application
import "sync"
// KeyBinding pairs an accelerator string with its registered callback.
//
// binding := &KeyBinding{Accelerator: "CmdOrCtrl+K", Callback: handler}
type KeyBinding struct {
Accelerator string
Callback func(window Window)
}
// KeyBindingManager stores and dispatches global key bindings.
//
// manager.Add("CmdOrCtrl+K", func(w Window) { w.Focus() })
// handled := manager.Process("CmdOrCtrl+K", currentWindow)
type KeyBindingManager struct {
mu sync.RWMutex
bindings map[string]func(window Window)
}
// Add registers a callback for the given accelerator string.
//
// manager.Add("CmdOrCtrl+Shift+P", func(w Window) { launchCommandPalette(w) })
func (m *KeyBindingManager) Add(accelerator string, callback func(window Window)) {
m.mu.Lock()
defer m.mu.Unlock()
if m.bindings == nil {
m.bindings = make(map[string]func(window Window))
}
m.bindings[accelerator] = callback
}
// Remove deregisters the callback for the given accelerator string.
//
// manager.Remove("CmdOrCtrl+Shift+P")
func (m *KeyBindingManager) Remove(accelerator string) {
m.mu.Lock()
defer m.mu.Unlock()
delete(m.bindings, accelerator)
}
// Process fires the callback for the given accelerator and returns true if handled.
//
// if manager.Process("CmdOrCtrl+K", window) { return }
func (m *KeyBindingManager) Process(accelerator string, window Window) bool {
m.mu.RLock()
callback, exists := m.bindings[accelerator]
m.mu.RUnlock()
if exists && callback != nil {
callback(window)
return true
}
return false
}
// GetAll returns a snapshot of all registered key bindings.
//
// for _, kb := range manager.GetAll() { log(kb.Accelerator) }
func (m *KeyBindingManager) GetAll() []*KeyBinding {
m.mu.RLock()
defer m.mu.RUnlock()
bindings := make([]*KeyBinding, 0, len(m.bindings))
for accelerator, callback := range m.bindings {
bindings = append(bindings, &KeyBinding{
Accelerator: accelerator,
Callback: callback,
})
}
return bindings
}

View file

@ -0,0 +1,304 @@
package application
// Role identifies a platform-specific menu role.
// It is the same underlying type as MenuRole so existing constants (AppMenu,
// FileMenu, EditMenu, ViewMenu, WindowMenu, HelpMenu) are valid Role values.
//
// quitItem := application.NewRole(application.Quit)
type Role = MenuRole
const (
// NoRole indicates no special platform role.
NoRole MenuRole = iota + 100
// ServicesMenu is the macOS Services sub-menu.
ServicesMenu
// SpeechMenu is the macOS Speech sub-menu.
SpeechMenu
// Hide hides the current application.
Hide
// HideOthers hides all other applications.
HideOthers
// UnHide shows all hidden applications.
UnHide
// Front brings all windows to front.
Front
// Undo triggers the standard Undo action.
Undo
// Redo triggers the standard Redo action.
Redo
// Cut triggers the standard Cut action.
Cut
// Copy triggers the standard Copy action.
Copy
// Paste triggers the standard Paste action.
Paste
// PasteAndMatchStyle pastes without source formatting.
PasteAndMatchStyle
// SelectAll triggers the standard Select All action.
SelectAll
// Delete triggers the standard Delete action.
Delete
// Quit quits the application.
Quit
// CloseWindow closes the focused window.
CloseWindow
// About opens the About panel.
About
// Reload reloads the current webview.
Reload
// ForceReload force-reloads the current webview.
ForceReload
// ToggleFullscreen toggles fullscreen mode.
ToggleFullscreen
// OpenDevTools opens the developer tools panel.
OpenDevTools
// ResetZoom resets the webview zoom level.
ResetZoom
// ZoomIn increases the webview zoom level.
ZoomIn
// ZoomOut decreases the webview zoom level.
ZoomOut
// Minimise minimises the focused window.
Minimise
// Zoom zooms the focused window (macOS).
Zoom
// FullScreen enters fullscreen (macOS).
FullScreen
// Print opens the print dialog.
Print
// PageLayout opens the page layout dialog.
PageLayout
// ShowAll shows all windows.
ShowAll
// BringAllToFront brings all windows to front.
BringAllToFront
// NewFile triggers the New File action.
NewFile
// Open triggers the Open action.
Open
// Save triggers the Save action.
Save
// SaveAs triggers the Save As action.
SaveAs
// StartSpeaking starts text-to-speech on selected text.
StartSpeaking
// StopSpeaking stops text-to-speech.
StopSpeaking
// Revert triggers the Revert action.
Revert
// Find triggers the Find action.
Find
// FindAndReplace triggers the Find and Replace action.
FindAndReplace
// FindNext finds the next match.
FindNext
// FindPrevious finds the previous match.
FindPrevious
// Help opens the application help.
Help
)
// NewMenuItem creates a new text menu item with the given label.
//
// item := application.NewMenuItem("Open File").SetAccelerator("CmdOrCtrl+O")
func NewMenuItem(label string) *MenuItem {
return &MenuItem{Label: label, Enabled: true}
}
// NewMenuItemSeparator creates a horizontal separator for use in menus.
//
// menu.Items = append(menu.Items, application.NewMenuItemSeparator())
func NewMenuItemSeparator() *MenuItem {
return &MenuItem{Label: "---"}
}
// NewMenuItemCheckbox creates a checkable menu item.
//
// item := application.NewMenuItemCheckbox("Show Toolbar", true)
func NewMenuItemCheckbox(label string, checked bool) *MenuItem {
return &MenuItem{Label: label, Checked: checked, Enabled: true}
}
// NewMenuItemRadio creates a radio-group menu item.
//
// item := application.NewMenuItemRadio("Small", false)
func NewMenuItemRadio(label string, checked bool) *MenuItem {
return &MenuItem{Label: label, Checked: checked, Enabled: true}
}
// NewSubMenuItem creates a menu item that opens a sub-menu.
//
// sub := application.NewSubMenuItem("Recent Files")
func NewSubMenuItem(label string) *MenuItem {
return &MenuItem{Label: label, Enabled: true}
}
// NewRole creates a menu item pre-configured for a platform role.
//
// quitItem := application.NewRole(application.Quit)
func NewRole(role Role) *MenuItem {
return &MenuItem{Label: roleLabel(role), Enabled: true}
}
// NewServicesMenu creates the macOS Services sub-menu item.
//
// servicesMenu := application.NewServicesMenu()
func NewServicesMenu() *MenuItem {
return NewSubMenuItem("Services")
}
// GetAccelerator returns the accelerator string currently set on the item.
//
// accel := item.GetAccelerator() // e.g. "CmdOrCtrl+S"
func (mi *MenuItem) GetAccelerator() string {
return mi.Accelerator
}
// roleLabel maps a Role constant to a human-readable label.
func roleLabel(role Role) string {
switch role {
case AppMenu:
return "App"
case EditMenu:
return "Edit"
case FileMenu:
return "File"
case ViewMenu:
return "View"
case ServicesMenu:
return "Services"
case SpeechMenu:
return "Speech"
case WindowMenu:
return "Window"
case HelpMenu:
return "Help"
case Hide:
return "Hide"
case HideOthers:
return "Hide Others"
case UnHide:
return "Show All"
case Front:
return "Bring All to Front"
case Undo:
return "Undo"
case Redo:
return "Redo"
case Cut:
return "Cut"
case Copy:
return "Copy"
case Paste:
return "Paste"
case PasteAndMatchStyle:
return "Paste and Match Style"
case SelectAll:
return "Select All"
case Delete:
return "Delete"
case Quit:
return "Quit"
case CloseWindow:
return "Close Window"
case About:
return "About"
case Reload:
return "Reload"
case ForceReload:
return "Force Reload"
case ToggleFullscreen:
return "Toggle Full Screen"
case OpenDevTools:
return "Open Developer Tools"
case ResetZoom:
return "Reset Zoom"
case ZoomIn:
return "Zoom In"
case ZoomOut:
return "Zoom Out"
case Minimise:
return "Minimise"
case Zoom:
return "Zoom"
case FullScreen:
return "Full Screen"
case Print:
return "Print"
case PageLayout:
return "Page Layout"
case ShowAll:
return "Show All"
case BringAllToFront:
return "Bring All to Front"
case NewFile:
return "New"
case Open:
return "Open"
case Save:
return "Save"
case SaveAs:
return "Save As"
case StartSpeaking:
return "Start Speaking"
case StopSpeaking:
return "Stop Speaking"
case Revert:
return "Revert"
case Find:
return "Find"
case FindAndReplace:
return "Find and Replace"
case FindNext:
return "Find Next"
case FindPrevious:
return "Find Previous"
case Help:
return "Help"
default:
return ""
}
}

View file

@ -0,0 +1,146 @@
package application
import "sync"
// Screen describes a physical or logical display.
//
// primary := manager.GetPrimary()
// if primary != nil { useSize(primary.Size) }
type Screen struct {
ID string // A unique identifier for the display
Name string // The name of the display
ScaleFactor float32 // The scale factor of the display (DPI/96)
X int // The x-coordinate of the top-left corner of the display
Y int // The y-coordinate of the top-left corner of the display
Size Size // The logical size of the display
Bounds Rect // The logical bounds of the display
PhysicalBounds Rect // The physical bounds before scaling
WorkArea Rect // The work area (excluding taskbars etc.)
PhysicalWorkArea Rect // The physical work area before scaling
IsPrimary bool // Whether this is the primary display
Rotation float32 // The rotation of the display in degrees
}
// Rect is an axis-aligned rectangle in either logical or physical pixels.
//
// if rect.Contains(Point{X: 100, Y: 200}) { ... }
type Rect struct {
X int
Y int
Width int
Height int
}
// Point is a two-dimensional coordinate.
//
// centre := Point{X: bounds.X + bounds.Width/2, Y: bounds.Y + bounds.Height/2}
type Point struct {
X int
Y int
}
// Size holds the width and height dimensions of a display or region.
//
// if size.Width > 1920 { useHiResLayout() }
type Size struct {
Width int
Height int
}
// Origin returns the top-left corner of the rectangle.
func (r Rect) Origin() Point {
return Point{X: r.X, Y: r.Y}
}
// Corner returns the exclusive bottom-right corner (X+Width, Y+Height).
func (r Rect) Corner() Point {
return Point{X: r.X + r.Width, Y: r.Y + r.Height}
}
// IsEmpty reports whether the rectangle has non-positive area.
func (r Rect) IsEmpty() bool {
return r.Width <= 0 || r.Height <= 0
}
// Contains reports whether point pt lies within the rectangle.
//
// if bounds.Contains(cursorPoint) { highlightWindow() }
func (r Rect) Contains(pt Point) bool {
return pt.X >= r.X && pt.X < r.X+r.Width && pt.Y >= r.Y && pt.Y < r.Y+r.Height
}
// RectSize returns the dimensions of the rectangle as a Size value.
func (r Rect) RectSize() Size {
return Size{Width: r.Width, Height: r.Height}
}
// ScreenManager tracks connected screens and the active screen.
//
// manager.SetScreens(detectedScreens)
// primary := manager.GetPrimary()
type ScreenManager struct {
mu sync.RWMutex
screens []*Screen
current *Screen
primary *Screen
}
// SetScreens replaces the full list of known screens and recomputes primary.
//
// manager.SetScreens(platformDetectedScreens)
func (m *ScreenManager) SetScreens(screens []*Screen) {
m.mu.Lock()
defer m.mu.Unlock()
m.screens = screens
m.primary = nil
for _, screen := range screens {
if screen.IsPrimary {
m.primary = screen
break
}
}
if m.current == nil && m.primary != nil {
m.current = m.primary
}
}
// SetCurrent marks the given screen as the currently active one.
//
// manager.SetCurrent(screenUnderPointer)
func (m *ScreenManager) SetCurrent(screen *Screen) {
m.mu.Lock()
m.current = screen
m.mu.Unlock()
}
// GetAll returns all registered screens.
//
// for _, s := range manager.GetAll() { renderMonitorPreview(s) }
func (m *ScreenManager) GetAll() []*Screen {
m.mu.RLock()
defer m.mu.RUnlock()
out := make([]*Screen, len(m.screens))
copy(out, m.screens)
return out
}
// GetPrimary returns the primary screen, or nil if none has been registered.
//
// primary := manager.GetPrimary()
func (m *ScreenManager) GetPrimary() *Screen {
m.mu.RLock()
defer m.mu.RUnlock()
return m.primary
}
// GetCurrent returns the most recently active screen, or the primary if unset.
//
// current := manager.GetCurrent()
func (m *ScreenManager) GetCurrent() *Screen {
m.mu.RLock()
defer m.mu.RUnlock()
if m.current != nil {
return m.current
}
return m.primary
}

View file

@ -0,0 +1,105 @@
package application
import (
"context"
"reflect"
)
// ServiceOptions provides optional parameters when constructing a Service.
//
// svc := NewServiceWithOptions(&myImpl, ServiceOptions{Name: "MyService", Route: "/api"})
type ServiceOptions struct {
// Name overrides the service name used for logging and debugging.
// When empty the name is derived from the ServiceName interface or the type name.
Name string
// Route mounts the service on the internal asset server at this prefix
// if the service instance implements http.Handler.
Route string
// MarshalError serialises error values returned by service methods to JSON.
// A nil return falls back to the globally configured error handler.
MarshalError func(error) []byte
}
// DefaultServiceOptions holds the default values used when no ServiceOptions
// are passed to NewService.
var DefaultServiceOptions = ServiceOptions{}
// Service wraps a bound type instance for registration with the application.
// The zero value is invalid; obtain valid values via NewService or NewServiceWithOptions.
//
// svc := NewService(&myStruct)
// app.RegisterService(svc)
type Service struct {
instance any
options ServiceOptions
}
// NewService wraps instance in a Service using DefaultServiceOptions.
//
// svc := NewService(&MyController{})
func NewService[T any](instance *T) Service {
return Service{instance: instance, options: DefaultServiceOptions}
}
// NewServiceWithOptions wraps instance in a Service with explicit options.
//
// svc := NewServiceWithOptions(&MyController{}, ServiceOptions{Name: "ctrl", Route: "/ctrl"})
func NewServiceWithOptions[T any](instance *T, options ServiceOptions) Service {
service := NewService(instance)
service.options = options
return service
}
// Instance returns the underlying pointer originally passed to NewService.
//
// ctrl := svc.Instance().(*MyController)
func (s Service) Instance() any {
return s.instance
}
// Options returns the ServiceOptions associated with this service.
func (s Service) Options() ServiceOptions {
return s.options
}
// ServiceName is an optional interface that service instances may implement
// to provide a human-readable name for logging and debugging.
//
// func (s *MyService) ServiceName() string { return "MyService" }
type ServiceName interface {
ServiceName() string
}
// ServiceStartup is an optional interface for service initialisation.
// Called during application startup in registration order.
// A non-nil return aborts startup and surfaces the error to the caller.
//
// func (s *MyService) ServiceStartup(ctx context.Context, opts ServiceOptions) error {
// return s.connect(ctx)
// }
type ServiceStartup interface {
ServiceStartup(ctx context.Context, options ServiceOptions) error
}
// ServiceShutdown is an optional interface for service cleanup.
// Called during application shutdown in reverse registration order,
// after all user-provided shutdown hooks have run.
//
// func (s *MyService) ServiceShutdown() error { return s.db.Close() }
type ServiceShutdown interface {
ServiceShutdown() error
}
// getServiceName resolves the display name for a service using, in order:
// the explicit options name, the ServiceName interface, and the reflect type name.
func getServiceName(service Service) string {
if service.options.Name != "" {
return service.options.Name
}
if named, ok := service.Instance().(ServiceName); ok {
return named.ServiceName()
}
return reflect.TypeOf(service.Instance()).Elem().String()
}

View file

@ -0,0 +1,574 @@
package application
import "github.com/wailsapp/wails/v3/pkg/events"
// WindowState represents the visible state of a window.
//
// opts := application.WebviewWindowOptions{StartState: application.WindowStateMaximised}
type WindowState int
const (
// WindowStateNormal is the default windowed state.
WindowStateNormal WindowState = iota
// WindowStateMinimised is the minimised (iconified) state.
WindowStateMinimised
// WindowStateMaximised fills the available work area.
WindowStateMaximised
// WindowStateFullscreen occupies the full screen.
WindowStateFullscreen
)
// WindowStartPosition controls where a window first appears.
//
// opts := application.WebviewWindowOptions{InitialPosition: application.WindowCentered}
type WindowStartPosition int
const (
// WindowCentered places the window at the centre of the screen.
WindowCentered WindowStartPosition = 0
// WindowXY places the window at the coordinates given by X and Y.
WindowXY WindowStartPosition = 1
)
// BackgroundType determines how the window background is rendered.
//
// opts := application.WebviewWindowOptions{BackgroundType: application.BackgroundTypeTranslucent}
type BackgroundType int
const (
// BackgroundTypeSolid renders a solid opaque background.
BackgroundTypeSolid BackgroundType = iota
// BackgroundTypeTransparent renders a fully transparent background.
BackgroundTypeTransparent
// BackgroundTypeTranslucent renders a frosted/blur translucent background.
BackgroundTypeTranslucent
)
// NewRGB constructs an opaque RGBA value from red, green, blue components.
//
// colour := application.NewRGB(0xff, 0x00, 0x00) // red
func NewRGB(red, green, blue uint8) RGBA {
return RGBA{Red: red, Green: green, Blue: blue, Alpha: 255}
}
// NewRGBPtr encodes red, green, blue as a packed *uint32 in 0x00BBGGRR order.
//
// ptr := application.NewRGBPtr(0xff, 0x80, 0x00)
func NewRGBPtr(red, green, blue uint8) *uint32 {
value := uint32(red) | uint32(green)<<8 | uint32(blue)<<16
return &value
}
/******* Windows Options *******/
// BackdropType selects the translucent backdrop style on Windows 11.
//
// opts.Windows = application.WindowsWindow{BackdropType: application.Mica}
type BackdropType int32
const (
// Auto lets the system choose the best backdrop.
Auto BackdropType = 0
// None disables the translucent backdrop.
None BackdropType = 1
// Mica applies the Mica material (Windows 11 22H2+).
Mica BackdropType = 2
// Acrylic applies the Acrylic blur-behind material.
Acrylic BackdropType = 3
// Tabbed applies the Tabbed/MICA-Alt material.
Tabbed BackdropType = 4
)
// CoreWebView2PermissionKind enumerates the types of WebView2 permissions.
type CoreWebView2PermissionKind uint32
const (
CoreWebView2PermissionKindUnknownPermission CoreWebView2PermissionKind = iota
CoreWebView2PermissionKindMicrophone
CoreWebView2PermissionKindCamera
CoreWebView2PermissionKindGeolocation
CoreWebView2PermissionKindNotifications
CoreWebView2PermissionKindOtherSensors
CoreWebView2PermissionKindClipboardRead
)
// CoreWebView2PermissionState enumerates the allowed states for a WebView2 permission.
type CoreWebView2PermissionState uint32
const (
CoreWebView2PermissionStateDefault CoreWebView2PermissionState = iota
CoreWebView2PermissionStateAllow
CoreWebView2PermissionStateDeny
)
// Theme selects between the system default, dark, and light UI themes on Windows.
//
// opts.Windows = application.WindowsWindow{Theme: application.Dark}
type Theme int
const (
// SystemDefault follows the OS theme and reacts to changes.
SystemDefault Theme = 0
// Dark forces the dark theme.
Dark Theme = 1
// Light forces the light theme.
Light Theme = 2
)
// WindowTheme defines colour overrides for a single window activity state.
//
// wt := &application.WindowTheme{TitleBarColour: application.NewRGBPtr(0x1e, 0x1e, 0x1e)}
type WindowTheme struct {
// BorderColour is the colour of the window border (0x00BBGGRR).
BorderColour *uint32
// TitleBarColour is the colour of the title bar (0x00BBGGRR).
TitleBarColour *uint32
// TitleTextColour is the colour of the title text (0x00BBGGRR).
TitleTextColour *uint32
}
// TextTheme defines foreground and background colours for a text element.
type TextTheme struct {
// Text is the foreground colour.
Text *uint32
// Background is the background colour.
Background *uint32
}
// MenuBarTheme defines colours for a menu bar in default, hovered, and selected states.
type MenuBarTheme struct {
// Default is the theme used when the item is neither hovered nor selected.
Default *TextTheme
// Hover is the theme used when the pointer is over the item.
Hover *TextTheme
// Selected is the theme used when the item is selected.
Selected *TextTheme
}
// ThemeSettings defines custom colours used in dark or light mode.
// Colour values use packed 0x00BBGGRR encoding — use NewRGBPtr to construct them.
//
// ts := application.ThemeSettings{
// DarkModeActive: &application.WindowTheme{TitleBarColour: application.NewRGBPtr(0x1e, 0x1e, 0x2e)},
// }
type ThemeSettings struct {
// DarkModeActive applies when the window is active in dark mode.
DarkModeActive *WindowTheme
// DarkModeInactive applies when the window is inactive in dark mode.
DarkModeInactive *WindowTheme
// LightModeActive applies when the window is active in light mode.
LightModeActive *WindowTheme
// LightModeInactive applies when the window is inactive in light mode.
LightModeInactive *WindowTheme
// DarkModeMenuBar applies to the menu bar in dark mode.
DarkModeMenuBar *MenuBarTheme
// LightModeMenuBar applies to the menu bar in light mode.
LightModeMenuBar *MenuBarTheme
}
// WindowsWindow contains Windows-specific window configuration.
//
// opts.Windows = application.WindowsWindow{BackdropType: application.Mica, Theme: application.Dark}
type WindowsWindow struct {
// BackdropType selects the translucent material. Requires Windows 11 22621+.
// Only used when BackgroundType is BackgroundTypeTranslucent.
// Default: Auto
BackdropType BackdropType
// DisableIcon removes the application icon from the title bar.
// Default: false
DisableIcon bool
// Theme selects between dark, light, or system-default title bar styling.
// Default: SystemDefault
Theme Theme
// CustomTheme overrides colours for dark/light active/inactive states.
// Default: zero value (no override)
CustomTheme ThemeSettings
// DisableFramelessWindowDecorations suppresses Aero shadow and rounded corners
// when the window is frameless. Rounded corners require Windows 11.
// Default: false
DisableFramelessWindowDecorations bool
// WindowMask sets the window shape via a PNG with an alpha channel.
// Default: nil
WindowMask []byte
// WindowMaskDraggable allows the window to be dragged via the mask area.
// Default: false
WindowMaskDraggable bool
// ResizeDebounceMS debounces WebView2 redraws during resize.
// Default: 0
ResizeDebounceMS uint16
// WindowDidMoveDebounceMS debounces the WindowDidMove event.
// Default: 0
WindowDidMoveDebounceMS uint16
// EventMapping translates platform window events to common event types.
// Default: nil
EventMapping map[events.WindowEventType]events.WindowEventType
// HiddenOnTaskbar excludes the window from the taskbar.
// Default: false
HiddenOnTaskbar bool
// EnableSwipeGestures enables horizontal swipe gestures.
// Default: false
EnableSwipeGestures bool
// Menu is the window-level menu.
Menu *Menu
// Permissions configures WebView2 permission grants.
// Default: nil (system defaults apply)
Permissions map[CoreWebView2PermissionKind]CoreWebView2PermissionState
// ExStyle is the extended window style flags (WS_EX_*).
ExStyle int
// GeneralAutofillEnabled enables general autofill in WebView2.
GeneralAutofillEnabled bool
// PasswordAutosaveEnabled enables password autosave in WebView2.
PasswordAutosaveEnabled bool
}
/****** Mac Options *******/
// MacBackdrop controls the translucency of the macOS window background.
//
// opts.Mac = application.MacWindow{Backdrop: application.MacBackdropTranslucent}
type MacBackdrop int
const (
// MacBackdropNormal renders a standard opaque background.
MacBackdropNormal MacBackdrop = iota
// MacBackdropTransparent renders a fully transparent background.
MacBackdropTransparent
// MacBackdropTranslucent renders a frosted vibrancy background.
MacBackdropTranslucent
// MacBackdropLiquidGlass applies Apple's Liquid Glass effect (macOS 15+,
// falls back to translucent on earlier releases).
MacBackdropLiquidGlass
)
// MacToolbarStyle controls the toolbar layout relative to the title bar.
//
// opts.Mac.TitleBar.ToolbarStyle = application.MacToolbarStyleUnified
type MacToolbarStyle int
const (
// MacToolbarStyleAutomatic lets the system decide based on configuration.
MacToolbarStyleAutomatic MacToolbarStyle = iota
// MacToolbarStyleExpanded shows the toolbar below the title bar.
MacToolbarStyleExpanded
// MacToolbarStylePreference shows the toolbar below the title bar with
// equal-width items where possible.
MacToolbarStylePreference
// MacToolbarStyleUnified merges the title bar and toolbar into one row.
MacToolbarStyleUnified
// MacToolbarStyleUnifiedCompact is like Unified but with reduced margins.
MacToolbarStyleUnifiedCompact
)
// MacLiquidGlassStyle defines the appearance of the Liquid Glass effect.
type MacLiquidGlassStyle int
const (
// LiquidGlassStyleAutomatic lets the system choose the best style.
LiquidGlassStyleAutomatic MacLiquidGlassStyle = iota
// LiquidGlassStyleLight uses a light glass appearance.
LiquidGlassStyleLight
// LiquidGlassStyleDark uses a dark glass appearance.
LiquidGlassStyleDark
// LiquidGlassStyleVibrant uses an enhanced vibrant glass appearance.
LiquidGlassStyleVibrant
)
// NSVisualEffectMaterial mirrors NSVisualEffectMaterial from the macOS SDK.
type NSVisualEffectMaterial int
const (
NSVisualEffectMaterialAppearanceBased NSVisualEffectMaterial = 0
NSVisualEffectMaterialLight NSVisualEffectMaterial = 1
NSVisualEffectMaterialDark NSVisualEffectMaterial = 2
NSVisualEffectMaterialTitlebar NSVisualEffectMaterial = 3
NSVisualEffectMaterialSelection NSVisualEffectMaterial = 4
NSVisualEffectMaterialMenu NSVisualEffectMaterial = 5
NSVisualEffectMaterialPopover NSVisualEffectMaterial = 6
NSVisualEffectMaterialSidebar NSVisualEffectMaterial = 7
NSVisualEffectMaterialHeaderView NSVisualEffectMaterial = 10
NSVisualEffectMaterialSheet NSVisualEffectMaterial = 11
NSVisualEffectMaterialWindowBackground NSVisualEffectMaterial = 12
NSVisualEffectMaterialHUDWindow NSVisualEffectMaterial = 13
NSVisualEffectMaterialFullScreenUI NSVisualEffectMaterial = 15
NSVisualEffectMaterialToolTip NSVisualEffectMaterial = 17
NSVisualEffectMaterialContentBackground NSVisualEffectMaterial = 18
NSVisualEffectMaterialUnderWindowBackground NSVisualEffectMaterial = 21
NSVisualEffectMaterialUnderPageBackground NSVisualEffectMaterial = 22
// NSVisualEffectMaterialAuto selects the material automatically based on Style.
NSVisualEffectMaterialAuto NSVisualEffectMaterial = -1
)
// MacLiquidGlass configures the Liquid Glass visual effect (macOS 15+).
//
// opts.Mac.LiquidGlass = application.MacLiquidGlass{Style: application.LiquidGlassStyleDark}
type MacLiquidGlass struct {
// Style of the glass effect.
Style MacLiquidGlassStyle
// Material for the NSVisualEffectView fallback.
// Use NSVisualEffectMaterialAuto for automatic selection based on Style.
Material NSVisualEffectMaterial
// CornerRadius specifies the corner radius in points (0 for square corners).
CornerRadius float64
// TintColor adds an optional colour tint to the glass (nil for no tint).
TintColor *RGBA
// GroupID merges multiple glass windows into a single visual group.
GroupID string
// GroupSpacing is the spacing between grouped glass elements in points.
GroupSpacing float64
}
// MacAppearanceType selects a Cocoa NSAppearance for the window.
//
// opts.Mac = application.MacWindow{Appearance: application.NSAppearanceNameDarkAqua}
type MacAppearanceType string
const (
// DefaultAppearance follows the system setting.
DefaultAppearance MacAppearanceType = ""
// NSAppearanceNameAqua is the standard light system appearance.
NSAppearanceNameAqua MacAppearanceType = "NSAppearanceNameAqua"
// NSAppearanceNameDarkAqua is the standard dark system appearance.
NSAppearanceNameDarkAqua MacAppearanceType = "NSAppearanceNameDarkAqua"
// NSAppearanceNameVibrantLight is the light vibrant appearance.
NSAppearanceNameVibrantLight MacAppearanceType = "NSAppearanceNameVibrantLight"
// NSAppearanceNameAccessibilityHighContrastAqua is high-contrast light.
NSAppearanceNameAccessibilityHighContrastAqua MacAppearanceType = "NSAppearanceNameAccessibilityHighContrastAqua"
// NSAppearanceNameAccessibilityHighContrastDarkAqua is high-contrast dark.
NSAppearanceNameAccessibilityHighContrastDarkAqua MacAppearanceType = "NSAppearanceNameAccessibilityHighContrastDarkAqua"
// NSAppearanceNameAccessibilityHighContrastVibrantLight is high-contrast light vibrant.
NSAppearanceNameAccessibilityHighContrastVibrantLight MacAppearanceType = "NSAppearanceNameAccessibilityHighContrastVibrantLight"
// NSAppearanceNameAccessibilityHighContrastVibrantDark is high-contrast dark vibrant.
NSAppearanceNameAccessibilityHighContrastVibrantDark MacAppearanceType = "NSAppearanceNameAccessibilityHighContrastVibrantDark"
)
// MacWindowLevel controls the z-order stacking group of the window.
//
// opts.Mac = application.MacWindow{WindowLevel: application.MacWindowLevelFloating}
type MacWindowLevel string
const (
MacWindowLevelNormal MacWindowLevel = "normal"
MacWindowLevelFloating MacWindowLevel = "floating"
MacWindowLevelTornOffMenu MacWindowLevel = "tornOffMenu"
MacWindowLevelModalPanel MacWindowLevel = "modalPanel"
MacWindowLevelMainMenu MacWindowLevel = "mainMenu"
MacWindowLevelStatus MacWindowLevel = "status"
MacWindowLevelPopUpMenu MacWindowLevel = "popUpMenu"
MacWindowLevelScreenSaver MacWindowLevel = "screenSaver"
)
// MacWindowCollectionBehavior controls how the window participates in macOS
// Spaces and fullscreen. Values correspond to NSWindowCollectionBehavior bits
// and may be combined with bitwise OR.
//
// opts.Mac.CollectionBehavior = application.MacWindowCollectionBehaviorCanJoinAllSpaces |
// application.MacWindowCollectionBehaviorFullScreenAuxiliary
type MacWindowCollectionBehavior int
const (
// MacWindowCollectionBehaviorDefault uses FullScreenPrimary for backwards compatibility.
MacWindowCollectionBehaviorDefault MacWindowCollectionBehavior = 0
// MacWindowCollectionBehaviorCanJoinAllSpaces shows the window on all Spaces.
MacWindowCollectionBehaviorCanJoinAllSpaces MacWindowCollectionBehavior = 1 << 0
// MacWindowCollectionBehaviorMoveToActiveSpace moves the window to the active Space when shown.
MacWindowCollectionBehaviorMoveToActiveSpace MacWindowCollectionBehavior = 1 << 1
// MacWindowCollectionBehaviorManaged is the default managed window behaviour.
MacWindowCollectionBehaviorManaged MacWindowCollectionBehavior = 1 << 2
// MacWindowCollectionBehaviorTransient marks the window as temporary.
MacWindowCollectionBehaviorTransient MacWindowCollectionBehavior = 1 << 3
// MacWindowCollectionBehaviorStationary keeps the window stationary during Space switches.
MacWindowCollectionBehaviorStationary MacWindowCollectionBehavior = 1 << 4
// MacWindowCollectionBehaviorParticipatesInCycle includes the window in Cmd+` cycling.
MacWindowCollectionBehaviorParticipatesInCycle MacWindowCollectionBehavior = 1 << 5
// MacWindowCollectionBehaviorIgnoresCycle excludes the window from Cmd+` cycling.
MacWindowCollectionBehaviorIgnoresCycle MacWindowCollectionBehavior = 1 << 6
// MacWindowCollectionBehaviorFullScreenPrimary allows the window to enter fullscreen.
MacWindowCollectionBehaviorFullScreenPrimary MacWindowCollectionBehavior = 1 << 7
// MacWindowCollectionBehaviorFullScreenAuxiliary allows the window to overlay fullscreen apps.
MacWindowCollectionBehaviorFullScreenAuxiliary MacWindowCollectionBehavior = 1 << 8
// MacWindowCollectionBehaviorFullScreenNone prevents the window from entering fullscreen (10.7+).
MacWindowCollectionBehaviorFullScreenNone MacWindowCollectionBehavior = 1 << 9
// MacWindowCollectionBehaviorFullScreenAllowsTiling allows side-by-side tiling (10.11+).
MacWindowCollectionBehaviorFullScreenAllowsTiling MacWindowCollectionBehavior = 1 << 11
// MacWindowCollectionBehaviorFullScreenDisallowsTiling prevents tiling in fullscreen (10.11+).
MacWindowCollectionBehaviorFullScreenDisallowsTiling MacWindowCollectionBehavior = 1 << 12
)
// MacWebviewPreferences configures Safari-level webview behaviour on macOS.
type MacWebviewPreferences struct {
// TabFocusesLinks enables keyboard navigation to links via Tab.
TabFocusesLinks bool
// TextInteractionEnabled allows the user to select and interact with text.
TextInteractionEnabled bool
// FullscreenEnabled allows the webview to enter fullscreen.
FullscreenEnabled bool
// AllowsBackForwardNavigationGestures enables horizontal swipe for navigation.
AllowsBackForwardNavigationGestures bool
}
// MacTitleBar configures the macOS title bar appearance.
//
// opts.Mac = application.MacWindow{TitleBar: application.MacTitleBarHiddenInset}
type MacTitleBar struct {
// AppearsTransparent makes the title bar background transparent.
AppearsTransparent bool
// Hide removes the title bar entirely.
Hide bool
// HideTitle omits the window title text.
HideTitle bool
// FullSizeContent extends the content area behind the title bar.
FullSizeContent bool
// UseToolbar renders a toolbar in place of the standard title bar.
UseToolbar bool
// HideToolbarSeparator removes the separator line below the toolbar.
HideToolbarSeparator bool
// ShowToolbarWhenFullscreen keeps the toolbar visible in fullscreen mode.
ShowToolbarWhenFullscreen bool
// ToolbarStyle controls how the toolbar relates to the title bar.
ToolbarStyle MacToolbarStyle
}
// MacTitleBarDefault is the stock macOS title bar with all decorations visible.
var MacTitleBarDefault = MacTitleBar{}
// MacTitleBarHidden hides the title text and extends content behind the title bar,
// while keeping the traffic-light window controls visible.
var MacTitleBarHidden = MacTitleBar{
AppearsTransparent: true,
HideTitle: true,
FullSizeContent: true,
}
// MacTitleBarHiddenInset is like MacTitleBarHidden but uses an inset toolbar so the
// traffic lights sit slightly further from the window edge.
var MacTitleBarHiddenInset = MacTitleBar{
AppearsTransparent: true,
HideTitle: true,
FullSizeContent: true,
UseToolbar: true,
HideToolbarSeparator: true,
}
// MacTitleBarHiddenInsetUnified is like MacTitleBarHiddenInset but merges the toolbar
// and title bar into a single unified row.
var MacTitleBarHiddenInsetUnified = MacTitleBar{
AppearsTransparent: true,
HideTitle: true,
FullSizeContent: true,
UseToolbar: true,
HideToolbarSeparator: true,
ToolbarStyle: MacToolbarStyleUnified,
}
// MacWindow contains macOS-specific window configuration.
//
// opts.Mac = application.MacWindow{
// Backdrop: application.MacBackdropTranslucent,
// TitleBar: application.MacTitleBarHiddenInset,
// Appearance: application.NSAppearanceNameDarkAqua,
// }
type MacWindow struct {
// Backdrop controls the translucency of the window background.
Backdrop MacBackdrop
// DisableShadow removes the drop shadow cast by the window.
DisableShadow bool
// TitleBar configures the title bar appearance.
TitleBar MacTitleBar
// Appearance sets a specific NSAppearance for the window.
Appearance MacAppearanceType
// InvisibleTitleBarHeight sets the height (in points) of a draggable but
// invisible title bar region at the top of the content area.
InvisibleTitleBarHeight int
// EventMapping translates platform window events to common event types.
EventMapping map[events.WindowEventType]events.WindowEventType
// EnableFraudulentWebsiteWarnings shows browser-level phishing warnings.
// Default: false
EnableFraudulentWebsiteWarnings bool
// WebviewPreferences configures Safari webview-level preferences.
WebviewPreferences MacWebviewPreferences
// WindowLevel controls the z-order stacking group of the window.
WindowLevel MacWindowLevel
// CollectionBehavior controls how the window interacts with Spaces and fullscreen.
CollectionBehavior MacWindowCollectionBehavior
// LiquidGlass configures the Liquid Glass visual effect (macOS 15+).
LiquidGlass MacLiquidGlass
}
/******** Linux Options ********/
// WebviewGpuPolicy controls hardware acceleration for the Linux webview.
//
// opts.Linux = application.LinuxWindow{WebviewGpuPolicy: application.WebviewGpuPolicyAlways}
type WebviewGpuPolicy int
const (
// WebviewGpuPolicyAlways always enables hardware acceleration.
WebviewGpuPolicyAlways WebviewGpuPolicy = iota
// WebviewGpuPolicyOnDemand enables acceleration as requested by web content.
WebviewGpuPolicyOnDemand
// WebviewGpuPolicyNever always disables hardware acceleration.
WebviewGpuPolicyNever
)
// LinuxMenuStyle controls how the application menu is displayed on Linux (GTK4 only).
// On GTK3 builds this option is ignored and MenuBar style is always used.
//
// opts.Linux = application.LinuxWindow{MenuStyle: application.LinuxMenuStylePrimaryMenu}
type LinuxMenuStyle int
const (
// LinuxMenuStyleMenuBar shows a traditional menu bar below the title bar.
LinuxMenuStyleMenuBar LinuxMenuStyle = iota
// LinuxMenuStylePrimaryMenu shows a primary menu button in the header bar (GNOME style).
LinuxMenuStylePrimaryMenu
)
// LinuxWindow contains Linux-specific window configuration.
//
// opts.Linux = application.LinuxWindow{
// WindowIsTranslucent: true,
// WebviewGpuPolicy: application.WebviewGpuPolicyAlways,
// }
type LinuxWindow struct {
// Icon is the window icon shown when the window is minimised.
// Provide PNG-encoded image data.
Icon []byte
// WindowIsTranslucent makes the window background transparent.
WindowIsTranslucent bool
// WebviewGpuPolicy sets the hardware acceleration policy for the webview.
// Defaults to WebviewGpuPolicyNever when LinuxWindow is nil in options.
WebviewGpuPolicy WebviewGpuPolicy
// WindowDidMoveDebounceMS is the debounce time in milliseconds for the
// WindowDidMove event.
WindowDidMoveDebounceMS uint16
// Menu is the window-level menu.
Menu *Menu
// MenuStyle controls how the menu is displayed (GTK4 only; ignored on GTK3).
MenuStyle LinuxMenuStyle
}

View file

@ -0,0 +1,240 @@
package application
import (
"unsafe"
"github.com/wailsapp/wails/v3/pkg/events"
)
// Window is the interface satisfied by both WebviewWindow and BrowserWindow.
// All methods mirror the Wails v3 Window interface exactly, including unexported
// platform hooks required for internal dispatch.
//
// var w Window = app.Window.NewWithOptions(opts)
// w.SetTitle("My App").Show()
type Window interface {
// Identity
// w.ID() → 1
ID() uint
// w.Name() → "main"
Name() string
// Lifecycle
// w.Show().Focus()
Show() Window
// w.Hide()
Hide() Window
// w.Close()
Close()
// w.Focus()
Focus()
// w.Run()
Run()
// w.Restore()
Restore()
// Geometry
// x, y := w.Position()
Position() (int, int)
// x, y := w.RelativePosition()
RelativePosition() (int, int)
// w, h := w.Size()
Size() (width int, height int)
// px := w.Width()
Width() int
// px := w.Height()
Height() int
// r := w.Bounds()
Bounds() Rect
// w.SetBounds(Rect{X: 0, Y: 0, Width: 1280, Height: 800})
SetBounds(bounds Rect)
// w.SetPosition(100, 200)
SetPosition(x, y int)
// w.SetRelativePosition(0, 0)
SetRelativePosition(x, y int) Window
// w.SetSize(1280, 800)
SetSize(width, height int) Window
// w.SetMinSize(640, 480)
SetMinSize(minWidth, minHeight int) Window
// w.SetMaxSize(3840, 2160)
SetMaxSize(maxWidth, maxHeight int) Window
// w.Center()
Center()
// Title and content
// t := w.Title() — not in interface, provided by concrete types
// w.SetTitle("My App")
SetTitle(title string) Window
// w.SetURL("https://example.com")
SetURL(url string) Window
// w.SetHTML("<h1>Hello</h1>")
SetHTML(html string) Window
// Visibility states
// w.IsVisible()
IsVisible() bool
// w.IsFullscreen()
IsFullscreen() bool
// w.IsMaximised()
IsMaximised() bool
// w.IsMinimised()
IsMinimised() bool
// w.IsFocused()
IsFocused() bool
// w.IsIgnoreMouseEvents()
IsIgnoreMouseEvents() bool
// w.Resizable()
Resizable() bool
// Window state transitions
// w.Fullscreen()
Fullscreen() Window
// w.UnFullscreen()
UnFullscreen()
// w.Maximise()
Maximise() Window
// w.UnMaximise()
UnMaximise()
// w.Minimise()
Minimise() Window
// w.UnMinimise()
UnMinimise()
// w.ToggleFullscreen()
ToggleFullscreen()
// w.ToggleMaximise()
ToggleMaximise()
// w.SnapAssist()
SnapAssist()
// Style
// w.SetAlwaysOnTop(true)
SetAlwaysOnTop(b bool) Window
// w.SetBackgroundColour(application.NewRGBA(0, 0, 0, 255))
SetBackgroundColour(colour RGBA) Window
// w.SetFrameless(true)
SetFrameless(frameless bool) Window
// w.SetResizable(false)
SetResizable(b bool) Window
// w.SetIgnoreMouseEvents(true)
SetIgnoreMouseEvents(ignore bool) Window
// w.SetMinimiseButtonState(ButtonHidden)
SetMinimiseButtonState(state ButtonState) Window
// w.SetMaximiseButtonState(ButtonDisabled)
SetMaximiseButtonState(state ButtonState) Window
// w.SetCloseButtonState(ButtonEnabled)
SetCloseButtonState(state ButtonState) Window
// w.SetEnabled(false)
SetEnabled(enabled bool)
// w.SetContentProtection(true)
SetContentProtection(protection bool) Window
// Menu
// w.SetMenu(myMenu)
SetMenu(menu *Menu)
// w.ShowMenuBar()
ShowMenuBar()
// w.HideMenuBar()
HideMenuBar()
// w.ToggleMenuBar()
ToggleMenuBar()
// w.ToggleFrameless()
ToggleFrameless()
// WebView
// w.ExecJS("document.title = 'Hello'")
ExecJS(js string)
// w.Reload()
Reload()
// w.ForceReload()
ForceReload()
// w.OpenDevTools()
OpenDevTools()
// w.OpenContextMenu(&ContextMenuData{Name: "main"})
OpenContextMenu(data *ContextMenuData)
// Zoom
// w.Zoom()
Zoom()
// w.ZoomIn()
ZoomIn()
// w.ZoomOut()
ZoomOut()
// w.ZoomReset()
ZoomReset() Window
// z := w.GetZoom()
GetZoom() float64
// w.SetZoom(1.5)
SetZoom(magnification float64) Window
// Events
// cancel := w.OnWindowEvent(events.Common.WindowFocus, func(e *WindowEvent) { ... })
OnWindowEvent(eventType events.WindowEventType, callback func(event *WindowEvent)) func()
// cancel := w.RegisterHook(events.Common.WindowClose, func(e *WindowEvent) { ... })
RegisterHook(eventType events.WindowEventType, callback func(event *WindowEvent)) func()
// w.EmitEvent("user:login", payload)
EmitEvent(name string, data ...any) bool
// w.DispatchWailsEvent(&CustomEvent{Name: "init"})
DispatchWailsEvent(event *CustomEvent)
// Screen and display
// screen, err := w.GetScreen()
GetScreen() (*Screen, error)
// borders := w.GetBorderSizes()
GetBorderSizes() *LRTB
// Constraints
// w.EnableSizeConstraints()
EnableSizeConstraints()
// w.DisableSizeConstraints()
DisableSizeConstraints()
// Modal
// w.AttachModal(modalWindow)
AttachModal(modalWindow Window)
// Utilities
// w.Flash(true)
Flash(enabled bool)
// err := w.Print()
Print() error
// w.Error("something went wrong: %s", details)
Error(message string, args ...any)
// w.Info("window ready")
Info(message string, args ...any)
// Platform
// ptr := w.NativeWindow()
NativeWindow() unsafe.Pointer
// Internal platform hooks — implemented by concrete window types.
handleDragAndDropMessage(filenames []string, dropTarget *DropTargetDetails)
InitiateFrontendDropProcessing(filenames []string, x int, y int)
HandleMessage(message string)
HandleWindowEvent(id uint)
HandleKeyEvent(acceleratorString string)
shouldUnconditionallyClose() bool
cut()
copy()
paste()
undo()
redo()
delete()
selectAll()
}

View file

@ -0,0 +1,32 @@
package application
// Get returns the first window whose Name() matches the given name, or nil if
// no match is found.
//
// w := app.Window.Get("main")
// if w == nil { panic("main window not registered") }
func (wm *WindowManager) Get(name string) Window {
wm.mu.RLock()
defer wm.mu.RUnlock()
for _, window := range wm.windows {
if window.Name() == name {
return window
}
}
return nil
}
// GetByID returns the window with the given numeric ID, or nil if not found.
//
// w := app.Window.GetByID(1)
// if w != nil { w.Focus() }
func (wm *WindowManager) GetByID(id uint) Window {
wm.mu.RLock()
defer wm.mu.RUnlock()
for _, window := range wm.windows {
if window.ID() == id {
return window
}
}
return nil
}

View file

@ -1,5 +1,8 @@
package events
// ApplicationEventType identifies an application-level event.
type ApplicationEventType int
// WindowEventType identifies a window event emitted by the application layer.
type WindowEventType int