gui/stubs/wails/pkg/application/application.go
Claude d9fa59ab04
Some checks failed
Security Scan / security (push) Failing after 24s
feat(stubs): rebuild Wails v3 stub bridge from clean dev — 15 files, 479 exports
Rebuilt from scratch on current dev (post-fleet AX passes).

Stub files:
- application.go (expanded App with 12 managers, WebviewWindow satisfies Window)
- application_options.go (Options, Mac/Win/Linux/iOS/Android, Server, TLS, Assets)
- browser_manager.go, browser_window.go (~47 no-op methods)
- clipboard.go, context_menu.go, dialog.go (full dialog builder API)
- environment.go, events.go (EventManager with On/Off/Emit/Reset)
- keybinding.go, menuitem.go (42 Role constants)
- screen.go (Rect/Point/Size geometry), services.go (generic Service[T])
- webview_window_options.go (full platform types)
- window.go (Window interface ~50 methods)

Wails v3 submodule at internal/wails3/ pinned to alpha 74.
All 16 gui packages build and test clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 17:20:22 +01:00

778 lines
20 KiB
Go

package application
import (
"sync"
"unsafe"
"github.com/wailsapp/wails/v3/pkg/events"
)
// Context mirrors the callback context type exposed by Wails.
//
// item.OnClick(func(ctx *Context) { openPrefs() })
type Context struct {
clickedMenuItem *MenuItem
contextMenuData *ContextMenuData
checked bool
}
func newContext() *Context { return &Context{} }
func (ctx *Context) withClickedMenuItem(item *MenuItem) *Context {
ctx.clickedMenuItem = item
return ctx
}
func (ctx *Context) withContextMenuData(data *ContextMenuData) *Context {
ctx.contextMenuData = data
return ctx
}
func (ctx *Context) withChecked(checked bool) *Context {
ctx.checked = checked
return ctx
}
// Logger is a minimal logger surface used by the GUI packages.
type Logger struct{}
func (l Logger) Info(message string, args ...any) {}
// RGBA stores a colour with alpha.
//
// colour := NewRGBA(255, 128, 0, 255) // opaque orange
type RGBA struct {
Red, Green, Blue, Alpha uint8
}
// NewRGBA constructs an RGBA value.
//
// colour := NewRGBA(255, 128, 0, 255) // opaque orange
func NewRGBA(red, green, blue, alpha uint8) RGBA {
return RGBA{Red: red, Green: green, Blue: blue, Alpha: alpha}
}
// Menu is a menu tree used by the GUI wrappers.
//
// menu := NewMenu()
// menu.Add("Save").SetAccelerator("CmdOrCtrl+S").OnClick(func(ctx *Context) { save() })
type Menu struct {
label string
Items []*MenuItem
}
// NewMenu creates a new, empty Menu.
//
// menu := NewMenu()
func NewMenu() *Menu { return &Menu{} }
// Add appends a new text item with the given label.
func (m *Menu) Add(label string) *MenuItem {
item := NewMenuItem(label)
item.disabled = false
m.Items = append(m.Items, item)
return item
}
// AddSeparator appends a separator item.
func (m *Menu) AddSeparator() {
m.Items = append(m.Items, NewMenuItemSeparator())
}
// AddSubmenu appends a submenu item and returns the child Menu.
func (m *Menu) AddSubmenu(label string) *Menu {
item := NewSubMenuItem(label)
m.Items = append(m.Items, item)
return item.submenu
}
// AddRole appends a platform-role item.
func (m *Menu) AddRole(role Role) {
m.Items = append(m.Items, NewRole(role))
}
// AppendItem appends an already-constructed MenuItem.
func (m *Menu) AppendItem(item *MenuItem) {
m.Items = append(m.Items, item)
}
// Clone returns a deep copy of the menu tree.
func (m *Menu) Clone() *Menu {
cloned := &Menu{label: m.label}
for _, item := range m.Items {
cloned.Items = append(cloned.Items, item.Clone())
}
return cloned
}
// Destroy frees all items in the menu.
func (m *Menu) Destroy() {
for _, item := range m.Items {
item.Destroy()
}
m.Items = nil
}
func (m *Menu) setContextData(data *ContextMenuData) {
for _, item := range m.Items {
item.contextMenuData = data
if item.submenu != nil {
item.submenu.setContextData(data)
}
}
}
// MenuManager owns the application menu.
//
// app.Menu.SetApplicationMenu(menu)
type MenuManager struct {
applicationMenu *Menu
}
func (m *MenuManager) SetApplicationMenu(menu *Menu) { m.applicationMenu = menu }
// SystemTray represents a tray instance.
type SystemTray struct {
icon []byte
templateIcon []byte
tooltip string
label string
menu *Menu
attachedWindow Window
}
func (t *SystemTray) SetIcon(data []byte) { t.icon = append([]byte(nil), data...) }
func (t *SystemTray) SetTemplateIcon(data []byte) { t.templateIcon = append([]byte(nil), data...) }
func (t *SystemTray) SetTooltip(text string) { t.tooltip = text }
func (t *SystemTray) SetLabel(text string) { t.label = text }
func (t *SystemTray) SetMenu(menu *Menu) { t.menu = menu }
// AttachWindow associates a window with the tray icon (shown on click).
func (t *SystemTray) AttachWindow(w Window) { t.attachedWindow = w }
// SystemTrayManager creates tray instances.
type SystemTrayManager struct{}
func (m *SystemTrayManager) New() *SystemTray { return &SystemTray{} }
// WindowEventContext carries drag-and-drop details for a window event.
type WindowEventContext struct {
droppedFiles []string
dropDetails *DropTargetDetails
}
func (c *WindowEventContext) DroppedFiles() []string {
return append([]string(nil), c.droppedFiles...)
}
func (c *WindowEventContext) DropTargetDetails() *DropTargetDetails {
if c.dropDetails == nil {
return nil
}
details := *c.dropDetails
return &details
}
// DropTargetDetails mirrors the fields consumed by the GUI wrappers.
type DropTargetDetails struct {
ElementID string
}
// WindowEvent mirrors the event object passed to window callbacks.
type WindowEvent struct {
ctx *WindowEventContext
}
func (e *WindowEvent) Context() *WindowEventContext {
if e.ctx == nil {
e.ctx = &WindowEventContext{}
}
return e.ctx
}
// WebviewWindow is a lightweight, in-memory window implementation
// that satisfies the Window interface.
type WebviewWindow struct {
mu sync.RWMutex
opts WebviewWindowOptions
windowID uint
title string
posX, posY int
sizeW, sizeH int
maximised bool
focused bool
visible bool
alwaysOnTop bool
isFullscreen bool
closed bool
zoom float64
resizable bool
ignoreMouseEvents bool
enabled bool
eventHandlers map[events.WindowEventType][]func(*WindowEvent)
}
var globalWindowID uint
var globalWindowIDMu sync.Mutex
func nextWindowID() uint {
globalWindowIDMu.Lock()
defer globalWindowIDMu.Unlock()
globalWindowID++
return globalWindowID
}
func newWebviewWindow(options WebviewWindowOptions) *WebviewWindow {
return &WebviewWindow{
opts: options,
windowID: nextWindowID(),
title: options.Title,
posX: options.X,
posY: options.Y,
sizeW: options.Width,
sizeH: options.Height,
visible: !options.Hidden,
alwaysOnTop: options.AlwaysOnTop,
zoom: options.Zoom,
resizable: !options.DisableResize,
enabled: true,
eventHandlers: make(map[events.WindowEventType][]func(*WindowEvent)),
}
}
// ID returns the unique numeric identifier for the window.
func (w *WebviewWindow) ID() uint { return w.windowID }
// Name returns the window name set in WebviewWindowOptions.
func (w *WebviewWindow) Name() string { return w.opts.Name }
// Show makes the window visible and returns it for chaining.
func (w *WebviewWindow) Show() Window {
w.mu.Lock()
w.visible = true
w.mu.Unlock()
return w
}
// Hide makes the window invisible and returns it for chaining.
func (w *WebviewWindow) Hide() Window {
w.mu.Lock()
w.visible = false
w.mu.Unlock()
return w
}
// IsVisible reports whether the window is currently visible.
func (w *WebviewWindow) IsVisible() bool {
w.mu.RLock()
defer w.mu.RUnlock()
return w.visible
}
// Close marks the window as closed.
func (w *WebviewWindow) Close() {
w.mu.Lock()
w.closed = true
w.mu.Unlock()
}
// Focus marks the window as focused.
func (w *WebviewWindow) Focus() {
w.mu.Lock()
w.focused = true
w.mu.Unlock()
}
// Run is a no-op in the stub (the real implementation enters the run loop).
func (w *WebviewWindow) Run() {}
// Center is a no-op in the stub.
func (w *WebviewWindow) Center() {}
// Position returns the current x/y position.
func (w *WebviewWindow) Position() (int, int) {
w.mu.RLock()
defer w.mu.RUnlock()
return w.posX, w.posY
}
// RelativePosition returns the position relative to the screen.
func (w *WebviewWindow) RelativePosition() (int, int) {
w.mu.RLock()
defer w.mu.RUnlock()
return w.posX, w.posY
}
// Size returns the current width and height.
func (w *WebviewWindow) Size() (int, int) {
w.mu.RLock()
defer w.mu.RUnlock()
return w.sizeW, w.sizeH
}
// Width returns the current window width.
func (w *WebviewWindow) Width() int {
w.mu.RLock()
defer w.mu.RUnlock()
return w.sizeW
}
// Height returns the current window height.
func (w *WebviewWindow) Height() int {
w.mu.RLock()
defer w.mu.RUnlock()
return w.sizeH
}
// Bounds returns the window's position and size as a Rect.
func (w *WebviewWindow) Bounds() Rect {
w.mu.RLock()
defer w.mu.RUnlock()
return Rect{X: w.posX, Y: w.posY, Width: w.sizeW, Height: w.sizeH}
}
// SetPosition sets the top-left corner position.
func (w *WebviewWindow) SetPosition(x, y int) {
w.mu.Lock()
w.posX, w.posY = x, y
w.mu.Unlock()
}
// SetRelativePosition sets position relative to the screen and returns the window.
func (w *WebviewWindow) SetRelativePosition(x, y int) Window {
w.SetPosition(x, y)
return w
}
// SetSize sets the window dimensions and returns the window.
func (w *WebviewWindow) SetSize(width, height int) Window {
w.mu.Lock()
w.sizeW, w.sizeH = width, height
w.mu.Unlock()
return w
}
// SetBounds sets position and size in one call.
func (w *WebviewWindow) SetBounds(bounds Rect) {
w.mu.Lock()
w.posX, w.posY = bounds.X, bounds.Y
w.sizeW, w.sizeH = bounds.Width, bounds.Height
w.mu.Unlock()
}
// SetMaxSize is a no-op in the stub.
func (w *WebviewWindow) SetMaxSize(maxWidth, maxHeight int) Window { return w }
// SetMinSize is a no-op in the stub.
func (w *WebviewWindow) SetMinSize(minWidth, minHeight int) Window { return w }
// EnableSizeConstraints is a no-op in the stub.
func (w *WebviewWindow) EnableSizeConstraints() {}
// DisableSizeConstraints is a no-op in the stub.
func (w *WebviewWindow) DisableSizeConstraints() {}
// Resizable reports whether the user can resize the window.
func (w *WebviewWindow) Resizable() bool {
w.mu.RLock()
defer w.mu.RUnlock()
return w.resizable
}
// SetResizable enables or disables user resizing and returns the window.
func (w *WebviewWindow) SetResizable(b bool) Window {
w.mu.Lock()
w.resizable = b
w.mu.Unlock()
return w
}
// Maximise maximises the window and returns it.
func (w *WebviewWindow) Maximise() Window {
w.mu.Lock()
w.maximised = true
w.mu.Unlock()
return w
}
// UnMaximise restores from maximised state.
func (w *WebviewWindow) UnMaximise() {
w.mu.Lock()
w.maximised = false
w.mu.Unlock()
}
// ToggleMaximise toggles between maximised and normal.
func (w *WebviewWindow) ToggleMaximise() {
w.mu.Lock()
w.maximised = !w.maximised
w.mu.Unlock()
}
// IsMaximised reports whether the window is maximised.
func (w *WebviewWindow) IsMaximised() bool {
w.mu.RLock()
defer w.mu.RUnlock()
return w.maximised
}
// Minimise minimises the window and returns it.
func (w *WebviewWindow) Minimise() Window {
w.mu.Lock()
w.visible = false
w.mu.Unlock()
return w
}
// UnMinimise restores from minimised state.
func (w *WebviewWindow) UnMinimise() {
w.mu.Lock()
w.visible = true
w.mu.Unlock()
}
// IsMinimised always returns false in the stub.
func (w *WebviewWindow) IsMinimised() bool { return false }
// Fullscreen enters fullscreen and returns the window.
func (w *WebviewWindow) Fullscreen() Window {
w.mu.Lock()
w.isFullscreen = true
w.mu.Unlock()
return w
}
// UnFullscreen exits fullscreen.
func (w *WebviewWindow) UnFullscreen() {
w.mu.Lock()
w.isFullscreen = false
w.mu.Unlock()
}
// ToggleFullscreen toggles between fullscreen and normal.
func (w *WebviewWindow) ToggleFullscreen() {
w.mu.Lock()
w.isFullscreen = !w.isFullscreen
w.mu.Unlock()
}
// IsFullscreen reports whether the window is in fullscreen mode.
func (w *WebviewWindow) IsFullscreen() bool {
w.mu.RLock()
defer w.mu.RUnlock()
return w.isFullscreen
}
// Restore exits both fullscreen and maximised states.
func (w *WebviewWindow) Restore() {
w.mu.Lock()
w.maximised = false
w.isFullscreen = false
w.mu.Unlock()
}
// SnapAssist is a no-op in the stub.
func (w *WebviewWindow) SnapAssist() {}
// SetTitle updates the window title and returns the window.
func (w *WebviewWindow) SetTitle(title string) Window {
w.mu.Lock()
w.title = title
w.mu.Unlock()
return w
}
// SetURL is a no-op in the stub.
func (w *WebviewWindow) SetURL(s string) Window { return w }
// SetHTML is a no-op in the stub.
func (w *WebviewWindow) SetHTML(html string) Window { return w }
// SetMinimiseButtonState is a no-op in the stub.
func (w *WebviewWindow) SetMinimiseButtonState(state ButtonState) Window { return w }
// SetMaximiseButtonState is a no-op in the stub.
func (w *WebviewWindow) SetMaximiseButtonState(state ButtonState) Window { return w }
// SetCloseButtonState is a no-op in the stub.
func (w *WebviewWindow) SetCloseButtonState(state ButtonState) Window { return w }
// SetMenu is a no-op in the stub.
func (w *WebviewWindow) SetMenu(menu *Menu) {}
// ShowMenuBar is a no-op in the stub.
func (w *WebviewWindow) ShowMenuBar() {}
// HideMenuBar is a no-op in the stub.
func (w *WebviewWindow) HideMenuBar() {}
// ToggleMenuBar is a no-op in the stub.
func (w *WebviewWindow) ToggleMenuBar() {}
// SetBackgroundColour is a no-op in the stub.
func (w *WebviewWindow) SetBackgroundColour(colour RGBA) Window { return w }
// SetAlwaysOnTop sets the always-on-top flag and returns the window.
func (w *WebviewWindow) SetAlwaysOnTop(b bool) Window {
w.mu.Lock()
w.alwaysOnTop = b
w.mu.Unlock()
return w
}
// SetFrameless is a no-op in the stub.
func (w *WebviewWindow) SetFrameless(frameless bool) Window { return w }
// ToggleFrameless is a no-op in the stub.
func (w *WebviewWindow) ToggleFrameless() {}
// SetIgnoreMouseEvents sets the mouse-event passthrough flag and returns the window.
func (w *WebviewWindow) SetIgnoreMouseEvents(ignore bool) Window {
w.mu.Lock()
w.ignoreMouseEvents = ignore
w.mu.Unlock()
return w
}
// IsIgnoreMouseEvents reports whether mouse events are being ignored.
func (w *WebviewWindow) IsIgnoreMouseEvents() bool {
w.mu.RLock()
defer w.mu.RUnlock()
return w.ignoreMouseEvents
}
// SetContentProtection is a no-op in the stub.
func (w *WebviewWindow) SetContentProtection(protection bool) Window { return w }
// GetZoom returns the current zoom magnification.
func (w *WebviewWindow) GetZoom() float64 {
w.mu.RLock()
defer w.mu.RUnlock()
return w.zoom
}
// SetZoom sets the zoom magnification and returns the window.
func (w *WebviewWindow) SetZoom(magnification float64) Window {
w.mu.Lock()
w.zoom = magnification
w.mu.Unlock()
return w
}
// Zoom is a no-op in the stub.
func (w *WebviewWindow) Zoom() {}
// ZoomIn is a no-op in the stub.
func (w *WebviewWindow) ZoomIn() {}
// ZoomOut is a no-op in the stub.
func (w *WebviewWindow) ZoomOut() {}
// ZoomReset resets zoom to 1.0 and returns the window.
func (w *WebviewWindow) ZoomReset() Window {
w.mu.Lock()
w.zoom = 1.0
w.mu.Unlock()
return w
}
// GetBorderSizes returns zero insets in the stub.
func (w *WebviewWindow) GetBorderSizes() *LRTB { return &LRTB{} }
// GetScreen returns nil in the stub.
func (w *WebviewWindow) GetScreen() (*Screen, error) { return nil, nil }
// ExecJS is a no-op in the stub.
func (w *WebviewWindow) ExecJS(js string) {}
// EmitEvent always returns false in the stub.
func (w *WebviewWindow) EmitEvent(name string, data ...any) bool { return false }
// DispatchWailsEvent is a no-op in the stub.
func (w *WebviewWindow) DispatchWailsEvent(event *CustomEvent) {}
// OnWindowEvent registers an event callback and returns an unsubscribe function.
func (w *WebviewWindow) OnWindowEvent(eventType events.WindowEventType, callback func(event *WindowEvent)) func() {
w.mu.Lock()
w.eventHandlers[eventType] = append(w.eventHandlers[eventType], callback)
w.mu.Unlock()
return func() {}
}
// RegisterHook is an alias for OnWindowEvent.
func (w *WebviewWindow) RegisterHook(eventType events.WindowEventType, callback func(event *WindowEvent)) func() {
return w.OnWindowEvent(eventType, callback)
}
// handleDragAndDropMessage is a no-op in the stub.
func (w *WebviewWindow) handleDragAndDropMessage(filenames []string, dropTarget *DropTargetDetails) {}
// InitiateFrontendDropProcessing is a no-op in the stub.
func (w *WebviewWindow) InitiateFrontendDropProcessing(filenames []string, x int, y int) {}
// HandleMessage is a no-op in the stub.
func (w *WebviewWindow) HandleMessage(message string) {}
// HandleWindowEvent is a no-op in the stub.
func (w *WebviewWindow) HandleWindowEvent(id uint) {}
// HandleKeyEvent is a no-op in the stub.
func (w *WebviewWindow) HandleKeyEvent(acceleratorString string) {}
// OpenContextMenu is a no-op in the stub.
func (w *WebviewWindow) OpenContextMenu(data *ContextMenuData) {}
// AttachModal is a no-op in the stub.
func (w *WebviewWindow) AttachModal(modalWindow Window) {}
// OpenDevTools is a no-op in the stub.
func (w *WebviewWindow) OpenDevTools() {}
// Print always returns nil in the stub.
func (w *WebviewWindow) Print() error { return nil }
// Flash is a no-op in the stub.
func (w *WebviewWindow) Flash(enabled bool) {}
// IsFocused reports whether the window has focus.
func (w *WebviewWindow) IsFocused() bool {
w.mu.RLock()
defer w.mu.RUnlock()
return w.focused
}
// NativeWindow returns nil in the stub.
func (w *WebviewWindow) NativeWindow() unsafe.Pointer { return nil }
// SetEnabled sets the window's enabled state.
func (w *WebviewWindow) SetEnabled(enabled bool) {
w.mu.Lock()
w.enabled = enabled
w.mu.Unlock()
}
// Reload is a no-op in the stub.
func (w *WebviewWindow) Reload() {}
// ForceReload is a no-op in the stub.
func (w *WebviewWindow) ForceReload() {}
// Info is a no-op in the stub.
func (w *WebviewWindow) Info(message string, args ...any) {}
// Error is a no-op in the stub.
func (w *WebviewWindow) Error(message string, args ...any) {}
// shouldUnconditionallyClose always returns false in the stub.
func (w *WebviewWindow) shouldUnconditionallyClose() bool { return false }
// Internal editing stubs — satisfy the Window interface.
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() {}
// Title returns the current window title.
func (w *WebviewWindow) Title() string {
w.mu.RLock()
defer w.mu.RUnlock()
return w.title
}
// WindowManager manages in-memory windows.
//
// win := app.Window.NewWithOptions(application.WebviewWindowOptions{Title: "Main"})
type WindowManager struct {
mu sync.RWMutex
windows map[uint]*WebviewWindow
}
func (wm *WindowManager) init() {
if wm.windows == nil {
wm.windows = make(map[uint]*WebviewWindow)
}
}
// NewWithOptions creates and registers a new window.
func (wm *WindowManager) NewWithOptions(options WebviewWindowOptions) *WebviewWindow {
window := newWebviewWindow(options)
wm.mu.Lock()
wm.init()
wm.windows[window.windowID] = window
wm.mu.Unlock()
return window
}
// New creates a window with default options.
func (wm *WindowManager) New() *WebviewWindow {
return wm.NewWithOptions(WebviewWindowOptions{})
}
// GetAll returns all managed windows as the Window interface slice.
func (wm *WindowManager) GetAll() []Window {
wm.mu.RLock()
defer wm.mu.RUnlock()
out := make([]Window, 0, len(wm.windows))
for _, window := range wm.windows {
out = append(out, window)
}
return out
}
// GetByName finds a window by name, returning it and whether it was found.
func (wm *WindowManager) GetByName(name string) (Window, bool) {
wm.mu.RLock()
defer wm.mu.RUnlock()
for _, window := range wm.windows {
if window.opts.Name == name {
return window, true
}
}
return nil, false
}
// GetByID finds a window by its numeric ID.
func (wm *WindowManager) GetByID(id uint) (Window, bool) {
wm.mu.RLock()
defer wm.mu.RUnlock()
window, exists := wm.windows[id]
return window, exists
}
// Remove unregisters a window by ID.
func (wm *WindowManager) Remove(windowID uint) {
wm.mu.Lock()
wm.init()
delete(wm.windows, windowID)
wm.mu.Unlock()
}
// App is the top-level application object used by the GUI packages.
//
// app := &application.App{}
// win := app.Window.NewWithOptions(application.WebviewWindowOptions{Title: "Main"})
type App struct {
Logger Logger
Window WindowManager
Menu MenuManager
SystemTray SystemTrayManager
Dialog DialogManager
Event EventManager
Browser BrowserManager
Clipboard ClipboardManager
ContextMenu ContextMenuManager
Environment EnvironmentManager
Screen ScreenManager
KeyBinding KeyBindingManager
}
// Quit is a no-op in the stub.
func (a *App) Quit() {}
// NewMenu creates a new empty Menu.
func (a *App) NewMenu() *Menu {
return NewMenu()
}