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

Initial implementation

This commit is contained in:
Lea Anthony 2023-03-18 08:11:36 +11:00
parent 86aaa3a956
commit 4300521064
17 changed files with 192 additions and 42 deletions

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
HELLO!
</body>
</html>

View 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())
}
}

View File

@ -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);
}

View File

@ -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);
}) });
} }
/** /**

View File

@ -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

View File

@ -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

View File

@ -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"`

View File

@ -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) {

View File

@ -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)
} }

View File

@ -18,6 +18,7 @@ type Options struct {
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.

View 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)
}