feat(stubs): rebuild Wails v3 stub bridge from clean dev — 15 files, 479 exports
Some checks failed
Security Scan / security (push) Failing after 24s
Some checks failed
Security Scan / security (push) Failing after 24s
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>
This commit is contained in:
parent
4533018f80
commit
d9fa59ab04
17 changed files with 3561 additions and 196 deletions
1
internal/wails3
Submodule
1
internal/wails3
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit bb4fbf95744fafe5acf84e143a419bfffc2159e6
|
||||
|
|
@ -2,12 +2,36 @@ package application
|
|||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
)
|
||||
|
||||
// Context mirrors the callback context type exposed by Wails.
|
||||
type Context struct{}
|
||||
//
|
||||
// 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{}
|
||||
|
|
@ -15,90 +39,92 @@ 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}
|
||||
}
|
||||
|
||||
// MenuRole identifies a platform menu role.
|
||||
type MenuRole int
|
||||
|
||||
const (
|
||||
AppMenu MenuRole = iota
|
||||
FileMenu
|
||||
EditMenu
|
||||
ViewMenu
|
||||
WindowMenu
|
||||
HelpMenu
|
||||
)
|
||||
|
||||
// MenuItem is a minimal menu item implementation.
|
||||
type MenuItem struct {
|
||||
Label string
|
||||
Accelerator string
|
||||
Tooltip string
|
||||
Checked bool
|
||||
Enabled bool
|
||||
onClick func(*Context)
|
||||
}
|
||||
|
||||
func (mi *MenuItem) SetAccelerator(accel string) { mi.Accelerator = accel }
|
||||
func (mi *MenuItem) SetTooltip(text string) { mi.Tooltip = text }
|
||||
func (mi *MenuItem) SetChecked(checked bool) { mi.Checked = checked }
|
||||
func (mi *MenuItem) SetEnabled(enabled bool) { mi.Enabled = enabled }
|
||||
func (mi *MenuItem) OnClick(fn func(*Context)) { mi.onClick = fn }
|
||||
|
||||
// Menu is a minimal menu tree used by the GUI wrappers.
|
||||
// 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 := &MenuItem{Label: label, Enabled: true}
|
||||
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, &MenuItem{Label: "---"})
|
||||
m.Items = append(m.Items, NewMenuItemSeparator())
|
||||
}
|
||||
|
||||
// AddSubmenu appends a submenu item and returns the child Menu.
|
||||
func (m *Menu) AddSubmenu(label string) *Menu {
|
||||
submenu := &Menu{}
|
||||
m.Items = append(m.Items, &MenuItem{Label: label})
|
||||
return submenu
|
||||
item := NewSubMenuItem(label)
|
||||
m.Items = append(m.Items, item)
|
||||
return item.submenu
|
||||
}
|
||||
|
||||
func (m *Menu) AddRole(role MenuRole) {
|
||||
m.Items = append(m.Items, &MenuItem{Label: role.String(), Enabled: true})
|
||||
// AddRole appends a platform-role item.
|
||||
func (m *Menu) AddRole(role Role) {
|
||||
m.Items = append(m.Items, NewRole(role))
|
||||
}
|
||||
|
||||
func (role MenuRole) String() string {
|
||||
switch role {
|
||||
case AppMenu:
|
||||
return "app"
|
||||
case FileMenu:
|
||||
return "file"
|
||||
case EditMenu:
|
||||
return "edit"
|
||||
case ViewMenu:
|
||||
return "view"
|
||||
case WindowMenu:
|
||||
return "window"
|
||||
case HelpMenu:
|
||||
return "help"
|
||||
default:
|
||||
return "unknown"
|
||||
// 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
|
||||
}
|
||||
|
|
@ -112,7 +138,7 @@ type SystemTray struct {
|
|||
tooltip string
|
||||
label string
|
||||
menu *Menu
|
||||
attachedWindow *WebviewWindow
|
||||
attachedWindow Window
|
||||
}
|
||||
|
||||
func (t *SystemTray) SetIcon(data []byte) { t.icon = append([]byte(nil), data...) }
|
||||
|
|
@ -120,9 +146,9 @@ func (t *SystemTray) SetTemplateIcon(data []byte) { t.templateIcon = append([]by
|
|||
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 }
|
||||
func (t *SystemTray) AttachWindow(w *WebviewWindow) {
|
||||
t.attachedWindow = w
|
||||
}
|
||||
|
||||
// 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{}
|
||||
|
|
@ -164,159 +190,408 @@ func (e *WindowEvent) Context() *WindowEventContext {
|
|||
return e.ctx
|
||||
}
|
||||
|
||||
// WebviewWindowOptions configures a window instance.
|
||||
type WebviewWindowOptions struct {
|
||||
Name string
|
||||
Title string
|
||||
URL string
|
||||
Width, Height int
|
||||
X, Y int
|
||||
MinWidth, MinHeight int
|
||||
MaxWidth, MaxHeight int
|
||||
Frameless bool
|
||||
Hidden bool
|
||||
AlwaysOnTop bool
|
||||
DisableResize bool
|
||||
EnableFileDrop bool
|
||||
BackgroundColour RGBA
|
||||
// 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)
|
||||
}
|
||||
|
||||
// WebviewWindow is a lightweight, in-memory window implementation.
|
||||
type WebviewWindow struct {
|
||||
mu sync.RWMutex
|
||||
opts WebviewWindowOptions
|
||||
title string
|
||||
x, y int
|
||||
width, height int
|
||||
maximised bool
|
||||
focused bool
|
||||
visible bool
|
||||
alwaysOnTop bool
|
||||
fullscreen bool
|
||||
closed 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,
|
||||
x: options.X,
|
||||
y: options.Y,
|
||||
width: options.Width,
|
||||
height: options.Height,
|
||||
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 }
|
||||
func (w *WebviewWindow) Title() string {
|
||||
|
||||
// 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.title
|
||||
}
|
||||
func (w *WebviewWindow) Position() (int, int) {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
return w.x, w.y
|
||||
}
|
||||
func (w *WebviewWindow) Size() (int, int) {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
return w.width, w.height
|
||||
}
|
||||
func (w *WebviewWindow) IsMaximised() bool {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
return w.maximised
|
||||
}
|
||||
func (w *WebviewWindow) IsFocused() bool {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
return w.focused
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) SetTitle(title string) {
|
||||
w.mu.Lock()
|
||||
w.title = title
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) SetPosition(x, y int) {
|
||||
w.mu.Lock()
|
||||
w.x = x
|
||||
w.y = y
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) SetSize(width, height int) {
|
||||
w.mu.Lock()
|
||||
w.width = width
|
||||
w.height = height
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) SetBackgroundColour(colour RGBA) {}
|
||||
|
||||
func (w *WebviewWindow) SetAlwaysOnTop(alwaysOnTop bool) {
|
||||
w.mu.Lock()
|
||||
w.alwaysOnTop = alwaysOnTop
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) Maximise() {
|
||||
w.mu.Lock()
|
||||
w.maximised = true
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) Restore() {
|
||||
w.mu.Lock()
|
||||
w.maximised = false
|
||||
w.fullscreen = false
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) Minimise() {}
|
||||
|
||||
func (w *WebviewWindow) Focus() {
|
||||
w.mu.Lock()
|
||||
w.focused = true
|
||||
w.mu.Unlock()
|
||||
return w.visible
|
||||
}
|
||||
|
||||
// Close marks the window as closed.
|
||||
func (w *WebviewWindow) Close() {
|
||||
w.mu.Lock()
|
||||
w.closed = true
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) Show() {
|
||||
// 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()
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) Hide() {
|
||||
// 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.visible = false
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) Fullscreen() {
|
||||
w.mu.Lock()
|
||||
w.fullscreen = true
|
||||
w.isFullscreen = true
|
||||
w.mu.Unlock()
|
||||
return w
|
||||
}
|
||||
|
||||
// UnFullscreen exits fullscreen.
|
||||
func (w *WebviewWindow) UnFullscreen() {
|
||||
w.mu.Lock()
|
||||
w.fullscreen = false
|
||||
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)
|
||||
|
|
@ -324,40 +599,180 @@ func (w *WebviewWindow) OnWindowEvent(eventType events.WindowEventType, callback
|
|||
return func() {}
|
||||
}
|
||||
|
||||
// WindowManager manages in-memory windows.
|
||||
type WindowManager struct {
|
||||
mu sync.RWMutex
|
||||
windows []*WebviewWindow
|
||||
// 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.windows = append(wm.windows, window)
|
||||
wm.init()
|
||||
wm.windows[window.windowID] = window
|
||||
wm.mu.Unlock()
|
||||
return window
|
||||
}
|
||||
|
||||
func (wm *WindowManager) GetAll() []any {
|
||||
// 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([]any, 0, len(wm.windows))
|
||||
out := make([]Window, 0, len(wm.windows))
|
||||
for _, window := range wm.windows {
|
||||
out = append(out, window)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// App is the top-level application object used by the GUI packages.
|
||||
type App struct {
|
||||
Logger Logger
|
||||
Window WindowManager
|
||||
Menu MenuManager
|
||||
SystemTray SystemTrayManager
|
||||
// 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()
|
||||
}
|
||||
|
|
|
|||
348
stubs/wails/pkg/application/application_options.go
Normal file
348
stubs/wails/pkg/application/application_options.go
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Options is the top-level application configuration passed to New().
|
||||
// app := application.New(application.Options{Name: "MyApp", Services: []Service{...}})
|
||||
type Options struct {
|
||||
// Name is displayed in the default about box.
|
||||
Name string
|
||||
|
||||
// Description is displayed in the default about box.
|
||||
Description string
|
||||
|
||||
// Icon is the application icon shown in the about box.
|
||||
Icon []byte
|
||||
|
||||
// Mac contains macOS-specific options.
|
||||
Mac MacOptions
|
||||
|
||||
// Windows contains Windows-specific options.
|
||||
Windows WindowsOptions
|
||||
|
||||
// Linux contains Linux-specific options.
|
||||
Linux LinuxOptions
|
||||
|
||||
// IOS contains iOS-specific options.
|
||||
IOS IOSOptions
|
||||
|
||||
// Android contains Android-specific options.
|
||||
Android AndroidOptions
|
||||
|
||||
// Services lists bound Go services exposed to the frontend.
|
||||
Services []Service
|
||||
|
||||
// MarshalError serialises service method errors to JSON.
|
||||
// Return nil to fall 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
|
||||
|
||||
// Assets configures the embedded asset server.
|
||||
Assets AssetOptions
|
||||
|
||||
// Flags are key/value pairs available to the frontend at startup.
|
||||
Flags map[string]any
|
||||
|
||||
// PanicHandler is called when an unhandled panic occurs.
|
||||
PanicHandler func(*PanicDetails)
|
||||
|
||||
// DisableDefaultSignalHandler prevents Wails from handling OS signals.
|
||||
DisableDefaultSignalHandler bool
|
||||
|
||||
// KeyBindings maps accelerator strings to window callbacks.
|
||||
// Example: map[string]func(Window){"Ctrl+Q": func(w Window) { w.Close() }}
|
||||
KeyBindings map[string]func(window Window)
|
||||
|
||||
// OnShutdown is called before the application terminates.
|
||||
OnShutdown func()
|
||||
|
||||
// PostShutdown is called after shutdown, just before process exit.
|
||||
PostShutdown func()
|
||||
|
||||
// ShouldQuit is called when the user attempts to quit.
|
||||
// Return false to prevent the application from quitting.
|
||||
ShouldQuit func() bool
|
||||
|
||||
// RawMessageHandler handles raw messages 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 a non-fatal error occurs.
|
||||
ErrorHandler func(err error)
|
||||
|
||||
// FileAssociations lists file extensions associated with this application.
|
||||
// Each extension must include the leading dot, e.g. ".txt".
|
||||
FileAssociations []string
|
||||
|
||||
// SingleInstance configures single-instance enforcement.
|
||||
SingleInstance *SingleInstanceOptions
|
||||
|
||||
// Server configures the headless HTTP server (enabled via the "server" build tag).
|
||||
Server ServerOptions
|
||||
}
|
||||
|
||||
// ServerOptions configures the headless HTTP server started in server mode.
|
||||
// opts.Server = application.ServerOptions{Host: "0.0.0.0", Port: 8080}
|
||||
type ServerOptions struct {
|
||||
// Host is the bind address. Defaults to "localhost".
|
||||
Host string
|
||||
|
||||
// Port is the TCP port. Defaults to 8080.
|
||||
Port int
|
||||
|
||||
// ReadTimeout is the maximum duration for reading a complete request.
|
||||
ReadTimeout time.Duration
|
||||
|
||||
// WriteTimeout is the maximum duration before timing out a response write.
|
||||
WriteTimeout time.Duration
|
||||
|
||||
// IdleTimeout is the maximum duration to wait for the next request.
|
||||
IdleTimeout time.Duration
|
||||
|
||||
// ShutdownTimeout is the maximum duration to wait for active connections on shutdown.
|
||||
ShutdownTimeout time.Duration
|
||||
|
||||
// TLS configures HTTPS. If nil, plain HTTP is used.
|
||||
TLS *TLSOptions
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// AssetOptions configures the embedded asset server.
|
||||
// opts.Assets = application.AssetOptions{Handler: http.FileServer(http.FS(assets))}
|
||||
type AssetOptions struct {
|
||||
// Handler serves all content to the WebView.
|
||||
Handler http.Handler
|
||||
|
||||
// Middleware injects into the asset server request chain before Wails internals.
|
||||
Middleware Middleware
|
||||
|
||||
// DisableLogging suppresses per-request asset server log output.
|
||||
DisableLogging bool
|
||||
}
|
||||
|
||||
// Middleware is an HTTP middleware function for the asset server.
|
||||
// type Middleware func(next http.Handler) http.Handler
|
||||
type Middleware func(next http.Handler) http.Handler
|
||||
|
||||
// ChainMiddleware composes multiple Middleware values into a single Middleware.
|
||||
// chain := application.ChainMiddleware(authMiddleware, loggingMiddleware)
|
||||
func ChainMiddleware(middleware ...Middleware) Middleware {
|
||||
return func(handler http.Handler) http.Handler {
|
||||
for index := len(middleware) - 1; index >= 0; index-- {
|
||||
handler = middleware[index](handler)
|
||||
}
|
||||
return handler
|
||||
}
|
||||
}
|
||||
|
||||
// PanicDetails carries information about an unhandled panic.
|
||||
// opts.PanicHandler = func(d *application.PanicDetails) { log(d.StackTrace) }
|
||||
type PanicDetails struct {
|
||||
StackTrace string
|
||||
Error error
|
||||
FullStackTrace string
|
||||
}
|
||||
|
||||
// OriginInfo carries frame-origin metadata for raw message handling.
|
||||
// opts.RawMessageHandler = func(w Window, msg string, o *application.OriginInfo) { ... }
|
||||
type OriginInfo struct {
|
||||
Origin string
|
||||
TopOrigin string
|
||||
IsMainFrame bool
|
||||
}
|
||||
|
||||
// SingleInstanceOptions configures single-instance enforcement.
|
||||
// opts.SingleInstance = &application.SingleInstanceOptions{UniqueID: "com.example.myapp"}
|
||||
type SingleInstanceOptions struct {
|
||||
// UniqueID identifies the application instance, e.g. "com.example.myapp".
|
||||
UniqueID string
|
||||
|
||||
// OnSecondInstanceLaunch is called when a second instance attempts to start.
|
||||
OnSecondInstanceLaunch func(data SecondInstanceData)
|
||||
|
||||
// AdditionalData passes custom data from the second instance to the first.
|
||||
AdditionalData map[string]string
|
||||
}
|
||||
|
||||
// SecondInstanceData describes a second-instance launch event.
|
||||
type SecondInstanceData struct {
|
||||
Args []string `json:"args"`
|
||||
WorkingDir string `json:"workingDir"`
|
||||
AdditionalData map[string]string `json:"additionalData,omitempty"`
|
||||
}
|
||||
|
||||
// ActivationPolicy controls when a macOS application activates.
|
||||
// opts.Mac.ActivationPolicy = application.ActivationPolicyAccessory
|
||||
type ActivationPolicy int
|
||||
|
||||
const (
|
||||
// ActivationPolicyRegular is used for applications with a main window.
|
||||
ActivationPolicyRegular ActivationPolicy = iota
|
||||
// ActivationPolicyAccessory is used for menu-bar or background applications.
|
||||
ActivationPolicyAccessory
|
||||
// ActivationPolicyProhibited prevents the application from activating.
|
||||
ActivationPolicyProhibited
|
||||
)
|
||||
|
||||
// MacOptions contains macOS-specific application options.
|
||||
// opts.Mac = application.MacOptions{ActivationPolicy: application.ActivationPolicyRegular}
|
||||
type MacOptions struct {
|
||||
// ActivationPolicy controls how and when the application becomes active.
|
||||
ActivationPolicy ActivationPolicy
|
||||
|
||||
// ApplicationShouldTerminateAfterLastWindowClosed quits the app when
|
||||
// the last window closes, matching standard macOS behaviour.
|
||||
ApplicationShouldTerminateAfterLastWindowClosed bool
|
||||
}
|
||||
|
||||
// WindowsOptions contains Windows-specific application options.
|
||||
// opts.Windows = application.WindowsOptions{WndClass: "MyAppWindow"}
|
||||
type WindowsOptions struct {
|
||||
// WndClass is the Windows window class name. Defaults to "WailsWebviewWindow".
|
||||
WndClass string
|
||||
|
||||
// WndProcInterceptor intercepts all Win32 messages in the main message loop.
|
||||
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 sets the WebView2 user-data directory.
|
||||
// Defaults to %APPDATA%\[BinaryName.exe].
|
||||
WebviewUserDataPath string
|
||||
|
||||
// WebviewBrowserPath sets the directory containing WebView2 executables.
|
||||
// Defaults to the system-installed WebView2.
|
||||
WebviewBrowserPath string
|
||||
|
||||
// EnabledFeatures lists WebView2 feature flags to enable.
|
||||
EnabledFeatures []string
|
||||
|
||||
// DisabledFeatures lists WebView2 feature flags to disable.
|
||||
DisabledFeatures []string
|
||||
|
||||
// AdditionalBrowserArgs are extra Chromium command-line arguments.
|
||||
// Each argument must include the "--" prefix, e.g. "--remote-debugging-port=9222".
|
||||
AdditionalBrowserArgs []string
|
||||
}
|
||||
|
||||
// LinuxOptions contains Linux-specific application options.
|
||||
// opts.Linux = application.LinuxOptions{ProgramName: "myapp"}
|
||||
type LinuxOptions struct {
|
||||
// DisableQuitOnLastWindowClosed prevents auto-quit when the last window closes.
|
||||
DisableQuitOnLastWindowClosed bool
|
||||
|
||||
// ProgramName is passed to g_set_prgname() for window grouping in GTK.
|
||||
ProgramName string
|
||||
}
|
||||
|
||||
// IOSOptions contains iOS-specific application options.
|
||||
// opts.IOS = application.IOSOptions{EnableInlineMediaPlayback: true}
|
||||
type IOSOptions struct {
|
||||
// DisableInputAccessoryView hides the Next/Previous/Done toolbar above the keyboard.
|
||||
DisableInputAccessoryView bool
|
||||
|
||||
// DisableScroll disables WebView scrolling.
|
||||
DisableScroll bool
|
||||
|
||||
// DisableBounce disables the iOS overscroll bounce effect.
|
||||
DisableBounce bool
|
||||
|
||||
// DisableScrollIndicators hides scroll indicator bars.
|
||||
DisableScrollIndicators bool
|
||||
|
||||
// EnableBackForwardNavigationGestures enables swipe navigation gestures.
|
||||
EnableBackForwardNavigationGestures bool
|
||||
|
||||
// DisableLinkPreview disables long-press link preview (peek and pop).
|
||||
DisableLinkPreview bool
|
||||
|
||||
// EnableInlineMediaPlayback allows video to play inline rather than full-screen.
|
||||
EnableInlineMediaPlayback bool
|
||||
|
||||
// EnableAutoplayWithoutUserAction allows media to autoplay without a user gesture.
|
||||
EnableAutoplayWithoutUserAction bool
|
||||
|
||||
// DisableInspectable disables the Safari remote web inspector.
|
||||
DisableInspectable bool
|
||||
|
||||
// UserAgent overrides the WKWebView user agent string.
|
||||
UserAgent string
|
||||
|
||||
// ApplicationNameForUserAgent appends a name to the user agent. Defaults to "wails.io".
|
||||
ApplicationNameForUserAgent string
|
||||
|
||||
// AppBackgroundColourSet enables BackgroundColour for the main iOS window.
|
||||
AppBackgroundColourSet bool
|
||||
|
||||
// BackgroundColour is applied to the iOS app window before any WebView is created.
|
||||
BackgroundColour RGBA
|
||||
|
||||
// EnableNativeTabs enables a native UITabBar at the bottom of the screen.
|
||||
EnableNativeTabs bool
|
||||
|
||||
// NativeTabsItems configures the labels and SF Symbol icons for the native UITabBar.
|
||||
NativeTabsItems []NativeTabItem
|
||||
}
|
||||
|
||||
// NativeTabItem describes a single tab in the iOS native UITabBar.
|
||||
// item := application.NativeTabItem{Title: "Home", SystemImage: application.NativeTabIconHouse}
|
||||
type NativeTabItem struct {
|
||||
Title string `json:"Title"`
|
||||
SystemImage NativeTabIcon `json:"SystemImage"`
|
||||
}
|
||||
|
||||
// NativeTabIcon is an SF Symbols name used for a UITabBar icon.
|
||||
// tab := application.NativeTabItem{SystemImage: application.NativeTabIconGear}
|
||||
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"
|
||||
)
|
||||
|
||||
// AndroidOptions contains Android-specific application options.
|
||||
// opts.Android = application.AndroidOptions{EnableZoom: true}
|
||||
type AndroidOptions struct {
|
||||
// DisableScroll disables WebView scrolling.
|
||||
DisableScroll bool
|
||||
|
||||
// DisableOverscroll disables the overscroll bounce effect.
|
||||
DisableOverscroll bool
|
||||
|
||||
// EnableZoom enables pinch-to-zoom in the WebView.
|
||||
EnableZoom bool
|
||||
|
||||
// UserAgent overrides the WebView user agent string.
|
||||
UserAgent string
|
||||
|
||||
// BackgroundColour sets the WebView background colour.
|
||||
BackgroundColour RGBA
|
||||
|
||||
// DisableHardwareAcceleration disables GPU acceleration for the WebView.
|
||||
DisableHardwareAcceleration bool
|
||||
}
|
||||
21
stubs/wails/pkg/application/browser_manager.go
Normal file
21
stubs/wails/pkg/application/browser_manager.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package application
|
||||
|
||||
// BrowserManager handles opening URLs and files in the system browser.
|
||||
//
|
||||
// manager.OpenURL("https://lthn.io")
|
||||
// manager.OpenFile("/home/user/document.pdf")
|
||||
type BrowserManager struct{}
|
||||
|
||||
// OpenURL opens the given URL in the default browser.
|
||||
//
|
||||
// err := manager.OpenURL("https://lthn.io")
|
||||
func (bm *BrowserManager) OpenURL(url string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpenFile opens the given file path in the default browser or file handler.
|
||||
//
|
||||
// err := manager.OpenFile("/home/user/report.html")
|
||||
func (bm *BrowserManager) OpenFile(path string) error {
|
||||
return nil
|
||||
}
|
||||
230
stubs/wails/pkg/application/browser_window.go
Normal file
230
stubs/wails/pkg/application/browser_window.go
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
)
|
||||
|
||||
// ButtonState represents the visual state of a window control button.
|
||||
//
|
||||
// window.SetMinimiseButtonState(ButtonHidden)
|
||||
type ButtonState int
|
||||
|
||||
const (
|
||||
ButtonEnabled ButtonState = 0
|
||||
ButtonDisabled ButtonState = 1
|
||||
ButtonHidden ButtonState = 2
|
||||
)
|
||||
|
||||
// LRTB holds left/right/top/bottom border sizes in pixels.
|
||||
//
|
||||
// sizes := window.GetBorderSizes()
|
||||
type LRTB struct {
|
||||
Left int
|
||||
Right int
|
||||
Top int
|
||||
Bottom int
|
||||
}
|
||||
|
||||
// ContextMenuData carries context-menu position and metadata from the frontend.
|
||||
//
|
||||
// window.OpenContextMenu(&ContextMenuData{Id: "file-menu"})
|
||||
type ContextMenuData struct {
|
||||
Id string `json:"id"`
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
func (c *ContextMenuData) clone() *ContextMenuData {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
copy := *c
|
||||
return ©
|
||||
}
|
||||
|
||||
// CustomEvent carries a named event with arbitrary data from the frontend.
|
||||
//
|
||||
// window.DispatchWailsEvent(&CustomEvent{Name: "ready", Data: nil})
|
||||
type CustomEvent struct {
|
||||
Name string `json:"name"`
|
||||
Data any `json:"data"`
|
||||
Sender string `json:"sender,omitempty"`
|
||||
cancelled bool
|
||||
}
|
||||
|
||||
// Cancel prevents the event from reaching further listeners.
|
||||
func (e *CustomEvent) Cancel() { e.cancelled = true }
|
||||
|
||||
// IsCancelled reports whether Cancel has been called.
|
||||
func (e *CustomEvent) IsCancelled() bool { return e.cancelled }
|
||||
|
||||
// BrowserWindow represents a browser client connection in server mode.
|
||||
// It satisfies the Window interface so browser clients are treated
|
||||
// uniformly with native windows throughout the codebase.
|
||||
//
|
||||
// bw := NewBrowserWindow(1, "nano-abc123")
|
||||
// bw.Focus() // no-op in browser mode
|
||||
type BrowserWindow struct {
|
||||
mu sync.RWMutex
|
||||
id uint
|
||||
name string
|
||||
clientID string
|
||||
}
|
||||
|
||||
// NewBrowserWindow constructs a BrowserWindow with the given numeric ID and client nano-ID.
|
||||
//
|
||||
// bw := NewBrowserWindow(1, "nano-abc123")
|
||||
func NewBrowserWindow(id uint, clientID string) *BrowserWindow {
|
||||
return &BrowserWindow{
|
||||
id: id,
|
||||
name: "browser-window",
|
||||
clientID: clientID,
|
||||
}
|
||||
}
|
||||
|
||||
// ID returns the numeric window identifier.
|
||||
func (b *BrowserWindow) ID() uint { return b.id }
|
||||
|
||||
// Name returns the window name.
|
||||
func (b *BrowserWindow) Name() string {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
return b.name
|
||||
}
|
||||
|
||||
// ClientID returns the runtime nano-ID for this client.
|
||||
func (b *BrowserWindow) ClientID() string {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
return b.clientID
|
||||
}
|
||||
|
||||
// No-op windowing methods — browser windows have no native chrome.
|
||||
|
||||
func (b *BrowserWindow) Center() {}
|
||||
func (b *BrowserWindow) Close() {}
|
||||
func (b *BrowserWindow) DisableSizeConstraints() {}
|
||||
func (b *BrowserWindow) EnableSizeConstraints() {}
|
||||
func (b *BrowserWindow) ExecJS(_ string) {}
|
||||
func (b *BrowserWindow) Focus() {}
|
||||
func (b *BrowserWindow) ForceReload() {}
|
||||
func (b *BrowserWindow) HideMenuBar() {}
|
||||
func (b *BrowserWindow) OpenDevTools() {}
|
||||
func (b *BrowserWindow) Reload() {}
|
||||
func (b *BrowserWindow) Restore() {}
|
||||
func (b *BrowserWindow) Run() {}
|
||||
func (b *BrowserWindow) SetPosition(_ int, _ int) {}
|
||||
func (b *BrowserWindow) ShowMenuBar() {}
|
||||
func (b *BrowserWindow) SnapAssist() {}
|
||||
func (b *BrowserWindow) ToggleFrameless() {}
|
||||
func (b *BrowserWindow) ToggleFullscreen() {}
|
||||
func (b *BrowserWindow) ToggleMaximise() {}
|
||||
func (b *BrowserWindow) ToggleMenuBar() {}
|
||||
func (b *BrowserWindow) UnFullscreen() {}
|
||||
func (b *BrowserWindow) UnMaximise() {}
|
||||
func (b *BrowserWindow) UnMinimise() {}
|
||||
func (b *BrowserWindow) SetEnabled(_ bool) {}
|
||||
func (b *BrowserWindow) Flash(_ bool) {}
|
||||
func (b *BrowserWindow) SetMenu(_ *Menu) {}
|
||||
func (b *BrowserWindow) SetBounds(_ Rect) {}
|
||||
func (b *BrowserWindow) Zoom() {}
|
||||
func (b *BrowserWindow) ZoomIn() {}
|
||||
func (b *BrowserWindow) ZoomOut() {}
|
||||
func (b *BrowserWindow) OpenContextMenu(_ *ContextMenuData) {}
|
||||
func (b *BrowserWindow) HandleMessage(_ string) {}
|
||||
func (b *BrowserWindow) HandleWindowEvent(_ uint) {}
|
||||
func (b *BrowserWindow) HandleKeyEvent(_ string) {}
|
||||
func (b *BrowserWindow) AttachModal(_ Window) {}
|
||||
|
||||
// Internal editing stubs.
|
||||
func (b *BrowserWindow) cut() {}
|
||||
func (b *BrowserWindow) copy() {}
|
||||
func (b *BrowserWindow) paste() {}
|
||||
func (b *BrowserWindow) undo() {}
|
||||
func (b *BrowserWindow) redo() {}
|
||||
func (b *BrowserWindow) delete() {}
|
||||
func (b *BrowserWindow) selectAll() {}
|
||||
|
||||
// shouldUnconditionallyClose always returns false for browser windows.
|
||||
func (b *BrowserWindow) shouldUnconditionallyClose() bool { return false }
|
||||
|
||||
// Methods returning Window for chaining — always no-op for browser windows.
|
||||
|
||||
func (b *BrowserWindow) Fullscreen() Window { return b }
|
||||
func (b *BrowserWindow) Hide() Window { return b }
|
||||
func (b *BrowserWindow) Maximise() Window { return b }
|
||||
func (b *BrowserWindow) Minimise() Window { return b }
|
||||
func (b *BrowserWindow) Show() Window { return b }
|
||||
func (b *BrowserWindow) SetAlwaysOnTop(_ bool) Window { return b }
|
||||
func (b *BrowserWindow) SetBackgroundColour(_ RGBA) Window { return b }
|
||||
func (b *BrowserWindow) SetFrameless(_ bool) Window { return b }
|
||||
func (b *BrowserWindow) SetHTML(_ string) Window { return b }
|
||||
func (b *BrowserWindow) SetMinimiseButtonState(_ ButtonState) Window { return b }
|
||||
func (b *BrowserWindow) SetMaximiseButtonState(_ ButtonState) Window { return b }
|
||||
func (b *BrowserWindow) SetCloseButtonState(_ ButtonState) Window { return b }
|
||||
func (b *BrowserWindow) SetMaxSize(_ int, _ int) Window { return b }
|
||||
func (b *BrowserWindow) SetMinSize(_ int, _ int) Window { return b }
|
||||
func (b *BrowserWindow) SetRelativePosition(_ int, _ int) Window { return b }
|
||||
func (b *BrowserWindow) SetResizable(_ bool) Window { return b }
|
||||
func (b *BrowserWindow) SetIgnoreMouseEvents(_ bool) Window { return b }
|
||||
func (b *BrowserWindow) SetSize(_ int, _ int) Window { return b }
|
||||
func (b *BrowserWindow) SetTitle(_ string) Window { return b }
|
||||
func (b *BrowserWindow) SetURL(_ string) Window { return b }
|
||||
func (b *BrowserWindow) SetZoom(_ float64) Window { return b }
|
||||
func (b *BrowserWindow) SetContentProtection(_ bool) Window { return b }
|
||||
func (b *BrowserWindow) ZoomReset() Window { return b }
|
||||
|
||||
// Methods returning simple zero values.
|
||||
|
||||
func (b *BrowserWindow) GetBorderSizes() *LRTB { return nil }
|
||||
func (b *BrowserWindow) GetScreen() (*Screen, error) { return nil, nil }
|
||||
func (b *BrowserWindow) GetZoom() float64 { return 1.0 }
|
||||
func (b *BrowserWindow) Height() int { return 0 }
|
||||
func (b *BrowserWindow) Width() int { return 0 }
|
||||
func (b *BrowserWindow) IsFocused() bool { return false }
|
||||
func (b *BrowserWindow) IsFullscreen() bool { return false }
|
||||
func (b *BrowserWindow) IsIgnoreMouseEvents() bool { return false }
|
||||
func (b *BrowserWindow) IsMaximised() bool { return false }
|
||||
func (b *BrowserWindow) IsMinimised() bool { return false }
|
||||
func (b *BrowserWindow) IsVisible() bool { return true }
|
||||
func (b *BrowserWindow) Resizable() bool { return false }
|
||||
func (b *BrowserWindow) Position() (int, int) { return 0, 0 }
|
||||
func (b *BrowserWindow) RelativePosition() (int, int) { return 0, 0 }
|
||||
func (b *BrowserWindow) Size() (int, int) { return 0, 0 }
|
||||
func (b *BrowserWindow) Bounds() Rect { return Rect{} }
|
||||
func (b *BrowserWindow) NativeWindow() unsafe.Pointer { return nil }
|
||||
func (b *BrowserWindow) Print() error { return nil }
|
||||
|
||||
// DispatchWailsEvent is a no-op for browser windows; events are broadcast via WebSocket.
|
||||
func (b *BrowserWindow) DispatchWailsEvent(_ *CustomEvent) {}
|
||||
|
||||
// EmitEvent broadcasts a named event; always returns false in the stub.
|
||||
func (b *BrowserWindow) EmitEvent(_ string, _ ...any) bool { return false }
|
||||
|
||||
// Error logs an error message (no-op in the stub).
|
||||
func (b *BrowserWindow) Error(_ string, _ ...any) {}
|
||||
|
||||
// Info logs an info message (no-op in the stub).
|
||||
func (b *BrowserWindow) Info(_ string, _ ...any) {}
|
||||
|
||||
// OnWindowEvent registers a callback for a window event type; returns an unsubscribe func.
|
||||
//
|
||||
// unsubscribe := bw.OnWindowEvent(events.Common.WindowClosing, fn)
|
||||
func (b *BrowserWindow) OnWindowEvent(_ events.WindowEventType, _ func(*WindowEvent)) func() {
|
||||
return func() {}
|
||||
}
|
||||
|
||||
// RegisterHook registers a lifecycle hook; returns an unsubscribe func.
|
||||
func (b *BrowserWindow) RegisterHook(_ events.WindowEventType, _ func(*WindowEvent)) func() {
|
||||
return func() {}
|
||||
}
|
||||
|
||||
// handleDragAndDropMessage is a no-op for browser windows.
|
||||
func (b *BrowserWindow) handleDragAndDropMessage(_ []string, _ *DropTargetDetails) {}
|
||||
|
||||
// InitiateFrontendDropProcessing is a no-op for browser windows.
|
||||
func (b *BrowserWindow) InitiateFrontendDropProcessing(_ []string, _ int, _ int) {}
|
||||
65
stubs/wails/pkg/application/clipboard.go
Normal file
65
stubs/wails/pkg/application/clipboard.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package application
|
||||
|
||||
import "sync"
|
||||
|
||||
// Clipboard provides direct read/write access to the system clipboard.
|
||||
//
|
||||
// ok := clipboard.SetText("hello")
|
||||
// text, ok := clipboard.Text()
|
||||
type Clipboard struct {
|
||||
mu sync.RWMutex
|
||||
text string
|
||||
}
|
||||
|
||||
// SetText writes the given text to the clipboard and returns true on success.
|
||||
//
|
||||
// ok := clipboard.SetText("copied text")
|
||||
func (c *Clipboard) SetText(text string) bool {
|
||||
c.mu.Lock()
|
||||
c.text = text
|
||||
c.mu.Unlock()
|
||||
return true
|
||||
}
|
||||
|
||||
// Text reads the current clipboard text. Returns the text and true on success.
|
||||
//
|
||||
// text, ok := clipboard.Text()
|
||||
func (c *Clipboard) Text() (string, bool) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.text, true
|
||||
}
|
||||
|
||||
// ClipboardManager is the application-level clipboard surface.
|
||||
// Lazily initialises the underlying Clipboard on first use.
|
||||
//
|
||||
// manager.SetText("hello")
|
||||
// text, ok := manager.Text()
|
||||
type ClipboardManager struct {
|
||||
mu sync.Mutex
|
||||
clipboard *Clipboard
|
||||
}
|
||||
|
||||
// SetText writes text to the clipboard and returns true on success.
|
||||
//
|
||||
// ok := manager.SetText("hello")
|
||||
func (cm *ClipboardManager) SetText(text string) bool {
|
||||
return cm.instance().SetText(text)
|
||||
}
|
||||
|
||||
// Text reads the current clipboard text. Returns the text and true on success.
|
||||
//
|
||||
// text, ok := manager.Text()
|
||||
func (cm *ClipboardManager) Text() (string, bool) {
|
||||
return cm.instance().Text()
|
||||
}
|
||||
|
||||
// instance returns the Clipboard, creating it if it does not yet exist.
|
||||
func (cm *ClipboardManager) instance() *Clipboard {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
if cm.clipboard == nil {
|
||||
cm.clipboard = &Clipboard{}
|
||||
}
|
||||
return cm.clipboard
|
||||
}
|
||||
73
stubs/wails/pkg/application/context_menu.go
Normal file
73
stubs/wails/pkg/application/context_menu.go
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package application
|
||||
|
||||
import "sync"
|
||||
|
||||
// ContextMenu is a named Menu used as a right-click context menu.
|
||||
//
|
||||
// cm := manager.New()
|
||||
// cm.Add("Cut").OnClick(func(*Context) { ... })
|
||||
type ContextMenu struct {
|
||||
*Menu
|
||||
name string
|
||||
}
|
||||
|
||||
// ContextMenuManager manages named context menus for the application.
|
||||
//
|
||||
// manager.Add("fileList", cm)
|
||||
// menu, ok := manager.Get("fileList")
|
||||
type ContextMenuManager struct {
|
||||
mu sync.RWMutex
|
||||
menus map[string]*ContextMenu
|
||||
}
|
||||
|
||||
// New creates an empty, unnamed ContextMenu ready for population.
|
||||
//
|
||||
// cm := manager.New()
|
||||
// cm.Add("Open")
|
||||
func (cmm *ContextMenuManager) New() *ContextMenu {
|
||||
return &ContextMenu{Menu: NewMenu()}
|
||||
}
|
||||
|
||||
// Add registers a ContextMenu under the given name, replacing any existing entry.
|
||||
//
|
||||
// manager.Add("fileList", cm)
|
||||
func (cmm *ContextMenuManager) Add(name string, menu *ContextMenu) {
|
||||
cmm.mu.Lock()
|
||||
defer cmm.mu.Unlock()
|
||||
if cmm.menus == nil {
|
||||
cmm.menus = make(map[string]*ContextMenu)
|
||||
}
|
||||
cmm.menus[name] = menu
|
||||
}
|
||||
|
||||
// Remove unregisters the context menu with the given name.
|
||||
//
|
||||
// manager.Remove("fileList")
|
||||
func (cmm *ContextMenuManager) Remove(name string) {
|
||||
cmm.mu.Lock()
|
||||
defer cmm.mu.Unlock()
|
||||
delete(cmm.menus, name)
|
||||
}
|
||||
|
||||
// Get retrieves a registered context menu by name.
|
||||
//
|
||||
// menu, ok := manager.Get("fileList")
|
||||
func (cmm *ContextMenuManager) Get(name string) (*ContextMenu, bool) {
|
||||
cmm.mu.RLock()
|
||||
defer cmm.mu.RUnlock()
|
||||
menu, exists := cmm.menus[name]
|
||||
return menu, exists
|
||||
}
|
||||
|
||||
// GetAll returns all registered context menus as a slice.
|
||||
//
|
||||
// for _, cm := range manager.GetAll() { ... }
|
||||
func (cmm *ContextMenuManager) GetAll() []*ContextMenu {
|
||||
cmm.mu.RLock()
|
||||
defer cmm.mu.RUnlock()
|
||||
result := make([]*ContextMenu, 0, len(cmm.menus))
|
||||
for _, menu := range cmm.menus {
|
||||
result = append(result, menu)
|
||||
}
|
||||
return result
|
||||
}
|
||||
481
stubs/wails/pkg/application/dialog.go
Normal file
481
stubs/wails/pkg/application/dialog.go
Normal file
|
|
@ -0,0 +1,481 @@
|
|||
package application
|
||||
|
||||
// DialogType identifies the visual style of a message dialog.
|
||||
type DialogType int
|
||||
|
||||
const (
|
||||
InfoDialogType DialogType = iota
|
||||
QuestionDialogType DialogType = iota
|
||||
WarningDialogType DialogType = iota
|
||||
ErrorDialogType DialogType = iota
|
||||
)
|
||||
|
||||
// FileFilter describes a file type filter for open/save dialogs.
|
||||
//
|
||||
// filter := FileFilter{DisplayName: "Images (*.png;*.jpg)", Pattern: "*.png;*.jpg"}
|
||||
type FileFilter struct {
|
||||
DisplayName string
|
||||
Pattern string
|
||||
}
|
||||
|
||||
// Button is a labelled action in a MessageDialog.
|
||||
//
|
||||
// btn := dialog.AddButton("OK")
|
||||
// btn.SetAsDefault().OnClick(func() { ... })
|
||||
type Button struct {
|
||||
Label string
|
||||
IsCancel bool
|
||||
IsDefault bool
|
||||
Callback func()
|
||||
}
|
||||
|
||||
// OnClick registers a click handler on the button and returns itself for chaining.
|
||||
//
|
||||
// btn.OnClick(func() { saveFile() })
|
||||
func (b *Button) OnClick(callback func()) *Button {
|
||||
b.Callback = callback
|
||||
return b
|
||||
}
|
||||
|
||||
// SetAsDefault marks this button as the default (Enter key) action.
|
||||
func (b *Button) SetAsDefault() *Button {
|
||||
b.IsDefault = true
|
||||
return b
|
||||
}
|
||||
|
||||
// SetAsCancel marks this button as the cancel (Escape key) action.
|
||||
func (b *Button) SetAsCancel() *Button {
|
||||
b.IsCancel = true
|
||||
return b
|
||||
}
|
||||
|
||||
// MessageDialogOptions holds configuration for a MessageDialog.
|
||||
type MessageDialogOptions struct {
|
||||
DialogType DialogType
|
||||
Title string
|
||||
Message string
|
||||
Buttons []*Button
|
||||
Icon []byte
|
||||
}
|
||||
|
||||
// MessageDialog is an in-memory message dialog (info / question / warning / error).
|
||||
//
|
||||
// dialog.Info().SetTitle("Done").SetMessage("File saved.").Show()
|
||||
type MessageDialog struct {
|
||||
MessageDialogOptions
|
||||
}
|
||||
|
||||
// SetTitle sets the dialog window title.
|
||||
//
|
||||
// dialog.SetTitle("Confirm Delete")
|
||||
func (d *MessageDialog) SetTitle(title string) *MessageDialog {
|
||||
d.Title = title
|
||||
return d
|
||||
}
|
||||
|
||||
// SetMessage sets the body text shown in the dialog.
|
||||
//
|
||||
// dialog.SetMessage("Are you sure?")
|
||||
func (d *MessageDialog) SetMessage(message string) *MessageDialog {
|
||||
d.Message = message
|
||||
return d
|
||||
}
|
||||
|
||||
// SetIcon sets the icon bytes shown in the dialog.
|
||||
func (d *MessageDialog) SetIcon(icon []byte) *MessageDialog {
|
||||
d.Icon = icon
|
||||
return d
|
||||
}
|
||||
|
||||
// AddButton appends a labelled button and returns it for further configuration.
|
||||
//
|
||||
// btn := dialog.AddButton("Yes")
|
||||
func (d *MessageDialog) AddButton(label string) *Button {
|
||||
btn := &Button{Label: label}
|
||||
d.Buttons = append(d.Buttons, btn)
|
||||
return btn
|
||||
}
|
||||
|
||||
// AddButtons replaces the button list in bulk.
|
||||
func (d *MessageDialog) AddButtons(buttons []*Button) *MessageDialog {
|
||||
d.Buttons = buttons
|
||||
return d
|
||||
}
|
||||
|
||||
// SetDefaultButton marks the given button as the default action.
|
||||
func (d *MessageDialog) SetDefaultButton(button *Button) *MessageDialog {
|
||||
for _, b := range d.Buttons {
|
||||
b.IsDefault = false
|
||||
}
|
||||
button.IsDefault = true
|
||||
return d
|
||||
}
|
||||
|
||||
// SetCancelButton marks the given button as the cancel action.
|
||||
func (d *MessageDialog) SetCancelButton(button *Button) *MessageDialog {
|
||||
for _, b := range d.Buttons {
|
||||
b.IsCancel = false
|
||||
}
|
||||
button.IsCancel = true
|
||||
return d
|
||||
}
|
||||
|
||||
// AttachToWindow associates the dialog with a parent window (no-op in the stub).
|
||||
func (d *MessageDialog) AttachToWindow(window *WebviewWindow) *MessageDialog {
|
||||
return d
|
||||
}
|
||||
|
||||
// Show presents the dialog. No-op in the stub.
|
||||
func (d *MessageDialog) Show() {}
|
||||
|
||||
func newMessageDialog(dialogType DialogType) *MessageDialog {
|
||||
return &MessageDialog{
|
||||
MessageDialogOptions: MessageDialogOptions{DialogType: dialogType},
|
||||
}
|
||||
}
|
||||
|
||||
// OpenFileDialogOptions configures an OpenFileDialogStruct.
|
||||
type OpenFileDialogOptions struct {
|
||||
CanChooseDirectories bool
|
||||
CanChooseFiles bool
|
||||
CanCreateDirectories bool
|
||||
ShowHiddenFiles bool
|
||||
ResolvesAliases bool
|
||||
AllowsMultipleSelection bool
|
||||
HideExtension bool
|
||||
CanSelectHiddenExtension bool
|
||||
TreatsFilePackagesAsDirectories bool
|
||||
AllowsOtherFileTypes bool
|
||||
Filters []FileFilter
|
||||
Title string
|
||||
Message string
|
||||
ButtonText string
|
||||
Directory string
|
||||
}
|
||||
|
||||
// OpenFileDialogStruct is a builder for file-open dialogs.
|
||||
//
|
||||
// path, err := manager.OpenFile().SetTitle("Pick a file").PromptForSingleSelection()
|
||||
type OpenFileDialogStruct struct {
|
||||
canChooseDirectories bool
|
||||
canChooseFiles bool
|
||||
canCreateDirectories bool
|
||||
showHiddenFiles bool
|
||||
resolvesAliases bool
|
||||
allowsMultipleSelection bool
|
||||
hideExtension bool
|
||||
canSelectHiddenExtension bool
|
||||
treatsFilePackagesAsDirectories bool
|
||||
allowsOtherFileTypes bool
|
||||
filters []FileFilter
|
||||
title string
|
||||
message string
|
||||
buttonText string
|
||||
directory string
|
||||
}
|
||||
|
||||
func newOpenFileDialog() *OpenFileDialogStruct {
|
||||
return &OpenFileDialogStruct{
|
||||
canChooseFiles: true,
|
||||
canCreateDirectories: true,
|
||||
}
|
||||
}
|
||||
|
||||
// SetOptions applies all fields from OpenFileDialogOptions to the dialog.
|
||||
func (d *OpenFileDialogStruct) SetOptions(options *OpenFileDialogOptions) {
|
||||
d.title = options.Title
|
||||
d.message = options.Message
|
||||
d.buttonText = options.ButtonText
|
||||
d.directory = options.Directory
|
||||
d.canChooseDirectories = options.CanChooseDirectories
|
||||
d.canChooseFiles = options.CanChooseFiles
|
||||
d.canCreateDirectories = options.CanCreateDirectories
|
||||
d.showHiddenFiles = options.ShowHiddenFiles
|
||||
d.resolvesAliases = options.ResolvesAliases
|
||||
d.allowsMultipleSelection = options.AllowsMultipleSelection
|
||||
d.hideExtension = options.HideExtension
|
||||
d.canSelectHiddenExtension = options.CanSelectHiddenExtension
|
||||
d.treatsFilePackagesAsDirectories = options.TreatsFilePackagesAsDirectories
|
||||
d.allowsOtherFileTypes = options.AllowsOtherFileTypes
|
||||
d.filters = options.Filters
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) SetTitle(title string) *OpenFileDialogStruct {
|
||||
d.title = title
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) SetMessage(message string) *OpenFileDialogStruct {
|
||||
d.message = message
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) SetButtonText(text string) *OpenFileDialogStruct {
|
||||
d.buttonText = text
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) SetDirectory(directory string) *OpenFileDialogStruct {
|
||||
d.directory = directory
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) CanChooseFiles(canChooseFiles bool) *OpenFileDialogStruct {
|
||||
d.canChooseFiles = canChooseFiles
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) CanChooseDirectories(canChooseDirectories bool) *OpenFileDialogStruct {
|
||||
d.canChooseDirectories = canChooseDirectories
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) CanCreateDirectories(canCreateDirectories bool) *OpenFileDialogStruct {
|
||||
d.canCreateDirectories = canCreateDirectories
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) ShowHiddenFiles(showHiddenFiles bool) *OpenFileDialogStruct {
|
||||
d.showHiddenFiles = showHiddenFiles
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) HideExtension(hideExtension bool) *OpenFileDialogStruct {
|
||||
d.hideExtension = hideExtension
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) CanSelectHiddenExtension(canSelectHiddenExtension bool) *OpenFileDialogStruct {
|
||||
d.canSelectHiddenExtension = canSelectHiddenExtension
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) ResolvesAliases(resolvesAliases bool) *OpenFileDialogStruct {
|
||||
d.resolvesAliases = resolvesAliases
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) AllowsOtherFileTypes(allowsOtherFileTypes bool) *OpenFileDialogStruct {
|
||||
d.allowsOtherFileTypes = allowsOtherFileTypes
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) TreatsFilePackagesAsDirectories(treats bool) *OpenFileDialogStruct {
|
||||
d.treatsFilePackagesAsDirectories = treats
|
||||
return d
|
||||
}
|
||||
|
||||
// AddFilter appends a file type filter to the dialog.
|
||||
//
|
||||
// dialog.AddFilter("Images", "*.png;*.jpg")
|
||||
func (d *OpenFileDialogStruct) AddFilter(displayName, pattern string) *OpenFileDialogStruct {
|
||||
d.filters = append(d.filters, FileFilter{DisplayName: displayName, Pattern: pattern})
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *OpenFileDialogStruct) AttachToWindow(window *WebviewWindow) *OpenFileDialogStruct {
|
||||
return d
|
||||
}
|
||||
|
||||
// PromptForSingleSelection shows the dialog and returns the chosen path.
|
||||
// Always returns ("", nil) in the stub.
|
||||
//
|
||||
// path, err := dialog.PromptForSingleSelection()
|
||||
func (d *OpenFileDialogStruct) PromptForSingleSelection() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// PromptForMultipleSelection shows the dialog and returns all chosen paths.
|
||||
// Always returns (nil, nil) in the stub.
|
||||
//
|
||||
// paths, err := dialog.PromptForMultipleSelection()
|
||||
func (d *OpenFileDialogStruct) PromptForMultipleSelection() ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SaveFileDialogOptions configures a SaveFileDialogStruct.
|
||||
type SaveFileDialogOptions struct {
|
||||
CanCreateDirectories bool
|
||||
ShowHiddenFiles bool
|
||||
CanSelectHiddenExtension bool
|
||||
AllowOtherFileTypes bool
|
||||
HideExtension bool
|
||||
TreatsFilePackagesAsDirectories bool
|
||||
Title string
|
||||
Message string
|
||||
Directory string
|
||||
Filename string
|
||||
ButtonText string
|
||||
Filters []FileFilter
|
||||
}
|
||||
|
||||
// SaveFileDialogStruct is a builder for file-save dialogs.
|
||||
//
|
||||
// path, err := manager.SaveFile().SetTitle("Save As").PromptForSingleSelection()
|
||||
type SaveFileDialogStruct struct {
|
||||
canCreateDirectories bool
|
||||
showHiddenFiles bool
|
||||
canSelectHiddenExtension bool
|
||||
allowOtherFileTypes bool
|
||||
hideExtension bool
|
||||
treatsFilePackagesAsDirectories bool
|
||||
title string
|
||||
message string
|
||||
directory string
|
||||
filename string
|
||||
buttonText string
|
||||
filters []FileFilter
|
||||
}
|
||||
|
||||
func newSaveFileDialog() *SaveFileDialogStruct {
|
||||
return &SaveFileDialogStruct{canCreateDirectories: true}
|
||||
}
|
||||
|
||||
// SetOptions applies all fields from SaveFileDialogOptions to the dialog.
|
||||
func (d *SaveFileDialogStruct) SetOptions(options *SaveFileDialogOptions) {
|
||||
d.title = options.Title
|
||||
d.canCreateDirectories = options.CanCreateDirectories
|
||||
d.showHiddenFiles = options.ShowHiddenFiles
|
||||
d.canSelectHiddenExtension = options.CanSelectHiddenExtension
|
||||
d.allowOtherFileTypes = options.AllowOtherFileTypes
|
||||
d.hideExtension = options.HideExtension
|
||||
d.treatsFilePackagesAsDirectories = options.TreatsFilePackagesAsDirectories
|
||||
d.message = options.Message
|
||||
d.directory = options.Directory
|
||||
d.filename = options.Filename
|
||||
d.buttonText = options.ButtonText
|
||||
d.filters = options.Filters
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) SetTitle(title string) *SaveFileDialogStruct {
|
||||
d.title = title
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) SetMessage(message string) *SaveFileDialogStruct {
|
||||
d.message = message
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) SetDirectory(directory string) *SaveFileDialogStruct {
|
||||
d.directory = directory
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) SetFilename(filename string) *SaveFileDialogStruct {
|
||||
d.filename = filename
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) SetButtonText(text string) *SaveFileDialogStruct {
|
||||
d.buttonText = text
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) CanCreateDirectories(canCreateDirectories bool) *SaveFileDialogStruct {
|
||||
d.canCreateDirectories = canCreateDirectories
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) ShowHiddenFiles(showHiddenFiles bool) *SaveFileDialogStruct {
|
||||
d.showHiddenFiles = showHiddenFiles
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) CanSelectHiddenExtension(canSelectHiddenExtension bool) *SaveFileDialogStruct {
|
||||
d.canSelectHiddenExtension = canSelectHiddenExtension
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) AllowsOtherFileTypes(allowOtherFileTypes bool) *SaveFileDialogStruct {
|
||||
d.allowOtherFileTypes = allowOtherFileTypes
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) HideExtension(hideExtension bool) *SaveFileDialogStruct {
|
||||
d.hideExtension = hideExtension
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) TreatsFilePackagesAsDirectories(treats bool) *SaveFileDialogStruct {
|
||||
d.treatsFilePackagesAsDirectories = treats
|
||||
return d
|
||||
}
|
||||
|
||||
// AddFilter appends a file type filter to the dialog.
|
||||
//
|
||||
// dialog.AddFilter("Text files", "*.txt")
|
||||
func (d *SaveFileDialogStruct) AddFilter(displayName, pattern string) *SaveFileDialogStruct {
|
||||
d.filters = append(d.filters, FileFilter{DisplayName: displayName, Pattern: pattern})
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *SaveFileDialogStruct) AttachToWindow(window *WebviewWindow) *SaveFileDialogStruct {
|
||||
return d
|
||||
}
|
||||
|
||||
// PromptForSingleSelection shows the save dialog and returns the chosen path.
|
||||
// Always returns ("", nil) in the stub.
|
||||
//
|
||||
// path, err := dialog.PromptForSingleSelection()
|
||||
func (d *SaveFileDialogStruct) PromptForSingleSelection() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// DialogManager exposes factory methods for all dialog types.
|
||||
//
|
||||
// manager.Info().SetMessage("Saved!").Show()
|
||||
// path, _ := manager.OpenFile().PromptForSingleSelection()
|
||||
type DialogManager struct{}
|
||||
|
||||
// OpenFile creates a file-open dialog builder.
|
||||
func (dm *DialogManager) OpenFile() *OpenFileDialogStruct {
|
||||
return newOpenFileDialog()
|
||||
}
|
||||
|
||||
// OpenFileWithOptions creates a file-open dialog builder pre-populated from options.
|
||||
func (dm *DialogManager) OpenFileWithOptions(options *OpenFileDialogOptions) *OpenFileDialogStruct {
|
||||
result := newOpenFileDialog()
|
||||
result.SetOptions(options)
|
||||
return result
|
||||
}
|
||||
|
||||
// SaveFile creates a file-save dialog builder.
|
||||
func (dm *DialogManager) SaveFile() *SaveFileDialogStruct {
|
||||
return newSaveFileDialog()
|
||||
}
|
||||
|
||||
// SaveFileWithOptions creates a file-save dialog builder pre-populated from options.
|
||||
func (dm *DialogManager) SaveFileWithOptions(options *SaveFileDialogOptions) *SaveFileDialogStruct {
|
||||
result := newSaveFileDialog()
|
||||
result.SetOptions(options)
|
||||
return result
|
||||
}
|
||||
|
||||
// Info creates an information message dialog.
|
||||
//
|
||||
// manager.Info().SetMessage("Done").Show()
|
||||
func (dm *DialogManager) Info() *MessageDialog {
|
||||
return newMessageDialog(InfoDialogType)
|
||||
}
|
||||
|
||||
// Question creates a question message dialog.
|
||||
//
|
||||
// manager.Question().SetMessage("Continue?").Show()
|
||||
func (dm *DialogManager) Question() *MessageDialog {
|
||||
return newMessageDialog(QuestionDialogType)
|
||||
}
|
||||
|
||||
// Warning creates a warning message dialog.
|
||||
//
|
||||
// manager.Warning().SetMessage("Low disk space").Show()
|
||||
func (dm *DialogManager) Warning() *MessageDialog {
|
||||
return newMessageDialog(WarningDialogType)
|
||||
}
|
||||
|
||||
// Error creates an error message dialog.
|
||||
//
|
||||
// manager.Error().SetMessage("Write failed").Show()
|
||||
func (dm *DialogManager) Error() *MessageDialog {
|
||||
return newMessageDialog(ErrorDialogType)
|
||||
}
|
||||
63
stubs/wails/pkg/application/environment.go
Normal file
63
stubs/wails/pkg/application/environment.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package application
|
||||
|
||||
// EnvironmentInfo holds runtime information about the host OS and build.
|
||||
//
|
||||
// info := manager.Info()
|
||||
// fmt.Println(info.OS, info.Arch)
|
||||
type EnvironmentInfo struct {
|
||||
OS string
|
||||
Arch string
|
||||
Debug bool
|
||||
PlatformInfo map[string]any
|
||||
}
|
||||
|
||||
// EnvironmentManager provides queries about the host OS environment.
|
||||
//
|
||||
// if manager.IsDarkMode() { applyDarkTheme() }
|
||||
// accent := manager.GetAccentColor()
|
||||
type EnvironmentManager struct {
|
||||
darkMode bool
|
||||
accentColor string
|
||||
}
|
||||
|
||||
// IsDarkMode returns true when the OS is using a dark colour scheme.
|
||||
//
|
||||
// if manager.IsDarkMode() { applyDarkTheme() }
|
||||
func (em *EnvironmentManager) IsDarkMode() bool {
|
||||
return em.darkMode
|
||||
}
|
||||
|
||||
// GetAccentColor returns the OS accent colour as an rgb() string.
|
||||
//
|
||||
// colour := manager.GetAccentColor() // e.g. "rgb(0,122,255)"
|
||||
func (em *EnvironmentManager) GetAccentColor() string {
|
||||
if em.accentColor == "" {
|
||||
return "rgb(0,122,255)"
|
||||
}
|
||||
return em.accentColor
|
||||
}
|
||||
|
||||
// Info returns a snapshot of OS and build environment information.
|
||||
//
|
||||
// info := manager.Info()
|
||||
func (em *EnvironmentManager) Info() EnvironmentInfo {
|
||||
return EnvironmentInfo{
|
||||
PlatformInfo: make(map[string]any),
|
||||
}
|
||||
}
|
||||
|
||||
// OpenFileManager opens the file manager at the given path, optionally selecting the file.
|
||||
// No-op in the stub.
|
||||
//
|
||||
// err := manager.OpenFileManager("/home/user/docs", false)
|
||||
func (em *EnvironmentManager) OpenFileManager(path string, selectFile bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasFocusFollowsMouse reports whether the Linux desktop is configured for
|
||||
// focus-follows-mouse. Always returns false in the stub.
|
||||
//
|
||||
// if manager.HasFocusFollowsMouse() { adjustMouseBehaviour() }
|
||||
func (em *EnvironmentManager) HasFocusFollowsMouse() bool {
|
||||
return false
|
||||
}
|
||||
297
stubs/wails/pkg/application/events.go
Normal file
297
stubs/wails/pkg/application/events.go
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
)
|
||||
|
||||
// ApplicationEventContext carries structured data for an ApplicationEvent.
|
||||
//
|
||||
// files := event.Context().OpenedFiles()
|
||||
// dark := event.Context().IsDarkMode()
|
||||
type ApplicationEventContext struct {
|
||||
data map[string]any
|
||||
}
|
||||
|
||||
// OpenedFiles returns the list of files provided via the event context, or nil.
|
||||
//
|
||||
// for _, path := range event.Context().OpenedFiles() { open(path) }
|
||||
func (c *ApplicationEventContext) OpenedFiles() []string {
|
||||
if c.data == nil {
|
||||
return nil
|
||||
}
|
||||
files, ok := c.data["openedFiles"]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
result, ok := files.([]string)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// IsDarkMode returns true when the event context reports dark mode active.
|
||||
//
|
||||
// if event.Context().IsDarkMode() { applyDark() }
|
||||
func (c *ApplicationEventContext) IsDarkMode() bool {
|
||||
return c.getBool("isDarkMode")
|
||||
}
|
||||
|
||||
// HasVisibleWindows returns true when the event context reports at least one visible window.
|
||||
//
|
||||
// if event.Context().HasVisibleWindows() { ... }
|
||||
func (c *ApplicationEventContext) HasVisibleWindows() bool {
|
||||
return c.getBool("hasVisibleWindows")
|
||||
}
|
||||
|
||||
// Filename returns the filename value from the event context, or "".
|
||||
//
|
||||
// path := event.Context().Filename()
|
||||
func (c *ApplicationEventContext) Filename() string {
|
||||
if c.data == nil {
|
||||
return ""
|
||||
}
|
||||
v, ok := c.data["filename"]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
result, ok := v.(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// URL returns the URL value from the event context, or "".
|
||||
//
|
||||
// url := event.Context().URL()
|
||||
func (c *ApplicationEventContext) URL() string {
|
||||
if c.data == nil {
|
||||
return ""
|
||||
}
|
||||
v, ok := c.data["url"]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
result, ok := v.(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *ApplicationEventContext) getBool(key string) bool {
|
||||
if c.data == nil {
|
||||
return false
|
||||
}
|
||||
v, ok := c.data[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
result, ok := v.(bool)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ApplicationEvent is the event object delivered to OnApplicationEvent listeners.
|
||||
//
|
||||
// em.OnApplicationEvent(events.Common.ThemeChanged, func(e *application.ApplicationEvent) {
|
||||
// dark := e.Context().IsDarkMode()
|
||||
// })
|
||||
type ApplicationEvent struct {
|
||||
Id uint
|
||||
ctx *ApplicationEventContext
|
||||
cancelled atomic.Bool
|
||||
}
|
||||
|
||||
// Context returns the ApplicationEventContext attached to the event.
|
||||
func (e *ApplicationEvent) Context() *ApplicationEventContext {
|
||||
if e.ctx == nil {
|
||||
e.ctx = &ApplicationEventContext{data: make(map[string]any)}
|
||||
}
|
||||
return e.ctx
|
||||
}
|
||||
|
||||
// Cancel marks the event as cancelled, preventing further listener dispatch.
|
||||
func (e *ApplicationEvent) Cancel() {
|
||||
e.cancelled.Store(true)
|
||||
}
|
||||
|
||||
// IsCancelled reports whether the event has been cancelled.
|
||||
func (e *ApplicationEvent) IsCancelled() bool {
|
||||
return e.cancelled.Load()
|
||||
}
|
||||
|
||||
// customEventListener is an internal listener registration.
|
||||
type customEventListener struct {
|
||||
callback func(*CustomEvent)
|
||||
counter int // -1 = unlimited
|
||||
}
|
||||
|
||||
// applicationEventListener is an internal listener registration.
|
||||
type applicationEventListener struct {
|
||||
callback func(*ApplicationEvent)
|
||||
}
|
||||
|
||||
// EventManager manages custom and application-level event subscriptions.
|
||||
//
|
||||
// em.Emit("build:done", result)
|
||||
// cancel := em.On("build:done", func(e *application.CustomEvent) { ... })
|
||||
// em.OnApplicationEvent(events.Common.ThemeChanged, func(e *application.ApplicationEvent) { ... })
|
||||
type EventManager struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
customListeners map[string][]*customEventListener
|
||||
applicationListeners map[uint][]*applicationEventListener
|
||||
}
|
||||
|
||||
func (em *EventManager) ensureCustomListeners() {
|
||||
if em.customListeners == nil {
|
||||
em.customListeners = make(map[string][]*customEventListener)
|
||||
}
|
||||
}
|
||||
|
||||
func (em *EventManager) ensureApplicationListeners() {
|
||||
if em.applicationListeners == nil {
|
||||
em.applicationListeners = make(map[uint][]*applicationEventListener)
|
||||
}
|
||||
}
|
||||
|
||||
// Emit emits a custom event by name with optional data arguments.
|
||||
// Returns true if the event was cancelled by a listener.
|
||||
//
|
||||
// cancelled := em.Emit("build:done", buildResult)
|
||||
func (em *EventManager) Emit(name string, data ...any) bool {
|
||||
event := &CustomEvent{Name: name}
|
||||
if len(data) == 1 {
|
||||
event.Data = data[0]
|
||||
} else if len(data) > 1 {
|
||||
event.Data = data
|
||||
}
|
||||
return em.EmitEvent(event)
|
||||
}
|
||||
|
||||
// EmitEvent emits a pre-constructed CustomEvent.
|
||||
// Returns true if the event was cancelled by a listener.
|
||||
//
|
||||
// cancelled := em.EmitEvent(&application.CustomEvent{Name: "ping"})
|
||||
func (em *EventManager) EmitEvent(event *CustomEvent) bool {
|
||||
em.mu.RLock()
|
||||
listeners := append([]*customEventListener(nil), em.customListeners[event.Name]...)
|
||||
em.mu.RUnlock()
|
||||
|
||||
toRemove := []int{}
|
||||
for index, listener := range listeners {
|
||||
if event.IsCancelled() {
|
||||
break
|
||||
}
|
||||
listener.callback(event)
|
||||
if listener.counter > 0 {
|
||||
listener.counter--
|
||||
if listener.counter == 0 {
|
||||
toRemove = append(toRemove, index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(toRemove) > 0 {
|
||||
em.mu.Lock()
|
||||
remaining := em.customListeners[event.Name]
|
||||
for _, index := range toRemove {
|
||||
if index < len(remaining) {
|
||||
remaining = append(remaining[:index], remaining[index+1:]...)
|
||||
}
|
||||
}
|
||||
em.customListeners[event.Name] = remaining
|
||||
em.mu.Unlock()
|
||||
}
|
||||
|
||||
return event.IsCancelled()
|
||||
}
|
||||
|
||||
// On registers a persistent listener for the named custom event.
|
||||
// Returns a cancel function that deregisters the listener.
|
||||
//
|
||||
// cancel := em.On("build:done", func(e *application.CustomEvent) { ... })
|
||||
// defer cancel()
|
||||
func (em *EventManager) On(name string, callback func(event *CustomEvent)) func() {
|
||||
em.mu.Lock()
|
||||
em.ensureCustomListeners()
|
||||
listener := &customEventListener{callback: callback, counter: -1}
|
||||
em.customListeners[name] = append(em.customListeners[name], listener)
|
||||
em.mu.Unlock()
|
||||
|
||||
return func() {
|
||||
em.mu.Lock()
|
||||
defer em.mu.Unlock()
|
||||
slice := em.customListeners[name]
|
||||
for i, l := range slice {
|
||||
if l == listener {
|
||||
em.customListeners[name] = append(slice[:i], slice[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Off removes all listeners for the named custom event.
|
||||
//
|
||||
// em.Off("build:done")
|
||||
func (em *EventManager) Off(name string) {
|
||||
em.mu.Lock()
|
||||
delete(em.customListeners, name)
|
||||
em.mu.Unlock()
|
||||
}
|
||||
|
||||
// OnMultiple registers a listener that fires at most counter times, then auto-deregisters.
|
||||
//
|
||||
// em.OnMultiple("ping", func(e *application.CustomEvent) { ... }, 3)
|
||||
func (em *EventManager) OnMultiple(name string, callback func(event *CustomEvent), counter int) {
|
||||
em.mu.Lock()
|
||||
em.ensureCustomListeners()
|
||||
em.customListeners[name] = append(em.customListeners[name], &customEventListener{
|
||||
callback: callback,
|
||||
counter: counter,
|
||||
})
|
||||
em.mu.Unlock()
|
||||
}
|
||||
|
||||
// Reset removes all custom event listeners.
|
||||
//
|
||||
// em.Reset()
|
||||
func (em *EventManager) Reset() {
|
||||
em.mu.Lock()
|
||||
em.customListeners = make(map[string][]*customEventListener)
|
||||
em.mu.Unlock()
|
||||
}
|
||||
|
||||
// OnApplicationEvent registers a listener for a platform application event.
|
||||
// Returns a cancel function that deregisters the listener.
|
||||
//
|
||||
// cancel := em.OnApplicationEvent(events.Common.ThemeChanged, func(e *application.ApplicationEvent) { ... })
|
||||
// defer cancel()
|
||||
func (em *EventManager) OnApplicationEvent(eventType events.ApplicationEventType, callback func(event *ApplicationEvent)) func() {
|
||||
eventID := uint(eventType)
|
||||
em.mu.Lock()
|
||||
em.ensureApplicationListeners()
|
||||
listener := &applicationEventListener{callback: callback}
|
||||
em.applicationListeners[eventID] = append(em.applicationListeners[eventID], listener)
|
||||
em.mu.Unlock()
|
||||
|
||||
return func() {
|
||||
em.mu.Lock()
|
||||
defer em.mu.Unlock()
|
||||
slice := em.applicationListeners[eventID]
|
||||
for i, l := range slice {
|
||||
if l == listener {
|
||||
em.applicationListeners[eventID] = append(slice[:i], slice[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
69
stubs/wails/pkg/application/keybinding.go
Normal file
69
stubs/wails/pkg/application/keybinding.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package application
|
||||
|
||||
import "sync"
|
||||
|
||||
// KeyBinding pairs an accelerator string with its callback.
|
||||
// binding := &KeyBinding{Accelerator: "Ctrl+K", Callback: fn}
|
||||
type KeyBinding struct {
|
||||
Accelerator string
|
||||
Callback func(window Window)
|
||||
}
|
||||
|
||||
// KeyBindingManager holds all registered global key bindings in memory.
|
||||
// manager.Add("Ctrl+K", fn) — manager.Remove("Ctrl+K") — manager.GetAll()
|
||||
type KeyBindingManager struct {
|
||||
mu sync.RWMutex
|
||||
bindings map[string]func(window Window)
|
||||
}
|
||||
|
||||
// NewKeyBindingManager constructs an empty KeyBindingManager.
|
||||
// manager := NewKeyBindingManager()
|
||||
func NewKeyBindingManager() *KeyBindingManager {
|
||||
return &KeyBindingManager{
|
||||
bindings: make(map[string]func(window Window)),
|
||||
}
|
||||
}
|
||||
|
||||
// Add registers a callback for the given accelerator string.
|
||||
// manager.Add("Ctrl+Shift+P", func(w Window) { w.Focus() })
|
||||
func (m *KeyBindingManager) Add(accelerator string, callback func(window Window)) {
|
||||
m.mu.Lock()
|
||||
m.bindings[accelerator] = callback
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes the binding for the given accelerator.
|
||||
// manager.Remove("Ctrl+Shift+P")
|
||||
func (m *KeyBindingManager) Remove(accelerator string) {
|
||||
m.mu.Lock()
|
||||
delete(m.bindings, accelerator)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Process fires the callback for accelerator if registered, returning true when handled.
|
||||
// handled := manager.Process("Ctrl+K", window)
|
||||
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 bindings.
|
||||
// for _, b := range manager.GetAll() { fmt.Println(b.Accelerator) }
|
||||
func (m *KeyBindingManager) GetAll() []*KeyBinding {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
result := make([]*KeyBinding, 0, len(m.bindings))
|
||||
for accelerator, callback := range m.bindings {
|
||||
result = append(result, &KeyBinding{
|
||||
Accelerator: accelerator,
|
||||
Callback: callback,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
379
stubs/wails/pkg/application/menuitem.go
Normal file
379
stubs/wails/pkg/application/menuitem.go
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
package application
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
// Role identifies a platform-defined menu role.
|
||||
//
|
||||
// item := NewRole(Quit) // produces the platform quit item
|
||||
type Role uint
|
||||
|
||||
const (
|
||||
NoRole Role = iota
|
||||
AppMenu Role = iota
|
||||
EditMenu Role = iota
|
||||
ViewMenu Role = iota
|
||||
WindowMenu Role = iota
|
||||
ServicesMenu Role = iota
|
||||
HelpMenu Role = iota
|
||||
SpeechMenu Role = iota
|
||||
FileMenu Role = iota
|
||||
|
||||
Hide Role = iota
|
||||
HideOthers Role = iota
|
||||
ShowAll Role = iota
|
||||
BringAllToFront Role = iota
|
||||
UnHide Role = iota
|
||||
About Role = iota
|
||||
Undo Role = iota
|
||||
Redo Role = iota
|
||||
Cut Role = iota
|
||||
Copy Role = iota
|
||||
Paste Role = iota
|
||||
PasteAndMatchStyle Role = iota
|
||||
SelectAll Role = iota
|
||||
Delete Role = iota
|
||||
Quit Role = iota
|
||||
CloseWindow Role = iota
|
||||
Reload Role = iota
|
||||
ForceReload Role = iota
|
||||
OpenDevTools Role = iota
|
||||
ResetZoom Role = iota
|
||||
ZoomIn Role = iota
|
||||
ZoomOut Role = iota
|
||||
ToggleFullscreen Role = iota
|
||||
Minimise Role = iota
|
||||
Zoom Role = iota
|
||||
FullScreen Role = iota
|
||||
NewFile Role = iota
|
||||
Open Role = iota
|
||||
Save Role = iota
|
||||
SaveAs Role = iota
|
||||
StartSpeaking Role = iota
|
||||
StopSpeaking Role = iota
|
||||
Revert Role = iota
|
||||
Print Role = iota
|
||||
PageLayout Role = iota
|
||||
Find Role = iota
|
||||
FindAndReplace Role = iota
|
||||
FindNext Role = iota
|
||||
FindPrevious Role = iota
|
||||
Front Role = iota
|
||||
Help Role = iota
|
||||
)
|
||||
|
||||
// menuItemType classifies what kind of item a MenuItem is.
|
||||
type menuItemType int
|
||||
|
||||
const (
|
||||
menuItemTypeText menuItemType = iota
|
||||
menuItemTypeSeparator menuItemType = iota
|
||||
menuItemTypeCheckbox menuItemType = iota
|
||||
menuItemTypeRadio menuItemType = iota
|
||||
menuItemTypeSubmenu menuItemType = iota
|
||||
)
|
||||
|
||||
var globalMenuItemID uintptr
|
||||
|
||||
// MenuItem is a node in a menu tree.
|
||||
//
|
||||
// item := NewMenuItem("Preferences").
|
||||
// SetAccelerator("CmdOrCtrl+,").
|
||||
// OnClick(func(ctx *Context) { openPrefs() })
|
||||
type MenuItem struct {
|
||||
id uint
|
||||
label string
|
||||
tooltip string
|
||||
disabled bool
|
||||
checked bool
|
||||
hidden bool
|
||||
bitmap []byte
|
||||
submenu *Menu
|
||||
callback func(*Context)
|
||||
itemType menuItemType
|
||||
acceleratorStr string
|
||||
role Role
|
||||
contextMenuData *ContextMenuData
|
||||
|
||||
radioGroupMembers []*MenuItem
|
||||
}
|
||||
|
||||
func nextMenuItemID() uint {
|
||||
return uint(atomic.AddUintptr(&globalMenuItemID, 1))
|
||||
}
|
||||
|
||||
// NewMenuItem creates a standard clickable menu item with the given label.
|
||||
//
|
||||
// item := NewMenuItem("Save").OnClick(func(ctx *Context) { save() })
|
||||
func NewMenuItem(label string) *MenuItem {
|
||||
return &MenuItem{
|
||||
id: nextMenuItemID(),
|
||||
label: label,
|
||||
itemType: menuItemTypeText,
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMenuItemSeparator creates a horizontal separator.
|
||||
//
|
||||
// menu.AppendItem(NewMenuItemSeparator())
|
||||
func NewMenuItemSeparator() *MenuItem {
|
||||
return &MenuItem{
|
||||
id: nextMenuItemID(),
|
||||
itemType: menuItemTypeSeparator,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMenuItemCheckbox creates a checkable menu item.
|
||||
//
|
||||
// item := NewMenuItemCheckbox("Show Toolbar", true)
|
||||
func NewMenuItemCheckbox(label string, checked bool) *MenuItem {
|
||||
return &MenuItem{
|
||||
id: nextMenuItemID(),
|
||||
label: label,
|
||||
checked: checked,
|
||||
itemType: menuItemTypeCheckbox,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMenuItemRadio creates a radio-group menu item.
|
||||
//
|
||||
// light := NewMenuItemRadio("Light Theme", true)
|
||||
func NewMenuItemRadio(label string, checked bool) *MenuItem {
|
||||
return &MenuItem{
|
||||
id: nextMenuItemID(),
|
||||
label: label,
|
||||
checked: checked,
|
||||
itemType: menuItemTypeRadio,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSubMenuItem creates an item that reveals a child menu on hover.
|
||||
//
|
||||
// sub := NewSubMenuItem("Recent Files")
|
||||
// sub.GetSubmenu().Add("report.pdf")
|
||||
func NewSubMenuItem(label string) *MenuItem {
|
||||
return &MenuItem{
|
||||
id: nextMenuItemID(),
|
||||
label: label,
|
||||
itemType: menuItemTypeSubmenu,
|
||||
submenu: &Menu{label: label},
|
||||
}
|
||||
}
|
||||
|
||||
// NewRole creates a platform-managed menu item for the given role.
|
||||
//
|
||||
// menu.AppendItem(NewRole(Quit))
|
||||
func NewRole(role Role) *MenuItem {
|
||||
item := &MenuItem{
|
||||
id: nextMenuItemID(),
|
||||
label: roleLabel(role),
|
||||
itemType: menuItemTypeText,
|
||||
role: role,
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func roleLabel(role Role) string {
|
||||
switch role {
|
||||
case AppMenu:
|
||||
return "Application"
|
||||
case EditMenu:
|
||||
return "Edit"
|
||||
case ViewMenu:
|
||||
return "View"
|
||||
case WindowMenu:
|
||||
return "Window"
|
||||
case ServicesMenu:
|
||||
return "Services"
|
||||
case HelpMenu:
|
||||
return "Help"
|
||||
case SpeechMenu:
|
||||
return "Speech"
|
||||
case FileMenu:
|
||||
return "File"
|
||||
case Hide:
|
||||
return "Hide"
|
||||
case HideOthers:
|
||||
return "Hide Others"
|
||||
case ShowAll:
|
||||
return "Show All"
|
||||
case BringAllToFront:
|
||||
return "Bring All to Front"
|
||||
case UnHide:
|
||||
return "Unhide"
|
||||
case About:
|
||||
return "About"
|
||||
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 Reload:
|
||||
return "Reload"
|
||||
case ForceReload:
|
||||
return "Force Reload"
|
||||
case OpenDevTools:
|
||||
return "Open Dev Tools"
|
||||
case ResetZoom:
|
||||
return "Reset Zoom"
|
||||
case ZoomIn:
|
||||
return "Zoom In"
|
||||
case ZoomOut:
|
||||
return "Zoom Out"
|
||||
case ToggleFullscreen:
|
||||
return "Toggle Fullscreen"
|
||||
case Minimise:
|
||||
return "Minimise"
|
||||
case Zoom:
|
||||
return "Zoom"
|
||||
case FullScreen:
|
||||
return "Fullscreen"
|
||||
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 Print:
|
||||
return "Print"
|
||||
case PageLayout:
|
||||
return "Page Layout"
|
||||
case Find:
|
||||
return "Find"
|
||||
case FindAndReplace:
|
||||
return "Find and Replace"
|
||||
case FindNext:
|
||||
return "Find Next"
|
||||
case FindPrevious:
|
||||
return "Find Previous"
|
||||
case Front:
|
||||
return "Bring All to Front"
|
||||
case Help:
|
||||
return "Help"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Fluent setters — all return *MenuItem for chaining.
|
||||
|
||||
// SetLabel updates the visible label.
|
||||
func (m *MenuItem) SetLabel(s string) *MenuItem { m.label = s; return m }
|
||||
|
||||
// SetAccelerator sets the keyboard shortcut string (e.g. "CmdOrCtrl+S").
|
||||
func (m *MenuItem) SetAccelerator(shortcut string) *MenuItem {
|
||||
m.acceleratorStr = shortcut
|
||||
return m
|
||||
}
|
||||
|
||||
// GetAccelerator returns the raw accelerator string.
|
||||
func (m *MenuItem) GetAccelerator() string { return m.acceleratorStr }
|
||||
|
||||
// RemoveAccelerator clears the keyboard shortcut.
|
||||
func (m *MenuItem) RemoveAccelerator() { m.acceleratorStr = "" }
|
||||
|
||||
// SetTooltip sets the hover tooltip.
|
||||
func (m *MenuItem) SetTooltip(s string) *MenuItem { m.tooltip = s; return m }
|
||||
|
||||
// SetRole assigns a platform role to the item.
|
||||
func (m *MenuItem) SetRole(role Role) *MenuItem { m.role = role; return m }
|
||||
|
||||
// SetEnabled enables or disables the item.
|
||||
func (m *MenuItem) SetEnabled(enabled bool) *MenuItem { m.disabled = !enabled; return m }
|
||||
|
||||
// SetBitmap sets a raw-bytes icon for the item (Windows).
|
||||
func (m *MenuItem) SetBitmap(bitmap []byte) *MenuItem {
|
||||
m.bitmap = append([]byte(nil), bitmap...)
|
||||
return m
|
||||
}
|
||||
|
||||
// SetChecked sets the checked state for checkbox/radio items.
|
||||
func (m *MenuItem) SetChecked(checked bool) *MenuItem { m.checked = checked; return m }
|
||||
|
||||
// SetHidden hides or shows the item without removing it.
|
||||
func (m *MenuItem) SetHidden(hidden bool) *MenuItem { m.hidden = hidden; return m }
|
||||
|
||||
// OnClick registers the callback invoked when the item is activated.
|
||||
func (m *MenuItem) OnClick(f func(*Context)) *MenuItem { m.callback = f; return m }
|
||||
|
||||
// Getters
|
||||
|
||||
// Label returns the item's display label.
|
||||
func (m *MenuItem) Label() string { return m.label }
|
||||
|
||||
// Tooltip returns the hover tooltip.
|
||||
func (m *MenuItem) Tooltip() string { return m.tooltip }
|
||||
|
||||
// Enabled reports whether the item is interactive.
|
||||
func (m *MenuItem) Enabled() bool { return !m.disabled }
|
||||
|
||||
// Checked reports whether the item is checked.
|
||||
func (m *MenuItem) Checked() bool { return m.checked }
|
||||
|
||||
// Hidden reports whether the item is hidden.
|
||||
func (m *MenuItem) Hidden() bool { return m.hidden }
|
||||
|
||||
// IsSeparator reports whether this is a separator item.
|
||||
func (m *MenuItem) IsSeparator() bool { return m.itemType == menuItemTypeSeparator }
|
||||
|
||||
// IsSubmenu reports whether this item contains a child menu.
|
||||
func (m *MenuItem) IsSubmenu() bool { return m.itemType == menuItemTypeSubmenu }
|
||||
|
||||
// IsCheckbox reports whether this is a checkbox item.
|
||||
func (m *MenuItem) IsCheckbox() bool { return m.itemType == menuItemTypeCheckbox }
|
||||
|
||||
// IsRadio reports whether this is a radio item.
|
||||
func (m *MenuItem) IsRadio() bool { return m.itemType == menuItemTypeRadio }
|
||||
|
||||
// GetSubmenu returns the submenu, or nil if this is not a submenu item.
|
||||
func (m *MenuItem) GetSubmenu() *Menu { return m.submenu }
|
||||
|
||||
// Clone returns a shallow copy of the MenuItem.
|
||||
func (m *MenuItem) Clone() *MenuItem {
|
||||
cloned := *m
|
||||
if m.submenu != nil {
|
||||
cloned.submenu = m.submenu.Clone()
|
||||
}
|
||||
if m.bitmap != nil {
|
||||
cloned.bitmap = append([]byte(nil), m.bitmap...)
|
||||
}
|
||||
if m.contextMenuData != nil {
|
||||
cloned.contextMenuData = m.contextMenuData.clone()
|
||||
}
|
||||
cloned.radioGroupMembers = nil
|
||||
return &cloned
|
||||
}
|
||||
|
||||
// Destroy frees resources held by the item and its submenu.
|
||||
func (m *MenuItem) Destroy() {
|
||||
if m.submenu != nil {
|
||||
m.submenu.Destroy()
|
||||
m.submenu = nil
|
||||
}
|
||||
m.callback = nil
|
||||
m.radioGroupMembers = nil
|
||||
m.contextMenuData = nil
|
||||
}
|
||||
202
stubs/wails/pkg/application/screen.go
Normal file
202
stubs/wails/pkg/application/screen.go
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Rect describes an axis-aligned rectangle.
|
||||
// r := Rect{X: 0, Y: 0, Width: 1920, Height: 1080}
|
||||
type Rect struct {
|
||||
X int
|
||||
Y int
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
// Point is a 2D integer coordinate.
|
||||
// p := Point{X: 100, Y: 200}
|
||||
type Point struct {
|
||||
X int
|
||||
Y int
|
||||
}
|
||||
|
||||
// Size holds a width/height pair.
|
||||
// s := Size{Width: 1920, Height: 1080}
|
||||
type Size struct {
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
// Origin returns the top-left corner of the rect as a Point.
|
||||
// origin := r.Origin()
|
||||
func (r Rect) Origin() Point { return Point{X: r.X, Y: r.Y} }
|
||||
|
||||
// Corner returns the exclusive bottom-right corner of the rect.
|
||||
// corner := r.Corner()
|
||||
func (r Rect) Corner() Point { return Point{X: r.X + r.Width, Y: r.Y + r.Height} }
|
||||
|
||||
// InsideCorner returns the last pixel inside the rect.
|
||||
// inside := r.InsideCorner()
|
||||
func (r Rect) InsideCorner() Point { return Point{X: r.X + r.Width - 1, Y: r.Y + r.Height - 1} }
|
||||
|
||||
// RectSize returns the size of the rect.
|
||||
// s := r.RectSize()
|
||||
func (r Rect) RectSize() Size { return Size{Width: r.Width, Height: r.Height} }
|
||||
|
||||
// IsEmpty reports whether the rect has no area.
|
||||
// if r.IsEmpty() { return }
|
||||
func (r Rect) IsEmpty() bool { return r.Width <= 0 || r.Height <= 0 }
|
||||
|
||||
// Contains reports whether the point lies inside the rect.
|
||||
// if r.Contains(Point{X: 100, Y: 200}) { ... }
|
||||
func (r Rect) Contains(point Point) bool {
|
||||
return point.X >= r.X && point.X < r.X+r.Width &&
|
||||
point.Y >= r.Y && point.Y < r.Y+r.Height
|
||||
}
|
||||
|
||||
// Intersect returns the overlapping rectangle of r and other.
|
||||
// overlap := r.Intersect(otherRect)
|
||||
func (r Rect) Intersect(other Rect) Rect {
|
||||
if r.IsEmpty() || other.IsEmpty() {
|
||||
return Rect{}
|
||||
}
|
||||
maxLeft := max(r.X, other.X)
|
||||
maxTop := max(r.Y, other.Y)
|
||||
minRight := min(r.X+r.Width, other.X+other.Width)
|
||||
minBottom := min(r.Y+r.Height, other.Y+other.Height)
|
||||
if minRight > maxLeft && minBottom > maxTop {
|
||||
return Rect{X: maxLeft, Y: maxTop, Width: minRight - maxLeft, Height: minBottom - maxTop}
|
||||
}
|
||||
return Rect{}
|
||||
}
|
||||
|
||||
// Screen describes a physical display.
|
||||
// primary := manager.GetPrimary()
|
||||
type Screen struct {
|
||||
ID string
|
||||
Name string
|
||||
ScaleFactor float32
|
||||
X int
|
||||
Y int
|
||||
Size Size
|
||||
Bounds Rect
|
||||
PhysicalBounds Rect
|
||||
WorkArea Rect
|
||||
PhysicalWorkArea Rect
|
||||
IsPrimary bool
|
||||
Rotation float32
|
||||
}
|
||||
|
||||
// Origin returns the logical origin of the screen.
|
||||
// origin := screen.Origin()
|
||||
func (s Screen) Origin() Point { return Point{X: s.X, Y: s.Y} }
|
||||
|
||||
// scale converts a value between physical and DIP coordinates for this screen.
|
||||
func (s Screen) scale(value int, toDIP bool) int {
|
||||
if toDIP {
|
||||
return int(math.Ceil(float64(value) / float64(s.ScaleFactor)))
|
||||
}
|
||||
return int(math.Floor(float64(value) * float64(s.ScaleFactor)))
|
||||
}
|
||||
|
||||
// ScreenManager holds the list of known screens in memory.
|
||||
// screens := manager.GetAll()
|
||||
type ScreenManager struct {
|
||||
mu sync.RWMutex
|
||||
screens []*Screen
|
||||
primaryScreen *Screen
|
||||
}
|
||||
|
||||
// NewScreenManager constructs an empty ScreenManager.
|
||||
// manager := NewScreenManager()
|
||||
func NewScreenManager() *ScreenManager {
|
||||
return &ScreenManager{}
|
||||
}
|
||||
|
||||
// SetScreens replaces the tracked screen list; the first screen with IsPrimary == true becomes primary.
|
||||
// manager.SetScreens([]*Screen{primary, secondary})
|
||||
func (m *ScreenManager) SetScreens(screens []*Screen) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.screens = screens
|
||||
m.primaryScreen = nil
|
||||
for _, screen := range screens {
|
||||
if screen.IsPrimary {
|
||||
m.primaryScreen = screen
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetAll returns all tracked screens.
|
||||
// for _, s := range manager.GetAll() { fmt.Println(s.Name) }
|
||||
func (m *ScreenManager) GetAll() []*Screen {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
result := make([]*Screen, len(m.screens))
|
||||
copy(result, m.screens)
|
||||
return result
|
||||
}
|
||||
|
||||
// GetPrimary returns the primary screen, or nil if none is set.
|
||||
// primary := manager.GetPrimary()
|
||||
func (m *ScreenManager) GetPrimary() *Screen {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.primaryScreen
|
||||
}
|
||||
|
||||
// GetCurrent returns the screen nearest to the given DIP point.
|
||||
// current := manager.GetCurrent(Point{X: 500, Y: 300})
|
||||
func (m *ScreenManager) GetCurrent(dipPoint Point) *Screen {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for _, screen := range m.screens {
|
||||
if screen.Bounds.Contains(dipPoint) {
|
||||
return screen
|
||||
}
|
||||
}
|
||||
return m.primaryScreen
|
||||
}
|
||||
|
||||
// DipToPhysicalPoint converts a DIP point to physical pixels using the nearest screen.
|
||||
// physical := manager.DipToPhysicalPoint(Point{X: 100, Y: 200})
|
||||
func (m *ScreenManager) DipToPhysicalPoint(dipPoint Point) Point {
|
||||
screen := m.GetCurrent(dipPoint)
|
||||
if screen == nil {
|
||||
return dipPoint
|
||||
}
|
||||
relativeX := dipPoint.X - screen.Bounds.X
|
||||
relativeY := dipPoint.Y - screen.Bounds.Y
|
||||
return Point{
|
||||
X: screen.PhysicalBounds.X + screen.scale(relativeX, false),
|
||||
Y: screen.PhysicalBounds.Y + screen.scale(relativeY, false),
|
||||
}
|
||||
}
|
||||
|
||||
// PhysicalToDipPoint converts physical pixel coordinates to DIP using the nearest screen.
|
||||
// dip := manager.PhysicalToDipPoint(Point{X: 200, Y: 400})
|
||||
func (m *ScreenManager) PhysicalToDipPoint(physicalPoint Point) Point {
|
||||
m.mu.RLock()
|
||||
var nearest *Screen
|
||||
for _, screen := range m.screens {
|
||||
if screen.PhysicalBounds.Contains(physicalPoint) {
|
||||
nearest = screen
|
||||
break
|
||||
}
|
||||
}
|
||||
if nearest == nil {
|
||||
nearest = m.primaryScreen
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
if nearest == nil {
|
||||
return physicalPoint
|
||||
}
|
||||
relativeX := physicalPoint.X - nearest.PhysicalBounds.X
|
||||
relativeY := physicalPoint.Y - nearest.PhysicalBounds.Y
|
||||
return Point{
|
||||
X: nearest.Bounds.X + nearest.scale(relativeX, true),
|
||||
Y: nearest.Bounds.Y + nearest.scale(relativeY, true),
|
||||
}
|
||||
}
|
||||
76
stubs/wails/pkg/application/services.go
Normal file
76
stubs/wails/pkg/application/services.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package application
|
||||
|
||||
import "context"
|
||||
|
||||
// Service wraps a bound type instance for registration with the application.
|
||||
// svc := NewService(&MyService{})
|
||||
type Service struct {
|
||||
instance any
|
||||
options ServiceOptions
|
||||
}
|
||||
|
||||
// ServiceOptions provides optional configuration for a Service.
|
||||
// opts := ServiceOptions{Name: "my-service", Route: "/api/my"}
|
||||
type ServiceOptions struct {
|
||||
// Name overrides the service name used in logging and debugging.
|
||||
// Defaults to the value from ServiceName interface, or the type name.
|
||||
Name string
|
||||
|
||||
// Route mounts the service on the internal asset server at this path
|
||||
// when the instance implements http.Handler.
|
||||
Route string
|
||||
|
||||
// MarshalError serialises errors returned by service methods to JSON.
|
||||
// Return nil to fall back to the globally configured error handler.
|
||||
MarshalError func(error) []byte
|
||||
}
|
||||
|
||||
// DefaultServiceOptions is the default service options used by NewService.
|
||||
var DefaultServiceOptions = ServiceOptions{}
|
||||
|
||||
// NewService wraps instance as a Service.
|
||||
// svc := NewService(&Calculator{})
|
||||
func NewService[T any](instance *T) Service {
|
||||
return Service{instance: instance, options: DefaultServiceOptions}
|
||||
}
|
||||
|
||||
// NewServiceWithOptions wraps instance as a Service with explicit options.
|
||||
// svc := NewServiceWithOptions(&Calculator{}, ServiceOptions{Name: "calculator"})
|
||||
func NewServiceWithOptions[T any](instance *T, options ServiceOptions) Service {
|
||||
service := NewService(instance)
|
||||
service.options = options
|
||||
return service
|
||||
}
|
||||
|
||||
// Instance returns the underlying pointer passed to NewService.
|
||||
// raw := svc.Instance().(*Calculator)
|
||||
func (s Service) Instance() any {
|
||||
return s.instance
|
||||
}
|
||||
|
||||
// Options returns the ServiceOptions for this service.
|
||||
// opts := svc.Options()
|
||||
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 "my-service" }
|
||||
type ServiceName interface {
|
||||
ServiceName() string
|
||||
}
|
||||
|
||||
// ServiceStartup is an optional interface for services that need initialisation.
|
||||
// Called in registration order during application startup.
|
||||
// func (s *MyService) ServiceStartup(ctx context.Context, opts ServiceOptions) error { ... }
|
||||
type ServiceStartup interface {
|
||||
ServiceStartup(ctx context.Context, options ServiceOptions) error
|
||||
}
|
||||
|
||||
// ServiceShutdown is an optional interface for services that need cleanup.
|
||||
// Called in reverse registration order during application shutdown.
|
||||
// func (s *MyService) ServiceShutdown() error { ... }
|
||||
type ServiceShutdown interface {
|
||||
ServiceShutdown() error
|
||||
}
|
||||
471
stubs/wails/pkg/application/webview_window_options.go
Normal file
471
stubs/wails/pkg/application/webview_window_options.go
Normal file
|
|
@ -0,0 +1,471 @@
|
|||
package application
|
||||
|
||||
import "github.com/wailsapp/wails/v3/pkg/events"
|
||||
|
||||
// WindowState represents the starting visual state of a window.
|
||||
type WindowState int
|
||||
|
||||
const (
|
||||
WindowStateNormal WindowState = iota
|
||||
WindowStateMinimised WindowState = iota
|
||||
WindowStateMaximised WindowState = iota
|
||||
WindowStateFullscreen WindowState = iota
|
||||
)
|
||||
|
||||
// WindowStartPosition determines how the initial X/Y coordinates are interpreted.
|
||||
type WindowStartPosition int
|
||||
|
||||
const (
|
||||
// WindowCentered places the window in the centre of the screen on first show.
|
||||
WindowCentered WindowStartPosition = 0
|
||||
// WindowXY places the window at the explicit X/Y coordinates.
|
||||
WindowXY WindowStartPosition = 1
|
||||
)
|
||||
|
||||
// BackgroundType controls window transparency.
|
||||
type BackgroundType int
|
||||
|
||||
const (
|
||||
BackgroundTypeSolid BackgroundType = iota
|
||||
BackgroundTypeTransparent BackgroundType = iota
|
||||
BackgroundTypeTranslucent BackgroundType = iota
|
||||
)
|
||||
|
||||
// WebviewWindowOptions holds all configuration for a webview window.
|
||||
//
|
||||
// opts := WebviewWindowOptions{
|
||||
// Name: "main",
|
||||
// Title: "My App",
|
||||
// Width: 1280,
|
||||
// Height: 800,
|
||||
// Mac: MacWindow{TitleBar: MacTitleBarHiddenInset},
|
||||
// }
|
||||
type WebviewWindowOptions struct {
|
||||
// Name is a unique identifier for the window.
|
||||
Name string
|
||||
// Title is shown in the title bar.
|
||||
Title string
|
||||
// Width is the initial width in logical pixels.
|
||||
Width int
|
||||
// Height is the initial height in logical pixels.
|
||||
Height int
|
||||
// AlwaysOnTop makes the window float above others.
|
||||
AlwaysOnTop bool
|
||||
// URL is the URL to load on creation.
|
||||
URL string
|
||||
// HTML is inline HTML to load (alternative to URL).
|
||||
HTML string
|
||||
// JS is inline JavaScript to inject.
|
||||
JS string
|
||||
// CSS is inline CSS to inject.
|
||||
CSS string
|
||||
// DisableResize prevents the user from resizing the window.
|
||||
DisableResize bool
|
||||
// Frameless removes the OS window frame.
|
||||
Frameless bool
|
||||
// MinWidth is the minimum allowed width.
|
||||
MinWidth int
|
||||
// MinHeight is the minimum allowed height.
|
||||
MinHeight int
|
||||
// MaxWidth is the maximum allowed width (0 = unlimited).
|
||||
MaxWidth int
|
||||
// MaxHeight is the maximum allowed height (0 = unlimited).
|
||||
MaxHeight int
|
||||
// StartState sets the visual state when first shown.
|
||||
StartState WindowState
|
||||
// BackgroundType controls transparency.
|
||||
BackgroundType BackgroundType
|
||||
// BackgroundColour is the solid background fill colour.
|
||||
BackgroundColour RGBA
|
||||
// InitialPosition controls how X/Y are interpreted.
|
||||
InitialPosition WindowStartPosition
|
||||
// X is the initial horizontal position.
|
||||
X int
|
||||
// Y is the initial vertical position.
|
||||
Y int
|
||||
// Hidden creates the window without showing it.
|
||||
Hidden bool
|
||||
// Zoom sets the initial zoom magnification (0 = default = 1.0).
|
||||
Zoom float64
|
||||
// ZoomControlEnabled allows the user to change zoom.
|
||||
ZoomControlEnabled bool
|
||||
// EnableFileDrop enables drag-and-drop of files onto the window.
|
||||
EnableFileDrop bool
|
||||
// OpenInspectorOnStartup opens the web inspector when first shown.
|
||||
OpenInspectorOnStartup bool
|
||||
// DevToolsEnabled exposes the developer tools (default true in debug builds).
|
||||
DevToolsEnabled bool
|
||||
// DefaultContextMenuDisabled disables the built-in right-click menu.
|
||||
DefaultContextMenuDisabled bool
|
||||
// KeyBindings is a map of accelerator strings to window callbacks.
|
||||
KeyBindings map[string]func(window Window)
|
||||
// IgnoreMouseEvents passes all mouse events through (Windows + macOS only).
|
||||
IgnoreMouseEvents bool
|
||||
// ContentProtectionEnabled prevents screen capture (Windows + macOS only).
|
||||
ContentProtectionEnabled bool
|
||||
// HideOnFocusLost hides the window when it loses focus.
|
||||
HideOnFocusLost bool
|
||||
// HideOnEscape hides the window when Escape is pressed.
|
||||
HideOnEscape bool
|
||||
// UseApplicationMenu uses the application-level menu for this window.
|
||||
UseApplicationMenu bool
|
||||
// MinimiseButtonState controls the minimise button state.
|
||||
MinimiseButtonState ButtonState
|
||||
// MaximiseButtonState controls the maximise/zoom button state.
|
||||
MaximiseButtonState ButtonState
|
||||
// CloseButtonState controls the close button state.
|
||||
CloseButtonState ButtonState
|
||||
// Mac contains macOS-specific window options.
|
||||
Mac MacWindow
|
||||
// Windows contains Windows-specific window options.
|
||||
Windows WindowsWindow
|
||||
// Linux contains Linux-specific window options.
|
||||
Linux LinuxWindow
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// macOS-specific types
|
||||
// -------------------------
|
||||
|
||||
// MacBackdrop is the backdrop material for a macOS window.
|
||||
type MacBackdrop int
|
||||
|
||||
const (
|
||||
// MacBackdropNormal uses an opaque background.
|
||||
MacBackdropNormal MacBackdrop = iota
|
||||
// MacBackdropTransparent shows the desktop behind the window.
|
||||
MacBackdropTransparent MacBackdrop = iota
|
||||
// MacBackdropTranslucent applies a frosted-glass vibrancy effect.
|
||||
MacBackdropTranslucent MacBackdrop = iota
|
||||
// MacBackdropLiquidGlass uses the Apple Liquid Glass effect (macOS 15+).
|
||||
MacBackdropLiquidGlass MacBackdrop = iota
|
||||
)
|
||||
|
||||
// MacToolbarStyle controls toolbar layout relative to the title bar.
|
||||
type MacToolbarStyle int
|
||||
|
||||
const (
|
||||
MacToolbarStyleAutomatic MacToolbarStyle = iota
|
||||
MacToolbarStyleExpanded MacToolbarStyle = iota
|
||||
MacToolbarStylePreference MacToolbarStyle = iota
|
||||
MacToolbarStyleUnified MacToolbarStyle = iota
|
||||
MacToolbarStyleUnifiedCompact MacToolbarStyle = iota
|
||||
)
|
||||
|
||||
// MacLiquidGlassStyle defines the tint of the Liquid Glass effect.
|
||||
type MacLiquidGlassStyle int
|
||||
|
||||
const (
|
||||
LiquidGlassStyleAutomatic MacLiquidGlassStyle = iota
|
||||
LiquidGlassStyleLight MacLiquidGlassStyle = iota
|
||||
LiquidGlassStyleDark MacLiquidGlassStyle = iota
|
||||
LiquidGlassStyleVibrant MacLiquidGlassStyle = iota
|
||||
)
|
||||
|
||||
// NSVisualEffectMaterial maps to the NSVisualEffectMaterial macOS enum.
|
||||
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 NSVisualEffectMaterial = -1
|
||||
)
|
||||
|
||||
// MacLiquidGlass configures the Liquid Glass compositor effect.
|
||||
type MacLiquidGlass struct {
|
||||
Style MacLiquidGlassStyle
|
||||
Material NSVisualEffectMaterial
|
||||
CornerRadius float64
|
||||
TintColor *RGBA
|
||||
GroupID string
|
||||
GroupSpacing float64
|
||||
}
|
||||
|
||||
// MacAppearanceType is the NSAppearance name string for a macOS window.
|
||||
type MacAppearanceType string
|
||||
|
||||
const (
|
||||
DefaultAppearance MacAppearanceType = ""
|
||||
NSAppearanceNameAqua MacAppearanceType = "NSAppearanceNameAqua"
|
||||
NSAppearanceNameDarkAqua MacAppearanceType = "NSAppearanceNameDarkAqua"
|
||||
NSAppearanceNameVibrantLight MacAppearanceType = "NSAppearanceNameVibrantLight"
|
||||
NSAppearanceNameAccessibilityHighContrastAqua MacAppearanceType = "NSAppearanceNameAccessibilityHighContrastAqua"
|
||||
NSAppearanceNameAccessibilityHighContrastDarkAqua MacAppearanceType = "NSAppearanceNameAccessibilityHighContrastDarkAqua"
|
||||
NSAppearanceNameAccessibilityHighContrastVibrantLight MacAppearanceType = "NSAppearanceNameAccessibilityHighContrastVibrantLight"
|
||||
NSAppearanceNameAccessibilityHighContrastVibrantDark MacAppearanceType = "NSAppearanceNameAccessibilityHighContrastVibrantDark"
|
||||
)
|
||||
|
||||
// MacWindowLevel controls Z-ordering relative to other windows.
|
||||
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 is a bitmask controlling Spaces and fullscreen behaviour.
|
||||
type MacWindowCollectionBehavior int
|
||||
|
||||
const (
|
||||
MacWindowCollectionBehaviorDefault MacWindowCollectionBehavior = 0
|
||||
MacWindowCollectionBehaviorCanJoinAllSpaces MacWindowCollectionBehavior = 1 << 0
|
||||
MacWindowCollectionBehaviorMoveToActiveSpace MacWindowCollectionBehavior = 1 << 1
|
||||
MacWindowCollectionBehaviorManaged MacWindowCollectionBehavior = 1 << 2
|
||||
MacWindowCollectionBehaviorTransient MacWindowCollectionBehavior = 1 << 3
|
||||
MacWindowCollectionBehaviorStationary MacWindowCollectionBehavior = 1 << 4
|
||||
MacWindowCollectionBehaviorParticipatesInCycle MacWindowCollectionBehavior = 1 << 5
|
||||
MacWindowCollectionBehaviorIgnoresCycle MacWindowCollectionBehavior = 1 << 6
|
||||
MacWindowCollectionBehaviorFullScreenPrimary MacWindowCollectionBehavior = 1 << 7
|
||||
MacWindowCollectionBehaviorFullScreenAuxiliary MacWindowCollectionBehavior = 1 << 8
|
||||
MacWindowCollectionBehaviorFullScreenNone MacWindowCollectionBehavior = 1 << 9
|
||||
MacWindowCollectionBehaviorFullScreenAllowsTiling MacWindowCollectionBehavior = 1 << 11
|
||||
MacWindowCollectionBehaviorFullScreenDisallowsTiling MacWindowCollectionBehavior = 1 << 12
|
||||
)
|
||||
|
||||
// MacWebviewPreferences holds WKWebView preference flags for macOS.
|
||||
// Use integer tristate: 0 = unset, 1 = true, 2 = false.
|
||||
type MacWebviewPreferences struct {
|
||||
TabFocusesLinks int
|
||||
TextInteractionEnabled int
|
||||
FullscreenEnabled int
|
||||
AllowsBackForwardNavigationGestures int
|
||||
}
|
||||
|
||||
// MacTitleBar configures the macOS title bar appearance.
|
||||
type MacTitleBar struct {
|
||||
// AppearsTransparent removes the title bar background.
|
||||
AppearsTransparent bool
|
||||
// Hide removes the title bar entirely.
|
||||
Hide bool
|
||||
// HideTitle hides only the text title.
|
||||
HideTitle bool
|
||||
// FullSizeContent extends window content into the title bar area.
|
||||
FullSizeContent bool
|
||||
// UseToolbar replaces the title bar with an NSToolbar.
|
||||
UseToolbar bool
|
||||
// HideToolbarSeparator removes the line between toolbar and content.
|
||||
HideToolbarSeparator bool
|
||||
// ShowToolbarWhenFullscreen keeps the toolbar visible in fullscreen.
|
||||
ShowToolbarWhenFullscreen bool
|
||||
// ToolbarStyle selects the toolbar layout style.
|
||||
ToolbarStyle MacToolbarStyle
|
||||
}
|
||||
|
||||
// MacWindow contains macOS-specific window options.
|
||||
type MacWindow struct {
|
||||
Backdrop MacBackdrop
|
||||
DisableShadow bool
|
||||
TitleBar MacTitleBar
|
||||
Appearance MacAppearanceType
|
||||
InvisibleTitleBarHeight int
|
||||
EventMapping map[events.WindowEventType]events.WindowEventType
|
||||
EnableFraudulentWebsiteWarnings bool
|
||||
WebviewPreferences MacWebviewPreferences
|
||||
WindowLevel MacWindowLevel
|
||||
CollectionBehavior MacWindowCollectionBehavior
|
||||
LiquidGlass MacLiquidGlass
|
||||
}
|
||||
|
||||
// Pre-built MacTitleBar configurations — use directly in MacWindow.TitleBar.
|
||||
|
||||
// MacTitleBarDefault produces the standard macOS title bar.
|
||||
//
|
||||
// Mac: MacWindow{TitleBar: MacTitleBarDefault}
|
||||
var MacTitleBarDefault = MacTitleBar{}
|
||||
|
||||
// MacTitleBarHidden hides the title text while keeping the traffic-light buttons.
|
||||
//
|
||||
// Mac: MacWindow{TitleBar: MacTitleBarHidden}
|
||||
var MacTitleBarHidden = MacTitleBar{
|
||||
AppearsTransparent: true,
|
||||
HideTitle: true,
|
||||
FullSizeContent: true,
|
||||
}
|
||||
|
||||
// MacTitleBarHiddenInset keeps traffic lights slightly inset from the window edge.
|
||||
//
|
||||
// Mac: MacWindow{TitleBar: MacTitleBarHiddenInset}
|
||||
var MacTitleBarHiddenInset = MacTitleBar{
|
||||
AppearsTransparent: true,
|
||||
HideTitle: true,
|
||||
FullSizeContent: true,
|
||||
UseToolbar: true,
|
||||
HideToolbarSeparator: true,
|
||||
}
|
||||
|
||||
// MacTitleBarHiddenInsetUnified uses the unified toolbar style for a more compact look.
|
||||
//
|
||||
// Mac: MacWindow{TitleBar: MacTitleBarHiddenInsetUnified}
|
||||
var MacTitleBarHiddenInsetUnified = MacTitleBar{
|
||||
AppearsTransparent: true,
|
||||
HideTitle: true,
|
||||
FullSizeContent: true,
|
||||
UseToolbar: true,
|
||||
HideToolbarSeparator: true,
|
||||
ToolbarStyle: MacToolbarStyleUnified,
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// Windows-specific types
|
||||
// -------------------------
|
||||
|
||||
// BackdropType selects the Windows 11 compositor effect.
|
||||
type BackdropType int32
|
||||
|
||||
const (
|
||||
Auto BackdropType = 0
|
||||
None BackdropType = 1
|
||||
Mica BackdropType = 2
|
||||
Acrylic BackdropType = 3
|
||||
Tabbed BackdropType = 4
|
||||
)
|
||||
|
||||
// Theme selects dark or light mode on Windows.
|
||||
type Theme int
|
||||
|
||||
const (
|
||||
SystemDefault Theme = 0
|
||||
Dark Theme = 1
|
||||
Light Theme = 2
|
||||
)
|
||||
|
||||
// WindowTheme holds custom title-bar colours for a Windows window.
|
||||
type WindowTheme struct {
|
||||
BorderColour *uint32
|
||||
TitleBarColour *uint32
|
||||
TitleTextColour *uint32
|
||||
}
|
||||
|
||||
// TextTheme holds foreground/background colour pair for menu text.
|
||||
type TextTheme struct {
|
||||
Text *uint32
|
||||
Background *uint32
|
||||
}
|
||||
|
||||
// MenuBarTheme holds per-state text themes for the Windows menu bar.
|
||||
type MenuBarTheme struct {
|
||||
Default *TextTheme
|
||||
Hover *TextTheme
|
||||
Selected *TextTheme
|
||||
}
|
||||
|
||||
// ThemeSettings holds custom colours for dark/light mode on Windows.
|
||||
// Colours use 0x00BBGGRR encoding.
|
||||
type ThemeSettings struct {
|
||||
DarkModeActive *WindowTheme
|
||||
DarkModeInactive *WindowTheme
|
||||
LightModeActive *WindowTheme
|
||||
LightModeInactive *WindowTheme
|
||||
DarkModeMenuBar *MenuBarTheme
|
||||
LightModeMenuBar *MenuBarTheme
|
||||
}
|
||||
|
||||
// CoreWebView2PermissionKind identifies a WebView2 permission category.
|
||||
type CoreWebView2PermissionKind uint32
|
||||
|
||||
const (
|
||||
CoreWebView2PermissionKindUnknownPermission CoreWebView2PermissionKind = iota
|
||||
CoreWebView2PermissionKindMicrophone
|
||||
CoreWebView2PermissionKindCamera
|
||||
CoreWebView2PermissionKindGeolocation
|
||||
CoreWebView2PermissionKindNotifications
|
||||
CoreWebView2PermissionKindOtherSensors
|
||||
CoreWebView2PermissionKindClipboardRead
|
||||
)
|
||||
|
||||
// CoreWebView2PermissionState sets whether a permission is granted.
|
||||
type CoreWebView2PermissionState uint32
|
||||
|
||||
const (
|
||||
CoreWebView2PermissionStateDefault CoreWebView2PermissionState = iota
|
||||
CoreWebView2PermissionStateAllow
|
||||
CoreWebView2PermissionStateDeny
|
||||
)
|
||||
|
||||
// WindowsWindow contains Windows-specific window options.
|
||||
type WindowsWindow struct {
|
||||
BackdropType BackdropType
|
||||
DisableIcon bool
|
||||
Theme Theme
|
||||
CustomTheme ThemeSettings
|
||||
DisableFramelessWindowDecorations bool
|
||||
WindowMask []byte
|
||||
WindowMaskDraggable bool
|
||||
ResizeDebounceMS uint16
|
||||
WindowDidMoveDebounceMS uint16
|
||||
EventMapping map[events.WindowEventType]events.WindowEventType
|
||||
HiddenOnTaskbar bool
|
||||
EnableSwipeGestures bool
|
||||
Menu *Menu
|
||||
Permissions map[CoreWebView2PermissionKind]CoreWebView2PermissionState
|
||||
ExStyle int
|
||||
GeneralAutofillEnabled bool
|
||||
PasswordAutosaveEnabled bool
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// Linux-specific types
|
||||
// -------------------------
|
||||
|
||||
// WebviewGpuPolicy controls GPU acceleration for the Linux webview.
|
||||
type WebviewGpuPolicy int
|
||||
|
||||
const (
|
||||
WebviewGpuPolicyAlways WebviewGpuPolicy = iota
|
||||
WebviewGpuPolicyOnDemand WebviewGpuPolicy = iota
|
||||
WebviewGpuPolicyNever WebviewGpuPolicy = iota
|
||||
)
|
||||
|
||||
// LinuxMenuStyle selects how the application menu is rendered on Linux.
|
||||
type LinuxMenuStyle int
|
||||
|
||||
const (
|
||||
LinuxMenuStyleMenuBar LinuxMenuStyle = iota
|
||||
LinuxMenuStylePrimaryMenu LinuxMenuStyle = iota
|
||||
)
|
||||
|
||||
// LinuxWindow contains Linux-specific window options.
|
||||
type LinuxWindow struct {
|
||||
Icon []byte
|
||||
WindowIsTranslucent bool
|
||||
WebviewGpuPolicy WebviewGpuPolicy
|
||||
WindowDidMoveDebounceMS uint16
|
||||
Menu *Menu
|
||||
MenuStyle LinuxMenuStyle
|
||||
}
|
||||
|
||||
// NewRGB constructs an RGBA value with full opacity from RGB components.
|
||||
//
|
||||
// colour := NewRGB(255, 128, 0)
|
||||
func NewRGB(red, green, blue uint8) RGBA {
|
||||
return RGBA{Red: red, Green: green, Blue: blue, Alpha: 255}
|
||||
}
|
||||
|
||||
// NewRGBPtr encodes RGB as a packed uint32 pointer (0x00BBGGRR) for ThemeSettings.
|
||||
//
|
||||
// theme.BorderColour = NewRGBPtr(255, 0, 0)
|
||||
func NewRGBPtr(red, green, blue uint8) *uint32 {
|
||||
result := uint32(red) | uint32(green)<<8 | uint32(blue)<<16
|
||||
return &result
|
||||
}
|
||||
160
stubs/wails/pkg/application/window.go
Normal file
160
stubs/wails/pkg/application/window.go
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
)
|
||||
|
||||
// Window is the interface satisfied by all window types in the application.
|
||||
// Fluent mutating methods return Window so callers can chain calls:
|
||||
//
|
||||
// app.Window.NewWithOptions(opts).SetTitle("Main").Show()
|
||||
type Window interface {
|
||||
// Identity
|
||||
ID() uint
|
||||
Name() string
|
||||
|
||||
// Visibility
|
||||
Show() Window
|
||||
Hide() Window
|
||||
IsVisible() bool
|
||||
|
||||
// Lifecycle
|
||||
Close()
|
||||
Focus()
|
||||
Run()
|
||||
|
||||
// Geometry
|
||||
Center()
|
||||
Position() (x int, y int)
|
||||
RelativePosition() (x int, y int)
|
||||
Size() (width int, height int)
|
||||
Width() int
|
||||
Height() int
|
||||
Bounds() Rect
|
||||
SetPosition(x, y int)
|
||||
SetRelativePosition(x, y int) Window
|
||||
SetSize(width, height int) Window
|
||||
SetBounds(bounds Rect)
|
||||
SetMaxSize(maxWidth, maxHeight int) Window
|
||||
SetMinSize(minWidth, minHeight int) Window
|
||||
EnableSizeConstraints()
|
||||
DisableSizeConstraints()
|
||||
Resizable() bool
|
||||
SetResizable(b bool) Window
|
||||
|
||||
// State
|
||||
Maximise() Window
|
||||
UnMaximise()
|
||||
ToggleMaximise()
|
||||
IsMaximised() bool
|
||||
Minimise() Window
|
||||
UnMinimise()
|
||||
IsMinimised() bool
|
||||
Fullscreen() Window
|
||||
UnFullscreen()
|
||||
ToggleFullscreen()
|
||||
IsFullscreen() bool
|
||||
Restore()
|
||||
SnapAssist()
|
||||
|
||||
// Title and content
|
||||
SetTitle(title string) Window
|
||||
SetURL(s string) Window
|
||||
SetHTML(html string) Window
|
||||
|
||||
// Titlebar buttons (macOS / Windows)
|
||||
SetMinimiseButtonState(state ButtonState) Window
|
||||
SetMaximiseButtonState(state ButtonState) Window
|
||||
SetCloseButtonState(state ButtonState) Window
|
||||
|
||||
// Menu bar
|
||||
SetMenu(menu *Menu)
|
||||
ShowMenuBar()
|
||||
HideMenuBar()
|
||||
ToggleMenuBar()
|
||||
|
||||
// Appearance
|
||||
SetBackgroundColour(colour RGBA) Window
|
||||
SetAlwaysOnTop(b bool) Window
|
||||
SetFrameless(frameless bool) Window
|
||||
ToggleFrameless()
|
||||
SetIgnoreMouseEvents(ignore bool) Window
|
||||
IsIgnoreMouseEvents() bool
|
||||
SetContentProtection(protection bool) Window
|
||||
|
||||
// Zoom
|
||||
GetZoom() float64
|
||||
SetZoom(magnification float64) Window
|
||||
Zoom()
|
||||
ZoomIn()
|
||||
ZoomOut()
|
||||
ZoomReset() Window
|
||||
|
||||
// Border sizes (Windows)
|
||||
GetBorderSizes() *LRTB
|
||||
|
||||
// Screen
|
||||
GetScreen() (*Screen, error)
|
||||
|
||||
// JavaScript / events
|
||||
ExecJS(js string)
|
||||
EmitEvent(name string, data ...any) bool
|
||||
DispatchWailsEvent(event *CustomEvent)
|
||||
OnWindowEvent(eventType events.WindowEventType, callback func(event *WindowEvent)) func()
|
||||
RegisterHook(eventType events.WindowEventType, callback func(event *WindowEvent)) func()
|
||||
|
||||
// Drag-and-drop (internal message bus)
|
||||
handleDragAndDropMessage(filenames []string, dropTarget *DropTargetDetails)
|
||||
InitiateFrontendDropProcessing(filenames []string, x int, y int)
|
||||
|
||||
// Message handling (internal)
|
||||
HandleMessage(message string)
|
||||
HandleWindowEvent(id uint)
|
||||
HandleKeyEvent(acceleratorString string)
|
||||
|
||||
// Context menu
|
||||
OpenContextMenu(data *ContextMenuData)
|
||||
|
||||
// Modal
|
||||
AttachModal(modalWindow Window)
|
||||
|
||||
// DevTools
|
||||
OpenDevTools()
|
||||
|
||||
// Print
|
||||
Print() error
|
||||
|
||||
// Flash (Windows taskbar flash)
|
||||
Flash(enabled bool)
|
||||
|
||||
// Focus tracking
|
||||
IsFocused() bool
|
||||
|
||||
// Native handle (platform-specific, use with care)
|
||||
NativeWindow() unsafe.Pointer
|
||||
|
||||
// Enabled state
|
||||
SetEnabled(enabled bool)
|
||||
|
||||
// Reload
|
||||
Reload()
|
||||
ForceReload()
|
||||
|
||||
// Logging (routed to the application logger)
|
||||
Info(message string, args ...any)
|
||||
Error(message string, args ...any)
|
||||
|
||||
// Internal hooks
|
||||
shouldUnconditionallyClose() bool
|
||||
|
||||
// Editing operations (routed to focused element)
|
||||
cut()
|
||||
copy()
|
||||
paste()
|
||||
undo()
|
||||
redo()
|
||||
delete()
|
||||
selectAll()
|
||||
}
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
package events
|
||||
|
||||
// ApplicationEventType identifies a platform-level application event.
|
||||
// Matches the type used by the real Wails v3 package.
|
||||
//
|
||||
// em.OnApplicationEvent(events.Common.ThemeChanged, handler)
|
||||
type ApplicationEventType uint
|
||||
|
||||
// WindowEventType identifies a window event emitted by the application layer.
|
||||
type WindowEventType int
|
||||
|
||||
|
|
@ -14,17 +20,25 @@ const (
|
|||
|
||||
// Common matches the event namespace used by the real Wails package.
|
||||
var Common = struct {
|
||||
WindowFocus WindowEventType
|
||||
WindowLostFocus WindowEventType
|
||||
WindowDidMove WindowEventType
|
||||
WindowDidResize WindowEventType
|
||||
WindowClosing WindowEventType
|
||||
WindowFilesDropped WindowEventType
|
||||
ApplicationOpenedWithFile ApplicationEventType
|
||||
ApplicationStarted ApplicationEventType
|
||||
ApplicationLaunchedWithUrl ApplicationEventType
|
||||
ThemeChanged ApplicationEventType
|
||||
WindowFocus WindowEventType
|
||||
WindowLostFocus WindowEventType
|
||||
WindowDidMove WindowEventType
|
||||
WindowDidResize WindowEventType
|
||||
WindowClosing WindowEventType
|
||||
WindowFilesDropped WindowEventType
|
||||
}{
|
||||
WindowFocus: WindowFocus,
|
||||
WindowLostFocus: WindowLostFocus,
|
||||
WindowDidMove: WindowDidMove,
|
||||
WindowDidResize: WindowDidResize,
|
||||
WindowClosing: WindowClosing,
|
||||
WindowFilesDropped: WindowFilesDropped,
|
||||
ApplicationOpenedWithFile: 1024,
|
||||
ApplicationStarted: 1025,
|
||||
ApplicationLaunchedWithUrl: 1026,
|
||||
ThemeChanged: 1027,
|
||||
WindowFocus: WindowFocus,
|
||||
WindowLostFocus: WindowLostFocus,
|
||||
WindowDidMove: WindowDidMove,
|
||||
WindowDidResize: WindowDidResize,
|
||||
WindowClosing: WindowClosing,
|
||||
WindowFilesDropped: WindowFilesDropped,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue