5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-03 00:09:56 +08:00

Merge branch 'v3/central-assetserver' into v3/plugins

This commit is contained in:
Lea Anthony 2023-03-17 17:03:21 +11:00
commit 86aaa3a956
16 changed files with 184 additions and 137 deletions

View File

@ -20,13 +20,13 @@ func main() {
Mac: application.MacOptions{ Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true, ApplicationShouldTerminateAfterLastWindowClosed: true,
}, },
Assets: application.AssetOptions{
FS: assets,
},
}) })
mainWindow := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ mainWindow := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Title: "Context Menu Demo", Title: "Context Menu Demo",
Assets: application.AssetOptions{
FS: assets,
},
Mac: application.MacWindow{ Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent, Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified, TitleBar: application.MacTitleBarHiddenInsetUnified,
@ -36,9 +36,6 @@ func main() {
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Title: "Context Menu Demo", Title: "Context Menu Demo",
Assets: application.AssetOptions{
FS: assets,
},
Mac: application.MacWindow{ Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent, Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified, TitleBar: application.MacTitleBarHiddenInsetUnified,

View File

@ -20,13 +20,13 @@ func main() {
Mac: application.MacOptions{ Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true, ApplicationShouldTerminateAfterLastWindowClosed: true,
}, },
Assets: application.AssetOptions{
FS: assets,
},
}) })
window := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ window := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Title: "Drag-n-drop Demo", Title: "Drag-n-drop Demo",
Assets: application.AssetOptions{
FS: assets,
},
Mac: application.MacWindow{ Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent, Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified, TitleBar: application.MacTitleBarHiddenInsetUnified,

View File

@ -21,6 +21,9 @@ func main() {
Mac: application.MacOptions{ Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true, ApplicationShouldTerminateAfterLastWindowClosed: true,
}, },
Assets: application.AssetOptions{
FS: assets,
},
}) })
app.Events.On("myevent", func(e *application.CustomEvent) { app.Events.On("myevent", func(e *application.CustomEvent) {
@ -40,9 +43,6 @@ func main() {
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Title: "Events Demo", Title: "Events Demo",
Assets: application.AssetOptions{
FS: assets,
},
Mac: application.MacWindow{ Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent, Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified, TitleBar: application.MacTitleBarHiddenInsetUnified,
@ -51,9 +51,6 @@ func main() {
}) })
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Title: "Events Demo", Title: "Events Demo",
Assets: application.AssetOptions{
FS: assets,
},
Mac: application.MacWindow{ Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent, Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified, TitleBar: application.MacTitleBarHiddenInsetUnified,

View File

@ -15,9 +15,15 @@ func main() {
Mac: application.MacOptions{ Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true, ApplicationShouldTerminateAfterLastWindowClosed: true,
}, },
Assets: application.AssetOptions{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`<html><head><title>Plain Bundle</title></head><body><div class="main"><h1>Plain Bundle</h1><p>This is a plain bundle. It has no frontend code but this was Served by the AssetServer's Handler.</p><br/><br/><p data-wml-event="clicked">Clicking this paragraph emits an event...<p></div></body></html>`))
}),
},
}) })
// Create window // Create window
myWindow := app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Title: "Plain Bundle", Title: "Plain Bundle",
CSS: `body { background-color: rgba(255, 255, 255, 0); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; user-select: none; -ms-user-select: none; -webkit-user-select: none; } .main { color: white; margin: 20%; }`, CSS: `body { background-color: rgba(255, 255, 255, 0); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; user-select: none; -ms-user-select: none; -webkit-user-select: none; } .main { color: white; margin: 20%; }`,
Mac: application.MacWindow{ Mac: application.MacWindow{
@ -26,12 +32,6 @@ func main() {
TitleBar: application.MacTitleBarHiddenInsetUnified, TitleBar: application.MacTitleBarHiddenInsetUnified,
}, },
URL: "/", URL: "/",
Assets: application.AssetOptions{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`<html><head><title>Plain Bundle</title></head><body><div class="main"><h1>Plain Bundle</h1><p>This is a plain bundle. It has no frontend code but this was Served by the AssetServer's Handler.</p><br/><br/><p data-wml-event="clicked">Clicking this paragraph emits an event...<p></div></body></html>`))
}),
},
}) })
app.Events.On("clicked", func(_ *application.CustomEvent) { app.Events.On("clicked", func(_ *application.CustomEvent) {

View File

@ -19,15 +19,15 @@ func main() {
Mac: application.MacOptions{ Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true, ApplicationShouldTerminateAfterLastWindowClosed: true,
}, },
Assets: application.AssetOptions{
FS: assets,
},
}) })
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Title: "Screen Demo", Title: "Screen Demo",
Width: 800, Width: 800,
Height: 600, Height: 600,
Assets: application.AssetOptions{
FS: assets,
},
Mac: application.MacWindow{ Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent, Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified, TitleBar: application.MacTitleBarHiddenInsetUnified,

View File

@ -21,6 +21,9 @@ func main() {
Mac: application.MacOptions{ Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true, ApplicationShouldTerminateAfterLastWindowClosed: true,
}, },
Assets: application.AssetOptions{
FS: assets,
},
}) })
// Create a custom menu // Create a custom menu
@ -30,11 +33,7 @@ func main() {
windowCounter := 1 windowCounter := 1
newWindow := func() { newWindow := func() {
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{}).
Assets: application.AssetOptions{
FS: assets,
},
}).
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)). SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)). SetPosition(rand.Intn(1000), rand.Intn(800)).
Show() Show()

View File

@ -19,15 +19,15 @@ func main() {
Mac: application.MacOptions{ Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true, ApplicationShouldTerminateAfterLastWindowClosed: true,
}, },
Assets: application.AssetOptions{
FS: assets,
},
}) })
app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{ app.NewWebviewWindowWithOptions(&application.WebviewWindowOptions{
Title: "Wails ML Demo", Title: "Wails ML Demo",
Width: 800, Width: 800,
Height: 600, Height: 600,
Assets: application.AssetOptions{
FS: assets,
},
Mac: application.MacWindow{ Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent, Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified, TitleBar: application.MacTitleBarHiddenInsetUnified,

View File

@ -3,14 +3,19 @@ package application
import "C" import "C"
import ( import (
"log" "log"
"net/http"
"os" "os"
"runtime" "runtime"
"strconv"
"sync" "sync"
"github.com/wailsapp/wails/v3/pkg/logger" "github.com/wailsapp/wails/v2/pkg/assetserver"
"github.com/wailsapp/wails/v2/pkg/assetserver/webview" "github.com/wailsapp/wails/v2/pkg/assetserver/webview"
assetserveroptions "github.com/wailsapp/wails/v2/pkg/options/assetserver"
wailsruntime "github.com/wailsapp/wails/v3/internal/runtime"
"github.com/wailsapp/wails/v3/pkg/events" "github.com/wailsapp/wails/v3/pkg/events"
"github.com/wailsapp/wails/v3/pkg/logger"
) )
var globalApplication *App var globalApplication *App
@ -39,6 +44,22 @@ func New(appOptions Options) *App {
} }
result.Events = NewCustomEventProcessor(result.dispatchEventToWindows) result.Events = NewCustomEventProcessor(result.dispatchEventToWindows)
opts := assetserveroptions.Options{
Assets: appOptions.Assets.FS,
Handler: appOptions.Assets.Handler,
Middleware: assetserveroptions.Middleware(appOptions.Assets.Middleware),
}
// TODO ServingFrom disk?
srv, err := assetserver.NewAssetServer("", opts, false, nil, wailsruntime.RuntimeAssetsBundle)
if err != nil {
result.fatal(err.Error())
}
srv.UseRuntimeHandler(NewMessageProcessor())
result.assets = srv
globalApplication = result globalApplication = result
return result return result
} }
@ -85,9 +106,24 @@ type dragAndDropMessage struct {
var windowDragAndDropBuffer = make(chan *dragAndDropMessage) var windowDragAndDropBuffer = make(chan *dragAndDropMessage)
var _ webview.Request = &webViewAssetRequest{}
const webViewRequestHeaderWindowId = "x-wails-window-id"
type webViewAssetRequest struct { type webViewAssetRequest struct {
webview.Request
windowId uint windowId uint
request webview.Request }
func (r *webViewAssetRequest) Header() (http.Header, error) {
h, err := r.Request.Header()
if err != nil {
return nil, err
}
hh := h.Clone()
hh.Set(webViewRequestHeaderWindowId, strconv.FormatUint(uint64(r.windowId), 10))
return hh, nil
} }
var webviewRequests = make(chan *webViewAssetRequest) var webviewRequests = make(chan *webViewAssetRequest)
@ -127,6 +163,8 @@ type App struct {
contextMenus map[string]*Menu contextMenus map[string]*Menu
contextMenusLock sync.Mutex contextMenusLock sync.Mutex
assets *assetserver.AssetServer
} }
func (a *App) getSystemTrayID() uint { func (a *App) getSystemTrayID() uint {
@ -243,9 +281,9 @@ func (a *App) Run() error {
}() }()
go func() { go func() {
for { for {
event := <-webviewRequests request := <-webviewRequests
a.handleWebViewRequest(event) a.handleWebViewRequest(request)
err := event.request.Release() err := request.Release()
if err != nil { if err != nil {
a.error("Failed to release webview request: %s", err.Error()) a.error("Failed to release webview request: %s", err.Error())
} }
@ -334,17 +372,11 @@ func (a *App) handleWindowMessage(event *windowMessage) {
window.handleMessage(event.message) window.handleMessage(event.message)
} }
func (a *App) handleWebViewRequest(event *webViewAssetRequest) { func (a *App) handleWebViewRequest(request *webViewAssetRequest) {
// Get window from window map // Get window from window map
a.windowsLock.Lock() url, _ := request.URL()
window, ok := a.windows[event.windowId] a.info("Window: %d, Request: %s", request.windowId, url)
a.windowsLock.Unlock() a.assets.ServeWebViewRequest(request)
if !ok {
log.Printf("WebviewWindow #%d not found", event.windowId)
return
}
// Get callback from window
window.handleWebViewRequest(event.request)
} }
func (a *App) handleWindowEvent(event *WindowEvent) { func (a *App) handleWindowEvent(event *WindowEvent) {

View File

@ -225,8 +225,8 @@ func processMessage(windowID C.uint, message *C.char) {
//export processURLRequest //export processURLRequest
func processURLRequest(windowID C.uint, wkUrlSchemeTask unsafe.Pointer) { func processURLRequest(windowID C.uint, wkUrlSchemeTask unsafe.Pointer) {
webviewRequests <- &webViewAssetRequest{ webviewRequests <- &webViewAssetRequest{
Request: webview.NewRequest(wkUrlSchemeTask),
windowId: uint(windowID), windowId: uint(windowID),
request: webview.NewRequest(wkUrlSchemeTask),
} }
} }

View File

@ -3,19 +3,19 @@ package application
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strconv"
"strings" "strings"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
) )
type MessageProcessor struct { // TODO maybe we could use a new struct that has the targetWindow as an attribute so we could get rid of passing the targetWindow
window *WebviewWindow // as parameter through every function call.
}
func NewMessageProcessor(w *WebviewWindow) *MessageProcessor { type MessageProcessor struct{}
return &MessageProcessor{
window: w, func NewMessageProcessor() *MessageProcessor {
} return &MessageProcessor{}
} }
func (m *MessageProcessor) httpError(rw http.ResponseWriter, message string, args ...any) { func (m *MessageProcessor) httpError(rw http.ResponseWriter, message string, args ...any) {
@ -43,15 +43,27 @@ func (m *MessageProcessor) HandleRuntimeCall(rw http.ResponseWriter, r *http.Req
params := QueryParams(r.URL.Query()) params := QueryParams(r.URL.Query())
var targetWindow = m.window var windowID uint
windowID := params.UInt("windowID") if hWindowID := r.Header.Get(webViewRequestHeaderWindowId); hWindowID != "" {
if windowID != nil { // Get windowID out of the request header
// Get window for ID wID, err := strconv.ParseUint(hWindowID, 10, 64)
targetWindow = globalApplication.getWindowForID(*windowID) if err != nil {
if targetWindow == nil { m.Error("Window ID '%s' not parsable: %s", hWindowID, err)
m.Error("Window ID %s not found", *windowID)
return return
} }
windowID = uint(wID)
}
if qWindowID := params.UInt("windowID"); qWindowID != nil {
// Get windowID out of the query parameters if provided
windowID = *qWindowID
}
targetWindow := globalApplication.getWindowForID(windowID)
if targetWindow == nil {
m.Error("Window ID %s not found", windowID)
return
} }
switch object { switch object {
@ -79,10 +91,6 @@ func (m *MessageProcessor) HandleRuntimeCall(rw http.ResponseWriter, r *http.Req
} }
func (m *MessageProcessor) ProcessMessage(message string) {
m.Info("ProcessMessage from front end:", message)
}
func (m *MessageProcessor) Error(message string, args ...any) { func (m *MessageProcessor) Error(message string, args ...any) {
fmt.Printf("[MessageProcessor] Error: "+message, args...) fmt.Printf("[MessageProcessor] Error: "+message, args...)
} }

View File

@ -7,19 +7,19 @@ import (
"strconv" "strconv"
) )
func (m *MessageProcessor) callErrorCallback(message string, callID *string, err error) { func (m *MessageProcessor) callErrorCallback(window *WebviewWindow, message string, callID *string, err error) {
errorMsg := fmt.Sprintf(message, err) errorMsg := fmt.Sprintf(message, err)
m.Error(errorMsg) m.Error(errorMsg)
msg := "_wails.callErrorCallback('" + *callID + "', " + strconv.Quote(errorMsg) + ");" msg := "_wails.callErrorCallback('" + *callID + "', " + strconv.Quote(errorMsg) + ");"
m.window.ExecJS(msg) window.ExecJS(msg)
} }
func (m *MessageProcessor) callCallback(callID *string, result string, isJSON bool) { func (m *MessageProcessor) callCallback(window *WebviewWindow, callID *string, result string, isJSON bool) {
msg := fmt.Sprintf("_wails.callCallback('%s', %s, %v);", *callID, strconv.Quote(result), isJSON) msg := fmt.Sprintf("_wails.callCallback('%s', %s, %v);", *callID, strconv.Quote(result), isJSON)
m.window.ExecJS(msg) window.ExecJS(msg)
} }
func (m *MessageProcessor) processCallMethod(method string, rw http.ResponseWriter, _ *http.Request, _ *WebviewWindow, params QueryParams) { func (m *MessageProcessor) processCallMethod(method string, rw http.ResponseWriter, _ *http.Request, window *WebviewWindow, params QueryParams) {
args, err := params.Args() args, err := params.Args()
if err != nil { if err != nil {
m.httpError(rw, "Unable to parse arguments: %s", err) m.httpError(rw, "Unable to parse arguments: %s", err)
@ -35,27 +35,27 @@ func (m *MessageProcessor) processCallMethod(method string, rw http.ResponseWrit
var options CallOptions var options CallOptions
err := params.ToStruct(&options) err := params.ToStruct(&options)
if err != nil { if err != nil {
m.callErrorCallback("Error parsing call options: %s", callID, err) m.callErrorCallback(window, "Error parsing call options: %s", callID, err)
return return
} }
bindings := globalApplication.bindings.Get(&options) bindings := globalApplication.bindings.Get(&options)
if bindings == nil { if bindings == nil {
m.callErrorCallback("Error getting binding for method: %s", callID, fmt.Errorf("'%s' not found", options.MethodName)) m.callErrorCallback(window, "Error getting binding for method: %s", callID, fmt.Errorf("'%s' not found", options.MethodName))
return return
} }
go func() { go func() {
result, err := bindings.Call(options.Args) result, err := bindings.Call(options.Args)
if err != nil { if err != nil {
m.callErrorCallback("Error calling method: %s", callID, err) m.callErrorCallback(window, "Error calling method: %s", callID, err)
return return
} }
// convert result to json // convert result to json
jsonResult, err := json.Marshal(result) jsonResult, err := json.Marshal(result)
if err != nil { if err != nil {
m.callErrorCallback("Error converting result to json: %s", callID, err) m.callErrorCallback(window, "Error converting result to json: %s", callID, err)
return return
} }
m.callCallback(callID, string(jsonResult), true) m.callCallback(window, callID, string(jsonResult), true)
}() }()
m.ok(rw) m.ok(rw)
default: default:

View File

@ -8,16 +8,16 @@ import (
"strconv" "strconv"
) )
func (m *MessageProcessor) dialogErrorCallback(message string, dialogID *string, err error) { func (m *MessageProcessor) dialogErrorCallback(window *WebviewWindow, message string, dialogID *string, err error) {
errorMsg := fmt.Sprintf(message, err) errorMsg := fmt.Sprintf(message, err)
m.Error(errorMsg) m.Error(errorMsg)
msg := "_wails.dialogErrorCallback('" + *dialogID + "', " + strconv.Quote(errorMsg) + ");" msg := "_wails.dialogErrorCallback('" + *dialogID + "', " + strconv.Quote(errorMsg) + ");"
m.window.ExecJS(msg) window.ExecJS(msg)
} }
func (m *MessageProcessor) dialogCallback(dialogID *string, result string, isJSON bool) { func (m *MessageProcessor) dialogCallback(window *WebviewWindow, dialogID *string, result string, isJSON bool) {
msg := fmt.Sprintf("_wails.dialogCallback('%s', %s, %v);", *dialogID, strconv.Quote(result), isJSON) msg := fmt.Sprintf("_wails.dialogCallback('%s', %s, %v);", *dialogID, strconv.Quote(result), isJSON)
m.window.ExecJS(msg) window.ExecJS(msg)
} }
func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, params QueryParams) { func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, params QueryParams) {
@ -37,7 +37,7 @@ func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWr
var options MessageDialogOptions var options MessageDialogOptions
err := params.ToStruct(&options) err := params.ToStruct(&options)
if err != nil { if err != nil {
m.dialogErrorCallback("Error parsing dialog options: %s", dialogID, err) m.dialogErrorCallback(window, "Error parsing dialog options: %s", dialogID, err)
return return
} }
if len(options.Buttons) == 0 { if len(options.Buttons) == 0 {
@ -63,7 +63,7 @@ func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWr
for _, button := range options.Buttons { for _, button := range options.Buttons {
label := button.Label label := button.Label
button.OnClick(func() { button.OnClick(func() {
m.dialogCallback(dialogID, label, false) m.dialogCallback(window, dialogID, label, false)
}) })
} }
dialog.AddButtons(options.Buttons) dialog.AddButtons(options.Buttons)
@ -82,23 +82,23 @@ func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWr
if options.AllowsMultipleSelection { if options.AllowsMultipleSelection {
files, err := dialog.PromptForMultipleSelection() files, err := dialog.PromptForMultipleSelection()
if err != nil { if err != nil {
m.dialogErrorCallback("Error getting selection: %s", dialogID, err) m.dialogErrorCallback(window, "Error getting selection: %s", dialogID, err)
return return
} else { } else {
result, err := json.Marshal(files) result, err := json.Marshal(files)
if err != nil { if err != nil {
m.dialogErrorCallback("Error marshalling files: %s", dialogID, err) m.dialogErrorCallback(window, "Error marshalling files: %s", dialogID, err)
return return
} }
m.dialogCallback(dialogID, string(result), true) m.dialogCallback(window, dialogID, string(result), true)
} }
} else { } else {
file, err := dialog.PromptForSingleSelection() file, err := dialog.PromptForSingleSelection()
if err != nil { if err != nil {
m.dialogErrorCallback("Error getting selection: %s", dialogID, err) m.dialogErrorCallback(window, "Error getting selection: %s", dialogID, err)
return return
} }
m.dialogCallback(dialogID, file, false) m.dialogCallback(window, dialogID, file, false)
} }
}() }()
m.ok(rw) m.ok(rw)
@ -114,10 +114,10 @@ func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWr
go func() { go func() {
file, err := dialog.PromptForSingleSelection() file, err := dialog.PromptForSingleSelection()
if err != nil { if err != nil {
m.dialogErrorCallback("Error getting selection: %s", dialogID, err) m.dialogErrorCallback(window, "Error getting selection: %s", dialogID, err)
return return
} }
m.dialogCallback(dialogID, file, false) m.dialogCallback(window, dialogID, file, false)
}() }()
m.ok(rw) m.ok(rw)

View File

@ -1,6 +1,9 @@
package application package application
import ( import (
"io/fs"
"net/http"
"github.com/wailsapp/wails/v3/pkg/logger" "github.com/wailsapp/wails/v3/pkg/logger"
) )
@ -14,4 +17,49 @@ type Options struct {
Silent bool Silent bool
CustomLoggers []logger.Output CustomLoggers []logger.Output
} }
Assets AssetOptions
}
// AssetOptions defines the configuration of the AssetServer.
type AssetOptions struct {
// FS defines the static assets to be used. A GET request is first tried to be served from this FS. If the FS returns
// `os.ErrNotExist` for that file, the request handling will fallback to the Handler and tries to serve the GET
// request from it.
//
// If set to nil, all GET requests will be forwarded to Handler.
FS fs.FS
// Handler will be called for every GET request that can't be served from FS, due to `os.ErrNotExist`. Furthermore all
// non GET requests will always be served from this Handler.
//
// If not defined, the result is the following in cases where the Handler would have been called:
// GET request: `http.StatusNotFound`
// Other request: `http.StatusMethodNotAllowed`
Handler http.Handler
// Middleware is a HTTP Middleware which allows to hook into the AssetServer request chain. It allows to skip the default
// request handler dynamically, e.g. implement specialized Routing etc.
// The Middleware is called to build a new `http.Handler` used by the AssetSever and it also receives the default
// handler used by the AssetServer as an argument.
//
// If not defined, the default AssetServer request chain is executed.
//
// Multiple Middlewares can be chained together with:
// ChainMiddleware(middleware ...Middleware) Middleware
Middleware Middleware
}
// Middleware defines a HTTP middleware that can be applied to the AssetServer.
// The handler passed as next is the next handler in the chain. One can decide to call the next handler
// or implement a specialized handling.
type Middleware func(next http.Handler) http.Handler
// ChainMiddleware allows chaining multiple middlewares to one middleware.
func ChainMiddleware(middleware ...Middleware) Middleware {
return func(h http.Handler) http.Handler {
for i := len(middleware) - 1; i >= 0; i-- {
h = middleware[i](h)
}
return h
}
} }

View File

@ -1,10 +1,5 @@
package application package application
import (
"io/fs"
"net/http"
)
type WindowState int type WindowState int
const ( const (
@ -29,7 +24,6 @@ type WebviewWindowOptions struct {
StartState WindowState StartState WindowState
Mac MacWindow Mac MacWindow
BackgroundColour *RGBA BackgroundColour *RGBA
Assets AssetOptions
HTML string HTML string
JS string JS string
CSS string CSS string
@ -49,14 +43,6 @@ var WebviewWindowDefaults = &WebviewWindowOptions{
URL: "", URL: "",
} }
type AssetOptions struct {
// FS to use for loading assets from
FS fs.FS
// Handler is a custom handler to use for serving assets. If this is set, the `URL` and `FS` fields are ignored.
Handler http.Handler
// Middleware is a custom middleware to use for serving assets. If this is set, the `URL` and `FS` fields are ignored.
Middleware func(http.Handler) http.Handler
}
type RGBA struct { type RGBA struct {
Red, Green, Blue, Alpha uint8 Red, Green, Blue, Alpha uint8

View File

@ -7,10 +7,6 @@ import (
"github.com/wailsapp/wails/v3/pkg/logger" "github.com/wailsapp/wails/v3/pkg/logger"
"github.com/wailsapp/wails/v2/pkg/assetserver"
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
assetserveroptions "github.com/wailsapp/wails/v2/pkg/options/assetserver"
"github.com/wailsapp/wails/v3/internal/runtime"
"github.com/wailsapp/wails/v3/pkg/events" "github.com/wailsapp/wails/v3/pkg/events"
) )
@ -72,9 +68,6 @@ type WebviewWindow struct {
implLock sync.RWMutex implLock sync.RWMutex
id uint id uint
assets *assetserver.AssetServer
messageProcessor *MessageProcessor
eventListeners map[uint][]func(ctx *WindowEventContext) eventListeners map[uint][]func(ctx *WindowEventContext)
eventListenersLock sync.RWMutex eventListenersLock sync.RWMutex
@ -103,25 +96,13 @@ func NewWindow(options *WebviewWindowOptions) *WebviewWindow {
options.URL = "/" options.URL = "/"
} }
opts := assetserveroptions.Options{Assets: options.Assets.FS, Handler: options.Assets.Handler, Middleware: options.Assets.Middleware}
// TODO Bindings, ServingFrom disk?
srv, err := assetserver.NewAssetServer("", opts, false, nil, runtime.RuntimeAssetsBundle)
if err != nil {
globalApplication.fatal(err.Error())
}
result := &WebviewWindow{ result := &WebviewWindow{
id: getWindowID(), id: getWindowID(),
options: options, options: options,
eventListeners: make(map[uint][]func(ctx *WindowEventContext)), eventListeners: make(map[uint][]func(ctx *WindowEventContext)),
contextMenus: make(map[string]*Menu), contextMenus: make(map[string]*Menu),
assets: srv,
} }
result.messageProcessor = NewMessageProcessor(result)
srv.UseRuntimeHandler(result.messageProcessor)
return result return result
} }
@ -383,16 +364,10 @@ func (w *WebviewWindow) handleMessage(message string) {
if message == "test" { if message == "test" {
w.SetTitle("Hello World") w.SetTitle("Hello World")
} }
w.messageProcessor.ProcessMessage(message) w.info("ProcessMessage from front end:", message)
} }
func (w *WebviewWindow) handleWebViewRequest(request webview.Request) {
url, _ := request.URL()
w.info("Request: %s", url)
w.assets.ServeWebViewRequest(request)
}
func (w *WebviewWindow) Center() { func (w *WebviewWindow) Center() {
if w.impl == nil { if w.impl == nil {
return return

View File

@ -790,6 +790,7 @@ static void windowSetFrameless(void *window, bool frameless) {
*/ */
import "C" import "C"
import ( import (
"net/url"
"sync" "sync"
"unsafe" "unsafe"
@ -969,13 +970,17 @@ func (w *macosWebviewWindow) execJS(js string) {
C.windowExecJS(w.nsWindow, C.CString(js)) C.windowExecJS(w.nsWindow, C.CString(js))
} }
func (w *macosWebviewWindow) setURL(url string) { func (w *macosWebviewWindow) setURL(uri string) {
if url == "/" { if uri != "" {
// TODO handle this in a central location and handle all urls without scheme and host. This might be platform url, err := url.Parse(uri)
// dependant if err == nil && url.Scheme == "" && url.Host == "" {
url = "wails://wails/" // TODO handle this in a central location, the scheme and host might be platform dependant.
url.Scheme = "wails"
url.Host = "wails"
uri = url.String()
}
} }
C.navigationLoadURL(w.nsWindow, C.CString(url)) C.navigationLoadURL(w.nsWindow, C.CString(uri))
} }
func (w *macosWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) { func (w *macosWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) {