gui/pkg/window/wails.go
Snider f884d698b2
Some checks failed
Security Scan / security (push) Failing after 9s
Test / test (push) Has been cancelled
fix(display): correct GetWindowTitle and add WS input validation
GetWindowTitle was returning info.Name (the window's identifier) instead
of the actual title. Added Title() to the PlatformWindow interface and
Title field to WindowInfo so the real title flows through queries.

Added wsRequire() helper and input validation for all webview:* WS
message cases — window name is required for every webview action, and
selectors/URLs are validated where they'd cause errors if empty.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 16:25:29 +00:00

143 lines
4.9 KiB
Go

// pkg/window/wails.go
package window
import (
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/events"
)
// WailsPlatform implements Platform using Wails v3.
type WailsPlatform struct {
app *application.App
}
// NewWailsPlatform creates a Wails-backed Platform.
func NewWailsPlatform(app *application.App) *WailsPlatform {
return &WailsPlatform{app: app}
}
func (wp *WailsPlatform) CreateWindow(opts PlatformWindowOptions) PlatformWindow {
wOpts := application.WebviewWindowOptions{
Name: opts.Name,
Title: opts.Title,
URL: opts.URL,
Width: opts.Width,
Height: opts.Height,
X: opts.X,
Y: opts.Y,
MinWidth: opts.MinWidth,
MinHeight: opts.MinHeight,
MaxWidth: opts.MaxWidth,
MaxHeight: opts.MaxHeight,
Frameless: opts.Frameless,
Hidden: opts.Hidden,
AlwaysOnTop: opts.AlwaysOnTop,
DisableResize: opts.DisableResize,
EnableFileDrop: opts.EnableFileDrop,
BackgroundColour: application.NewRGBA(opts.BackgroundColour[0], opts.BackgroundColour[1], opts.BackgroundColour[2], opts.BackgroundColour[3]),
}
w := wp.app.Window.NewWithOptions(wOpts)
return &wailsWindow{w: w, title: opts.Title}
}
func (wp *WailsPlatform) GetWindows() []PlatformWindow {
all := wp.app.Window.GetAll()
out := make([]PlatformWindow, 0, len(all))
for _, w := range all {
if wv, ok := w.(*application.WebviewWindow); ok {
out = append(out, &wailsWindow{w: wv})
}
}
return out
}
// wailsWindow wraps *application.WebviewWindow to implement PlatformWindow.
// It stores the title locally because Wails v3 does not expose a title getter.
type wailsWindow struct {
w *application.WebviewWindow
title string
}
func (ww *wailsWindow) Name() string { return ww.w.Name() }
func (ww *wailsWindow) Title() string { return ww.title }
func (ww *wailsWindow) Position() (int, int) { return ww.w.Position() }
func (ww *wailsWindow) Size() (int, int) { return ww.w.Size() }
func (ww *wailsWindow) IsMaximised() bool { return ww.w.IsMaximised() }
func (ww *wailsWindow) IsFocused() bool { return ww.w.IsFocused() }
func (ww *wailsWindow) SetTitle(title string) { ww.title = title; ww.w.SetTitle(title) }
func (ww *wailsWindow) SetPosition(x, y int) { ww.w.SetPosition(x, y) }
func (ww *wailsWindow) SetSize(width, height int) { ww.w.SetSize(width, height) }
func (ww *wailsWindow) SetBackgroundColour(r, g, b, a uint8) {
ww.w.SetBackgroundColour(application.NewRGBA(r, g, b, a))
}
func (ww *wailsWindow) SetVisibility(visible bool) {
if visible {
ww.w.Show()
} else {
ww.w.Hide()
}
}
func (ww *wailsWindow) SetAlwaysOnTop(alwaysOnTop bool) { ww.w.SetAlwaysOnTop(alwaysOnTop) }
func (ww *wailsWindow) Maximise() { ww.w.Maximise() }
func (ww *wailsWindow) Restore() { ww.w.Restore() }
func (ww *wailsWindow) Minimise() { ww.w.Minimise() }
func (ww *wailsWindow) Focus() { ww.w.Focus() }
func (ww *wailsWindow) Close() { ww.w.Close() }
func (ww *wailsWindow) Show() { ww.w.Show() }
func (ww *wailsWindow) Hide() { ww.w.Hide() }
func (ww *wailsWindow) Fullscreen() { ww.w.Fullscreen() }
func (ww *wailsWindow) UnFullscreen() { ww.w.UnFullscreen() }
func (ww *wailsWindow) OnWindowEvent(handler func(event WindowEvent)) {
name := ww.w.Name()
// Map common Wails window events to our WindowEvent type.
eventMap := map[events.WindowEventType]string{
events.Common.WindowFocus: "focus",
events.Common.WindowLostFocus: "blur",
events.Common.WindowDidMove: "move",
events.Common.WindowDidResize: "resize",
events.Common.WindowClosing: "close",
}
for eventType, eventName := range eventMap {
typeName := eventName // capture for closure
ww.w.OnWindowEvent(eventType, func(event *application.WindowEvent) {
data := make(map[string]any)
switch typeName {
case "move":
x, y := ww.w.Position()
data["x"] = x
data["y"] = y
case "resize":
w, h := ww.w.Size()
data["width"] = w
data["height"] = h
}
handler(WindowEvent{
Type: typeName,
Name: name,
Data: data,
})
})
}
}
func (ww *wailsWindow) OnFileDrop(handler func(paths []string, targetID string)) {
ww.w.OnWindowEvent(events.Common.WindowFilesDropped, func(event *application.WindowEvent) {
files := event.Context().DroppedFiles()
details := event.Context().DropTargetDetails()
targetID := ""
if details != nil {
targetID = details.ElementID
}
handler(files, targetID)
})
}
// Ensure wailsWindow satisfies PlatformWindow at compile time.
var _ PlatformWindow = (*wailsWindow)(nil)
// Ensure WailsPlatform satisfies Platform at compile time.
var _ Platform = (*WailsPlatform)(nil)