mirror of
https://github.com/wavetermdev/wails.git
synced 2026-04-22 15:26:15 -07:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2aae05f5bb | |||
| a4971ab58e | |||
| fe618b71b3 | |||
| c5c3f2ab5e | |||
| fc2c5207fc | |||
| 2db9537c8c | |||
| 38475a5da4 | |||
| bf87f026f7 | |||
| 724e0c7b86 |
@@ -0,0 +1,65 @@
|
||||
# Wails v3 Plugin Guide
|
||||
|
||||
Wails v3 introduces the concept of plugins. A plugin is a self-contained module that can extend the functionality of your Wails application.
|
||||
This guide will walk you through the structure and functionality of a Wails plugin.
|
||||
|
||||
## Plugin Structure
|
||||
|
||||
A Wails plugin is a standard Go module, typically consisting of the following files:
|
||||
|
||||
- `plugin.go`: This is the main Go file where the plugin's functionality is implemented.
|
||||
- `plugin.yaml`: This is the plugin's metadata file. It contains information about the plugin such as its name, author, version, and more.
|
||||
- `assets/`: This directory contains any static assets that the plugin might need.
|
||||
- `README.md`: This file provides documentation for the plugin.
|
||||
|
||||
## Plugin Implementation
|
||||
|
||||
In `plugin.go`, a plugin is defined as a struct that implements the `application.Plugin` interface. This interface requires the following methods:
|
||||
|
||||
- `Init()`: This method is called when the plugin is initialized.
|
||||
- `Shutdown()`: This method is called when the application is shutting down.
|
||||
- `Name()`: This method returns the name of the plugin.
|
||||
- `CallableByJS()`: This method returns a list of method names that can be called from the frontend.
|
||||
|
||||
In addition to these methods, you can define any number of additional methods that implement the plugin's functionality.
|
||||
These methods can be called from the frontend using the `wails.Plugin()` function.
|
||||
|
||||
## Plugin Metadata
|
||||
|
||||
The `plugin.yaml` file contains metadata about the plugin. This includes the plugin's name, description, author, version, website, repository, and license.
|
||||
|
||||
## Plugin Assets
|
||||
|
||||
Any static assets that the plugin needs can be placed in the `assets/` directory.
|
||||
These assets can be accessed by the frontend by requesting them at the plugin base path.
|
||||
This path is `/wails/plugins/<plugin-name>/`.
|
||||
|
||||
### Example
|
||||
|
||||
If a plugin named `logopack` has an asset named `logo.png`, the frontend can access it at `/wails/plugins/logopack/logo.png`.
|
||||
|
||||
## Plugin Documentation
|
||||
|
||||
The `README.md` file provides documentation for the plugin. This should include instructions on how to install and use the plugin, as well as any other information that users of the plugin might find useful.
|
||||
|
||||
## Example
|
||||
|
||||
Here's an example of how to use a plugin in your Wails application:
|
||||
|
||||
```go
|
||||
Plugins: map[string]application.Plugin{
|
||||
"log": log.NewPlugin(),
|
||||
},
|
||||
```
|
||||
And here's how you can call a plugin method from the frontend:
|
||||
|
||||
```js
|
||||
wails.Plugin("log","Debug","hello world")
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter any issues with a plugin, please raise a ticket in the plugin's repository.
|
||||
|
||||
!!! note
|
||||
The Wails team does not provide support for third-party plugins.
|
||||
@@ -141,6 +141,7 @@ nav:
|
||||
- Next Steps: getting-started/next-steps.md
|
||||
- Learn More:
|
||||
- Runtime: learn/runtime.md
|
||||
- Plugins: learn/plugins.md
|
||||
- Feedback: getting-started/feedback.md
|
||||
- Feedback: getting-started/feedback.md
|
||||
- What's New in v3?: whats-new.md
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="quarter" style="background-color: lightblue; --webkit-app-region: drag">Draggable</div>
|
||||
<div class="quarter" style="background-color: lightblue; --wails-draggable: drag">Draggable</div>
|
||||
<div class="quarter" style="background-color: lightgreen;">Not Draggable</div>
|
||||
<div class="quarter" style="background-color: lightpink;">Not Draggable</div>
|
||||
<div class="quarter" style="background-color: lightyellow; --webkit-app-region: drag">Draggable</div>
|
||||
<div class="quarter" style="background-color: lightyellow; --wails-draggable: drag">Draggable</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,202 @@
|
||||
html {
|
||||
background-color: rgba(33, 37, 43);
|
||||
text-align: center;
|
||||
color: white;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 40px;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 30%;
|
||||
height: 20%;
|
||||
}
|
||||
/* CSS */
|
||||
.button-42 {
|
||||
background-color: initial;
|
||||
background-image: linear-gradient(-180deg, #555555, #2f2f2f);
|
||||
border-radius: 6px;
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0 2px 4px;
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-family: Inter,-apple-system,system-ui,Roboto,"Helvetica Neue",Arial,sans-serif;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
outline: 0;
|
||||
overflow: hidden;
|
||||
padding: 0 20px;
|
||||
pointer-events: auto;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
touch-action: manipulation;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
z-index: 9;
|
||||
border: 0;
|
||||
transition: box-shadow .2s;
|
||||
font-size: medium;
|
||||
}
|
||||
p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.button-42:hover {
|
||||
background-image: linear-gradient(-180deg, #cb3939, #610000);
|
||||
}
|
||||
html {
|
||||
background-color: rgba(33, 37, 43);
|
||||
text-align: center;
|
||||
color: white;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 40px;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
.button-42 {
|
||||
background-color: initial;
|
||||
background-image: linear-gradient(-180deg, #555555, #2f2f2f);
|
||||
border-radius: 6px;
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0 2px 4px;
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-family: Inter,-apple-system,system-ui,Roboto,"Helvetica Neue",Arial,sans-serif;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
outline: 0;
|
||||
overflow: hidden;
|
||||
padding: 0 20px;
|
||||
pointer-events: auto;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
touch-action: manipulation;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
z-index: 9;
|
||||
border: 0;
|
||||
transition: box-shadow .2s;
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.button-42:hover {
|
||||
background-image: linear-gradient(-180deg, #cb3939, #610000);
|
||||
}
|
||||
|
||||
.tab {
|
||||
overflow: hidden;
|
||||
background-color: #f1f1f100;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.tab button {
|
||||
background-color: transparent; /* Make the background transparent */
|
||||
color: white; /* Make the text white */
|
||||
float: left;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
padding: 14px 16px;
|
||||
transition: 0.3s;
|
||||
position: relative; /* Added for the underline */
|
||||
}
|
||||
|
||||
.tab button::after { /* Added for the underline */
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #a40505;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.tab button:hover::after, /* Added for the underline */
|
||||
.tab button.active::after { /* Added for the underline */
|
||||
width: 100%;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.tab button.active {
|
||||
background-color: transparent; /* Make the background transparent */
|
||||
color: red;
|
||||
}
|
||||
.tabcontent {
|
||||
display: none;
|
||||
padding: 6px 12px;
|
||||
border-top: none;
|
||||
}
|
||||
#sqlresults, #hashresults {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
min-height: 100px;
|
||||
}
|
||||
#selectquery {
|
||||
width: 90%;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #005467;
|
||||
color: white;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background-color: #888;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #FF4B2B; /* Bright red color for better visibility in dark mode */
|
||||
background-color: #1E1E1E; /* Dark background for the error message */
|
||||
border: 1px solid #FF4B2B; /* Border color same as text color */
|
||||
padding: 10px; /* Padding around the text */
|
||||
margin: 10px 0; /* Margin top and bottom */
|
||||
border-radius: 5px; /* Rounded corners */
|
||||
text-align: center; /* Center the text */
|
||||
}
|
||||
|
||||
.narrowColumn {
|
||||
width: 1%; /* Adjust as needed */
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
# Experimental Plugins
|
||||
|
||||
This directory contains experimental plugins. These plugins are not supported by the Wails team and are provided as-is.
|
||||
They may be removed at any time. If you have any thoughts about these plugins, they can be discussed on the [Wails Discord](https://discord.gg/u8JkcWsG).
|
||||
+2
-1
@@ -15,7 +15,8 @@ Add the plugin to the `Plugins` option in the Applications options:
|
||||
You can then call the Generate method from the frontend:
|
||||
|
||||
```js
|
||||
wails.Plugin("hashes","Generate","hello world").then((result) => console.log(result))
|
||||
import {Call} from "/wails/runtime.js";
|
||||
Call.Plugin("hashes","Generate","hello world").then((result) => console.log(result))
|
||||
```
|
||||
|
||||
This method returns a struct with the following fields:
|
||||
+3
-2
@@ -6,6 +6,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
// ---------------- Plugin Setup ----------------
|
||||
@@ -32,8 +33,8 @@ func (r *Plugin) CallableByJS() []string {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Plugin) InjectJS() string {
|
||||
return ""
|
||||
func (r *Plugin) Assets() fs.FS {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---------------- Plugin Methods ----------------
|
||||
@@ -2,8 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"log/slog"
|
||||
"os"
|
||||
"plugin_demo/plugins/hashes"
|
||||
"plugin_demo/hashes"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
"github.com/wailsapp/wails/v3/plugins/kvstore"
|
||||
@@ -24,6 +25,7 @@ func main() {
|
||||
Mac: application.MacOptions{
|
||||
ApplicationShouldTerminateAfterLastWindowClosed: true,
|
||||
},
|
||||
LogLevel: slog.LevelDebug,
|
||||
Plugins: map[string]application.Plugin{
|
||||
"hashes": hashes.NewPlugin(),
|
||||
"log": log.NewPlugin(),
|
||||
@@ -34,10 +36,6 @@ func main() {
|
||||
Filename: "store.json",
|
||||
AutoSave: true,
|
||||
}),
|
||||
//"server": server.NewPlugin(&server.Config{
|
||||
// Enabled: true,
|
||||
// Port: 34115,
|
||||
//}),
|
||||
"single_instance": single_instance.NewPlugin(&single_instance.Config{
|
||||
// When true, the original app will be activated when a second instance is launched
|
||||
ActivateAppOnSubsequentLaunch: true,
|
||||
@@ -50,8 +48,8 @@ func main() {
|
||||
})
|
||||
|
||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
DevToolsEnabled: true,
|
||||
OpenInspectorOnStartup: true,
|
||||
Width: 1024,
|
||||
Height: 768,
|
||||
})
|
||||
|
||||
err := app.Run()
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
export namespace HashesPlugin {
|
||||
export interface Hashes {
|
||||
md5: string;
|
||||
sha1: string;
|
||||
sha256: string;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
// Generate takes a string and returns a number of hashes for it
|
||||
export function Generate(input) {
|
||||
return wails.Plugin("hashes","Generate",input);
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{"url2":"https://reddit.com"}
|
||||
{"q":"www","url2":"https://reddit.com"}
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
module github.com/wailsapp/wails/v3
|
||||
|
||||
go 1.22
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/atterpac/refresh v0.6.3
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package assetserver
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"html"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -14,6 +16,7 @@ import (
|
||||
const (
|
||||
webViewRequestHeaderWindowId = "x-wails-window-id"
|
||||
webViewRequestHeaderWindowName = "x-wails-window-name"
|
||||
pluginPrefix = "/wails/plugin"
|
||||
)
|
||||
|
||||
type RuntimeHandler interface {
|
||||
@@ -25,13 +28,17 @@ type AssetServer struct {
|
||||
|
||||
handler http.Handler
|
||||
|
||||
pluginScripts map[string]string
|
||||
//pluginScripts map[string]string
|
||||
|
||||
assetServerWebView
|
||||
pluginAssets map[string]fs.FS
|
||||
}
|
||||
|
||||
func NewAssetServer(options *Options) (*AssetServer, error) {
|
||||
result := &AssetServer{options: options}
|
||||
result := &AssetServer{
|
||||
options: options,
|
||||
pluginAssets: make(map[string]fs.FS),
|
||||
}
|
||||
|
||||
userHandler := options.Handler
|
||||
if userHandler == nil {
|
||||
@@ -84,8 +91,8 @@ func (a *AssetServer) serveHTTP(rw http.ResponseWriter, req *http.Request, userH
|
||||
// header.Add(HeaderCacheControl, "no-cache")
|
||||
//}
|
||||
|
||||
path := req.URL.Path
|
||||
switch path {
|
||||
reqPath := req.URL.Path
|
||||
switch reqPath {
|
||||
case "", "/", "/index.html":
|
||||
recorder := httptest.NewRecorder()
|
||||
userHandler.ServeHTTP(recorder, req)
|
||||
@@ -105,9 +112,34 @@ func (a *AssetServer) serveHTTP(rw http.ResponseWriter, req *http.Request, userH
|
||||
}
|
||||
|
||||
default:
|
||||
// Check if this is a plugin script
|
||||
if script, ok := a.pluginScripts[path]; ok {
|
||||
a.writeBlob(rw, path, []byte(script))
|
||||
|
||||
// Check if this is a plugin asset
|
||||
if !strings.HasPrefix(reqPath, pluginPrefix) {
|
||||
userHandler.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure there is 4 parts to the reqPath
|
||||
parts := strings.SplitN(reqPath, "/", 5)
|
||||
if len(parts) < 5 {
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the first 3 parts of the reqPath
|
||||
pluginPath := "/" + path.Join(parts[1], parts[2], parts[3])
|
||||
// Get the remaining part of the reqPath
|
||||
fileName := parts[4]
|
||||
|
||||
// Check if this is a registered plugin asset
|
||||
if assetFS, ok := a.pluginAssets[pluginPath]; ok {
|
||||
// Check if the file exists
|
||||
file, err := fs.ReadFile(assetFS, fileName)
|
||||
if err != nil {
|
||||
a.serveError(rw, err, "Unable to read file %s", reqPath)
|
||||
return
|
||||
}
|
||||
a.writeBlob(rw, reqPath, file)
|
||||
} else {
|
||||
userHandler.ServeHTTP(rw, req)
|
||||
}
|
||||
@@ -127,14 +159,28 @@ func (a *AssetServer) serveError(rw http.ResponseWriter, err error, msg string,
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func (a *AssetServer) AddPluginScript(pluginName string, script string) {
|
||||
if a.pluginScripts == nil {
|
||||
a.pluginScripts = make(map[string]string)
|
||||
//func (a *AssetServer) AddPluginScript(pluginName string, script string) {
|
||||
// if a.pluginScripts == nil {
|
||||
// a.pluginScripts = make(map[string]string)
|
||||
// }
|
||||
// pluginName = strings.ReplaceAll(pluginName, "/", "_")
|
||||
// pluginName = html.EscapeString(pluginName)
|
||||
// pluginScriptName := fmt.Sprintf("/wails/plugin/%s.js", pluginName)
|
||||
// a.pluginScripts[pluginScriptName] = script
|
||||
//}
|
||||
|
||||
func (a *AssetServer) AddPluginAssets(pluginPath string, vfs fs.FS) error {
|
||||
pluginPath = path.Join(pluginPrefix, pluginPath)
|
||||
_, exists := a.pluginAssets[pluginPath]
|
||||
if exists {
|
||||
return fmt.Errorf("plugin path already exists: %s", pluginPath)
|
||||
}
|
||||
pluginName = strings.ReplaceAll(pluginName, "/", "_")
|
||||
pluginName = html.EscapeString(pluginName)
|
||||
pluginScriptName := fmt.Sprintf("/wails/plugin/%s.js", pluginName)
|
||||
a.pluginScripts[pluginScriptName] = script
|
||||
if embedFs, isEmbedFs := vfs.(embed.FS); isEmbedFs {
|
||||
rootFolder, _ := findEmbedRootPath(embedFs)
|
||||
vfs, _ = fs.Sub(vfs, path.Clean(rootFolder))
|
||||
}
|
||||
a.pluginAssets[pluginPath] = vfs
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetStartURL(userURL string) (string, error) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -7,91 +7,85 @@
|
||||
The electron alternative for Go
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
|
||||
/* jshint esversion: 9 */
|
||||
|
||||
import {invoke, IsWindows} from "./system";
|
||||
import {GetFlag} from "./flags";
|
||||
|
||||
// Setup
|
||||
let shouldDrag = false;
|
||||
let resizable = false;
|
||||
let resizeEdge = null;
|
||||
let defaultCursor = "auto";
|
||||
|
||||
window._wails = window._wails || {};
|
||||
window._wails.setResizable = setResizable;
|
||||
window._wails.endDrag = endDrag;
|
||||
|
||||
window._wails.setResizable = function(value) {
|
||||
resizable = value;
|
||||
};
|
||||
|
||||
window._wails.endDrag = function() {
|
||||
document.body.style.cursor = 'default';
|
||||
shouldDrag = false;
|
||||
};
|
||||
|
||||
window.addEventListener('mousedown', onMouseDown);
|
||||
window.addEventListener('mousemove', onMouseMove);
|
||||
window.addEventListener('mouseup', onMouseUp);
|
||||
|
||||
|
||||
let shouldDrag = false;
|
||||
let resizeEdge = null;
|
||||
let resizable = false;
|
||||
let defaultCursor = "auto";
|
||||
|
||||
function dragTest(e) {
|
||||
let val = window.getComputedStyle(e.target).getPropertyValue("--webkit-app-region");
|
||||
if (!val || val === "" || val.trim() !== "drag" || e.buttons !== 1) {
|
||||
let val = window.getComputedStyle(e.target).getPropertyValue("--wails-draggable");
|
||||
let mousePressed = e.buttons !== undefined ? e.buttons : e.which;
|
||||
if (!val || val === "" || val.trim() !== "drag" || mousePressed === 0) {
|
||||
return false;
|
||||
}
|
||||
return e.detail === 1;
|
||||
}
|
||||
|
||||
function setResizable(value) {
|
||||
resizable = value;
|
||||
function onMouseDown(e) {
|
||||
|
||||
// Check for resizing
|
||||
if (resizeEdge) {
|
||||
invoke("resize:" + resizeEdge);
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (dragTest(e)) {
|
||||
// This checks for clicks on the scroll bar
|
||||
if (e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight) {
|
||||
return;
|
||||
}
|
||||
shouldDrag = true;
|
||||
} else {
|
||||
shouldDrag = false;
|
||||
}
|
||||
}
|
||||
|
||||
function endDrag() {
|
||||
document.body.style.cursor = 'default';
|
||||
function onMouseUp() {
|
||||
shouldDrag = false;
|
||||
}
|
||||
|
||||
function testResize() {
|
||||
if( resizeEdge ) {
|
||||
invoke(`resize:${resizeEdge}`);
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function onMouseDown(e) {
|
||||
if(IsWindows() && testResize() || dragTest(e)) {
|
||||
shouldDrag = !!isValidDrag(e);
|
||||
}
|
||||
}
|
||||
|
||||
function isValidDrag(e) {
|
||||
// Ignore drag on scrollbars
|
||||
return !(e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight);
|
||||
}
|
||||
|
||||
function onMouseUp(e) {
|
||||
let mousePressed = e.buttons !== undefined ? e.buttons : e.which;
|
||||
if (mousePressed > 0) {
|
||||
endDrag();
|
||||
}
|
||||
}
|
||||
|
||||
function setResize(cursor = defaultCursor) {
|
||||
document.documentElement.style.cursor = cursor;
|
||||
function setResize(cursor) {
|
||||
document.documentElement.style.cursor = cursor || defaultCursor;
|
||||
resizeEdge = cursor;
|
||||
}
|
||||
|
||||
function onMouseMove(e) {
|
||||
shouldDrag = checkDrag(e);
|
||||
if (IsWindows() && resizable) {
|
||||
handleResize(e);
|
||||
if (shouldDrag) {
|
||||
shouldDrag = false;
|
||||
let mousePressed = e.buttons !== undefined ? e.buttons : e.which;
|
||||
if (mousePressed > 0) {
|
||||
invoke("drag");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkDrag(e) {
|
||||
let mousePressed = e.buttons !== undefined ? e.buttons : e.which;
|
||||
if(shouldDrag && mousePressed > 0) {
|
||||
invoke("drag");
|
||||
return false;
|
||||
if (!resizable || !IsWindows()) {
|
||||
return;
|
||||
}
|
||||
if (defaultCursor == null) {
|
||||
defaultCursor = document.documentElement.style.cursor;
|
||||
}
|
||||
return shouldDrag;
|
||||
}
|
||||
|
||||
function handleResize(e) {
|
||||
let resizeHandleHeight = GetFlag("system.resizeHandleHeight") || 5;
|
||||
let resizeHandleWidth = GetFlag("system.resizeHandleWidth") || 5;
|
||||
|
||||
@@ -122,4 +116,4 @@ function handleResize(e) {
|
||||
else if (topBorder) setResize("n-resize");
|
||||
else if (bottomBorder) setResize("s-resize");
|
||||
else if (rightBorder) setResize("e-resize");
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package application
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wailsapp/wails/v3/internal/assetserver"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
type PluginAPI interface {
|
||||
@@ -13,7 +14,7 @@ type Plugin interface {
|
||||
Init(api PluginAPI) error
|
||||
Shutdown() error
|
||||
CallableByJS() []string
|
||||
InjectJS() string
|
||||
Assets() fs.FS
|
||||
}
|
||||
|
||||
type PluginManager struct {
|
||||
@@ -33,16 +34,19 @@ func NewPluginManager(plugins map[string]Plugin, assetServer *assetserver.AssetS
|
||||
func (p *PluginManager) Init() []error {
|
||||
|
||||
api := newPluginAPI()
|
||||
for _, plugin := range p.plugins {
|
||||
for id, plugin := range p.plugins {
|
||||
err := plugin.Init(api)
|
||||
if err != nil {
|
||||
globalApplication.error("Plugin failed to initialise:", "plugin", plugin.Name(), "error", err.Error())
|
||||
return p.Shutdown()
|
||||
}
|
||||
p.initialisedPlugins = append(p.initialisedPlugins, plugin)
|
||||
injectJS := plugin.InjectJS()
|
||||
if injectJS != "" {
|
||||
p.assetServer.AddPluginScript(plugin.Name(), injectJS)
|
||||
assets := plugin.Assets()
|
||||
if assets != nil {
|
||||
err = p.assetServer.AddPluginAssets(id, assets)
|
||||
if err != nil {
|
||||
return []error{errors.Wrap(err, "Failed to add plugin assets: "+plugin.Name())}
|
||||
}
|
||||
}
|
||||
globalApplication.debug("Plugin initialised: " + plugin.Name())
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user