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

[v3] Updated runtime to take window id into consideration

[v3] Ported most of the window functions for JS
This commit is contained in:
Lea Anthony 2023-01-26 20:42:43 +11:00
parent 2a68fae55e
commit 293d730708
No known key found for this signature in database
GPG Key ID: 33DAF7BB90A58405
18 changed files with 624 additions and 985 deletions

10
go.work Normal file
View File

@ -0,0 +1,10 @@
go 1.19
use (
v2/examples/customlayout
v2/internal/staticanalysis/test/standard
v2/examples/systray
.
v2
v3
)

View File

@ -15,9 +15,14 @@ Informal and incomplete list of things needed in v3.
## Runtime
- [ ] Pass window ID with runtime calls in JS
- [x] Pass window ID with window calls in JS
- [ ] Implement alias for `window` in JS
- [ ] Implement runtime dispatcher
- [ ] Log
- [ ] Same Window
- [ ] Other Window
- [ ] Dialogs
- [ ] Events
## Templates

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,64 @@
package main
import (
"embed"
_ "embed"
"log"
"math/rand"
"strconv"
"github.com/wailsapp/wails/v3/pkg/options"
"github.com/wailsapp/wails/v3/pkg/application"
)
//go:embed assets
var assets embed.FS
func main() {
app := application.New(options.Application{
Name: "WebviewWindow Javascript Demo",
Description: "A demo of the WebviewWindow API from Javascript",
Icon: nil,
Mac: options.Mac{
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
})
// Create a custom menu
menu := app.NewMenu()
menu.AddRole(application.AppMenu)
windowCounter := 1
newWindow := func() {
app.NewWebviewWindowWithOptions(&options.WebviewWindow{
Assets: options.Assets{
FS: assets,
},
}).
SetTitle("WebviewWindow "+strconv.Itoa(windowCounter)).
SetPosition(rand.Intn(1000), rand.Intn(800)).
Show()
windowCounter++
}
// Let's make a "Demo" menu
myMenu := menu.AddSubmenu("New")
myMenu.Add("New WebviewWindow").
SetAccelerator("CmdOrCtrl+N").
OnClick(func(ctx *application.Context) {
newWindow()
})
newWindow()
app.SetMenu(menu)
err := app.Run()
if err != nil {
log.Fatal(err)
}
}

View File

@ -7,17 +7,39 @@
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 6 */
/* jshint esversion: 9 */
/**
* WailsInvoke sends the given message to the backend
*
* @param {string} message
*/
let postMessage = null;
(function () {
window.WailsInvoke = (message) => {
WINDOWS && window.chrome.webview.postMessage(message);
(DARWIN || LINUX) && window.webkit.messageHandlers.wails.postMessage(message);
// Credit: https://stackoverflow.com/a/2631521
let _deeptest = function (s) {
var obj = window[s.shift()];
while (obj && s.length) obj = obj[s.shift()];
return obj;
};
let windows = _deeptest(["chrome", "webview", "postMessage"]);
let mac_linux = _deeptest(["webkit", "messageHandlers", "external", "postMessage"]);
if (!windows && !mac_linux) {
console.error("Unsupported Platform");
return;
}
})();
if (windows) {
postMessage = (message) => window.chrome.webview.postMessage(message);
}
if (mac_linux) {
postMessage = (message) => window.webkit.messageHandlers.external.postMessage(message);
}
})();
export function invoke(message, id) {
if( id && id !== -1) {
postMessage("WINDOWID:"+ id + ":" + message);
} else {
postMessage(message);
}
}

View File

@ -9,30 +9,12 @@ The electron alternative for Go
*/
/* jshint esversion: 9 */
import "./ipc.js";
import {invoke} from "./ipc.js";
import {Callback, callbacks} from './calls';
import {EventsNotify, eventListeners} from "./events";
import {SetBindings} from "./bindings";
import * as Window from "./window";
import * as Screen from "./screen";
import * as Browser from "./browser";
import * as Log from './log';
let windowID = -1;
export function Quit() {
window.WailsInvoke('Q');
}
export function Show() {
window.WailsInvoke('S');
}
export function Hide() {
window.WailsInvoke('H');
}
import {newWindow} from "./window";
// export function Environment() {
// return Call(":wails:Environment");
@ -45,34 +27,29 @@ window.wails = {
EventsNotify,
eventListeners,
SetBindings,
window: {
ID: () => {
return windowID
},
}
};
window.runtime = {
...Log,
...Window,
...Browser,
...Screen,
EventsOn,
EventsOnce,
EventsOnMultiple,
EventsEmit,
EventsOff,
// Environment,
Show,
Hide,
Quit,
export function newRuntime(id) {
return {
// Log: newLog(id),
// Browser: newBrowser(id),
// Screen: newScreen(id),
// Events: newEvents(id),
Window: newWindow(id),
Show: () => invoke("S"),
Hide: () => invoke("H"),
Quit: () => invoke("Q"),
// GetWindow: function (windowID) {
// if (!windowID) {
// return this.Window;
// }
// return newWindow(windowID);
// }
}
}
// Process the expected runtime config from the backend
if( window.wails_config ) {
windowID = window.wails_config.windowID;
window.wails_config = null;
}
window.runtime = newRuntime(-1);
if (DEBUG) {
console.log("Wails v3.0.0 Debug Mode Enabled");

View File

@ -12,258 +12,56 @@ The electron alternative for Go
import {Call} from "./calls";
import {invoke} from "./ipc";
export function WindowReload() {
window.location.reload();
export function newWindow(id) {
return {
// Reload: () => invoke('WR', id),
// ReloadApp: () => invoke('WR', id),
// SetSystemDefaultTheme: () => invoke('WASDT', id),
// SetLightTheme: () => invoke('WALT', id),
// SetDarkTheme: () => invoke('WADT', id),
Center: () => invoke('Wc', id),
SetTitle: (title) => invoke('WT' + title, id),
Fullscreen: () => invoke('WF', id),
UnFullscreen: () => invoke('Wf', id),
SetSize: (width, height) => invoke('WS' + width + ',' + height, id),
GetSize: () => {
return Call(":wails:WindowGetSize")
},
SetMaxSize: (width, height) => invoke('WZ:' + width + ':' + height, id),
SetMinSize: (width, height) => invoke('Wz:' + width + ':' + height, id),
SetAlwaysOnTop: (b) => invoke('WATP:' + (b ? '1' : '0'), id),
SetPosition: (x, y) => invoke('Wp:' + x + ':' + y, id),
GetPosition: () => {
return Call(":wails:WindowGetPos")
},
Hide: () => invoke('WH', id),
Maximise: () => invoke('WM', id),
Show: () => invoke('WS', id),
ToggleMaximise: () => invoke('Wt', id),
UnMaximise: () => invoke('WU', id),
Minimise: () => invoke('Wm', id),
UnMinimise: () => invoke('Wu', id),
SetBackgroundColour: (R, G, B, A) =>
invoke('Wr:' + JSON.stringify({
r: R || 0,
g: G || 0,
b: B || 0,
a: A || 255}, id)
),
}
}
export function WindowReloadApp() {
window.WailsInvoke('WR');
}
// export function IsFullscreen: ()=> // return Call(":wails:WindowIsFullscreen"),
//
export function WindowSetSystemDefaultTheme() {
window.WailsInvoke('WASDT');
}
// export function IsMaximised: ()=> // return Call(":wails:WindowIsMaximised"),
//
export function WindowSetLightTheme() {
window.WailsInvoke('WALT');
}
// export function IsMinimised: ()=> // return Call(":wails:WindowIsMinimised"),
//
export function WindowSetDarkTheme() {
window.WailsInvoke('WADT');
}
/**
* Place the window in the center of the screen
*
* @export
*/
export function WindowCenter() {
window.WailsInvoke('Wc');
}
/**
* Sets the window title
*
* @param {string} title
* @export
*/
export function WindowSetTitle(title) {
window.WailsInvoke('WT' + title);
}
/**
* Makes the window go fullscreen
*
* @export
*/
export function WindowFullscreen() {
window.WailsInvoke('WF');
}
/**
* Reverts the window from fullscreen
*
* @export
*/
export function WindowUnfullscreen() {
window.WailsInvoke('Wf');
}
/**
* Returns the state of the window, i.e. whether the window is in full screen mode or not.
*
* @export
* @return {Promise<boolean>} The state of the window
*/
export function WindowIsFullscreen() {
return Call(":wails:WindowIsFullscreen");
}
/**
* Set the Size of the window
*
* @export
* @param {number} width
* @param {number} height
*/
export function WindowSetSize(width, height) {
window.WailsInvoke('Ws:' + width + ':' + height);
}
/**
* Get the Size of the window
*
* @export
* @return {Promise<{w: number, h: number}>} The size of the window
*/
export function WindowGetSize() {
return Call(":wails:WindowGetSize");
}
/**
* Set the maximum size of the window
*
* @export
* @param {number} width
* @param {number} height
*/
export function WindowSetMaxSize(width, height) {
window.WailsInvoke('WZ:' + width + ':' + height);
}
/**
* Set the minimum size of the window
*
* @export
* @param {number} width
* @param {number} height
*/
export function WindowSetMinSize(width, height) {
window.WailsInvoke('Wz:' + width + ':' + height);
}
/**
* Set the window AlwaysOnTop or not on top
*
* @export
*/
export function WindowSetAlwaysOnTop(b) {
window.WailsInvoke('WATP:' + (b ? '1' : '0'));
}
/**
* Set the Position of the window
*
* @export
* @param {number} x
* @param {number} y
*/
export function WindowSetPosition(x, y) {
window.WailsInvoke('Wp:' + x + ':' + y);
}
/**
* Get the Position of the window
*
* @export
* @return {Promise<{x: number, y: number}>} The position of the window
*/
export function WindowGetPosition() {
return Call(":wails:WindowGetPos");
}
/**
* Hide the Window
*
* @export
*/
export function WindowHide() {
window.WailsInvoke('WH');
}
/**
* Show the Window
*
* @export
*/
export function WindowShow() {
window.WailsInvoke('WS');
}
/**
* Maximise the Window
*
* @export
*/
export function WindowMaximise() {
window.WailsInvoke('WM');
}
/**
* Toggle the Maximise of the Window
*
* @export
*/
export function WindowToggleMaximise() {
window.WailsInvoke('Wt');
}
/**
* Unmaximise the Window
*
* @export
*/
export function WindowUnmaximise() {
window.WailsInvoke('WU');
}
/**
* Returns the state of the window, i.e. whether the window is maximised or not.
*
* @export
* @return {Promise<boolean>} The state of the window
*/
export function WindowIsMaximised() {
return Call(":wails:WindowIsMaximised");
}
/**
* Minimise the Window
*
* @export
*/
export function WindowMinimise() {
window.WailsInvoke('Wm');
}
/**
* Unminimise the Window
*
* @export
*/
export function WindowUnminimise() {
window.WailsInvoke('Wu');
}
/**
* Returns the state of the window, i.e. whether the window is minimised or not.
*
* @export
* @return {Promise<boolean>} The state of the window
*/
export function WindowIsMinimised() {
return Call(":wails:WindowIsMinimised");
}
/**
* Returns the state of the window, i.e. whether the window is normal or not.
*
* @export
* @return {Promise<boolean>} The state of the window
*/
export function WindowIsNormal() {
return Call(":wails:WindowIsNormal");
}
/**
* Sets the background colour of the window
*
* @export
* @param {number} R Red
* @param {number} G Green
* @param {number} B Blue
* @param {number} A Alpha
*/
export function WindowSetBackgroundColour(R, G, B, A) {
let rgba = JSON.stringify({r: R || 0, g: G || 0, b: B || 0, a: A || 255});
window.WailsInvoke('Wr:' + rgba);
}
// export function IsNormal: ()=> // return Call(":wails:WindowIsNormal"),
//

View File

@ -1 +1 @@
(()=>{(function(){window.WailsInvoke=e=>{WINDOWS&&window.chrome.webview.postMessage(e),(DARWIN||LINUX)&&window.webkit.messageHandlers.wails.postMessage(e)}})();})();
(()=>{var o=null;(function(){let s=function(e){for(var n=window[e.shift()];n&&e.length;)n=n[e.shift()];return n},t=s(["chrome","webview","postMessage"]),i=s(["webkit","messageHandlers","external","postMessage"]);if(!t&&!i){console.error("Unsupported Platform");return}t&&(o=e=>window.chrome.webview.postMessage(e)),i&&(o=e=>window.webkit.messageHandlers.external.postMessage(e))})();function r(s,t){o(t&&t!==-1?"WINDOWID:"+t+":"+s:s)}})();

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

@ -0,0 +1,66 @@
package application
import (
"fmt"
"strings"
)
type MessageProcessor struct {
window *WebviewWindow
}
func NewMessageProcessor(w *WebviewWindow) *MessageProcessor {
return &MessageProcessor{
window: w,
}
}
func (m *MessageProcessor) ProcessMessage(message string) {
// TODO: Implement calls to other windows
// Check for prefix "WINDOWID"
// If prefix exists, get window ID by parsing: "WINDOWID:12:MESSAGE"
if strings.HasPrefix(message, "WINDOWID") {
m.Error("Window ID prefix not yet implemented")
return
}
window := m.window
if message == "" {
m.Error("Blank message received")
return
}
m.Info("Processing message: %s", message)
switch message[0] {
//case 'L':
// m.processLogMessage(message)
//case 'E':
// return m.processEventMessage(message)
//case 'C':
// return m.processCallMessage(message)
//case 'c':
// return m.processSecureCallMessage(message)
case 'W':
m.processWindowMessage(message, window)
//case 'B':
// return m.processBrowserMessage(message)
case 'Q':
globalApplication.Quit()
case 'S':
//globalApplication.Show()
case 'H':
//globalApplication.Hide()
default:
m.Error("Unknown message from front end:", message)
}
}
func (m *MessageProcessor) Error(message string, args ...any) {
fmt.Printf("[MessageProcessor] Error: "+message, args...)
}
func (m *MessageProcessor) Info(message string, args ...any) {
fmt.Printf("[MessageProcessor] Info: "+message, args...)
}

View File

@ -0,0 +1,47 @@
package application
//
//import "errors"
//
////var logLevelMap = map[byte]logger.LogLevel{
//// '1': pkgLogger.TRACE,
//// '2': pkgLogger.DEBUG,
//// '3': pkgLogger.INFO,
//// '4': pkgLogger.WARNING,
//// '5': pkgLogger.ERROR,
////}
//
//func (m *MessageProcessor) processLogMessage(message string) {
// if len(message) < 3 {
// m.Error("Invalid Log Message: " + message)
// return
// }
//
// messageText := message[2:]
//
// switch message[1] {
// case 'T':
// d.log.Trace(messageText)
// case 'P':
// d.log.Print(messageText)
// case 'D':
// d.log.Debug(messageText)
// case 'I':
// d.log.Info(messageText)
// case 'W':
// d.log.Warning(messageText)
// case 'E':
// d.log.Error(messageText)
// case 'F':
// d.log.Fatal(messageText)
// case 'S':
// loglevel, exists := logLevelMap[message[2]]
// if !exists {
// return "", errors.New("Invalid Set Log Level Message: " + message)
// }
// d.log.SetLogLevel(loglevel)
// default:
// return "", errors.New("Invalid Log Message: " + message)
// }
// return "", nil
//}

View File

@ -0,0 +1,95 @@
package application
import (
"encoding/json"
"strconv"
"strings"
"github.com/wailsapp/wails/v3/pkg/options"
)
func (m *MessageProcessor) mustAtoI(input string) int {
result, err := strconv.Atoi(input)
if err != nil {
m.Error("cannot convert %s to integer!", input)
}
return result
}
func (m *MessageProcessor) processWindowMessage(message string, window *WebviewWindow) {
if len(message) < 2 {
m.Error("Invalid Window Message: " + message)
}
switch message[1] {
case 'A':
switch message[2:] {
//case "SDT":
// go window.WindowSetSystemDefaultTheme()
//case "LT":
// go window.SetLightTheme()
//case "DT":
// go window.SetDarkTheme()
case "TP:0", "TP:1":
if message[2:] == "TP:0" {
go window.SetAlwaysOnTop(false)
} else if message[2:] == "TP:1" {
go window.SetAlwaysOnTop(true)
}
}
case 'c':
go window.Center()
case 'T':
title := message[2:]
go window.SetTitle(title)
case 'F':
go window.Fullscreen()
case 'f':
go window.UnFullscreen()
case 's':
parts := strings.Split(message[3:], ":")
w := m.mustAtoI(parts[0])
h := m.mustAtoI(parts[1])
go window.SetSize(w, h)
case 'p':
parts := strings.Split(message[3:], ":")
x := m.mustAtoI(parts[0])
y := m.mustAtoI(parts[1])
go window.SetPosition(x, y)
case 'H':
go window.Hide()
case 'S':
go window.Show()
//case 'R':
// go window.ReloadApp()
case 'r':
var rgba options.RGBA
err := json.Unmarshal([]byte(message[3:]), &rgba)
if err != nil {
m.Error("Invalid RGBA Message: %s", err.Error())
}
go window.SetBackgroundColour(&rgba)
case 'M':
go window.Maximise()
//case 't':
// go window.ToggleMaximise()
case 'U':
go window.UnMaximise()
case 'm':
go window.Minimise()
case 'u':
go window.UnMinimise()
case 'Z':
parts := strings.Split(message[3:], ":")
w := m.mustAtoI(parts[0])
h := m.mustAtoI(parts[1])
go window.SetMaxSize(w, h)
case 'z':
parts := strings.Split(message[3:], ":")
w := m.mustAtoI(parts[0])
h := m.mustAtoI(parts[1])
go window.SetMinSize(w, h)
default:
m.Error("unknown Window message: %s", message)
}
}

View File

@ -67,7 +67,8 @@ type WebviewWindow struct {
implLock sync.RWMutex
id uint
assets *assetserver.AssetServer
assets *assetserver.AssetServer
messageProcessor *MessageProcessor
eventListeners map[uint][]func()
eventListenersLock sync.RWMutex
@ -90,6 +91,9 @@ func NewWindow(options *options.WebviewWindow) *WebviewWindow {
if options.Height == 0 {
options.Height = 600
}
if options.URL == "" {
options.URL = "/"
}
opts := assetserveroptions.Options{Assets: options.Assets.FS, Handler: options.Assets.Handler, Middleware: options.Assets.Middleware}
// TODO Bindings, Logger, ServingFrom disk?
@ -99,13 +103,17 @@ func NewWindow(options *options.WebviewWindow) *WebviewWindow {
panic(err)
}
return &WebviewWindow{
result := &WebviewWindow{
id: getWindowID(),
options: options,
eventListeners: make(map[uint][]func()),
assets: srv,
}
result.messageProcessor = NewMessageProcessor(result)
return result
}
func (w *WebviewWindow) SetTitle(title string) *WebviewWindow {
@ -339,11 +347,13 @@ func (w *WebviewWindow) SetBackgroundColour(colour *options.RGBA) *WebviewWindow
}
func (w *WebviewWindow) handleMessage(message string) {
fmt.Printf("[window %d] %s", w.id, message)
fmt.Printf("[window %d] %s\n", w.id, message)
// Check for special messages
if message == "test" {
w.SetTitle("Hello World")
}
w.messageProcessor.ProcessMessage(message)
}
func (w *WebviewWindow) handleWebViewRequest(request webview.Request) {