Align Wails stub bridge with RFC parity
This commit is contained in:
parent
ae02c8574b
commit
cefcdfbb19
6 changed files with 196 additions and 20 deletions
8
stubs/wails/internal/operatingsystem/operatingsystem.go
Normal file
8
stubs/wails/internal/operatingsystem/operatingsystem.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package operatingsystem
|
||||
|
||||
// OS is the minimal host metadata exposed by the Wails environment API.
|
||||
type OS struct {
|
||||
Name string
|
||||
Version string
|
||||
Build string
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
|
|
@ -216,9 +217,13 @@ func (c *WindowEventContext) DropTargetDetails() *DropTargetDetails {
|
|||
return &details
|
||||
}
|
||||
|
||||
// DropTargetDetails mirrors the fields consumed by the GUI wrappers.
|
||||
// DropTargetDetails mirrors the Wails drop-target payload.
|
||||
type DropTargetDetails struct {
|
||||
ElementID string
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
ElementID string `json:"id"`
|
||||
ClassList []string `json:"classList"`
|
||||
Attributes map[string]string `json:"attributes,omitempty"`
|
||||
}
|
||||
|
||||
// WindowEvent mirrors the event object passed to window callbacks.
|
||||
|
|
@ -873,18 +878,19 @@ func (wm *WindowManager) RemoveByName(name string) bool {
|
|||
// 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
|
||||
Logger *slog.Logger
|
||||
Window *WindowManager
|
||||
Menu *MenuManager
|
||||
SystemTray *SystemTrayManager
|
||||
Dialog *DialogManager
|
||||
Event *EventManager
|
||||
Browser *BrowserManager
|
||||
Clipboard *ClipboardManager
|
||||
ContextMenu *ContextMenuManager
|
||||
Env *EnvironmentManager
|
||||
Environment *EnvironmentManager
|
||||
Screen *ScreenManager
|
||||
KeyBinding *KeyBindingManager
|
||||
}
|
||||
|
||||
// NewApp creates a zero-config in-memory application stub.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -43,6 +45,12 @@ type Options struct {
|
|||
// Example: map[uint32]uint32{1: 1411160069}
|
||||
BindAliases map[uint32]uint32
|
||||
|
||||
// Logger is the Wails system logger used by the runtime.
|
||||
Logger *slog.Logger
|
||||
|
||||
// LogLevel defines the desired system log level when Logger is nil.
|
||||
LogLevel slog.Level
|
||||
|
||||
// Assets configures the embedded asset server.
|
||||
Assets AssetOptions
|
||||
|
||||
|
|
@ -85,6 +93,9 @@ type Options struct {
|
|||
// SingleInstance configures single-instance enforcement.
|
||||
SingleInstance *SingleInstanceOptions
|
||||
|
||||
// Transport configures the IPC transport implementation.
|
||||
Transport Transport
|
||||
|
||||
// Server configures the headless HTTP server (enabled via the "server" build tag).
|
||||
Server ServerOptions
|
||||
}
|
||||
|
|
@ -141,6 +152,27 @@ type AssetOptions struct {
|
|||
// type Middleware func(next http.Handler) http.Handler
|
||||
type Middleware func(next http.Handler) http.Handler
|
||||
|
||||
// MessageProcessor is the placeholder type passed to Transport.Start.
|
||||
// The stub does not perform runtime IPC processing, but the type exists so
|
||||
// callers can satisfy the same API surface as real Wails.
|
||||
type MessageProcessor struct{}
|
||||
|
||||
// Transport is the custom IPC transport contract supported by Wails.
|
||||
//
|
||||
// type MyTransport struct{}
|
||||
// func (t *MyTransport) Start(ctx context.Context, processor *application.MessageProcessor) error { return nil }
|
||||
// func (t *MyTransport) Stop() error { return nil }
|
||||
type Transport interface {
|
||||
Start(ctx context.Context, processor *MessageProcessor) error
|
||||
Stop() error
|
||||
}
|
||||
|
||||
// AssetServerTransport optionally allows a custom transport to serve app assets.
|
||||
type AssetServerTransport interface {
|
||||
Transport
|
||||
ServeAssets(assetHandler http.Handler) error
|
||||
}
|
||||
|
||||
// ChainMiddleware composes multiple Middleware values into a single Middleware.
|
||||
// chain := application.ChainMiddleware(authMiddleware, loggingMiddleware)
|
||||
func ChainMiddleware(middleware ...Middleware) Middleware {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package application
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -53,19 +55,18 @@ func ensureAppCompatState(app *App) *appCompatState {
|
|||
if app == nil {
|
||||
return &appCompatState{ctx: context.Background()}
|
||||
}
|
||||
initialiseAppManagers(app, Options{})
|
||||
if state, ok := appCompatStates.Load(app); ok {
|
||||
return state.(*appCompatState)
|
||||
}
|
||||
state := &appCompatState{ctx: context.Background()}
|
||||
actual, _ := appCompatStates.LoadOrStore(app, state)
|
||||
if app.KeyBinding.bindings == nil {
|
||||
app.KeyBinding = *NewKeyBindingManager()
|
||||
}
|
||||
return actual.(*appCompatState)
|
||||
}
|
||||
|
||||
func newStubApp(options Options) *App {
|
||||
app := &App{}
|
||||
initialiseAppManagers(app, options)
|
||||
state := ensureAppCompatState(app)
|
||||
state.mu.Lock()
|
||||
state.options = options
|
||||
|
|
@ -77,6 +78,59 @@ func newStubApp(options Options) *App {
|
|||
return app
|
||||
}
|
||||
|
||||
func initialiseAppManagers(app *App, options Options) {
|
||||
if app == nil {
|
||||
return
|
||||
}
|
||||
if app.Logger == nil {
|
||||
if options.Logger != nil {
|
||||
app.Logger = options.Logger
|
||||
} else {
|
||||
handlerOptions := &slog.HandlerOptions{Level: options.LogLevel}
|
||||
app.Logger = slog.New(slog.NewTextHandler(io.Discard, handlerOptions))
|
||||
}
|
||||
}
|
||||
if app.Window == nil {
|
||||
app.Window = &WindowManager{}
|
||||
}
|
||||
if app.Menu == nil {
|
||||
app.Menu = &MenuManager{}
|
||||
}
|
||||
if app.SystemTray == nil {
|
||||
app.SystemTray = &SystemTrayManager{}
|
||||
}
|
||||
if app.Dialog == nil {
|
||||
app.Dialog = &DialogManager{}
|
||||
}
|
||||
if app.Event == nil {
|
||||
app.Event = &EventManager{}
|
||||
}
|
||||
if app.Browser == nil {
|
||||
app.Browser = &BrowserManager{}
|
||||
}
|
||||
if app.Clipboard == nil {
|
||||
app.Clipboard = &ClipboardManager{}
|
||||
}
|
||||
if app.ContextMenu == nil {
|
||||
app.ContextMenu = &ContextMenuManager{}
|
||||
}
|
||||
if app.Env == nil && app.Environment != nil {
|
||||
app.Env = app.Environment
|
||||
}
|
||||
if app.Env == nil {
|
||||
app.Env = &EnvironmentManager{}
|
||||
}
|
||||
if app.Environment == nil {
|
||||
app.Environment = app.Env
|
||||
}
|
||||
if app.Screen == nil {
|
||||
app.Screen = NewScreenManager()
|
||||
}
|
||||
if app.KeyBinding == nil {
|
||||
app.KeyBinding = NewKeyBindingManager()
|
||||
}
|
||||
}
|
||||
|
||||
func ensureWebviewCompatState(window *WebviewWindow) *webviewCompatState {
|
||||
if state, ok := webviewCompatStates.Load(window); ok {
|
||||
return state.(*webviewCompatState)
|
||||
|
|
@ -107,7 +161,7 @@ func ensureTrayCompatState(tray *SystemTray) *trayCompatState {
|
|||
|
||||
func appScreens() *ScreenManager {
|
||||
if globalApplication != nil {
|
||||
return &globalApplication.Screen
|
||||
return globalApplication.Screen
|
||||
}
|
||||
return defaultScreenManager
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/wailsapp/wails/v3/internal/operatingsystem"
|
||||
)
|
||||
|
||||
// EnvironmentInfo holds runtime information about the host OS and build.
|
||||
//
|
||||
// info := manager.Info()
|
||||
|
|
@ -8,6 +14,7 @@ type EnvironmentInfo struct {
|
|||
OS string
|
||||
Arch string
|
||||
Debug bool
|
||||
OSInfo *operatingsystem.OS
|
||||
PlatformInfo map[string]any
|
||||
}
|
||||
|
||||
|
|
@ -42,6 +49,9 @@ func (em *EnvironmentManager) GetAccentColor() string {
|
|||
// info := manager.Info()
|
||||
func (em *EnvironmentManager) Info() EnvironmentInfo {
|
||||
return EnvironmentInfo{
|
||||
OS: runtime.GOOS,
|
||||
Arch: runtime.GOARCH,
|
||||
OSInfo: &operatingsystem.OS{Name: runtime.GOOS, Version: "stub"},
|
||||
PlatformInfo: make(map[string]any),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,50 @@
|
|||
package application
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type noopTransport struct{}
|
||||
|
||||
func (noopTransport) Start(context.Context, *MessageProcessor) error { return nil }
|
||||
func (noopTransport) Stop() error { return nil }
|
||||
|
||||
func TestNewAndGetExposeSingletonState(t *testing.T) {
|
||||
globalApplication = nil
|
||||
app := New(Options{Name: "Parity"})
|
||||
logger := slog.Default()
|
||||
transport := noopTransport{}
|
||||
app := New(Options{
|
||||
Name: "Parity",
|
||||
Logger: logger,
|
||||
LogLevel: slog.LevelDebug,
|
||||
Transport: transport,
|
||||
})
|
||||
if Get() != app {
|
||||
t.Fatalf("Get() did not return singleton app")
|
||||
}
|
||||
if app.Config().Name != "Parity" {
|
||||
t.Fatalf("unexpected app name: %q", app.Config().Name)
|
||||
}
|
||||
if app.Config().Logger != logger {
|
||||
t.Fatalf("expected logger to round-trip through config")
|
||||
}
|
||||
if app.Config().LogLevel != slog.LevelDebug {
|
||||
t.Fatalf("expected log level to round-trip through config")
|
||||
}
|
||||
if app.Config().Transport == nil {
|
||||
t.Fatalf("expected transport to round-trip through config")
|
||||
}
|
||||
if app.Logger == nil {
|
||||
t.Fatalf("expected app logger to be initialised")
|
||||
}
|
||||
if app.Env == nil {
|
||||
t.Fatalf("expected Env manager to be initialised")
|
||||
}
|
||||
if app.Environment != app.Env {
|
||||
t.Fatalf("expected Environment alias to point at Env manager")
|
||||
}
|
||||
if err := app.Run(); err != nil {
|
||||
t.Fatalf("Run() failed: %v", err)
|
||||
}
|
||||
|
|
@ -61,3 +95,35 @@ func TestMenuAndWindowParityHelpers(t *testing.T) {
|
|||
t.Fatalf("expected devtools to be marked closed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvironmentAndDropTargetParity(t *testing.T) {
|
||||
info := (&EnvironmentManager{}).Info()
|
||||
if info.OS == "" || info.Arch == "" {
|
||||
t.Fatalf("expected runtime environment metadata, got %+v", info)
|
||||
}
|
||||
if info.OSInfo == nil {
|
||||
t.Fatalf("expected OSInfo to be populated")
|
||||
}
|
||||
|
||||
ctx := &WindowEventContext{
|
||||
dropDetails: &DropTargetDetails{
|
||||
X: 10,
|
||||
Y: 20,
|
||||
ElementID: "dropzone",
|
||||
ClassList: []string{"primary", "drop-target"},
|
||||
Attributes: map[string]string{
|
||||
"data-file-drop-target": "true",
|
||||
},
|
||||
},
|
||||
}
|
||||
details := ctx.DropTargetDetails()
|
||||
if details == nil {
|
||||
t.Fatalf("expected drop target details")
|
||||
}
|
||||
if details.X != 10 || details.Y != 20 || details.ElementID != "dropzone" {
|
||||
t.Fatalf("unexpected drop details: %+v", details)
|
||||
}
|
||||
if len(details.ClassList) != 2 || details.Attributes["data-file-drop-target"] != "true" {
|
||||
t.Fatalf("drop target metadata was not preserved: %+v", details)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue