mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-03 04:42:00 +08:00
Initial implementation
This commit is contained in:
parent
86aaa3a956
commit
4300521064
10
v3/examples/plugins/assets/index.html
Normal file
10
v3/examples/plugins/assets/index.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
HELLO!
|
||||||
|
</body>
|
||||||
|
</html>
|
52
v3/examples/plugins/main.go
Normal file
52
v3/examples/plugins/main.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
_ "embed"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed assets/*
|
||||||
|
var assets embed.FS
|
||||||
|
|
||||||
|
type RandomNumberPlugin struct{}
|
||||||
|
|
||||||
|
func (r *RandomNumberPlugin) Name() string {
|
||||||
|
return "Random Number Plugin"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RandomNumberPlugin) Init(_ *application.App) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RandomNumberPlugin) Call(args []any) (any, error) {
|
||||||
|
return rand.Intn(100), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
app := application.New(application.Options{
|
||||||
|
Name: "Plugin Demo",
|
||||||
|
Description: "A demo of the plugins API",
|
||||||
|
Mac: application.MacOptions{
|
||||||
|
ApplicationShouldTerminateAfterLastWindowClosed: true,
|
||||||
|
},
|
||||||
|
Plugins: map[string]application.Plugin{
|
||||||
|
"random": &RandomNumberPlugin{},
|
||||||
|
},
|
||||||
|
Assets: application.AssetOptions{
|
||||||
|
FS: assets,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
window := app.NewWebviewWindow()
|
||||||
|
window.ToggleDevTools()
|
||||||
|
|
||||||
|
err := app.Run()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
}
|
@ -59,8 +59,10 @@ function callBinding(type, options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function Call(options) {
|
export function Call(options) {
|
||||||
return callBinding("Call", options);
|
return callBinding("Call", options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function Plugin(options) {
|
||||||
|
return callBinding("Plugin", options);
|
||||||
|
}
|
@ -167,7 +167,7 @@ export function Off(eventName, ...additionalEventNames) {
|
|||||||
let eventsToRemove = [eventName, ...additionalEventNames];
|
let eventsToRemove = [eventName, ...additionalEventNames];
|
||||||
eventsToRemove.forEach(eventName => {
|
eventsToRemove.forEach(eventName => {
|
||||||
eventListeners.delete(eventName);
|
eventListeners.delete(eventName);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,7 +14,7 @@ import * as Clipboard from './clipboard';
|
|||||||
import * as Application from './application';
|
import * as Application from './application';
|
||||||
import * as Log from './log';
|
import * as Log from './log';
|
||||||
import * as Screens from './screens';
|
import * as Screens from './screens';
|
||||||
import {Call, callErrorCallback, callCallback} from "./calls";
|
import {Plugin, Call, callErrorCallback, callCallback} from "./calls";
|
||||||
import {newWindow} from "./window";
|
import {newWindow} from "./window";
|
||||||
import {dispatchCustomEvent, Emit, Off, OffAll, On, Once, OnMultiple} from "./events";
|
import {dispatchCustomEvent, Emit, Off, OffAll, On, Once, OnMultiple} from "./events";
|
||||||
import {dialogCallback, dialogErrorCallback, Error, Info, OpenFile, Question, SaveFile, Warning,} from "./dialogs";
|
import {dialogCallback, dialogErrorCallback, Error, Info, OpenFile, Question, SaveFile, Warning,} from "./dialogs";
|
||||||
@ -45,6 +45,7 @@ export function newRuntime(id) {
|
|||||||
Log,
|
Log,
|
||||||
Screens,
|
Screens,
|
||||||
Call,
|
Call,
|
||||||
|
Plugin,
|
||||||
WML: {
|
WML: {
|
||||||
Reload: reloadWML,
|
Reload: reloadWML,
|
||||||
},
|
},
|
||||||
@ -65,7 +66,7 @@ export function newRuntime(id) {
|
|||||||
OffAll,
|
OffAll,
|
||||||
},
|
},
|
||||||
Window: newWindow(id),
|
Window: newWindow(id),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -57,7 +57,8 @@ func New(appOptions Options) *App {
|
|||||||
result.fatal(err.Error())
|
result.fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.UseRuntimeHandler(NewMessageProcessor())
|
pluginManager := NewPluginManager(appOptions.Plugins)
|
||||||
|
srv.UseRuntimeHandler(NewMessageProcessor(pluginManager))
|
||||||
result.assets = srv
|
result.assets = srv
|
||||||
|
|
||||||
globalApplication = result
|
globalApplication = result
|
||||||
|
@ -15,6 +15,11 @@ type CallOptions struct {
|
|||||||
Args []any `json:"args"`
|
Args []any `json:"args"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PluginCallOptions struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Args []any `json:"args"`
|
||||||
|
}
|
||||||
|
|
||||||
// Parameter defines a Go method parameter
|
// Parameter defines a Go method parameter
|
||||||
type Parameter struct {
|
type Parameter struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
|
@ -12,10 +12,14 @@ import (
|
|||||||
// TODO maybe we could use a new struct that has the targetWindow as an attribute so we could get rid of passing the targetWindow
|
// TODO maybe we could use a new struct that has the targetWindow as an attribute so we could get rid of passing the targetWindow
|
||||||
// as parameter through every function call.
|
// as parameter through every function call.
|
||||||
|
|
||||||
type MessageProcessor struct{}
|
type MessageProcessor struct {
|
||||||
|
pluginManager *PluginManager
|
||||||
|
}
|
||||||
|
|
||||||
func NewMessageProcessor() *MessageProcessor {
|
func NewMessageProcessor(pluginManager *PluginManager) *MessageProcessor {
|
||||||
return &MessageProcessor{}
|
return &MessageProcessor{
|
||||||
|
pluginManager: pluginManager,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MessageProcessor) httpError(rw http.ResponseWriter, message string, args ...any) {
|
func (m *MessageProcessor) httpError(rw http.ResponseWriter, message string, args ...any) {
|
||||||
|
@ -58,6 +58,28 @@ func (m *MessageProcessor) processCallMethod(method string, rw http.ResponseWrit
|
|||||||
m.callCallback(window, callID, string(jsonResult), true)
|
m.callCallback(window, callID, string(jsonResult), true)
|
||||||
}()
|
}()
|
||||||
m.ok(rw)
|
m.ok(rw)
|
||||||
|
case "Plugin":
|
||||||
|
var options PluginCallOptions
|
||||||
|
err := params.ToStruct(&options)
|
||||||
|
if err != nil {
|
||||||
|
m.callErrorCallback(window, "Error parsing plugin options: %s", callID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
result, err := m.pluginManager.Call(options.Name, options.Args)
|
||||||
|
if err != nil {
|
||||||
|
m.callErrorCallback(window, "Error calling plugin: %s", callID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// convert result to json
|
||||||
|
jsonResult, err := json.Marshal(result)
|
||||||
|
if err != nil {
|
||||||
|
m.callErrorCallback(window, "Error converting result to json: %s", callID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.callCallback(window, callID, string(jsonResult), true)
|
||||||
|
}()
|
||||||
|
m.ok(rw)
|
||||||
default:
|
default:
|
||||||
m.httpError(rw, "Unknown dialog method: %s", method)
|
m.httpError(rw, "Unknown dialog method: %s", method)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,8 @@ type Options struct {
|
|||||||
Silent bool
|
Silent bool
|
||||||
CustomLoggers []logger.Output
|
CustomLoggers []logger.Output
|
||||||
}
|
}
|
||||||
Assets AssetOptions
|
Assets AssetOptions
|
||||||
|
Plugins map[string]Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssetOptions defines the configuration of the AssetServer.
|
// AssetOptions defines the configuration of the AssetServer.
|
||||||
|
40
v3/pkg/application/plugins.go
Normal file
40
v3/pkg/application/plugins.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Plugin interface {
|
||||||
|
Name() string
|
||||||
|
Init(app *App) error
|
||||||
|
Call(args []any) (any, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PluginManager struct {
|
||||||
|
plugins map[string]Plugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPluginManager(plugins map[string]Plugin) *PluginManager {
|
||||||
|
return &PluginManager{
|
||||||
|
plugins: plugins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginManager) Init() error {
|
||||||
|
for _, plugin := range p.plugins {
|
||||||
|
err := plugin.Init(globalApplication)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
globalApplication.info("Plugin '%s' initialised", plugin.Name())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginManager) Call(name string, args []any) (any, error) {
|
||||||
|
plugin, ok := p.plugins[name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("plugin '%s' not found", name)
|
||||||
|
}
|
||||||
|
return plugin.Call(args)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user