5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 15:30:37 +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 - [x] Same Window
- [ ] Other Window - [ ] Other Window
- [ ] Dialogs - [ ] Dialogs
- [x] Info
- [x] Warning
- [x] Error
- [x] Question
- [x] OpenFile
- [ ] SaveFile
- [ ] Events - [ ] Events
- [ ] Screens
- [x] Clipboard - [x] Clipboard
- [ ] Application
- [ ] Create `.d.ts` file - [ ] Create `.d.ts` file
## Templates ## 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 {SetBindings} from "./bindings";
import {Info, Warning, Error, Question, OpenFile, dialogCallback, dialogErrorCallback, } from "./dialogs";
import * as Clipboard from './clipboard'; import * as Clipboard from './clipboard';
import {newWindow} from "./window"; import {newWindow} from "./window";
@ -24,13 +26,14 @@ import {newWindow} from "./window";
// Internal wails endpoints // Internal wails endpoints
window.wails = { window.wails = {
Callback, ...newRuntime(-1),
callbacks,
EventsNotify,
eventListeners,
SetBindings,
}; };
window._wails = {
dialogCallback,
dialogErrorCallback,
}
export function newRuntime(id) { export function newRuntime(id) {
return { return {
@ -41,10 +44,19 @@ export function newRuntime(id) {
Clipboard: { Clipboard: {
...Clipboard ...Clipboard
}, },
Dialog: {
Info,
Warning,
Error,
Question,
OpenFile,
},
Window: newWindow(id), Window: newWindow(id),
Show: () => invoke("S"), Application: {
Hide: () => invoke("H"), Show: () => invoke("S"),
Quit: () => invoke("Q"), Hide: () => invoke("H"),
Quit: () => invoke("Q"),
}
// GetWindow: function (windowID) { // GetWindow: function (windowID) {
// if (!windowID) { // if (!windowID) {
// return this.Window; // return this.Window;
@ -54,8 +66,6 @@ export function newRuntime(id) {
} }
} }
window.runtime = newRuntime(-1);
if (DEBUG) { if (DEBUG) {
console.log("Wails v3.0.0 Debug Mode Enabled"); 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) { function runtimeCall(method, args) {
let url = new URL(runtimeURL); let url = new URL(runtimeURL);
url.searchParams.append("method", method); url.searchParams.append("method", method);
if (args) { url.searchParams.append("args", JSON.stringify(args));
for (let key in args) {
url.searchParams.append(key, args[key]);
}
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fetch(url) fetch(url)
.then(response => { .then(response => {
@ -41,6 +37,7 @@ function runtimeCall(method, args) {
export function newRuntimeCaller(object, id) { export function newRuntimeCaller(object, id) {
if (!id || id === -1) { if (!id || id === -1) {
return function (method, args) { return function (method, args) {
args = args || {};
return runtimeCall(object + "." + method, args); return runtimeCall(object + "." + method, args);
}; };
} }

View File

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

View File

@ -9,6 +9,7 @@
"devDependencies": { "devDependencies": {
"esbuild": "^0.17.5", "esbuild": "^0.17.5",
"happy-dom": "^8.1.5", "happy-dom": "^8.1.5",
"nanoid": "^4.0.0",
"npm-check-updates": "^16.6.3", "npm-check-updates": "^16.6.3",
"vitest": "^0.28.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 // set the application menu
a.impl.setApplicationMenu(a.ApplicationMenu) a.impl.setApplicationMenu(a.ApplicationMenu)
// set the application icon // set the application Icon
a.impl.setIcon(a.options.Icon) a.impl.setIcon(a.options.Icon)
return a.impl.run() return a.impl.run()
@ -389,3 +389,9 @@ func (a *App) dispatchOnMainThread(fn func()) {
// Call platform specific dispatch function // Call platform specific dispatch function
a.impl.dispatchOnMainThread(id) 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 { type Button struct {
label string Label string
isCancel bool IsCancel bool
isDefault bool IsDefault bool
callback func() callback func()
} }
@ -60,12 +60,16 @@ type messageDialogImpl interface {
show() show()
} }
type MessageDialogOptions struct {
DialogType DialogType
Title string
Message string
Buttons []*Button
Icon []byte
}
type MessageDialog struct { type MessageDialog struct {
dialogType DialogType MessageDialogOptions
title string
message string
buttons []*Button
icon []byte
// platform independent // platform independent
impl messageDialogImpl impl messageDialogImpl
@ -80,13 +84,16 @@ var defaultTitles = map[DialogType]string{
func newMessageDialog(dialogType DialogType) *MessageDialog { func newMessageDialog(dialogType DialogType) *MessageDialog {
return &MessageDialog{ return &MessageDialog{
dialogType: dialogType, MessageDialogOptions: MessageDialogOptions{
title: defaultTitles[dialogType], DialogType: dialogType,
Title: defaultTitles[dialogType],
},
impl: nil,
} }
} }
func (d *MessageDialog) SetTitle(title string) *MessageDialog { func (d *MessageDialog) SetTitle(title string) *MessageDialog {
d.title = title d.Title = title
return d return d
} }
@ -98,36 +105,41 @@ func (d *MessageDialog) Show() {
} }
func (d *MessageDialog) SetIcon(icon []byte) *MessageDialog { func (d *MessageDialog) SetIcon(icon []byte) *MessageDialog {
d.icon = icon d.Icon = icon
return d return d
} }
func (d *MessageDialog) AddButton(s string) *Button { func (d *MessageDialog) AddButton(s string) *Button {
result := &Button{ result := &Button{
label: s, Label: s,
} }
d.buttons = append(d.buttons, result) d.Buttons = append(d.Buttons, result)
return result return result
} }
func (d *MessageDialog) AddButtons(buttons []*Button) *MessageDialog {
d.Buttons = buttons
return d
}
func (d *MessageDialog) SetDefaultButton(button *Button) *MessageDialog { func (d *MessageDialog) SetDefaultButton(button *Button) *MessageDialog {
for _, b := range d.buttons { for _, b := range d.Buttons {
b.isDefault = false b.IsDefault = false
} }
button.isDefault = true button.IsDefault = true
return d return d
} }
func (d *MessageDialog) SetCancelButton(button *Button) *MessageDialog { func (d *MessageDialog) SetCancelButton(button *Button) *MessageDialog {
for _, b := range d.buttons { for _, b := range d.Buttons {
b.isCancel = false b.IsCancel = false
} }
button.isCancel = true button.IsCancel = true
return d return d
} }
func (d *MessageDialog) SetMessage(title string) *MessageDialog { func (d *MessageDialog) SetMessage(message string) *MessageDialog {
d.title = title d.Message = message
return d return d
} }
@ -135,9 +147,28 @@ type openFileDialogImpl interface {
show() ([]string, error) show() ([]string, error)
} }
type fileFilter struct { type FileFilter struct {
displayName string // Filter information EG: "Image Files (*.jpg, *.png)" DisplayName string // Filter information EG: "Image Files (*.jpg, *.png)"
pattern string // semicolon separated list of extensions, EG: "*.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 { type OpenFileDialog struct {
@ -152,7 +183,7 @@ type OpenFileDialog struct {
canSelectHiddenExtension bool canSelectHiddenExtension bool
treatsFilePackagesAsDirectories bool treatsFilePackagesAsDirectories bool
allowsOtherFileTypes bool allowsOtherFileTypes bool
filters []fileFilter filters []FileFilter
title string title string
message 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. // 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") // EG: AddFilter("Image Files", "*.jpg;*.png")
func (d *OpenFileDialog) AddFilter(displayName, pattern string) *OpenFileDialog { func (d *OpenFileDialog) AddFilter(displayName, pattern string) *OpenFileDialog {
d.filters = append(d.filters, fileFilter{ d.filters = append(d.filters, FileFilter{
displayName: strings.TrimSpace(displayName), DisplayName: strings.TrimSpace(displayName),
pattern: strings.TrimSpace(pattern), Pattern: strings.TrimSpace(pattern),
}) })
return d return d
} }
@ -265,6 +296,24 @@ func (d *OpenFileDialog) CanSelectHiddenExtension(canSelectHiddenExtension bool)
return d 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 { func newOpenFileDialog() *OpenFileDialog {
return &OpenFileDialog{ return &OpenFileDialog{
id: getDialogID(), id: getDialogID(),

View File

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

View File

@ -4,11 +4,11 @@ import (
"net/http" "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 { switch method {
case "SetText": case "SetText":
title := params.String("text") title := args.String("text")
if title == nil { if title == nil {
m.Error("SetText: text is required") m.Error("SetText: text is required")
return 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 package application
import "strconv" import (
"encoding/json"
"fmt"
"strconv"
)
type QueryParams map[string][]string type QueryParams map[string][]string
@ -93,3 +97,94 @@ func (qp QueryParams) Float64(key string) *float64 {
} }
return &result 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) { 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 { switch method {
case "SetTitle": case "SetTitle":
title := params.String("title") title := args.String("title")
if title == nil { if title == nil {
m.Error("SetTitle: title is required") m.Error("SetTitle: title is required")
return return
@ -18,19 +24,19 @@ func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWr
window.SetTitle(*title) window.SetTitle(*title)
m.ok(rw) m.ok(rw)
case "SetSize": case "SetSize":
width := params.Int("width") width := args.Int("width")
height := params.Int("height") height := args.Int("height")
if width == nil || height == nil { if width == nil || height == nil {
m.Error("Invalid SetSize message") m.Error("Invalid SetSize Message")
return return
} }
window.SetSize(*width, *height) window.SetSize(*width, *height)
m.ok(rw) m.ok(rw)
case "SetPosition": case "SetPosition":
x := params.Int("x") x := args.Int("x")
y := params.Int("y") y := args.Int("y")
if x == nil || y == nil { if x == nil || y == nil {
m.Error("Invalid SetPosition message") m.Error("Invalid SetPosition Message")
return return
} }
window.SetPosition(*x, *y) window.SetPosition(*x, *y)
@ -78,24 +84,24 @@ func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWr
"y": y, "y": y,
}) })
case "SetBackgroundColour": case "SetBackgroundColour":
r := params.UInt8("r") r := args.UInt8("r")
if r == nil { if r == nil {
m.Error("Invalid SetBackgroundColour message: 'r' value required") m.Error("Invalid SetBackgroundColour Message: 'r' value required")
return return
} }
g := params.UInt8("g") g := args.UInt8("g")
if g == nil { if g == nil {
m.Error("Invalid SetBackgroundColour message: 'g' value required") m.Error("Invalid SetBackgroundColour Message: 'g' value required")
return return
} }
b := params.UInt8("b") b := args.UInt8("b")
if b == nil { if b == nil {
m.Error("Invalid SetBackgroundColour message: 'b' value required") m.Error("Invalid SetBackgroundColour Message: 'b' value required")
return return
} }
a := params.UInt8("a") a := args.UInt8("a")
if a == nil { if a == nil {
m.Error("Invalid SetBackgroundColour message: 'a' value required") m.Error("Invalid SetBackgroundColour Message: 'a' value required")
return return
} }
window.SetBackgroundColour(&options.RGBA{ window.SetBackgroundColour(&options.RGBA{
@ -106,35 +112,35 @@ func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWr
}) })
m.ok(rw) m.ok(rw)
case "SetAlwaysOnTop": case "SetAlwaysOnTop":
alwaysOnTop := params.Bool("alwaysOnTop") alwaysOnTop := args.Bool("alwaysOnTop")
if alwaysOnTop == nil { if alwaysOnTop == nil {
m.Error("Invalid SetAlwaysOnTop message: 'alwaysOnTop' value required") m.Error("Invalid SetAlwaysOnTop Message: 'alwaysOnTop' value required")
return return
} }
window.SetAlwaysOnTop(*alwaysOnTop) window.SetAlwaysOnTop(*alwaysOnTop)
m.ok(rw) m.ok(rw)
case "SetResizable": case "SetResizable":
resizable := params.Bool("resizable") resizable := args.Bool("resizable")
if resizable == nil { if resizable == nil {
m.Error("Invalid SetResizable message: 'resizable' value required") m.Error("Invalid SetResizable Message: 'resizable' value required")
return return
} }
window.SetResizable(*resizable) window.SetResizable(*resizable)
m.ok(rw) m.ok(rw)
case "SetMinSize": case "SetMinSize":
width := params.Int("width") width := args.Int("width")
height := params.Int("height") height := args.Int("height")
if width == nil || height == nil { if width == nil || height == nil {
m.Error("Invalid SetMinSize message") m.Error("Invalid SetMinSize Message")
return return
} }
window.SetMinSize(*width, *height) window.SetMinSize(*width, *height)
m.ok(rw) m.ok(rw)
case "SetMaxSize": case "SetMaxSize":
width := params.Int("width") width := args.Int("width")
height := params.Int("height") height := args.Int("height")
if width == nil || height == nil { if width == nil || height == nil {
m.Error("Invalid SetMaxSize message") m.Error("Invalid SetMaxSize Message")
return return
} }
window.SetMaxSize(*width, *height) window.SetMaxSize(*width, *height)
@ -171,9 +177,9 @@ func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWr
} }
m.json(rw, screen) m.json(rw, screen)
case "SetZoom": case "SetZoom":
zoomLevel := params.Float64("zoomLevel") zoomLevel := args.Float64("zoomLevel")
if zoomLevel == nil { if zoomLevel == nil {
m.Error("Invalid SetZoom message: invalid 'zoomLevel' value") m.Error("Invalid SetZoom Message: invalid 'zoomLevel' value")
return return
} }
window.SetZoom(*zoomLevel) window.SetZoom(*zoomLevel)