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

Support most dialogs

Better JS->Go object mapping
Implement Go->JS callback mechanism
Rename `window.runtime` -> `window.wails` to better reflect the Go API
This commit is contained in:
Lea Anthony 2023-01-29 21:01:54 +11:00
parent 226572a1df
commit 73b08a45de
No known key found for this signature in database
GPG Key ID: 33DAF7BB90A58405
20 changed files with 841 additions and 619 deletions

View File

@ -22,8 +22,16 @@ Informal and incomplete list of things needed in v3.
- [x] Same Window
- [ ] Other Window
- [ ] Dialogs
- [x] Info
- [x] Warning
- [x] Error
- [x] Question
- [x] OpenFile
- [ ] SaveFile
- [ ] Events
- [ ] Screens
- [x] Clipboard
- [ ] Application
- [ ] Create `.d.ts` file
## Templates

View File

@ -0,0 +1,80 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 9 */
import {newRuntimeCaller} from "./runtime";
import { nanoid } from 'nanoid/non-secure'
let call = newRuntimeCaller("dialog");
let dialogResponses = new Map();
function generateID() {
let result;
do {
result = nanoid();
} while (dialogResponses.has(result));
return result;
}
export function dialogCallback(id, data, isJSON) {
let p = dialogResponses.get(id);
if (p) {
if (isJSON) {
p.resolve(JSON.parse(data));
} else {
p.resolve(data);
}
dialogResponses.delete(id);
}
}
export function dialogErrorCallback(id, message) {
let p = dialogResponses.get(id);
if (p) {
p.reject(message);
dialogResponses.delete(id);
}
}
function dialog(type, options) {
return new Promise((resolve, reject) => {
let id = generateID();
options = options || {};
options["dialog-id"] = id;
dialogResponses.set(id, {resolve, reject});
call(type, options).catch((error) => {
reject(error);
dialogResponses.delete(id);
})
});
}
export function Info(options) {
return dialog("Info", options);
}
export function Warning(options) {
return dialog("Warning", options);
}
export function Error(options) {
return dialog("Error", options);
}
export function Question(options) {
return dialog("Question", options);
}
export function OpenFile(options) {
return dialog("OpenFile", options);
}

View File

@ -15,6 +15,8 @@ import {EventsNotify, eventListeners} from "./events";
import {SetBindings} from "./bindings";
import {Info, Warning, Error, Question, OpenFile, dialogCallback, dialogErrorCallback, } from "./dialogs";
import * as Clipboard from './clipboard';
import {newWindow} from "./window";
@ -24,13 +26,14 @@ import {newWindow} from "./window";
// Internal wails endpoints
window.wails = {
Callback,
callbacks,
EventsNotify,
eventListeners,
SetBindings,
...newRuntime(-1),
};
window._wails = {
dialogCallback,
dialogErrorCallback,
}
export function newRuntime(id) {
return {
@ -41,10 +44,19 @@ export function newRuntime(id) {
Clipboard: {
...Clipboard
},
Dialog: {
Info,
Warning,
Error,
Question,
OpenFile,
},
Window: newWindow(id),
Show: () => invoke("S"),
Hide: () => invoke("H"),
Quit: () => invoke("Q"),
Application: {
Show: () => invoke("S"),
Hide: () => invoke("H"),
Quit: () => invoke("Q"),
}
// GetWindow: function (windowID) {
// if (!windowID) {
// return this.Window;
@ -54,8 +66,6 @@ export function newRuntime(id) {
}
}
window.runtime = newRuntime(-1);
if (DEBUG) {
console.log("Wails v3.0.0 Debug Mode Enabled");
}

View File

@ -15,11 +15,7 @@ const runtimeURL = window.location.origin + "/wails/runtime";
function runtimeCall(method, args) {
let url = new URL(runtimeURL);
url.searchParams.append("method", method);
if (args) {
for (let key in args) {
url.searchParams.append(key, args[key]);
}
}
url.searchParams.append("args", JSON.stringify(args));
return new Promise((resolve, reject) => {
fetch(url)
.then(response => {
@ -41,6 +37,7 @@ function runtimeCall(method, args) {
export function newRuntimeCaller(object, id) {
if (!id || id === -1) {
return function (method, args) {
args = args || {};
return runtimeCall(object + "." + method, args);
};
}

View File

@ -11,6 +11,7 @@
"devDependencies": {
"esbuild": "^0.17.5",
"happy-dom": "^8.1.5",
"nanoid": "^4.0.0",
"npm-check-updates": "^16.6.3",
"vitest": "^0.28.3"
}
@ -3013,15 +3014,15 @@
"dev": true
},
"node_modules/nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz",
"integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==",
"dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
"node": "^14 || ^16 || >=18"
}
},
"node_modules/negotiator": {
@ -3630,6 +3631,18 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss/node_modules/nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
@ -7157,9 +7170,9 @@
"dev": true
},
"nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz",
"integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==",
"dev": true
},
"negotiator": {
@ -7606,6 +7619,14 @@
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"dependencies": {
"nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true
}
}
},
"pretty-format": {

View File

@ -9,6 +9,7 @@
"devDependencies": {
"esbuild": "^0.17.5",
"happy-dom": "^8.1.5",
"nanoid": "^4.0.0",
"npm-check-updates": "^16.6.3",
"vitest": "^0.28.3"
}

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

@ -1 +1 @@
(()=>{var v=Object.defineProperty;var C=(o,e)=>{for(var t in e)v(o,t,{get:e[t],enumerable:!0})};var u=null;(function(){let o=function(n){let r=window[n.shift()];for(;r&&n.length;)r=r[n.shift()];return r},e=o(["chrome","webview","postMessage"]),t=o(["webkit","messageHandlers","external","postMessage"]);if(!e&&!t){console.error("Unsupported Platform");return}e&&(u=n=>window.chrome.webview.postMessage(n)),t&&(u=n=>window.webkit.messageHandlers.external.postMessage(n))})();function w(o,e){u(e&&e!==-1?"WINDOWID:"+e+":"+o:o)}var s={};function E(){let o=new Uint32Array(1);return window.crypto.getRandomValues(o)[0]}function O(){return Math.random()*9007199254740991}var f;window.crypto?f=E:f=O;function h(o,e,t){t==null&&(t=0);let n=window.wails.window.ID();return new Promise(function(r,i){let l;do l=o+"-"+f();while(s[l]);let a;t>0&&(a=setTimeout(function(){i(Error("Call to "+o+" timed out. Request ID: "+l))},t)),s[l]={timeoutHandle:a,reject:i,resolve:r};try{let m={name:o,args:e,callbackID:l,windowID:n};window.WailsInvoke("C"+JSON.stringify(m))}catch(m){console.error(m)}})}window.ObfuscatedCall=(o,e,t)=>(t==null&&(t=0),new Promise(function(n,r){let i;do i=o+"-"+f();while(s[i]);let l;t>0&&(l=setTimeout(function(){r(Error("Call to method "+o+" timed out. Request ID: "+i))},t)),s[i]={timeoutHandle:l,reject:r,resolve:n};try{let a={id:o,args:e,callbackID:i,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(a))}catch(a){console.error(a)}}));function x(o){let e;try{e=JSON.parse(o)}catch(r){let i=`Invalid JSON passed to callback: ${r.message}. Message: ${o}`;throw runtime.LogDebug(i),new Error(i)}let t=e.callbackid,n=s[t];if(!n){let r=`Callback '${t}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete s[t],e.error?n.reject(e.error):n.resolve(e.result)}var c={};function I(o){let e=o.name;if(c[e]){let t=c[e].slice();for(let n=0;n<c[e].length;n+=1){let r=c[e][n],i=o.data;r.Callback(i)&&t.splice(n,1)}t.length===0?M(e):c[e]=t}}function S(o){let e;try{e=JSON.parse(o)}catch{let n="Invalid JSON passed to Notify: "+o;throw new Error(n)}I(e)}function M(o){delete c[o],window.WailsInvoke("EX"+o)}window.go={};function y(o){try{o=JSON.parse(o)}catch(e){console.error(e)}window.go=window.go||{},Object.keys(o).forEach(e=>{window.go[e]=window.go[e]||{},Object.keys(o[e]).forEach(t=>{window.go[e][t]=window.go[e][t]||{},Object.keys(o[e][t]).forEach(n=>{window.go[e][t][n]=function(){let r=0;function i(){let l=[].slice.call(arguments);return h([e,t,n].join("."),l,r)}return i.setTimeout=function(l){r=l},i.getTimeout=function(){return r},i}()})})})}var p={};C(p,{SetText:()=>D,Text:()=>H});var T=window.location.origin+"/wails/runtime";function b(o,e){let t=new URL(T);if(t.searchParams.append("method",o),e)for(let n in e)t.searchParams.append(n,e[n]);return new Promise((n,r)=>{fetch(t).then(i=>{if(i.ok)return i.headers.get("content-type")&&i.headers.get("content-type").indexOf("application/json")!==-1?i.json():i.text();r(Error(i.statusText))}).then(i=>n(i)).catch(i=>r(i))})}function d(o,e){return!e||e===-1?function(t,n){return b(o+"."+t,n)}:function(t,n){return n=n||{},n.windowID=e,b(o+"."+t,n)}}var g=d("clipboard");function D(o){return g("SetText",{text:o})}function H(){return g("Text")}function k(o){let e=d("window",o);return{Center:()=>e("Center"),SetTitle:t=>e("SetTitle",{title:t}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(t,n)=>e("SetSize",{width:t,height:n}),Size:()=>e("Size"),SetMaxSize:(t,n)=>e("SetMaxSize",{width:t,height:n}),SetMinSize:(t,n)=>e("SetMinSize",{width:t,height:n}),SetAlwaysOnTop:t=>e("SetAlwaysOnTop",{alwaysOnTop:t}),SetPosition:(t,n)=>e("SetPosition",{x:t,y:n}),Position:()=>e("Position"),Screen:()=>e("Screen"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(t,n,r,i)=>e("SetBackgroundColour",{R,G,B,A})}}window.wails={Callback:x,callbacks:s,EventsNotify:S,eventListeners:c,SetBindings:y};function U(o){return{Clipboard:{...p},Window:k(o),Show:()=>w("S"),Hide:()=>w("H"),Quit:()=>w("Q")}}window.runtime=U(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
(()=>{var I=Object.defineProperty;var M=(t,e)=>{for(var n in e)I(t,n,{get:e[n],enumerable:!0})};var c=null;(function(){let t=function(o){let i=window[o.shift()];for(;i&&o.length;)i=i[o.shift()];return i},e=t(["chrome","webview","postMessage"]),n=t(["webkit","messageHandlers","external","postMessage"]);if(!e&&!n){console.error("Unsupported Platform");return}e&&(c=o=>window.chrome.webview.postMessage(o)),n&&(c=o=>window.webkit.messageHandlers.external.postMessage(o))})();function u(t,e){c(e&&e!==-1?"WINDOWID:"+e+":"+t:t)}var m={};function D(){let t=new Uint32Array(1);return window.crypto.getRandomValues(t)[0]}function T(){return Math.random()*9007199254740991}var w;window.crypto?w=D:w=T;window.ObfuscatedCall=(t,e,n)=>(n==null&&(n=0),new Promise(function(o,i){let r;do r=t+"-"+w();while(m[r]);let p;n>0&&(p=setTimeout(function(){i(Error("Call to method "+t+" timed out. Request ID: "+r))},n)),m[r]={timeoutHandle:p,reject:i,resolve:o};try{let f={id:t,args:e,callbackID:r,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(f))}catch(f){console.error(f)}}));window.go={};var W=window.location.origin+"/wails/runtime";function h(t,e){let n=new URL(W);return n.searchParams.append("method",t),n.searchParams.append("args",JSON.stringify(e)),new Promise((o,i)=>{fetch(n).then(r=>{if(r.ok)return r.headers.get("content-type")&&r.headers.get("content-type").indexOf("application/json")!==-1?r.json():r.text();i(Error(r.statusText))}).then(r=>o(r)).catch(r=>i(r))})}function s(t,e){return!e||e===-1?function(n,o){return o=o||{},h(t+"."+n,o)}:function(n,o){return o=o||{},o.windowID=e,h(t+"."+n,o)}}var H="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";var x=(t=21)=>{let e="",n=t;for(;n--;)e+=H[Math.random()*64|0];return e};var P=s("dialog"),l=new Map;function U(){let t;do t=x();while(l.has(t));return t}function g(t,e,n){let o=l.get(t);o&&(n?o.resolve(JSON.parse(e)):o.resolve(e),l.delete(t))}function S(t,e){let n=l.get(t);n&&(n.reject(e),l.delete(t))}function a(t,e){return new Promise((n,o)=>{let i=U();e=e||{},e["dialog-id"]=i,l.set(i,{resolve:n,reject:o}),P(t,e).catch(r=>{o(r),l.delete(i)})})}function b(t){return a("Info",t)}function y(t){return a("Warning",t)}function C(t){return a("Error",t)}function k(t){return a("Question",t)}function E(t){return a("OpenFile",t)}var d={};M(d,{SetText:()=>J,Text:()=>L});var O=s("clipboard");function J(t){return O("SetText",{text:t})}function L(){return O("Text")}function v(t){let e=s("window",t);return{Center:()=>e("Center"),SetTitle:n=>e("SetTitle",{title:n}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(n,o)=>e("SetSize",{width:n,height:o}),Size:()=>e("Size"),SetMaxSize:(n,o)=>e("SetMaxSize",{width:n,height:o}),SetMinSize:(n,o)=>e("SetMinSize",{width:n,height:o}),SetAlwaysOnTop:n=>e("SetAlwaysOnTop",{alwaysOnTop:n}),SetPosition:(n,o)=>e("SetPosition",{x:n,y:o}),Position:()=>e("Position"),Screen:()=>e("Screen"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(n,o,i,r)=>e("SetBackgroundColour",{R,G,B,A})}}window.wails={...z(-1)};window._wails={dialogCallback:g,dialogErrorCallback:S};function z(t){return{Clipboard:{...d},Dialog:{Info:b,Warning:y,Error:C,Question:k,OpenFile:E},Window:v(t),Application:{Show:()=>u("S"),Hide:()=>u("H"),Quit:()=>u("Q")}}}console.log("Wails v3.0.0 Debug Mode Enabled");})();

View File

@ -1 +1 @@
(()=>{var v=Object.defineProperty;var C=(o,e)=>{for(var t in e)v(o,t,{get:e[t],enumerable:!0})};var u=null;(function(){let o=function(n){let r=window[n.shift()];for(;r&&n.length;)r=r[n.shift()];return r},e=o(["chrome","webview","postMessage"]),t=o(["webkit","messageHandlers","external","postMessage"]);if(!e&&!t){console.error("Unsupported Platform");return}e&&(u=n=>window.chrome.webview.postMessage(n)),t&&(u=n=>window.webkit.messageHandlers.external.postMessage(n))})();function w(o,e){u(e&&e!==-1?"WINDOWID:"+e+":"+o:o)}var s={};function E(){let o=new Uint32Array(1);return window.crypto.getRandomValues(o)[0]}function O(){return Math.random()*9007199254740991}var f;window.crypto?f=E:f=O;function h(o,e,t){t==null&&(t=0);let n=window.wails.window.ID();return new Promise(function(r,i){let l;do l=o+"-"+f();while(s[l]);let a;t>0&&(a=setTimeout(function(){i(Error("Call to "+o+" timed out. Request ID: "+l))},t)),s[l]={timeoutHandle:a,reject:i,resolve:r};try{let m={name:o,args:e,callbackID:l,windowID:n};window.WailsInvoke("C"+JSON.stringify(m))}catch(m){console.error(m)}})}window.ObfuscatedCall=(o,e,t)=>(t==null&&(t=0),new Promise(function(n,r){let i;do i=o+"-"+f();while(s[i]);let l;t>0&&(l=setTimeout(function(){r(Error("Call to method "+o+" timed out. Request ID: "+i))},t)),s[i]={timeoutHandle:l,reject:r,resolve:n};try{let a={id:o,args:e,callbackID:i,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(a))}catch(a){console.error(a)}}));function x(o){let e;try{e=JSON.parse(o)}catch(r){let i=`Invalid JSON passed to callback: ${r.message}. Message: ${o}`;throw runtime.LogDebug(i),new Error(i)}let t=e.callbackid,n=s[t];if(!n){let r=`Callback '${t}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete s[t],e.error?n.reject(e.error):n.resolve(e.result)}var c={};function I(o){let e=o.name;if(c[e]){let t=c[e].slice();for(let n=0;n<c[e].length;n+=1){let r=c[e][n],i=o.data;r.Callback(i)&&t.splice(n,1)}t.length===0?M(e):c[e]=t}}function S(o){let e;try{e=JSON.parse(o)}catch{let n="Invalid JSON passed to Notify: "+o;throw new Error(n)}I(e)}function M(o){delete c[o],window.WailsInvoke("EX"+o)}window.go={};function y(o){try{o=JSON.parse(o)}catch(e){console.error(e)}window.go=window.go||{},Object.keys(o).forEach(e=>{window.go[e]=window.go[e]||{},Object.keys(o[e]).forEach(t=>{window.go[e][t]=window.go[e][t]||{},Object.keys(o[e][t]).forEach(n=>{window.go[e][t][n]=function(){let r=0;function i(){let l=[].slice.call(arguments);return h([e,t,n].join("."),l,r)}return i.setTimeout=function(l){r=l},i.getTimeout=function(){return r},i}()})})})}var p={};C(p,{SetText:()=>D,Text:()=>H});var T=window.location.origin+"/wails/runtime";function b(o,e){let t=new URL(T);if(t.searchParams.append("method",o),e)for(let n in e)t.searchParams.append(n,e[n]);return new Promise((n,r)=>{fetch(t).then(i=>{if(i.ok)return i.headers.get("content-type")&&i.headers.get("content-type").indexOf("application/json")!==-1?i.json():i.text();r(Error(i.statusText))}).then(i=>n(i)).catch(i=>r(i))})}function d(o,e){return!e||e===-1?function(t,n){return b(o+"."+t,n)}:function(t,n){return n=n||{},n.windowID=e,b(o+"."+t,n)}}var g=d("clipboard");function D(o){return g("SetText",{text:o})}function H(){return g("Text")}function k(o){let e=d("window",o);return{Center:()=>e("Center"),SetTitle:t=>e("SetTitle",{title:t}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(t,n)=>e("SetSize",{width:t,height:n}),Size:()=>e("Size"),SetMaxSize:(t,n)=>e("SetMaxSize",{width:t,height:n}),SetMinSize:(t,n)=>e("SetMinSize",{width:t,height:n}),SetAlwaysOnTop:t=>e("SetAlwaysOnTop",{alwaysOnTop:t}),SetPosition:(t,n)=>e("SetPosition",{x:t,y:n}),Position:()=>e("Position"),Screen:()=>e("Screen"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(t,n,r,i)=>e("SetBackgroundColour",{R,G,B,A})}}window.wails={Callback:x,callbacks:s,EventsNotify:S,eventListeners:c,SetBindings:y};function U(o){return{Clipboard:{...p},Window:k(o),Show:()=>w("S"),Hide:()=>w("H"),Quit:()=>w("Q")}}window.runtime=U(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
(()=>{var I=Object.defineProperty;var M=(t,e)=>{for(var n in e)I(t,n,{get:e[n],enumerable:!0})};var c=null;(function(){let t=function(o){let i=window[o.shift()];for(;i&&o.length;)i=i[o.shift()];return i},e=t(["chrome","webview","postMessage"]),n=t(["webkit","messageHandlers","external","postMessage"]);if(!e&&!n){console.error("Unsupported Platform");return}e&&(c=o=>window.chrome.webview.postMessage(o)),n&&(c=o=>window.webkit.messageHandlers.external.postMessage(o))})();function u(t,e){c(e&&e!==-1?"WINDOWID:"+e+":"+t:t)}var m={};function D(){let t=new Uint32Array(1);return window.crypto.getRandomValues(t)[0]}function T(){return Math.random()*9007199254740991}var w;window.crypto?w=D:w=T;window.ObfuscatedCall=(t,e,n)=>(n==null&&(n=0),new Promise(function(o,i){let r;do r=t+"-"+w();while(m[r]);let p;n>0&&(p=setTimeout(function(){i(Error("Call to method "+t+" timed out. Request ID: "+r))},n)),m[r]={timeoutHandle:p,reject:i,resolve:o};try{let f={id:t,args:e,callbackID:r,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(f))}catch(f){console.error(f)}}));window.go={};var W=window.location.origin+"/wails/runtime";function h(t,e){let n=new URL(W);return n.searchParams.append("method",t),n.searchParams.append("args",JSON.stringify(e)),new Promise((o,i)=>{fetch(n).then(r=>{if(r.ok)return r.headers.get("content-type")&&r.headers.get("content-type").indexOf("application/json")!==-1?r.json():r.text();i(Error(r.statusText))}).then(r=>o(r)).catch(r=>i(r))})}function s(t,e){return!e||e===-1?function(n,o){return o=o||{},h(t+"."+n,o)}:function(n,o){return o=o||{},o.windowID=e,h(t+"."+n,o)}}var H="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";var x=(t=21)=>{let e="",n=t;for(;n--;)e+=H[Math.random()*64|0];return e};var P=s("dialog"),l=new Map;function U(){let t;do t=x();while(l.has(t));return t}function g(t,e,n){let o=l.get(t);o&&(n?o.resolve(JSON.parse(e)):o.resolve(e),l.delete(t))}function S(t,e){let n=l.get(t);n&&(n.reject(e),l.delete(t))}function a(t,e){return new Promise((n,o)=>{let i=U();e=e||{},e["dialog-id"]=i,l.set(i,{resolve:n,reject:o}),P(t,e).catch(r=>{o(r),l.delete(i)})})}function b(t){return a("Info",t)}function y(t){return a("Warning",t)}function C(t){return a("Error",t)}function k(t){return a("Question",t)}function E(t){return a("OpenFile",t)}var d={};M(d,{SetText:()=>J,Text:()=>L});var O=s("clipboard");function J(t){return O("SetText",{text:t})}function L(){return O("Text")}function v(t){let e=s("window",t);return{Center:()=>e("Center"),SetTitle:n=>e("SetTitle",{title:n}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(n,o)=>e("SetSize",{width:n,height:o}),Size:()=>e("Size"),SetMaxSize:(n,o)=>e("SetMaxSize",{width:n,height:o}),SetMinSize:(n,o)=>e("SetMinSize",{width:n,height:o}),SetAlwaysOnTop:n=>e("SetAlwaysOnTop",{alwaysOnTop:n}),SetPosition:(n,o)=>e("SetPosition",{x:n,y:o}),Position:()=>e("Position"),Screen:()=>e("Screen"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(n,o,i,r)=>e("SetBackgroundColour",{R,G,B,A})}}window.wails={...z(-1)};window._wails={dialogCallback:g,dialogErrorCallback:S};function z(t){return{Clipboard:{...d},Dialog:{Info:b,Warning:y,Error:C,Question:k,OpenFile:E},Window:v(t),Application:{Show:()=>u("S"),Hide:()=>u("H"),Quit:()=>u("Q")}}}console.log("Wails v3.0.0 Debug Mode Enabled");})();

View File

@ -1 +1 @@
(()=>{var v=Object.defineProperty;var C=(o,e)=>{for(var t in e)v(o,t,{get:e[t],enumerable:!0})};var u=null;(function(){let o=function(n){let r=window[n.shift()];for(;r&&n.length;)r=r[n.shift()];return r},e=o(["chrome","webview","postMessage"]),t=o(["webkit","messageHandlers","external","postMessage"]);if(!e&&!t){console.error("Unsupported Platform");return}e&&(u=n=>window.chrome.webview.postMessage(n)),t&&(u=n=>window.webkit.messageHandlers.external.postMessage(n))})();function w(o,e){u(e&&e!==-1?"WINDOWID:"+e+":"+o:o)}var s={};function E(){let o=new Uint32Array(1);return window.crypto.getRandomValues(o)[0]}function O(){return Math.random()*9007199254740991}var f;window.crypto?f=E:f=O;function h(o,e,t){t==null&&(t=0);let n=window.wails.window.ID();return new Promise(function(r,i){let l;do l=o+"-"+f();while(s[l]);let a;t>0&&(a=setTimeout(function(){i(Error("Call to "+o+" timed out. Request ID: "+l))},t)),s[l]={timeoutHandle:a,reject:i,resolve:r};try{let m={name:o,args:e,callbackID:l,windowID:n};window.WailsInvoke("C"+JSON.stringify(m))}catch(m){console.error(m)}})}window.ObfuscatedCall=(o,e,t)=>(t==null&&(t=0),new Promise(function(n,r){let i;do i=o+"-"+f();while(s[i]);let l;t>0&&(l=setTimeout(function(){r(Error("Call to method "+o+" timed out. Request ID: "+i))},t)),s[i]={timeoutHandle:l,reject:r,resolve:n};try{let a={id:o,args:e,callbackID:i,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(a))}catch(a){console.error(a)}}));function x(o){let e;try{e=JSON.parse(o)}catch(r){let i=`Invalid JSON passed to callback: ${r.message}. Message: ${o}`;throw runtime.LogDebug(i),new Error(i)}let t=e.callbackid,n=s[t];if(!n){let r=`Callback '${t}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete s[t],e.error?n.reject(e.error):n.resolve(e.result)}var c={};function I(o){let e=o.name;if(c[e]){let t=c[e].slice();for(let n=0;n<c[e].length;n+=1){let r=c[e][n],i=o.data;r.Callback(i)&&t.splice(n,1)}t.length===0?M(e):c[e]=t}}function S(o){let e;try{e=JSON.parse(o)}catch{let n="Invalid JSON passed to Notify: "+o;throw new Error(n)}I(e)}function M(o){delete c[o],window.WailsInvoke("EX"+o)}window.go={};function y(o){try{o=JSON.parse(o)}catch(e){console.error(e)}window.go=window.go||{},Object.keys(o).forEach(e=>{window.go[e]=window.go[e]||{},Object.keys(o[e]).forEach(t=>{window.go[e][t]=window.go[e][t]||{},Object.keys(o[e][t]).forEach(n=>{window.go[e][t][n]=function(){let r=0;function i(){let l=[].slice.call(arguments);return h([e,t,n].join("."),l,r)}return i.setTimeout=function(l){r=l},i.getTimeout=function(){return r},i}()})})})}var p={};C(p,{SetText:()=>D,Text:()=>H});var T=window.location.origin+"/wails/runtime";function b(o,e){let t=new URL(T);if(t.searchParams.append("method",o),e)for(let n in e)t.searchParams.append(n,e[n]);return new Promise((n,r)=>{fetch(t).then(i=>{if(i.ok)return i.headers.get("content-type")&&i.headers.get("content-type").indexOf("application/json")!==-1?i.json():i.text();r(Error(i.statusText))}).then(i=>n(i)).catch(i=>r(i))})}function d(o,e){return!e||e===-1?function(t,n){return b(o+"."+t,n)}:function(t,n){return n=n||{},n.windowID=e,b(o+"."+t,n)}}var g=d("clipboard");function D(o){return g("SetText",{text:o})}function H(){return g("Text")}function k(o){let e=d("window",o);return{Center:()=>e("Center"),SetTitle:t=>e("SetTitle",{title:t}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(t,n)=>e("SetSize",{width:t,height:n}),Size:()=>e("Size"),SetMaxSize:(t,n)=>e("SetMaxSize",{width:t,height:n}),SetMinSize:(t,n)=>e("SetMinSize",{width:t,height:n}),SetAlwaysOnTop:t=>e("SetAlwaysOnTop",{alwaysOnTop:t}),SetPosition:(t,n)=>e("SetPosition",{x:t,y:n}),Position:()=>e("Position"),Screen:()=>e("Screen"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(t,n,r,i)=>e("SetBackgroundColour",{R,G,B,A})}}window.wails={Callback:x,callbacks:s,EventsNotify:S,eventListeners:c,SetBindings:y};function U(o){return{Clipboard:{...p},Window:k(o),Show:()=>w("S"),Hide:()=>w("H"),Quit:()=>w("Q")}}window.runtime=U(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
(()=>{var I=Object.defineProperty;var M=(t,e)=>{for(var n in e)I(t,n,{get:e[n],enumerable:!0})};var c=null;(function(){let t=function(o){let i=window[o.shift()];for(;i&&o.length;)i=i[o.shift()];return i},e=t(["chrome","webview","postMessage"]),n=t(["webkit","messageHandlers","external","postMessage"]);if(!e&&!n){console.error("Unsupported Platform");return}e&&(c=o=>window.chrome.webview.postMessage(o)),n&&(c=o=>window.webkit.messageHandlers.external.postMessage(o))})();function u(t,e){c(e&&e!==-1?"WINDOWID:"+e+":"+t:t)}var m={};function D(){let t=new Uint32Array(1);return window.crypto.getRandomValues(t)[0]}function T(){return Math.random()*9007199254740991}var w;window.crypto?w=D:w=T;window.ObfuscatedCall=(t,e,n)=>(n==null&&(n=0),new Promise(function(o,i){let r;do r=t+"-"+w();while(m[r]);let p;n>0&&(p=setTimeout(function(){i(Error("Call to method "+t+" timed out. Request ID: "+r))},n)),m[r]={timeoutHandle:p,reject:i,resolve:o};try{let f={id:t,args:e,callbackID:r,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(f))}catch(f){console.error(f)}}));window.go={};var W=window.location.origin+"/wails/runtime";function h(t,e){let n=new URL(W);return n.searchParams.append("method",t),n.searchParams.append("args",JSON.stringify(e)),new Promise((o,i)=>{fetch(n).then(r=>{if(r.ok)return r.headers.get("content-type")&&r.headers.get("content-type").indexOf("application/json")!==-1?r.json():r.text();i(Error(r.statusText))}).then(r=>o(r)).catch(r=>i(r))})}function s(t,e){return!e||e===-1?function(n,o){return o=o||{},h(t+"."+n,o)}:function(n,o){return o=o||{},o.windowID=e,h(t+"."+n,o)}}var H="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";var x=(t=21)=>{let e="",n=t;for(;n--;)e+=H[Math.random()*64|0];return e};var P=s("dialog"),l=new Map;function U(){let t;do t=x();while(l.has(t));return t}function g(t,e,n){let o=l.get(t);o&&(n?o.resolve(JSON.parse(e)):o.resolve(e),l.delete(t))}function S(t,e){let n=l.get(t);n&&(n.reject(e),l.delete(t))}function a(t,e){return new Promise((n,o)=>{let i=U();e=e||{},e["dialog-id"]=i,l.set(i,{resolve:n,reject:o}),P(t,e).catch(r=>{o(r),l.delete(i)})})}function b(t){return a("Info",t)}function y(t){return a("Warning",t)}function C(t){return a("Error",t)}function k(t){return a("Question",t)}function E(t){return a("OpenFile",t)}var d={};M(d,{SetText:()=>J,Text:()=>L});var O=s("clipboard");function J(t){return O("SetText",{text:t})}function L(){return O("Text")}function v(t){let e=s("window",t);return{Center:()=>e("Center"),SetTitle:n=>e("SetTitle",{title:n}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(n,o)=>e("SetSize",{width:n,height:o}),Size:()=>e("Size"),SetMaxSize:(n,o)=>e("SetMaxSize",{width:n,height:o}),SetMinSize:(n,o)=>e("SetMinSize",{width:n,height:o}),SetAlwaysOnTop:n=>e("SetAlwaysOnTop",{alwaysOnTop:n}),SetPosition:(n,o)=>e("SetPosition",{x:n,y:o}),Position:()=>e("Position"),Screen:()=>e("Screen"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(n,o,i,r)=>e("SetBackgroundColour",{R,G,B,A})}}window.wails={...z(-1)};window._wails={dialogCallback:g,dialogErrorCallback:S};function z(t){return{Clipboard:{...d},Dialog:{Info:b,Warning:y,Error:C,Question:k,OpenFile:E},Window:v(t),Application:{Show:()=>u("S"),Hide:()=>u("H"),Quit:()=>u("Q")}}}console.log("Wails v3.0.0 Debug Mode Enabled");})();

View File

@ -228,7 +228,7 @@ func (a *App) Run() error {
// set the application menu
a.impl.setApplicationMenu(a.ApplicationMenu)
// set the application icon
// set the application Icon
a.impl.setIcon(a.options.Icon)
return a.impl.run()
@ -389,3 +389,9 @@ func (a *App) dispatchOnMainThread(fn func()) {
// Call platform specific dispatch function
a.impl.dispatchOnMainThread(id)
}
func (a *App) OpenFileDialogWithOptions(options *OpenFileDialogOptions) *OpenFileDialog {
result := a.OpenFileDialog()
result.SetOptions(options)
return result
}

View File

@ -46,9 +46,9 @@ const (
)
type Button struct {
label string
isCancel bool
isDefault bool
Label string
IsCancel bool
IsDefault bool
callback func()
}
@ -60,12 +60,16 @@ type messageDialogImpl interface {
show()
}
type MessageDialogOptions struct {
DialogType DialogType
Title string
Message string
Buttons []*Button
Icon []byte
}
type MessageDialog struct {
dialogType DialogType
title string
message string
buttons []*Button
icon []byte
MessageDialogOptions
// platform independent
impl messageDialogImpl
@ -80,13 +84,16 @@ var defaultTitles = map[DialogType]string{
func newMessageDialog(dialogType DialogType) *MessageDialog {
return &MessageDialog{
dialogType: dialogType,
title: defaultTitles[dialogType],
MessageDialogOptions: MessageDialogOptions{
DialogType: dialogType,
Title: defaultTitles[dialogType],
},
impl: nil,
}
}
func (d *MessageDialog) SetTitle(title string) *MessageDialog {
d.title = title
d.Title = title
return d
}
@ -98,36 +105,41 @@ func (d *MessageDialog) Show() {
}
func (d *MessageDialog) SetIcon(icon []byte) *MessageDialog {
d.icon = icon
d.Icon = icon
return d
}
func (d *MessageDialog) AddButton(s string) *Button {
result := &Button{
label: s,
Label: s,
}
d.buttons = append(d.buttons, result)
d.Buttons = append(d.Buttons, result)
return result
}
func (d *MessageDialog) AddButtons(buttons []*Button) *MessageDialog {
d.Buttons = buttons
return d
}
func (d *MessageDialog) SetDefaultButton(button *Button) *MessageDialog {
for _, b := range d.buttons {
b.isDefault = false
for _, b := range d.Buttons {
b.IsDefault = false
}
button.isDefault = true
button.IsDefault = true
return d
}
func (d *MessageDialog) SetCancelButton(button *Button) *MessageDialog {
for _, b := range d.buttons {
b.isCancel = false
for _, b := range d.Buttons {
b.IsCancel = false
}
button.isCancel = true
button.IsCancel = true
return d
}
func (d *MessageDialog) SetMessage(title string) *MessageDialog {
d.title = title
func (d *MessageDialog) SetMessage(message string) *MessageDialog {
d.Message = message
return d
}
@ -135,9 +147,28 @@ type openFileDialogImpl interface {
show() ([]string, error)
}
type fileFilter struct {
displayName string // Filter information EG: "Image Files (*.jpg, *.png)"
pattern string // semicolon separated list of extensions, EG: "*.jpg;*.png"
type FileFilter struct {
DisplayName string // Filter information EG: "Image Files (*.jpg, *.png)"
Pattern string // semicolon separated list of extensions, EG: "*.jpg;*.png"
}
type OpenFileDialogOptions struct {
CanChooseDirectories bool
CanChooseFiles bool
CanCreateDirectories bool
ShowHiddenFiles bool
ResolvesAliases bool
AllowsMultipleSelection bool
HideExtension bool
CanSelectHiddenExtension bool
TreatsFilePackagesAsDirectories bool
AllowsOtherFileTypes bool
Filters []FileFilter
Title string
Message string
ButtonText string
Directory string
}
type OpenFileDialog struct {
@ -152,7 +183,7 @@ type OpenFileDialog struct {
canSelectHiddenExtension bool
treatsFilePackagesAsDirectories bool
allowsOtherFileTypes bool
filters []fileFilter
filters []FileFilter
title string
message string
@ -230,9 +261,9 @@ func (d *OpenFileDialog) PromptForSingleSelection() (string, error) {
// AddFilter adds a filter to the dialog. The filter is a display name and a semicolon separated list of extensions.
// EG: AddFilter("Image Files", "*.jpg;*.png")
func (d *OpenFileDialog) AddFilter(displayName, pattern string) *OpenFileDialog {
d.filters = append(d.filters, fileFilter{
displayName: strings.TrimSpace(displayName),
pattern: strings.TrimSpace(pattern),
d.filters = append(d.filters, FileFilter{
DisplayName: strings.TrimSpace(displayName),
Pattern: strings.TrimSpace(pattern),
})
return d
}
@ -265,6 +296,24 @@ func (d *OpenFileDialog) CanSelectHiddenExtension(canSelectHiddenExtension bool)
return d
}
func (d *OpenFileDialog) SetOptions(options *OpenFileDialogOptions) {
d.title = options.Title
d.message = options.Message
d.buttonText = options.ButtonText
d.directory = options.Directory
d.canChooseDirectories = options.CanChooseDirectories
d.canChooseFiles = options.CanChooseFiles
d.canCreateDirectories = options.CanCreateDirectories
d.showHiddenFiles = options.ShowHiddenFiles
d.resolvesAliases = options.ResolvesAliases
d.allowsMultipleSelection = options.AllowsMultipleSelection
d.hideExtension = options.HideExtension
d.canSelectHiddenExtension = options.CanSelectHiddenExtension
d.treatsFilePackagesAsDirectories = options.TreatsFilePackagesAsDirectories
d.allowsOtherFileTypes = options.AllowsOtherFileTypes
d.filters = options.Filters
}
func newOpenFileDialog() *OpenFileDialog {
return &OpenFileDialog{
id: getDialogID(),

View File

@ -61,7 +61,7 @@ static void* createAlert(int alertType, char* title, char *message, void *icon,
NSImage *image = [NSImage imageNamed:NSImageNameInfo];
[alert setIcon:image];
}
}
}
return alert;
}
@ -315,54 +315,54 @@ type macosDialog struct {
func (m *macosDialog) show() {
globalApplication.dispatchOnMainThread(func() {
// Mac can only have 4 buttons on a dialog
if len(m.dialog.buttons) > 4 {
m.dialog.buttons = m.dialog.buttons[:4]
// Mac can only have 4 Buttons on a dialog
if len(m.dialog.Buttons) > 4 {
m.dialog.Buttons = m.dialog.Buttons[:4]
}
if m.nsDialog != nil {
C.releaseDialog(m.nsDialog)
}
var title *C.char
if m.dialog.title != "" {
title = C.CString(m.dialog.title)
if m.dialog.Title != "" {
title = C.CString(m.dialog.Title)
}
var message *C.char
if m.dialog.message != "" {
message = C.CString(m.dialog.message)
if m.dialog.Message != "" {
message = C.CString(m.dialog.Message)
}
var iconData unsafe.Pointer
var iconLength C.int
if m.dialog.icon != nil {
iconData = unsafe.Pointer(&m.dialog.icon[0])
iconLength = C.int(len(m.dialog.icon))
if m.dialog.Icon != nil {
iconData = unsafe.Pointer(&m.dialog.Icon[0])
iconLength = C.int(len(m.dialog.Icon))
} else {
// if it's an error, use the application icon
if m.dialog.dialogType == ErrorDialog {
// if it's an error, use the application Icon
if m.dialog.DialogType == ErrorDialog {
iconData = unsafe.Pointer(&globalApplication.options.Icon[0])
iconLength = C.int(len(globalApplication.options.Icon))
}
}
alertType, ok := alertTypeMap[m.dialog.dialogType]
alertType, ok := alertTypeMap[m.dialog.DialogType]
if !ok {
alertType = C.NSAlertStyleInformational
}
m.nsDialog = C.createAlert(alertType, title, message, iconData, iconLength)
// Reverse the buttons so that the default is on the right
reversedButtons := make([]*Button, len(m.dialog.buttons))
// Reverse the Buttons so that the default is on the right
reversedButtons := make([]*Button, len(m.dialog.Buttons))
var count = 0
for i := len(m.dialog.buttons) - 1; i >= 0; i-- {
button := m.dialog.buttons[i]
C.alertAddButton(m.nsDialog, C.CString(button.label), C.bool(button.isDefault), C.bool(button.isCancel))
reversedButtons[count] = m.dialog.buttons[i]
for i := len(m.dialog.Buttons) - 1; i >= 0; i-- {
button := m.dialog.Buttons[i]
C.alertAddButton(m.nsDialog, C.CString(button.Label), C.bool(button.IsDefault), C.bool(button.IsCancel))
reversedButtons[count] = m.dialog.Buttons[i]
count++
}
buttonPressed := int(C.dialogRunModal(m.nsDialog))
if len(m.dialog.buttons) > buttonPressed {
if len(m.dialog.Buttons) > buttonPressed {
button := reversedButtons[buttonPressed]
if button.callback != nil {
button.callback()
@ -410,7 +410,7 @@ func (m *macosOpenFileDialog) show() ([]string, error) {
if len(m.dialog.filters) > 0 {
var allPatterns []string
for _, filter := range m.dialog.filters {
patternComponents := strings.Split(filter.pattern, ";")
patternComponents := strings.Split(filter.Pattern, ";")
for i, component := range patternComponents {
filterPattern := strings.TrimSpace(component)
filterPattern = strings.TrimPrefix(filterPattern, "*.")

View File

@ -21,7 +21,7 @@ func NewMessageProcessor(w *WebviewWindow) *MessageProcessor {
func (m *MessageProcessor) httpError(rw http.ResponseWriter, message string, args ...any) {
m.Error(message, args...)
rw.WriteHeader(http.StatusBadRequest)
rw.Write([]byte(message))
rw.Write([]byte(fmt.Sprintf(message, args...)))
}
func (m *MessageProcessor) HandleRuntimeCall(rw http.ResponseWriter, r *http.Request) {
@ -59,6 +59,8 @@ func (m *MessageProcessor) HandleRuntimeCall(rw http.ResponseWriter, r *http.Req
m.processWindowMethod(method, rw, r, targetWindow, params)
case "clipboard":
m.processClipboardMethod(method, rw, r, targetWindow, params)
case "dialog":
m.processDialogMethod(method, rw, r, targetWindow, params)
default:
m.httpError(rw, "Unknown runtime call: %s", object)
}

View File

@ -4,11 +4,11 @@ import (
"net/http"
)
func (m *MessageProcessor) processClipboardMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, params QueryParams) {
func (m *MessageProcessor) processClipboardMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, args QueryParams) {
switch method {
case "SetText":
title := params.String("text")
title := args.String("text")
if title == nil {
m.Error("SetText: text is required")
return

View File

@ -0,0 +1,109 @@
package application
import (
"encoding/json"
"fmt"
"net/http"
"runtime"
"strconv"
)
func (m *MessageProcessor) dialogErrorCallback(message string, dialogID *string, err error) {
errorMsg := fmt.Sprintf(message, err)
m.Error(errorMsg)
msg := "_wails.dialogErrorCallback('" + *dialogID + "', " + strconv.Quote(errorMsg) + ");"
m.window.ExecJS(msg)
}
func (m *MessageProcessor) dialogCallback(dialogID *string, result string, isJSON bool) {
msg := fmt.Sprintf("_wails.dialogCallback('%s', %s, %v);", *dialogID, strconv.Quote(result), isJSON)
m.window.ExecJS(msg)
}
func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, params QueryParams) {
args, err := params.Args()
if err != nil {
m.httpError(rw, "Unable to parse arguments: %s", err)
return
}
dialogID := args.String("dialog-id")
if dialogID == nil {
m.Error("dialog-id is required")
return
}
switch method {
case "Info", "Warning", "Error", "Question":
var options MessageDialogOptions
err := params.ToStruct(&options)
if err != nil {
m.dialogErrorCallback("Error parsing dialog options: %s", dialogID, err)
return
}
if len(options.Buttons) == 0 {
switch runtime.GOOS {
case "darwin":
options.Buttons = []*Button{{Label: "OK", IsDefault: true}}
}
}
var dialog *MessageDialog
switch method {
case "Info":
dialog = globalApplication.InfoDialog()
case "Warning":
dialog = globalApplication.WarningDialog()
case "Error":
dialog = globalApplication.ErrorDialog()
case "Question":
dialog = globalApplication.QuestionDialog()
}
// TODO: Add support for attaching Message dialogs to windows
dialog.SetTitle(options.Title)
dialog.SetMessage(options.Message)
for _, button := range options.Buttons {
label := button.Label
button.OnClick(func() {
m.dialogCallback(dialogID, label, false)
})
}
dialog.AddButtons(options.Buttons)
dialog.Show()
m.ok(rw)
case "OpenFile":
var options OpenFileDialogOptions
err := params.ToStruct(&options)
if err != nil {
m.httpError(rw, "Error parsing dialog options: %s", err.Error())
return
}
dialog := globalApplication.OpenFileDialogWithOptions(&options)
go func() {
if options.AllowsMultipleSelection {
files, err := dialog.PromptForMultipleSelection()
if err != nil {
m.dialogErrorCallback("Error getting selection: %s", dialogID, err)
return
} else {
result, err := json.Marshal(files)
if err != nil {
m.dialogErrorCallback("Error marshalling files: %s", dialogID, err)
return
}
m.dialogCallback(dialogID, string(result), true)
}
} else {
file, err := dialog.PromptForSingleSelection()
if err != nil {
m.dialogErrorCallback("Error getting selection: %s", dialogID, err)
return
}
m.dialogCallback(dialogID, file, false)
}
}()
m.ok(rw)
default:
m.httpError(rw, "Unknown dialog method: %s", method)
}
}

View File

@ -1,6 +1,10 @@
package application
import "strconv"
import (
"encoding/json"
"fmt"
"strconv"
)
type QueryParams map[string][]string
@ -93,3 +97,94 @@ func (qp QueryParams) Float64(key string) *float64 {
}
return &result
}
func (qp QueryParams) ToStruct(str any) error {
args := qp["args"]
if len(args) == 1 {
return json.Unmarshal([]byte(args[0]), &str)
}
return nil
}
type Args struct {
data map[string]any
}
func (a *Args) String(key string) *string {
if a == nil {
return nil
}
if val := a.data[key]; val != nil {
result := fmt.Sprintf("%v", val)
return &result
}
return nil
}
func (a *Args) Int(s string) *int {
if a == nil {
return nil
}
if val := a.data[s]; val != nil {
result := val.(int)
return &result
}
return nil
}
func (a *Args) UInt8(s string) *uint8 {
if a == nil {
return nil
}
if val := a.data[s]; val != nil {
result := val.(uint8)
return &result
}
return nil
}
func (a *Args) UInt(s string) *uint {
if a == nil {
return nil
}
if val := a.data[s]; val != nil {
result := val.(uint)
return &result
}
return nil
}
func (a *Args) Float64(s string) *float64 {
if a == nil {
return nil
}
if val := a.data[s]; val != nil {
result := val.(float64)
return &result
}
return nil
}
func (a *Args) Bool(s string) *bool {
if a == nil {
return nil
}
if val := a.data[s]; val != nil {
result := val.(bool)
return &result
}
return nil
}
func (qp QueryParams) Args() (*Args, error) {
argData := qp["args"]
var result = &Args{
data: make(map[string]any),
}
if len(argData) == 1 {
err := json.Unmarshal([]byte(argData[0]), &result.data)
if err != nil {
return nil, err
}
}
return result, nil
}

View File

@ -8,9 +8,15 @@ import (
func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, params QueryParams) {
args, err := params.Args()
if err != nil {
m.httpError(rw, "Unable to parse arguments: %s", err)
return
}
switch method {
case "SetTitle":
title := params.String("title")
title := args.String("title")
if title == nil {
m.Error("SetTitle: title is required")
return
@ -18,19 +24,19 @@ func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWr
window.SetTitle(*title)
m.ok(rw)
case "SetSize":
width := params.Int("width")
height := params.Int("height")
width := args.Int("width")
height := args.Int("height")
if width == nil || height == nil {
m.Error("Invalid SetSize message")
m.Error("Invalid SetSize Message")
return
}
window.SetSize(*width, *height)
m.ok(rw)
case "SetPosition":
x := params.Int("x")
y := params.Int("y")
x := args.Int("x")
y := args.Int("y")
if x == nil || y == nil {
m.Error("Invalid SetPosition message")
m.Error("Invalid SetPosition Message")
return
}
window.SetPosition(*x, *y)
@ -78,24 +84,24 @@ func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWr
"y": y,
})
case "SetBackgroundColour":
r := params.UInt8("r")
r := args.UInt8("r")
if r == nil {
m.Error("Invalid SetBackgroundColour message: 'r' value required")
m.Error("Invalid SetBackgroundColour Message: 'r' value required")
return
}
g := params.UInt8("g")
g := args.UInt8("g")
if g == nil {
m.Error("Invalid SetBackgroundColour message: 'g' value required")
m.Error("Invalid SetBackgroundColour Message: 'g' value required")
return
}
b := params.UInt8("b")
b := args.UInt8("b")
if b == nil {
m.Error("Invalid SetBackgroundColour message: 'b' value required")
m.Error("Invalid SetBackgroundColour Message: 'b' value required")
return
}
a := params.UInt8("a")
a := args.UInt8("a")
if a == nil {
m.Error("Invalid SetBackgroundColour message: 'a' value required")
m.Error("Invalid SetBackgroundColour Message: 'a' value required")
return
}
window.SetBackgroundColour(&options.RGBA{
@ -106,35 +112,35 @@ func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWr
})
m.ok(rw)
case "SetAlwaysOnTop":
alwaysOnTop := params.Bool("alwaysOnTop")
alwaysOnTop := args.Bool("alwaysOnTop")
if alwaysOnTop == nil {
m.Error("Invalid SetAlwaysOnTop message: 'alwaysOnTop' value required")
m.Error("Invalid SetAlwaysOnTop Message: 'alwaysOnTop' value required")
return
}
window.SetAlwaysOnTop(*alwaysOnTop)
m.ok(rw)
case "SetResizable":
resizable := params.Bool("resizable")
resizable := args.Bool("resizable")
if resizable == nil {
m.Error("Invalid SetResizable message: 'resizable' value required")
m.Error("Invalid SetResizable Message: 'resizable' value required")
return
}
window.SetResizable(*resizable)
m.ok(rw)
case "SetMinSize":
width := params.Int("width")
height := params.Int("height")
width := args.Int("width")
height := args.Int("height")
if width == nil || height == nil {
m.Error("Invalid SetMinSize message")
m.Error("Invalid SetMinSize Message")
return
}
window.SetMinSize(*width, *height)
m.ok(rw)
case "SetMaxSize":
width := params.Int("width")
height := params.Int("height")
width := args.Int("width")
height := args.Int("height")
if width == nil || height == nil {
m.Error("Invalid SetMaxSize message")
m.Error("Invalid SetMaxSize Message")
return
}
window.SetMaxSize(*width, *height)
@ -171,9 +177,9 @@ func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWr
}
m.json(rw, screen)
case "SetZoom":
zoomLevel := params.Float64("zoomLevel")
zoomLevel := args.Float64("zoomLevel")
if zoomLevel == nil {
m.Error("Invalid SetZoom message: invalid 'zoomLevel' value")
m.Error("Invalid SetZoom Message: invalid 'zoomLevel' value")
return
}
window.SetZoom(*zoomLevel)