From 5f0be6543b3f493fe075d98de12d42e1cd2696f8 Mon Sep 17 00:00:00 2001 From: Gregor Biswanger Date: Thu, 12 Oct 2017 02:24:27 +0200 Subject: [PATCH 1/8] implement first Electron-API Bridge functions - Add little sample in WebApp --- ElectronNET.API/App.cs | 73 ++++++++++++++++++- ElectronNET.API/ElectronNET.API.csproj | 4 - ElectronNET.API/Entities/PathName.cs | 24 ++++++ ElectronNET.API/Entities/RelaunchOptions.cs | 8 ++ ElectronNET.API/devCleanup.cmd | 2 +- ElectronNET.Host/api/app.js | 32 ++++++++ ElectronNET.Host/api/app.js.map | 1 + ElectronNET.Host/api/app.ts | 38 ++++++++++ ElectronNET.Host/main.js | 3 +- ElectronNET.Host/package-lock.json | 9 +++ ElectronNET.Host/package.json | 1 + .../Controllers/HomeController.cs | 6 ++ ElectronNET.WebApp/Views/Home/Index.cshtml | 14 ++++ 13 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 ElectronNET.API/Entities/PathName.cs create mode 100644 ElectronNET.API/Entities/RelaunchOptions.cs create mode 100644 ElectronNET.Host/api/app.js create mode 100644 ElectronNET.Host/api/app.js.map create mode 100644 ElectronNET.Host/api/app.ts diff --git a/ElectronNET.API/App.cs b/ElectronNET.API/App.cs index c08bb91..bf073f4 100644 --- a/ElectronNET.API/App.cs +++ b/ElectronNET.API/App.cs @@ -4,16 +4,17 @@ using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using Quobject.SocketIoClientDotNet.Client; using System; +using System.Threading.Tasks; namespace ElectronNET.API { public static class App { + public static IpcMain IpcMain { get; private set; } + private static Socket _socket; private static JsonSerializer _jsonSerializer; - public static IpcMain IpcMain { get; private set; } - public static void OpenWindow(int width, int height, bool show) { _jsonSerializer = new JsonSerializer() @@ -26,7 +27,8 @@ namespace ElectronNET.API { Console.WriteLine("Verbunden!"); - var browserWindowOptions = new BrowserWindowOptions() { + var browserWindowOptions = new BrowserWindowOptions() + { Height = height, Width = width, Show = show @@ -42,5 +44,70 @@ namespace ElectronNET.API { _socket.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer)); } + + public static void Quit() + { + _socket.Emit("appQuit"); + } + + public static void Exit(int exitCode = 0) + { + _socket.Emit("appExit", exitCode); + } + + public static void Relaunch() + { + _socket.Emit("appRelaunch"); + } + + public static void Relaunch(RelaunchOptions relaunchOptions) + { + _socket.Emit("appRelaunch", JObject.FromObject(relaunchOptions, _jsonSerializer)); + } + + public static void Focus() + { + _socket.Emit("appFocus"); + } + + public static void Hide() + { + _socket.Emit("appHide"); + } + + public static void Show() + { + _socket.Emit("appShow"); + } + + public async static Task GetAppPathAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetAppPathCompleted", (path) => + { + taskCompletionSource.SetResult(path.ToString()); + }); + + _socket.Emit("appGetAppPath"); + + return await taskCompletionSource.Task; + } + + public async static Task GetPathAsync(PathName pathName) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetPathCompleted", (path) => + { + taskCompletionSource.SetResult(path.ToString()); + }); + + _socket.Emit("appGetPath", pathName.ToString()); + + return await taskCompletionSource.Task; + } + + public static void Blub2() { } } } diff --git a/ElectronNET.API/ElectronNET.API.csproj b/ElectronNET.API/ElectronNET.API.csproj index 5c20f7f..104a13b 100644 --- a/ElectronNET.API/ElectronNET.API.csproj +++ b/ElectronNET.API/ElectronNET.API.csproj @@ -12,9 +12,5 @@ - - - - diff --git a/ElectronNET.API/Entities/PathName.cs b/ElectronNET.API/Entities/PathName.cs new file mode 100644 index 0000000..48173d8 --- /dev/null +++ b/ElectronNET.API/Entities/PathName.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ElectronNET.API.Entities +{ + public enum PathName + { + home, + appData, + userData, + temp, + exe, + module, + desktop, + documents, + downloads, + music, + pictures, + videos, + logs, + pepperFlashSystemPlugin + } +} diff --git a/ElectronNET.API/Entities/RelaunchOptions.cs b/ElectronNET.API/Entities/RelaunchOptions.cs new file mode 100644 index 0000000..55ff88e --- /dev/null +++ b/ElectronNET.API/Entities/RelaunchOptions.cs @@ -0,0 +1,8 @@ +namespace ElectronNET.API.Entities +{ + public class RelaunchOptions + { + public string[] Args { get; set; } + public string ExecPath { get; set; } + } +} diff --git a/ElectronNET.API/devCleanup.cmd b/ElectronNET.API/devCleanup.cmd index 23931d4..294a54c 100644 --- a/ElectronNET.API/devCleanup.cmd +++ b/ElectronNET.API/devCleanup.cmd @@ -1 +1 @@ -rd /s /q %userprofile%\.nuget\packages\electronnet.api +rd /s /q "%userprofile%\.nuget\packages\electronnet.api\" diff --git a/ElectronNET.Host/api/app.js b/ElectronNET.Host/api/app.js new file mode 100644 index 0000000..0190e5b --- /dev/null +++ b/ElectronNET.Host/api/app.js @@ -0,0 +1,32 @@ +"use strict"; +exports.__esModule = true; +module.exports = function (socket, app) { + socket.on('appQuit', function () { + app.quit(); + }); + socket.on('appExit', function (exitCode) { + if (exitCode === void 0) { exitCode = 0; } + app.exit(exitCode); + }); + socket.on('appRelaunch', function (options) { + app.relaunch(options); + }); + socket.on('appFocus', function () { + app.focus(); + }); + socket.on('appHide', function () { + app.hide(); + }); + socket.on('appShow', function () { + app.show(); + }); + socket.on('appGetAppPath', function () { + var path = app.getAppPath(); + socket.emit('appGetAppPathCompleted', path); + }); + socket.on('appGetPath', function (name) { + var path = app.getPath(name); + socket.emit('appGetPathCompleted', path); + }); +}; +//# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/app.js.map b/ElectronNET.Host/api/app.js.map new file mode 100644 index 0000000..44b340f --- /dev/null +++ b/ElectronNET.Host/api/app.js.map @@ -0,0 +1 @@ +{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":";;AAEA,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB,EAAE,GAAiB;IAExD,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE;QACjB,GAAG,CAAC,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,UAAC,QAAY;QAAZ,yBAAA,EAAA,YAAY;QAC9B,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,UAAC,OAAO;QAC7B,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE;QAClB,GAAG,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE;QACjB,GAAG,CAAC,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE;QACjB,GAAG,CAAC,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,eAAe,EAAE;QACvB,IAAM,IAAI,GAAG,GAAG,CAAC,UAAU,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,UAAC,IAAI;QACzB,IAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/app.ts b/ElectronNET.Host/api/app.ts new file mode 100644 index 0000000..e13c153 --- /dev/null +++ b/ElectronNET.Host/api/app.ts @@ -0,0 +1,38 @@ +import {} from 'electron'; + +module.exports = (socket: SocketIO.Server, app: Electron.App) => { + + socket.on('appQuit', () => { + app.quit(); + }); + + socket.on('appExit', (exitCode = 0) => { + app.exit(exitCode); + }); + + socket.on('appRelaunch', (options) => { + app.relaunch(options); + }); + + socket.on('appFocus', () => { + app.focus(); + }); + + socket.on('appHide', () => { + app.hide(); + }); + + socket.on('appShow', () => { + app.show(); + }); + + socket.on('appGetAppPath', () => { + const path = app.getAppPath(); + socket.emit('appGetAppPathCompleted', path); + }); + + socket.on('appGetPath', (name) => { + const path = app.getPath(name); + socket.emit('appGetPathCompleted', path); + }); +} \ No newline at end of file diff --git a/ElectronNET.Host/main.js b/ElectronNET.Host/main.js index 2045e43..242e236 100644 --- a/ElectronNET.Host/main.js +++ b/ElectronNET.Host/main.js @@ -3,7 +3,7 @@ const fs = require('fs'); const path = require('path'); const process = require('child_process').spawn; const portfinder = require('detect-port'); -let io, window, apiProcess, loadURL, ipc; +let io, window, apiProcess, loadURL, ipc, appApi; app.on('ready', () => { portfinder(8000, (error, port) => { @@ -17,6 +17,7 @@ function startSocketApiBridge(port) { io.on('connection', (socket) => { console.log('ASP.NET Core Application connected...'); + appApi = require('./api/app')(socket, app); socket.on('createBrowserWindow', (options) => { console.log(options); diff --git a/ElectronNET.Host/package-lock.json b/ElectronNET.Host/package-lock.json index 53050d4..8803dfc 100644 --- a/ElectronNET.Host/package-lock.json +++ b/ElectronNET.Host/package-lock.json @@ -4,6 +4,15 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/electron": { + "version": "1.6.10", + "resolved": "https://registry.npmjs.org/@types/electron/-/electron-1.6.10.tgz", + "integrity": "sha512-MOCVyzIwkBEloreoCVrTV108vSf8fFIJPsGruLCoAoBZdxtnJUqKA4lNonf/2u1twSjAspPEfmEheC+TLm/cMw==", + "dev": true, + "requires": { + "electron": "1.7.8" + } + }, "@types/node": { "version": "7.0.43", "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.43.tgz", diff --git a/ElectronNET.Host/package.json b/ElectronNET.Host/package.json index f219282..d9f8a56 100644 --- a/ElectronNET.Host/package.json +++ b/ElectronNET.Host/package.json @@ -15,6 +15,7 @@ "socket.io": "^2.0.3" }, "devDependencies": { + "@types/electron": "^1.6.10", "@types/socket.io": "^1.4.31" } } diff --git a/ElectronNET.WebApp/Controllers/HomeController.cs b/ElectronNET.WebApp/Controllers/HomeController.cs index 8868da4..f447614 100644 --- a/ElectronNET.WebApp/Controllers/HomeController.cs +++ b/ElectronNET.WebApp/Controllers/HomeController.cs @@ -18,6 +18,12 @@ namespace ElectronNET.WebApp.Controllers App.IpcMain.Send("Goodbye", "Elephant!"); }); + App.IpcMain.On("GetPath", async (args) => + { + string pathName = await App.GetPathAsync(PathName.pictures); + App.IpcMain.Send("GetPathComplete", pathName); + }); + return View(); } } diff --git a/ElectronNET.WebApp/Views/Home/Index.cshtml b/ElectronNET.WebApp/Views/Home/Index.cshtml index 2bcac87..22197ef 100644 --- a/ElectronNET.WebApp/Views/Home/Index.cshtml +++ b/ElectronNET.WebApp/Views/Home/Index.cshtml @@ -17,6 +17,11 @@

+
+

+ +
+ From aa526c4bcbde06b9ae0b94c5b34e5181abdcc5d6 Mon Sep 17 00:00:00 2001 From: Gregor Biswanger Date: Sat, 14 Oct 2017 00:06:58 +0200 Subject: [PATCH 2/8] Implement all functions from the Electron App-API --- ElectronNET.API/App.cs | 592 +++++++++++++++++- ElectronNET.API/Entities/AboutPanelOptions.cs | 30 + ElectronNET.API/Entities/CPUUsage.cs | 16 + ElectronNET.API/Entities/DockBounceType.cs | 8 + ElectronNET.API/Entities/FileIconOptions.cs | 12 + ElectronNET.API/Entities/FileIconSize.cs | 9 + ElectronNET.API/Entities/GPUFeatureStatus.cs | 82 +++ .../Entities/ImportCertificateOptions.cs | 15 + ElectronNET.API/Entities/JumpListCategory.cs | 11 + ElectronNET.API/Entities/JumpListItem.cs | 14 + ElectronNET.API/Entities/JumpListSettings.cs | 9 + ElectronNET.API/Entities/LoginItemSettings.cs | 36 ++ .../Entities/LoginItemSettingsOptions.cs | 15 + ElectronNET.API/Entities/LoginSettings.cs | 30 + ElectronNET.API/Entities/MemoryInfo.cs | 33 + ElectronNET.API/Entities/NativeImage.cs | 106 ++++ ElectronNET.API/Entities/ProcessMetric.cs | 25 + ElectronNET.API/Entities/UserTask.cs | 12 + ElectronNET.CLI/ElectronNET.CLI.csproj | 4 - ElectronNET.Host/api/app.js | 162 +++++ ElectronNET.Host/api/app.js.map | 2 +- ElectronNET.Host/api/app.ts | 225 ++++++- ElectronNET.Host/main.js | 7 +- .../Controllers/HomeController.cs | 10 +- 24 files changed, 1448 insertions(+), 17 deletions(-) create mode 100644 ElectronNET.API/Entities/AboutPanelOptions.cs create mode 100644 ElectronNET.API/Entities/CPUUsage.cs create mode 100644 ElectronNET.API/Entities/DockBounceType.cs create mode 100644 ElectronNET.API/Entities/FileIconOptions.cs create mode 100644 ElectronNET.API/Entities/FileIconSize.cs create mode 100644 ElectronNET.API/Entities/GPUFeatureStatus.cs create mode 100644 ElectronNET.API/Entities/ImportCertificateOptions.cs create mode 100644 ElectronNET.API/Entities/JumpListCategory.cs create mode 100644 ElectronNET.API/Entities/JumpListItem.cs create mode 100644 ElectronNET.API/Entities/JumpListSettings.cs create mode 100644 ElectronNET.API/Entities/LoginItemSettings.cs create mode 100644 ElectronNET.API/Entities/LoginItemSettingsOptions.cs create mode 100644 ElectronNET.API/Entities/LoginSettings.cs create mode 100644 ElectronNET.API/Entities/MemoryInfo.cs create mode 100644 ElectronNET.API/Entities/NativeImage.cs create mode 100644 ElectronNET.API/Entities/ProcessMetric.cs create mode 100644 ElectronNET.API/Entities/UserTask.cs diff --git a/ElectronNET.API/App.cs b/ElectronNET.API/App.cs index bf073f4..471e3f7 100644 --- a/ElectronNET.API/App.cs +++ b/ElectronNET.API/App.cs @@ -86,6 +86,7 @@ namespace ElectronNET.API _socket.On("appGetAppPathCompleted", (path) => { + _socket.Off("appGetAppPathCompleted"); taskCompletionSource.SetResult(path.ToString()); }); @@ -100,6 +101,8 @@ namespace ElectronNET.API _socket.On("appGetPathCompleted", (path) => { + _socket.Off("appGetPathCompleted"); + taskCompletionSource.SetResult(path.ToString()); }); @@ -108,6 +111,593 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } - public static void Blub2() { } + // TODO: Fertig coden + //public async static Task GetFileIconAsync(string filePath) + //{ + // var taskCompletionSource = new TaskCompletionSource(); + + // _socket.On("appGetFileIconCompleted", (results) => + // { + // _socket.Off("appGetFileIconCompleted"); + + // byte[] test = ((JArray)results).Last.ToObject(); + + + // //object[] result = results as object[]; + // //NativeImage nativeImage = (NativeImage)result[1]; + // //taskCompletionSource.SetResult(nativeImage); + // }); + // _socket.Emit("appGetFileIcon", filePath); + + // return await taskCompletionSource.Task; + //} + + // TODO: Fertig coden + //public async static Task GetFileIconAsync(string filePath, FileIconOptions fileIconOptions) + //{ + // var taskCompletionSource = new TaskCompletionSource(); + + // _socket.On("appGetFileIconCompleted", (results) => + // { + // _socket.Off("appGetFileIconCompleted"); + + // object[] result = results as object[]; + // NativeImage nativeImage = (NativeImage)result[1]; + // taskCompletionSource.SetResult(nativeImage); + // }); + // _socket.Emit("appGetFileIcon", filePath, JObject.FromObject(fileIconOptions, _jsonSerializer)); + + // return await taskCompletionSource.Task; + //} + + public static void SetPath(string name, string path) + { + _socket.Emit("appSetPath", name, path); + } + + public async static Task GetVersionAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetVersionCompleted", (version) => + { + _socket.Off("appGetVersionCompleted"); + taskCompletionSource.SetResult(version.ToString()); + }); + + _socket.Emit("appGetVersion"); + + return await taskCompletionSource.Task; + } + + public async static Task GetNameAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetNameCompleted", (name) => + { + _socket.Off("appGetNameCompleted"); + taskCompletionSource.SetResult(name.ToString()); + }); + + _socket.Emit("appGetName"); + + return await taskCompletionSource.Task; + } + + public static void SetName(string name) + { + _socket.Emit("appSetName", name); + } + + public async static Task GetLocaleAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetLocaleCompleted", (locale) => + { + _socket.Off("appGetLocaleCompleted"); + taskCompletionSource.SetResult(locale.ToString()); + }); + + _socket.Emit("appGetLocale"); + + return await taskCompletionSource.Task; + } + + public static void AddRecentDocument(string path) + { + _socket.Emit("appAddRecentDocument", path); + } + + public static void ClearRecentDocuments() + { + _socket.Emit("appClearRecentDocuments"); + } + + public async static Task SetAsDefaultProtocolClientAsync(string protocol) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appSetAsDefaultProtocolClientCompleted", (success) => + { + _socket.Off("appSetAsDefaultProtocolClientCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Emit("appSetAsDefaultProtocolClient", protocol); + + return await taskCompletionSource.Task; + } + + public async static Task SetAsDefaultProtocolClientAsync(string protocol, string path) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appSetAsDefaultProtocolClientCompleted", (success) => + { + _socket.Off("appSetAsDefaultProtocolClientCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Emit("appSetAsDefaultProtocolClient", protocol, path); + + return await taskCompletionSource.Task; + } + + public async static Task SetAsDefaultProtocolClientAsync(string protocol, string path, string[] args) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appSetAsDefaultProtocolClientCompleted", (success) => + { + _socket.Off("appSetAsDefaultProtocolClientCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Emit("appSetAsDefaultProtocolClient", protocol, path, args); + + return await taskCompletionSource.Task; + } + + public async static Task RemoveAsDefaultProtocolClientAsync(string protocol) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appRemoveAsDefaultProtocolClientCompleted", (success) => + { + _socket.Off("appRemoveAsDefaultProtocolClientCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Emit("appRemoveAsDefaultProtocolClient", protocol); + + return await taskCompletionSource.Task; + } + + public async static Task RemoveAsDefaultProtocolClientAsync(string protocol, string path) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appRemoveAsDefaultProtocolClientCompleted", (success) => + { + _socket.Off("appRemoveAsDefaultProtocolClientCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Emit("appRemoveAsDefaultProtocolClient", protocol, path); + + return await taskCompletionSource.Task; + } + + public async static Task RemoveAsDefaultProtocolClientAsync(string protocol, string path, string[] args) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appRemoveAsDefaultProtocolClientCompleted", (success) => + { + _socket.Off("appRemoveAsDefaultProtocolClientCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Emit("appRemoveAsDefaultProtocolClient", protocol, path, args); + + return await taskCompletionSource.Task; + } + + public async static Task IsDefaultProtocolClientAsync(string protocol) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appIsDefaultProtocolClientCompleted", (success) => + { + _socket.Off("appIsDefaultProtocolClientCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Emit("appIsDefaultProtocolClient", protocol); + + return await taskCompletionSource.Task; + } + + public async static Task IsDefaultProtocolClientAsync(string protocol, string path) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appIsDefaultProtocolClientCompleted", (success) => + { + _socket.Off("appIsDefaultProtocolClientCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Emit("appIsDefaultProtocolClient", protocol, path); + + return await taskCompletionSource.Task; + } + + public async static Task IsDefaultProtocolClientAsync(string protocol, string path, string[] args) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appIsDefaultProtocolClientCompleted", (success) => + { + _socket.Off("appIsDefaultProtocolClientCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Emit("appIsDefaultProtocolClient", protocol, path, args); + + return await taskCompletionSource.Task; + } + + public async static Task SetUserTasksAsync(UserTask[] userTasks) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appSetUserTasksCompleted", (success) => + { + _socket.Off("appSetUserTasksCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Emit("appSetUserTasks", JObject.FromObject(userTasks, _jsonSerializer)); + + return await taskCompletionSource.Task; + } + + public async static Task GetJumpListSettingsAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetJumpListSettingsCompleted", (success) => + { + _socket.Off("appGetJumpListSettingsCompleted"); + taskCompletionSource.SetResult(JObject.Parse(success.ToString()).ToObject()); + }); + + _socket.Emit("appGetJumpListSettings"); + + return await taskCompletionSource.Task; + } + + public static void SetJumpList(JumpListCategory[] jumpListCategories) + { + _socket.Emit("appSetJumpList", JObject.FromObject(jumpListCategories, _jsonSerializer)); + } + + public async static Task MakeSingleInstanceAsync(Action newInstanceOpened) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appMakeSingleInstanceCompleted", (success) => + { + _socket.Off("appMakeSingleInstanceCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Off("newInstanceOpened"); + _socket.On("newInstanceOpened", (result) => + { + JArray results = (JArray)result; + string[] args = results.First.ToObject(); + string workdirectory = results.Last.ToObject(); + + newInstanceOpened(args, workdirectory); + }); + + _socket.Emit("appMakeSingleInstance"); + + return await taskCompletionSource.Task; + } + + public static void ReleaseSingleInstance() + { + _socket.Emit("appReleaseSingleInstance"); + } + + public static void SetUserActivity(string type, object userInfo) + { + _socket.Emit("appSetUserActivity", type, userInfo); + } + + public static void SetUserActivity(string type, object userInfo, string webpageURL) + { + _socket.Emit("appSetUserActivity", type, userInfo, webpageURL); + } + + public async static Task GetCurrentActivityTypeAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetCurrentActivityTypeCompleted", (activityType) => + { + _socket.Off("appGetCurrentActivityTypeCompleted"); + taskCompletionSource.SetResult(activityType.ToString()); + }); + + _socket.Emit("appGetCurrentActivityType"); + + return await taskCompletionSource.Task; + } + + public static void SetAppUserModelId(string id) + { + _socket.Emit("appSetAppUserModelId", id); + } + + public async static Task ImportCertificateAsync(ImportCertificateOptions options) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appImportCertificateCompleted", (result) => + { + _socket.Off("appImportCertificateCompleted"); + taskCompletionSource.SetResult((int)result); + }); + + _socket.Emit("appImportCertificate", JObject.FromObject(options, _jsonSerializer)); + + return await taskCompletionSource.Task; + } + + public async static Task GetAppMetricsAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetAppMetricsCompleted", (result) => + { + _socket.Off("appGetAppMetricsCompleted"); + var processMetrics = ((JArray)result).ToObject(); + + taskCompletionSource.SetResult(processMetrics); + }); + + _socket.Emit("appGetAppMetrics"); + + return await taskCompletionSource.Task; + } + + public async static Task GetGpuFeatureStatusAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetGpuFeatureStatusCompleted", (result) => + { + _socket.Off("appGetGpuFeatureStatusCompleted"); + var gpuFeatureStatus = ((JObject)result).ToObject(); + + taskCompletionSource.SetResult(gpuFeatureStatus); + }); + + _socket.Emit("appGetGpuFeatureStatus"); + + return await taskCompletionSource.Task; + } + + public async static Task SetBadgeCountAsync(int count) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appSetBadgeCountCompleted", (success) => + { + _socket.Off("appSetBadgeCountCompleted"); + taskCompletionSource.SetResult((bool)success); + }); + + _socket.Emit("appSetBadgeCount", count); + + return await taskCompletionSource.Task; + } + + public async static Task GetBadgeCountAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetBadgeCountCompleted", (count) => + { + _socket.Off("appGetBadgeCountCompleted"); + taskCompletionSource.SetResult((int)count); + }); + + _socket.Emit("appGetBadgeCount"); + + return await taskCompletionSource.Task; + } + + public async static Task IsUnityRunningAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appIsUnityRunningCompleted", (isUnityRunning) => + { + _socket.Off("appIsUnityRunningCompleted"); + taskCompletionSource.SetResult((bool)isUnityRunning); + }); + + _socket.Emit("appIsUnityRunning"); + + return await taskCompletionSource.Task; + } + + public async static Task GetLoginItemSettingsAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetLoginItemSettingsCompleted", (loginItemSettings) => + { + _socket.Off("appGetLoginItemSettingsCompleted"); + taskCompletionSource.SetResult((LoginItemSettings)loginItemSettings); + }); + + _socket.Emit("appGetLoginItemSettings"); + + return await taskCompletionSource.Task; + } + + public async static Task GetLoginItemSettingsAsync(LoginItemSettingsOptions options) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appGetLoginItemSettingsCompleted", (loginItemSettings) => + { + _socket.Off("appGetLoginItemSettingsCompleted"); + taskCompletionSource.SetResult((LoginItemSettings)loginItemSettings); + }); + + _socket.Emit("appGetLoginItemSettings", JObject.FromObject(options, _jsonSerializer)); + + return await taskCompletionSource.Task; + } + + public static void SetLoginItemSettings(LoginSettings loginSettings) + { + _socket.Emit("appSetLoginItemSettings", JObject.FromObject(loginSettings, _jsonSerializer)); + } + + public async static Task IsAccessibilitySupportEnabledAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appIsAccessibilitySupportEnabledCompleted", (isAccessibilitySupportEnabled) => + { + _socket.Off("appIsAccessibilitySupportEnabledCompleted"); + taskCompletionSource.SetResult((bool)isAccessibilitySupportEnabled); + }); + + _socket.Emit("appIsAccessibilitySupportEnabled"); + + return await taskCompletionSource.Task; + } + + public static void SetAboutPanelOptions(AboutPanelOptions options) + { + _socket.Emit("appSetAboutPanelOptions", JObject.FromObject(options, _jsonSerializer)); + } + + public static void CommandLineAppendSwitch(string theSwtich) + { + _socket.Emit("appCommandLineAppendSwitch", theSwtich); + } + + public static void CommandLineAppendSwitch(string theSwtich, string value) + { + _socket.Emit("appCommandLineAppendSwitch", theSwtich, value); + } + + public static void CommandLineAppendArgument(string value) + { + _socket.Emit("appCommandLineAppendArgument", value); + } + + public static void EnableMixedSandbox() + { + _socket.Emit("appEnableMixedSandbox"); + } + + public async static Task DockBounceAsync(DockBounceType type) + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appDockBounceCompleted", (id) => + { + _socket.Off("appDockBounceCompleted"); + taskCompletionSource.SetResult((int)id); + }); + + _socket.Emit("appDockBounce", type.ToString()); + + return await taskCompletionSource.Task; + } + + public static void DockCancelBounce(int id) + { + _socket.Emit("appDockCancelBounce", id); + } + + public static void DockDownloadFinished(string filePath) + { + _socket.Emit("appDockDownloadFinished", filePath); + } + + public static void DockSetBadge(string text) + { + _socket.Emit("appDockSetBadge", text); + } + + public async static Task DockGetBadgeAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appDockGetBadgeCompleted", (text) => + { + _socket.Off("appDockGetBadgeCompleted"); + taskCompletionSource.SetResult((string)text); + }); + + _socket.Emit("appDockGetBadge"); + + return await taskCompletionSource.Task; + } + + public static void DockHide() + { + _socket.Emit("appDockHide"); + } + + public static void DockShow() + { + _socket.Emit("appDockShow"); + } + + public async static Task DockIsVisibleAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + _socket.On("appDockIsVisibleCompleted", (isVisible) => + { + _socket.Off("appDockIsVisibleCompleted"); + taskCompletionSource.SetResult((bool)isVisible); + }); + + _socket.Emit("appDockIsVisible"); + + return await taskCompletionSource.Task; + } + + // TODO: Menu lösung muss gemacht werden und imeplementiert + public static void DockSetMenu() + { + _socket.Emit("appDockSetMenu"); + } + + public static void DockSetIcon(string image) + { + _socket.Emit("appDockSetIcon", image); + } + + //public static void DockSetIcon(NativeImage image) + //{ + // _socket.Emit("appDockSetIcon", JObject.FromObject(image, _jsonSerializer)); + //} } } diff --git a/ElectronNET.API/Entities/AboutPanelOptions.cs b/ElectronNET.API/Entities/AboutPanelOptions.cs new file mode 100644 index 0000000..db47d7e --- /dev/null +++ b/ElectronNET.API/Entities/AboutPanelOptions.cs @@ -0,0 +1,30 @@ +namespace ElectronNET.API.Entities +{ + public class AboutPanelOptions + { + /// + /// The app's name. + /// + public string ApplicationName { get; set; } + + /// + /// The app's version. + /// + public string ApplicationVersion { get; set; } + + /// + /// Copyright information. + /// + public string Copyright { get; set; } + + /// + /// Credit information. + /// + public string Credits { get; set; } + + /// + /// The app's build version number. + /// + public string Version { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/CPUUsage.cs b/ElectronNET.API/Entities/CPUUsage.cs new file mode 100644 index 0000000..0fb461a --- /dev/null +++ b/ElectronNET.API/Entities/CPUUsage.cs @@ -0,0 +1,16 @@ +namespace ElectronNET.API.Entities +{ + public class CPUUsage + { + /// + /// The number of average idle cpu wakeups per second since the last call to + /// getCPUUsage.First call returns 0. + /// + public int IdleWakeupsPerSecond { get; set; } + + /// + /// Percentage of CPU used since the last call to getCPUUsage. First call returns 0. + /// + public int PercentCPUUsage { get; set; } + } +} diff --git a/ElectronNET.API/Entities/DockBounceType.cs b/ElectronNET.API/Entities/DockBounceType.cs new file mode 100644 index 0000000..5af53ed --- /dev/null +++ b/ElectronNET.API/Entities/DockBounceType.cs @@ -0,0 +1,8 @@ +namespace ElectronNET.API +{ + public enum DockBounceType + { + critical, + informational + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/FileIconOptions.cs b/ElectronNET.API/Entities/FileIconOptions.cs new file mode 100644 index 0000000..4293a0a --- /dev/null +++ b/ElectronNET.API/Entities/FileIconOptions.cs @@ -0,0 +1,12 @@ +namespace ElectronNET.API.Entities +{ + public class FileIconOptions + { + public string Size { get; private set; } + + public FileIconOptions(FileIconSize fileIconSize) + { + Size = fileIconSize.ToString(); + } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/FileIconSize.cs b/ElectronNET.API/Entities/FileIconSize.cs new file mode 100644 index 0000000..7841ee8 --- /dev/null +++ b/ElectronNET.API/Entities/FileIconSize.cs @@ -0,0 +1,9 @@ +namespace ElectronNET.API.Entities +{ + public enum FileIconSize + { + small, + normal, + large + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/GPUFeatureStatus.cs b/ElectronNET.API/Entities/GPUFeatureStatus.cs new file mode 100644 index 0000000..a21aac5 --- /dev/null +++ b/ElectronNET.API/Entities/GPUFeatureStatus.cs @@ -0,0 +1,82 @@ +using Newtonsoft.Json; + +namespace ElectronNET.API.Entities +{ + public class GPUFeatureStatus + { + /// + /// Canvas + /// + [JsonProperty("2d_canvas")] + public string Canvas { get; set; } + + /// + /// Flash + /// + [JsonProperty("flash_3d")] + public string Flash3D { get; set; } + + /// + /// Flash Stage3D + /// + [JsonProperty("flash_stage3d")] + public string FlashStage3D { get; set; } + + /// + /// Flash Stage3D Baseline profile + /// + [JsonProperty("flash_stage3d_baseline")] + public string FlashStage3dBaseline { get; set; } + + /// + /// Compositing + /// + [JsonProperty("gpu_compositing")] + public string GpuCompositing { get; set; } + + /// + /// Multiple Raster Threads + /// + [JsonProperty("multiple_raster_threads")] + public string MultipleRasterThreads { get; set; } + + /// + /// Native GpuMemoryBuffers + /// + [JsonProperty("native_gpu_memory_buffers")] + public string NativeGpuMemoryBuffers { get; set; } + + /// + /// Rasterization + /// + public string Rasterization { get; set; } + + /// + /// Video Decode + /// + [JsonProperty("video_decode")] + public string VideoDecode { get; set; } + + /// + /// Video Encode + /// + [JsonProperty("video_encode")] + public string VideoEncode { get; set; } + + /// + /// VPx Video Decode + /// + [JsonProperty("vpx_decode")] + public string VpxDecode { get; set; } + + /// + /// WebGL + /// + public string Webgl { get; set; } + + /// + /// WebGL2 + /// + public string Webgl2 { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/ImportCertificateOptions.cs b/ElectronNET.API/Entities/ImportCertificateOptions.cs new file mode 100644 index 0000000..779992c --- /dev/null +++ b/ElectronNET.API/Entities/ImportCertificateOptions.cs @@ -0,0 +1,15 @@ +namespace ElectronNET.API.Entities +{ + public class ImportCertificateOptions + { + /// + /// Path for the pkcs12 file. + /// + public string Certificate { get; set; } + + /// + /// Passphrase for the certificate. + /// + public string Password {get; set; } + } +} diff --git a/ElectronNET.API/Entities/JumpListCategory.cs b/ElectronNET.API/Entities/JumpListCategory.cs new file mode 100644 index 0000000..abf8c5c --- /dev/null +++ b/ElectronNET.API/Entities/JumpListCategory.cs @@ -0,0 +1,11 @@ +using ElectronNET.API.Entities; + +namespace ElectronNET.API +{ + public class JumpListCategory + { + public string Name { get; set; } = string.Empty; + public JumpListItem[] Items { get; set; } = new JumpListItem[0]; + public string Type { get; set; } = "tasks"; + } +} diff --git a/ElectronNET.API/Entities/JumpListItem.cs b/ElectronNET.API/Entities/JumpListItem.cs new file mode 100644 index 0000000..f38562b --- /dev/null +++ b/ElectronNET.API/Entities/JumpListItem.cs @@ -0,0 +1,14 @@ +namespace ElectronNET.API.Entities +{ + public class JumpListItem + { + public string Args { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public int IconIndex { get; set; } = 0; + public string IconPath { get; set; } = string.Empty; + public string Path { get; set; } = string.Empty; + public string Program { get; set; } = string.Empty; + public string Title { get; set; } = string.Empty; + public string Type {get; set; } = string.Empty; + } +} diff --git a/ElectronNET.API/Entities/JumpListSettings.cs b/ElectronNET.API/Entities/JumpListSettings.cs new file mode 100644 index 0000000..d4f3bc0 --- /dev/null +++ b/ElectronNET.API/Entities/JumpListSettings.cs @@ -0,0 +1,9 @@ +namespace ElectronNET.API.Entities +{ + public class JumpListSettings + { + public int MinItems { get; set; } = 0; + + public JumpListItem[] RemovedItems { get; set; } = new JumpListItem[0]; + } +} diff --git a/ElectronNET.API/Entities/LoginItemSettings.cs b/ElectronNET.API/Entities/LoginItemSettings.cs new file mode 100644 index 0000000..67e9535 --- /dev/null +++ b/ElectronNET.API/Entities/LoginItemSettings.cs @@ -0,0 +1,36 @@ +namespace ElectronNET.API.Entities +{ + public class LoginItemSettings + { + /// + /// true if the app is set to open at login. + /// + public bool OpenAtLogin { get; set; } + + /// + /// true if the app is set to open as hidden at login. This setting is only + /// supported on macOS. + /// + public bool OpenAsHidden { get; set; } + + /// + /// true if the app was opened at login automatically. This setting is only + /// supported on macOS. + /// + public bool WasOpenedAtLogin { get; set; } + + /// + /// true if the app was opened as a hidden login item. This indicates that the app + /// should not open any windows at startup.This setting is only supported on macOS. + /// + public bool WasOpenedAsHidden { get; set; } + + /// + /// true if the app was opened as a login item that should restore the state from + /// the previous session.This indicates that the app should restore the windows + /// that were open the last time the app was closed.This setting is only supported + /// on macOS. + /// + public bool RestoreState { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/LoginItemSettingsOptions.cs b/ElectronNET.API/Entities/LoginItemSettingsOptions.cs new file mode 100644 index 0000000..aee888d --- /dev/null +++ b/ElectronNET.API/Entities/LoginItemSettingsOptions.cs @@ -0,0 +1,15 @@ +namespace ElectronNET.API.Entities +{ + public class LoginItemSettingsOptions + { + /// + /// The executable path to compare against. Defaults to process.execPath. + /// + public string Path { get; set; } + + /// + /// The command-line arguments to compare against. Defaults to an empty array. + /// + public string[] Args { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/LoginSettings.cs b/ElectronNET.API/Entities/LoginSettings.cs new file mode 100644 index 0000000..aec8203 --- /dev/null +++ b/ElectronNET.API/Entities/LoginSettings.cs @@ -0,0 +1,30 @@ +namespace ElectronNET.API.Entities +{ + public class LoginSettings + { + /// + /// true to open the app at login, false to remove the app as a login item. Defaults + /// to false. + /// + public bool OpenAtLogin { get; set; } + + /// + /// true to open the app as hidden. Defaults to false. The user can edit this + /// setting from the System Preferences so + /// app.getLoginItemStatus().wasOpenedAsHidden should be checked when the app is + /// opened to know the current value.This setting is only supported on macOS. + /// + public bool OpenAsHidden { get; set; } + + /// + /// The executable to launch at login. Defaults to process.execPath. + /// + public string Path { get; set; } + + /// + /// The command-line arguments to pass to the executable. Defaults to an empty + /// array.Take care to wrap paths in quotes. + /// + public string[] Args { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/MemoryInfo.cs b/ElectronNET.API/Entities/MemoryInfo.cs new file mode 100644 index 0000000..f31423b --- /dev/null +++ b/ElectronNET.API/Entities/MemoryInfo.cs @@ -0,0 +1,33 @@ +namespace ElectronNET.API.Entities +{ + public class MemoryInfo + { + /// + /// The maximum amount of memory that has ever been pinned to actual physical RAM. + /// On macOS its value will always be 0. + /// + public int PeakWorkingSetSize { get; set; } + + /// + /// Process id of the process. + /// + public int Pid { get; set; } + + /// + /// The amount of memory not shared by other processes, such as JS heap or HTML + /// content. + /// + public int PrivateBytes { get; set; } + + /// + /// The amount of memory shared between processes, typically memory consumed by the + /// Electron code itself + /// + public int SharedBytes { get; set; } + + /// + /// The amount of memory currently pinned to actual physical RAM. + /// + public int WorkingSetSize {get; set; } + } +} diff --git a/ElectronNET.API/Entities/NativeImage.cs b/ElectronNET.API/Entities/NativeImage.cs new file mode 100644 index 0000000..deb6210 --- /dev/null +++ b/ElectronNET.API/Entities/NativeImage.cs @@ -0,0 +1,106 @@ +namespace ElectronNET.API.Entities +{ + // TODO: Fertig coden + public class NativeImage + { + // public static NativeImage CreateEmpty() + // { + // throw new NotImplementedException(); + // } + + // public static NativeImage CreateFromBuffer(byte[] buffer) + // { + // throw new NotImplementedException(); + // } + + // public static NativeImage CreateFromBuffer(byte[] buffer, CreateFromBufferOptions options) + // { + // throw new NotImplementedException(); + // } + + // public static NativeImage CreateFromDataURL(string dataURL) + // { + // throw new NotImplementedException(); + // } + + // public static NativeImage CreateFromPath(string path) + // { + // throw new NotImplementedException(); + // } + + // public void AddRepresentation(AddRepresentationOptions options) + // { + // throw new NotImplementedException(); + // } + + // public NativeImage Crop(Rectangle rect) + // { + // throw new NotImplementedException(); + // } + + // public int GetAspectRatio() + // { + // throw new NotImplementedException(); + // } + + // public byte[] GetBitmap() + // { + // throw new NotImplementedException(); + // } + + // public byte[] GetBitmap(BitmapOptions options) + // { + // throw new NotImplementedException(); + // } + + // public byte[] GetNativeHandle() + // { + // throw new NotImplementedException(); + // } + + // public Size GetSize() + // { + // throw new NotImplementedException(); + // } + + // public bool IsEmpty() + // { + // throw new NotImplementedException(); + // } + + // public bool IsTemplateImage() + // { + // throw new NotImplementedException(); + // } + + // public NativeImage Resize(ResizeOptions options) + // { + // throw new NotImplementedException(); + // } + + // public void SetTemplateImage(bool option) + // { + // throw new NotImplementedException(); + // } + + // public byte[] ToBitmap(ToBitmapOptions options) + // { + // throw new NotImplementedException(); + // } + + // public string ToDataURL(ToDataURLOptions options) + // { + // throw new NotImplementedException(); + // } + + // public byte[] ToJPEG(int quality) + // { + // throw new NotImplementedException(); + // } + + // public byte[] ToPNG(ToPNGOptions options) + // { + // throw new NotImplementedException(); + // } + } +} diff --git a/ElectronNET.API/Entities/ProcessMetric.cs b/ElectronNET.API/Entities/ProcessMetric.cs new file mode 100644 index 0000000..79cab89 --- /dev/null +++ b/ElectronNET.API/Entities/ProcessMetric.cs @@ -0,0 +1,25 @@ +namespace ElectronNET.API.Entities +{ + public class ProcessMetric + { + /// + /// CPU usage of the process. + /// + public CPUUsage Cpu { get; set; } + + /// + /// Memory information for the process. + /// + public MemoryInfo Memory {get; set;} + + /// + /// Process id of the process. + /// + public int Pid { get; set; } + + /// + /// Process type (Browser or Tab or GPU etc). + /// + public string Type { get; set; } + } +} diff --git a/ElectronNET.API/Entities/UserTask.cs b/ElectronNET.API/Entities/UserTask.cs new file mode 100644 index 0000000..1b37d67 --- /dev/null +++ b/ElectronNET.API/Entities/UserTask.cs @@ -0,0 +1,12 @@ +namespace ElectronNET.API.Entities +{ + public class UserTask + { + public string Arguments { get; set; } + public string Description { get; set; } + public int IconIndex { get; set; } + public string IconPath { get; set; } + public string Program { get; set; } + public string Title { get; set; } + } +} diff --git a/ElectronNET.CLI/ElectronNET.CLI.csproj b/ElectronNET.CLI/ElectronNET.CLI.csproj index f8dc6f7..3a7efa6 100644 --- a/ElectronNET.CLI/ElectronNET.CLI.csproj +++ b/ElectronNET.CLI/ElectronNET.CLI.csproj @@ -34,8 +34,4 @@ - - - - diff --git a/ElectronNET.Host/api/app.js b/ElectronNET.Host/api/app.js index 0190e5b..aba7a4f 100644 --- a/ElectronNET.Host/api/app.js +++ b/ElectronNET.Host/api/app.js @@ -28,5 +28,167 @@ module.exports = function (socket, app) { var path = app.getPath(name); socket.emit('appGetPathCompleted', path); }); + socket.on('appGetFileIcon', function (path, options) { + if (options) { + app.getFileIcon(path, options, function (error, nativeImage) { + socket.emit('appGetFileIconCompleted', [error, nativeImage]); + }); + } + else { + app.getFileIcon(path, function (error, nativeImage) { + socket.emit('appGetFileIconCompleted', [error, nativeImage]); + }); + } + }); + socket.on('appSetPath', function (name, path) { + app.setPath(name, path); + }); + socket.on('appGetVersion', function () { + var version = app.getVersion(); + socket.emit('appGetVersionCompleted', version); + }); + socket.on('appGetName', function () { + var name = app.getName(); + socket.emit('appGetNameCompleted', name); + }); + socket.on('appSetName', function (name) { + app.setName(name); + }); + socket.on('appGetLocale', function () { + var locale = app.getLocale(); + socket.emit('appGetLocaleCompleted', locale); + }); + socket.on('appAddRecentDocument', function (path) { + app.addRecentDocument(path); + }); + socket.on('appClearRecentDocuments', function () { + app.clearRecentDocuments(); + }); + socket.on('appSetAsDefaultProtocolClient', function (protocol, path, args) { + var success = app.setAsDefaultProtocolClient(protocol, path, args); + socket.emit('appSetAsDefaultProtocolClientCompleted', success); + }); + socket.on('appRemoveAsDefaultProtocolClient', function (protocol, path, args) { + var success = app.removeAsDefaultProtocolClient(protocol, path, args); + socket.emit('appRemoveAsDefaultProtocolClientCompleted', success); + }); + socket.on('appIsDefaultProtocolClient', function (protocol, path, args) { + var success = app.isDefaultProtocolClient(protocol, path, args); + socket.emit('appIsDefaultProtocolClientCompleted', success); + }); + socket.on('appSetUserTasks', function (tasks) { + var success = app.setUserTasks(tasks); + socket.emit('appSetUserTasksCompleted', success); + }); + socket.on('appGetJumpListSettings', function () { + var jumpListSettings = app.getJumpListSettings(); + socket.emit('appGetJumpListSettingsCompleted', jumpListSettings); + }); + socket.on('appSetJumpList', function (categories) { + app.setJumpList(categories); + }); + socket.on('appMakeSingleInstance', function () { + var success = app.makeSingleInstance(function (args, workingDirectory) { + socket.emit('newInstanceOpened', [args, workingDirectory]); + }); + socket.emit('appMakeSingleInstanceCompleted', success); + }); + socket.on('appReleaseSingleInstance', function () { + app.releaseSingleInstance(); + }); + socket.on('appSetUserActivity', function (type, userInfo, webpageURL) { + app.setUserActivity(type, userInfo, webpageURL); + }); + socket.on('appGetCurrentActivityType', function () { + var activityType = app.getCurrentActivityType(); + socket.emit('appGetCurrentActivityTypeCompleted', activityType); + }); + socket.on('appSetAppUserModelId', function (id) { + app.setAppUserModelId(id); + }); + socket.on('appImportCertificate', function (options) { + app.importCertificate(options, function (result) { + socket.emit('appImportCertificateCompleted', result); + }); + }); + socket.on('appGetAppMetrics', function () { + var processMetrics = app.getAppMetrics(); + socket.emit('appGetAppMetricsCompleted', processMetrics); + }); + socket.on('appGetGpuFeatureStatus', function () { + // TS Workaround - TS say getGpuFeatureStatus - but it is getGPUFeatureStatus + var x = app; + var gpuFeatureStatus = x.getGPUFeatureStatus(); + socket.emit('appGetGpuFeatureStatusCompleted', gpuFeatureStatus); + }); + socket.on('appSetBadgeCount', function (count) { + var success = app.setBadgeCount(count); + socket.emit('appSetBadgeCountCompleted', success); + }); + socket.on('appGetBadgeCount', function () { + var count = app.getBadgeCount(); + socket.emit('appGetBadgeCountCompleted', count); + }); + socket.on('appIsUnityRunning', function () { + var isUnityRunning = app.isUnityRunning(); + socket.emit('appIsUnityRunningCompleted', isUnityRunning); + }); + socket.on('appGetLoginItemSettings', function (options) { + var loginItemSettings = app.getLoginItemSettings(options); + socket.emit('appGetLoginItemSettingsCompleted', loginItemSettings); + }); + socket.on('appSetLoginItemSettings', function (settings) { + app.setLoginItemSettings(settings); + }); + socket.on('appIsAccessibilitySupportEnabled', function () { + var isAccessibilitySupportEnabled = app.isAccessibilitySupportEnabled(); + socket.emit('appIsAccessibilitySupportEnabledCompleted', isAccessibilitySupportEnabled); + }); + socket.on('appSetAboutPanelOptions', function (options) { + app.setAboutPanelOptions(options); + }); + socket.on('appCommandLineAppendSwitch', function (theSwitch, value) { + app.commandLine.appendSwitch(theSwitch, value); + }); + socket.on('appCommandLineAppendArgument', function (value) { + app.commandLine.appendArgument(value); + }); + socket.on('appEnableMixedSandbox', function () { + app.enableMixedSandbox(); + }); + socket.on('appDockBounce', function (type) { + var id = app.dock.bounce(type); + socket.emit('appDockBounceCompleted', id); + }); + socket.on('appDockCancelBounce', function (id) { + app.dock.cancelBounce(id); + }); + socket.on('appDockDownloadFinished', function (filePath) { + app.dock.downloadFinished(filePath); + }); + socket.on('appDockSetBadge', function (text) { + app.dock.setBadge(text); + }); + socket.on('appDockGetBadge', function () { + var text = app.dock.getBadge(); + socket.emit('appDockGetBadgeCompleted', text); + }); + socket.on('appDockHide', function () { + app.dock.hide(); + }); + socket.on('appDockShow', function () { + app.dock.show(); + }); + socket.on('appDockIsVisible', function () { + var isVisible = app.dock.isVisible(); + socket.emit('appDockIsVisibleCompleted', isVisible); + }); + // TODO: Menü Lösung muss noch implementiert werden + socket.on('appDockSetMenu', function (menu) { + app.dock.setMenu(menu); + }); + socket.on('appDockSetIcon', function (image) { + app.dock.setIcon(image); + }); }; //# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/app.js.map b/ElectronNET.Host/api/app.js.map index 44b340f..3e3c634 100644 --- a/ElectronNET.Host/api/app.js.map +++ b/ElectronNET.Host/api/app.js.map @@ -1 +1 @@ -{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":";;AAEA,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB,EAAE,GAAiB;IAExD,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE;QACjB,GAAG,CAAC,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,UAAC,QAAY;QAAZ,yBAAA,EAAA,YAAY;QAC9B,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,UAAC,OAAO;QAC7B,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE;QAClB,GAAG,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE;QACjB,GAAG,CAAC,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE;QACjB,GAAG,CAAC,IAAI,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,eAAe,EAAE;QACvB,IAAM,IAAI,GAAG,GAAG,CAAC,UAAU,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,UAAC,IAAI;QACzB,IAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/ElectronNET.Host/api/app.ts b/ElectronNET.Host/api/app.ts index e13c153..2490111 100644 --- a/ElectronNET.Host/api/app.ts +++ b/ElectronNET.Host/api/app.ts @@ -1,11 +1,11 @@ -import {} from 'electron'; +import { nativeImage as NativeImage } from 'electron'; module.exports = (socket: SocketIO.Server, app: Electron.App) => { socket.on('appQuit', () => { app.quit(); }); - + socket.on('appExit', (exitCode = 0) => { app.exit(exitCode); }); @@ -25,7 +25,7 @@ module.exports = (socket: SocketIO.Server, app: Electron.App) => { socket.on('appShow', () => { app.show(); }); - + socket.on('appGetAppPath', () => { const path = app.getAppPath(); socket.emit('appGetAppPathCompleted', path); @@ -33,6 +33,221 @@ module.exports = (socket: SocketIO.Server, app: Electron.App) => { socket.on('appGetPath', (name) => { const path = app.getPath(name); - socket.emit('appGetPathCompleted', path); - }); + socket.emit('appGetPathCompleted', path); + }); + + // const nativeImages = {}; + + // function addNativeImage(nativeImage: Electron.NativeImage) { + + // if(Object.keys(nativeImages).length === 0) { + // nativeImage['1'] = nativeImage; + // } else { + // let indexCount = Object.keys(nativeImages).length + 1; + // nativeImage[indexCount] = nativeImage; + // } + // } + + socket.on('appGetFileIcon', (path, options) => { + if(options) { + app.getFileIcon(path, options, (error, nativeImage) => { + socket.emit('appGetFileIconCompleted', [error, nativeImage]); + }); + } else { + app.getFileIcon(path, (error, nativeImage) => { + socket.emit('appGetFileIconCompleted', [error, nativeImage]); + }); + } + }); + + socket.on('appSetPath', (name, path) => { + app.setPath(name, path); + }); + + socket.on('appGetVersion', () => { + const version = app.getVersion(); + socket.emit('appGetVersionCompleted', version); + }); + + socket.on('appGetName', () => { + const name = app.getName(); + socket.emit('appGetNameCompleted', name); + }); + + socket.on('appSetName', (name) => { + app.setName(name); + }); + + socket.on('appGetLocale', () => { + const locale = app.getLocale(); + socket.emit('appGetLocaleCompleted', locale); + }); + + socket.on('appAddRecentDocument', (path) => { + app.addRecentDocument(path); + }); + + socket.on('appClearRecentDocuments', () => { + app.clearRecentDocuments(); + }); + + socket.on('appSetAsDefaultProtocolClient', (protocol, path, args) => { + const success = app.setAsDefaultProtocolClient(protocol, path, args); + socket.emit('appSetAsDefaultProtocolClientCompleted', success); + }); + + socket.on('appRemoveAsDefaultProtocolClient', (protocol, path, args) => { + const success = app.removeAsDefaultProtocolClient(protocol, path, args); + socket.emit('appRemoveAsDefaultProtocolClientCompleted', success); + }); + + socket.on('appIsDefaultProtocolClient', (protocol, path, args) => { + const success = app.isDefaultProtocolClient(protocol, path, args); + socket.emit('appIsDefaultProtocolClientCompleted', success); + }); + + socket.on('appSetUserTasks', (tasks) => { + const success = app.setUserTasks(tasks); + socket.emit('appSetUserTasksCompleted', success); + }); + + socket.on('appGetJumpListSettings', () => { + const jumpListSettings = app.getJumpListSettings(); + socket.emit('appGetJumpListSettingsCompleted', jumpListSettings); + }); + + socket.on('appSetJumpList', (categories) => { + app.setJumpList(categories); + }); + + socket.on('appMakeSingleInstance', () => { + const success = app.makeSingleInstance((args, workingDirectory) => { + socket.emit('newInstanceOpened', [args, workingDirectory]); + }); + socket.emit('appMakeSingleInstanceCompleted', success); + }); + + socket.on('appReleaseSingleInstance', () => { + app.releaseSingleInstance(); + }); + + socket.on('appSetUserActivity', (type, userInfo, webpageURL) => { + app.setUserActivity(type, userInfo, webpageURL); + }); + + socket.on('appGetCurrentActivityType', () => { + const activityType = app.getCurrentActivityType(); + socket.emit('appGetCurrentActivityTypeCompleted', activityType); + }); + + socket.on('appSetAppUserModelId', (id) => { + app.setAppUserModelId(id); + }); + + socket.on('appImportCertificate', (options) => { + app.importCertificate(options, (result) => { + socket.emit('appImportCertificateCompleted', result); + }); + }); + + socket.on('appGetAppMetrics', () => { + const processMetrics = app.getAppMetrics(); + socket.emit('appGetAppMetricsCompleted', processMetrics); + }); + + socket.on('appGetGpuFeatureStatus', () => { + // TS Workaround - TS say getGpuFeatureStatus - but it is getGPUFeatureStatus + let x = app; + const gpuFeatureStatus = x.getGPUFeatureStatus(); + socket.emit('appGetGpuFeatureStatusCompleted', gpuFeatureStatus); + }); + + socket.on('appSetBadgeCount', (count) => { + const success = app.setBadgeCount(count); + socket.emit('appSetBadgeCountCompleted', success); + }); + + socket.on('appGetBadgeCount', () => { + const count = app.getBadgeCount(); + socket.emit('appGetBadgeCountCompleted', count); + }); + + socket.on('appIsUnityRunning', () => { + const isUnityRunning = app.isUnityRunning(); + socket.emit('appIsUnityRunningCompleted', isUnityRunning); + }); + + socket.on('appGetLoginItemSettings', (options) => { + const loginItemSettings = app.getLoginItemSettings(options); + socket.emit('appGetLoginItemSettingsCompleted', loginItemSettings); + }); + + socket.on('appSetLoginItemSettings', (settings) => { + app.setLoginItemSettings(settings); + }); + + socket.on('appIsAccessibilitySupportEnabled', () => { + const isAccessibilitySupportEnabled = app.isAccessibilitySupportEnabled(); + socket.emit('appIsAccessibilitySupportEnabledCompleted', isAccessibilitySupportEnabled); + }); + + socket.on('appSetAboutPanelOptions', (options) => { + app.setAboutPanelOptions(options); + }); + + socket.on('appCommandLineAppendSwitch', (theSwitch, value) => { + app.commandLine.appendSwitch(theSwitch, value); + }); + + socket.on('appCommandLineAppendArgument', (value) => { + app.commandLine.appendArgument(value); + }); + + socket.on('appEnableMixedSandbox', () => { + app.enableMixedSandbox(); + }); + + socket.on('appDockBounce', (type) => { + const id = app.dock.bounce(type); + socket.emit('appDockBounceCompleted', id); + }); + + socket.on('appDockCancelBounce', (id) => { + app.dock.cancelBounce(id); + }); + + socket.on('appDockDownloadFinished', (filePath) => { + app.dock.downloadFinished(filePath); + }); + + socket.on('appDockSetBadge', (text) => { + app.dock.setBadge(text); + }); + + socket.on('appDockGetBadge', () => { + const text = app.dock.getBadge(); + socket.emit('appDockGetBadgeCompleted', text); + }); + + socket.on('appDockHide', () => { + app.dock.hide(); + }); + + socket.on('appDockShow', () => { + app.dock.show(); + }); + + socket.on('appDockIsVisible', () => { + const isVisible = app.dock.isVisible(); + socket.emit('appDockIsVisibleCompleted', isVisible); + }); + + // TODO: Menü Lösung muss noch implementiert werden + socket.on('appDockSetMenu', (menu) => { + app.dock.setMenu(menu); + }); + + socket.on('appDockSetIcon', (image) => { + app.dock.setIcon(image); + }); } \ No newline at end of file diff --git a/ElectronNET.Host/main.js b/ElectronNET.Host/main.js index 242e236..e22ac47 100644 --- a/ElectronNET.Host/main.js +++ b/ElectronNET.Host/main.js @@ -20,9 +20,6 @@ function startSocketApiBridge(port) { appApi = require('./api/app')(socket, app); socket.on('createBrowserWindow', (options) => { - console.log(options); - options.show = true; - window = new BrowserWindow(options); window.loadURL(loadURL); @@ -45,14 +42,14 @@ function startSocketApiBridge(port) { function startAspCoreBackend(electronPort) { portfinder(8000, (error, electronWebPort) => { loadURL = `http://localhost:${electronWebPort}` - const arguments = [`/electronPort=${electronPort}`, `/electronWebPort=${electronWebPort}`]; + const params = [`/electronPort=${electronPort}`, `/electronWebPort=${electronWebPort}`]; var binPath = path.join(__dirname, 'bin'); fs.readdir(binPath, (error, files) => { const exeFiles = files.filter((name) => name.indexOf('.exe') > -1); const exeFileName = exeFiles[0]; const apipath = path.join(binPath, exeFileName); - apiProcess = process(apipath, arguments); + apiProcess = process(apipath, params); apiProcess.stdout.on('data', (data) => { var text = data.toString(); diff --git a/ElectronNET.WebApp/Controllers/HomeController.cs b/ElectronNET.WebApp/Controllers/HomeController.cs index f447614..1de15fa 100644 --- a/ElectronNET.WebApp/Controllers/HomeController.cs +++ b/ElectronNET.WebApp/Controllers/HomeController.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.Mvc; using ElectronNET.API; using ElectronNET.API.Entities; +using System; +using System.IO; namespace ElectronNET.WebApp.Controllers { @@ -21,7 +23,13 @@ namespace ElectronNET.WebApp.Controllers App.IpcMain.On("GetPath", async (args) => { string pathName = await App.GetPathAsync(PathName.pictures); - App.IpcMain.Send("GetPathComplete", pathName); + //App.IpcMain.Send("GetPathComplete", pathName); + + var result = await App.GetPathAsync(PathName.exe); + //var imagePath = Path.Combine(result, "Electron.png"); + App.IpcMain.Send("GetPathComplete", result); + + var image = await App.GetFileIconAsync(result); }); return View(); From 9848a38b0d6a1480beadd899cbbb172a5ff56ff9 Mon Sep 17 00:00:00 2001 From: Gregor Biswanger Date: Sat, 14 Oct 2017 14:41:11 +0200 Subject: [PATCH 3/8] write code documentation for App-API and IpcMain-API --- ElectronNET.API/App.cs | 429 ++++++++++++++++++ ElectronNET.API/Entities/JumpListCategory.cs | 11 + ElectronNET.API/Entities/JumpListItem.cs | 37 ++ ElectronNET.API/Entities/JumpListSettings.cs | 6 + ElectronNET.API/Entities/PathName.cs | 62 ++- ElectronNET.API/IpcMain.cs | 57 +-- .../Controllers/HomeController.cs | 4 +- 7 files changed, 564 insertions(+), 42 deletions(-) diff --git a/ElectronNET.API/App.cs b/ElectronNET.API/App.cs index 471e3f7..2192143 100644 --- a/ElectronNET.API/App.cs +++ b/ElectronNET.API/App.cs @@ -10,6 +10,9 @@ namespace ElectronNET.API { public static class App { + /// + /// Communicate asynchronously from the main process to renderer processes. + /// public static IpcMain IpcMain { get; private set; } private static Socket _socket; @@ -45,41 +48,89 @@ namespace ElectronNET.API _socket.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer)); } + /// + /// Try to close all windows. The before-quit event will be emitted first. If all + /// windows are successfully closed, the will-quit event will be emitted and by + /// default the application will terminate. This method guarantees that all + /// beforeunload and unload event handlers are correctly executed. It is possible + /// that a window cancels the quitting by returning false in the beforeunload event + /// handler. + /// public static void Quit() { _socket.Emit("appQuit"); } + /// + /// All windows will be closed immediately without asking user and + /// the before-quit and will-quit events will not be emitted. + /// + /// Exits immediately with exitCode. exitCode defaults to 0. public static void Exit(int exitCode = 0) { _socket.Emit("appExit", exitCode); } + /// + /// Relaunches the app when current instance exits. By default the new instance will + /// use the same working directory and command line arguments with current instance. + /// When args is specified, the args will be passed as command line arguments + /// instead. When execPath is specified, the execPath will be executed for relaunch + /// instead of current app. Note that this method does not quit the app when + /// executed, you have to call app.quit or app.exit after calling app.relaunch to + /// make the app restart. When app.relaunch is called for multiple times, multiple + /// instances will be started after current instance exited. + /// public static void Relaunch() { _socket.Emit("appRelaunch"); } + /// + /// Relaunches the app when current instance exits. By default the new instance will + /// use the same working directory and command line arguments with current instance. + /// When args is specified, the args will be passed as command line arguments + /// instead. When execPath is specified, the execPath will be executed for relaunch + /// instead of current app. Note that this method does not quit the app when + /// executed, you have to call app.quit or app.exit after calling app.relaunch to + /// make the app restart. When app.relaunch is called for multiple times, multiple + /// instances will be started after current instance exited. + /// + /// public static void Relaunch(RelaunchOptions relaunchOptions) { _socket.Emit("appRelaunch", JObject.FromObject(relaunchOptions, _jsonSerializer)); } + /// + /// On Linux, focuses on the first visible window. On macOS, makes the application + /// the active app.On Windows, focuses on the application's first window. + /// public static void Focus() { _socket.Emit("appFocus"); } + /// + /// Hides all application windows without minimizing them. + /// public static void Hide() { _socket.Emit("appHide"); } + /// + /// Shows application windows after they were hidden. Does not automatically focus them. + /// public static void Show() { _socket.Emit("appShow"); } + /// + /// The current application directory. + /// + /// public async static Task GetAppPathAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -95,6 +146,11 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// You can request the following paths by the name. + /// + /// + /// A path to a special directory or file associated with name. public async static Task GetPathAsync(PathName pathName) { var taskCompletionSource = new TaskCompletionSource(); @@ -112,6 +168,12 @@ namespace ElectronNET.API } // TODO: Fertig coden + /// + /// Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux + /// and macOS, icons depend on the application associated with file mime type. + /// + /// + /// //public async static Task GetFileIconAsync(string filePath) //{ // var taskCompletionSource = new TaskCompletionSource(); @@ -133,6 +195,14 @@ namespace ElectronNET.API //} // TODO: Fertig coden + + /// + /// Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux + /// and macOS, icons depend on the application associated with file mime type. + /// + /// + /// + /// //public async static Task GetFileIconAsync(string filePath, FileIconOptions fileIconOptions) //{ // var taskCompletionSource = new TaskCompletionSource(); @@ -150,11 +220,27 @@ namespace ElectronNET.API // return await taskCompletionSource.Task; //} + /// + /// Overrides the path to a special directory or file associated with name. If the + /// path specifies a directory that does not exist, the directory will be created by + /// this method.On failure an Error is thrown.You can only override paths of a + /// name defined in app.getPath. By default, web pages' cookies and caches will be + /// stored under the userData directory.If you want to change this location, you + /// have to override the userData path before the ready event of the app module is emitted. + /// + /// + /// public static void SetPath(string name, string path) { _socket.Emit("appSetPath", name, path); } + /// + /// The version of the loaded application. + /// If no version is found in the application’s package.json file, + /// the version of the current bundle or executable is returned. + /// + /// public async static Task GetVersionAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -170,6 +256,13 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Usually the name field of package.json is a short lowercased name, according to + /// the npm modules spec. You should usually also specify a productName field, which + /// is your application's full capitalized name, and which will be preferred over + /// name by Electron. + /// + /// The current application’s name, which is the name in the application’s package.json file. public async static Task GetNameAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -185,11 +278,21 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Overrides the current application's name. + /// + /// Application's name public static void SetName(string name) { _socket.Emit("appSetName", name); } + /// + /// The current application locale. + /// Note: When distributing your packaged app, you have to also ship the locales + /// folder.Note: On Windows you have to call it after the ready events gets emitted. + /// + /// public async static Task GetLocaleAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -205,16 +308,43 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Adds path to the recent documents list. This list is managed by the OS. On + /// Windows you can visit the list from the task bar, and on macOS you can visit it + /// from dock menu. + /// + /// public static void AddRecentDocument(string path) { _socket.Emit("appAddRecentDocument", path); } + /// + /// Clears the recent documents list. + /// public static void ClearRecentDocuments() { _socket.Emit("appClearRecentDocuments"); } + /// + /// This method sets the current executable as the default handler for a protocol + /// (aka URI scheme). It allows you to integrate your app deeper into the operating + /// system.Once registered, all links with your-protocol:// will be opened with the + /// current executable. The whole link, including protocol, will be passed to your + /// application as a parameter. On Windows you can provide optional parameters path, + /// the path to your executable, and args, an array of arguments to be passed to + /// your executable when it launches.Note: On macOS, you can only register + /// protocols that have been added to your app's info.plist, which can not be + /// modified at runtime.You can however change the file with a simple text editor + /// or script during build time. Please refer to Apple's documentation for details. + /// The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme + /// internally. + /// + /// The name of your protocol, without ://. + /// If you want your app to handle electron:// links, + /// call this method with electron as the parameter. + /// Whether the call succeeded. public async static Task SetAsDefaultProtocolClientAsync(string protocol) { var taskCompletionSource = new TaskCompletionSource(); @@ -230,6 +360,25 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// This method sets the current executable as the default handler for a protocol + /// (aka URI scheme). It allows you to integrate your app deeper into the operating + /// system.Once registered, all links with your-protocol:// will be opened with the + /// current executable. The whole link, including protocol, will be passed to your + /// application as a parameter. On Windows you can provide optional parameters path, + /// the path to your executable, and args, an array of arguments to be passed to + /// your executable when it launches.Note: On macOS, you can only register + /// protocols that have been added to your app's info.plist, which can not be + /// modified at runtime.You can however change the file with a simple text editor + /// or script during build time. Please refer to Apple's documentation for details. + /// The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme + /// internally. + /// + /// The name of your protocol, without ://. + /// If you want your app to handle electron:// links, + /// call this method with electron as the parameter. + /// Defaults to process.execPath + /// Whether the call succeeded. public async static Task SetAsDefaultProtocolClientAsync(string protocol, string path) { var taskCompletionSource = new TaskCompletionSource(); @@ -245,6 +394,26 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// This method sets the current executable as the default handler for a protocol + /// (aka URI scheme). It allows you to integrate your app deeper into the operating + /// system.Once registered, all links with your-protocol:// will be opened with the + /// current executable. The whole link, including protocol, will be passed to your + /// application as a parameter. On Windows you can provide optional parameters path, + /// the path to your executable, and args, an array of arguments to be passed to + /// your executable when it launches.Note: On macOS, you can only register + /// protocols that have been added to your app's info.plist, which can not be + /// modified at runtime.You can however change the file with a simple text editor + /// or script during build time. Please refer to Apple's documentation for details. + /// The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme + /// internally. + /// + /// The name of your protocol, without ://. + /// If you want your app to handle electron:// links, + /// call this method with electron as the parameter. + /// Defaults to process.execPath + /// Defaults to an empty array + /// Whether the call succeeded. public async static Task SetAsDefaultProtocolClientAsync(string protocol, string path, string[] args) { var taskCompletionSource = new TaskCompletionSource(); @@ -260,6 +429,12 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// This method checks if the current executable as the default handler for a + /// protocol(aka URI scheme). If so, it will remove the app as the default handler. + /// + /// The name of your protocol, without ://. + /// Whether the call succeeded. public async static Task RemoveAsDefaultProtocolClientAsync(string protocol) { var taskCompletionSource = new TaskCompletionSource(); @@ -275,6 +450,13 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// This method checks if the current executable as the default handler for a + /// protocol(aka URI scheme). If so, it will remove the app as the default handler. + /// + /// The name of your protocol, without ://. + /// Defaults to process.execPath. + /// Whether the call succeeded. public async static Task RemoveAsDefaultProtocolClientAsync(string protocol, string path) { var taskCompletionSource = new TaskCompletionSource(); @@ -290,6 +472,14 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// This method checks if the current executable as the default handler for a + /// protocol(aka URI scheme). If so, it will remove the app as the default handler. + /// + /// The name of your protocol, without ://. + /// Defaults to process.execPath. + /// Defaults to an empty array. + /// Whether the call succeeded. public async static Task RemoveAsDefaultProtocolClientAsync(string protocol, string path, string[] args) { var taskCompletionSource = new TaskCompletionSource(); @@ -305,6 +495,17 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// This method checks if the current executable is the default handler for a + /// protocol(aka URI scheme). If so, it will return true. Otherwise, it will return + /// false. Note: On macOS, you can use this method to check if the app has been + /// registered as the default protocol handler for a protocol.You can also verify + /// this by checking ~/Library/Preferences/com.apple.LaunchServices.plist on the + /// macOS machine.Please refer to Apple's documentation for details. The API uses + /// the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. + /// + /// The name of your protocol, without ://. + /// Returns Boolean public async static Task IsDefaultProtocolClientAsync(string protocol) { var taskCompletionSource = new TaskCompletionSource(); @@ -320,6 +521,18 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// This method checks if the current executable is the default handler for a + /// protocol(aka URI scheme). If so, it will return true. Otherwise, it will return + /// false. Note: On macOS, you can use this method to check if the app has been + /// registered as the default protocol handler for a protocol.You can also verify + /// this by checking ~/Library/Preferences/com.apple.LaunchServices.plist on the + /// macOS machine.Please refer to Apple's documentation for details. The API uses + /// the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. + /// + /// The name of your protocol, without ://. + /// Defaults to process.execPath. + /// Returns Boolean public async static Task IsDefaultProtocolClientAsync(string protocol, string path) { var taskCompletionSource = new TaskCompletionSource(); @@ -335,6 +548,19 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// This method checks if the current executable is the default handler for a + /// protocol(aka URI scheme). If so, it will return true. Otherwise, it will return + /// false. Note: On macOS, you can use this method to check if the app has been + /// registered as the default protocol handler for a protocol.You can also verify + /// this by checking ~/Library/Preferences/com.apple.LaunchServices.plist on the + /// macOS machine.Please refer to Apple's documentation for details. The API uses + /// the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. + /// + /// The name of your protocol, without ://. + /// Defaults to process.execPath. + /// Defaults to an empty array. + /// Returns Boolean public async static Task IsDefaultProtocolClientAsync(string protocol, string path, string[] args) { var taskCompletionSource = new TaskCompletionSource(); @@ -350,6 +576,13 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Adds tasks to the Tasks category of the JumpList on Windows. tasks is an array + /// of Task objects.Note: If you'd like to customize the Jump List even more use + /// app.setJumpList(categories) instead. + /// + /// Array of Task objects. + /// Whether the call succeeded. public async static Task SetUserTasksAsync(UserTask[] userTasks) { var taskCompletionSource = new TaskCompletionSource(); @@ -365,6 +598,10 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Jump List settings for the application. + /// + /// public async static Task GetJumpListSettingsAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -380,11 +617,50 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Sets or removes a custom Jump List for the application, and returns one of the + /// following strings: If categories is null the previously set custom Jump List(if + /// any) will be replaced by the standard Jump List for the app(managed by + /// Windows). Note: If a JumpListCategory object has neither the type nor the name + /// property set then its type is assumed to be tasks.If the name property is set + /// but the type property is omitted then the type is assumed to be custom. Note: + /// Users can remove items from custom categories, and Windows will not allow a + /// removed item to be added back into a custom category until after the next + /// successful call to app.setJumpList(categories). Any attempt to re-add a removed + /// item to a custom category earlier than that will result in the entire custom + /// category being omitted from the Jump List. The list of removed items can be + /// obtained using app.getJumpListSettings(). + /// + /// public static void SetJumpList(JumpListCategory[] jumpListCategories) { _socket.Emit("appSetJumpList", JObject.FromObject(jumpListCategories, _jsonSerializer)); } + /// + /// This method makes your application a Single Instance Application - instead of + /// allowing multiple instances of your app to run, this will ensure that only a + /// single instance of your app is running, and other instances signal this instance + /// and exit.callback will be called by the first instance with callback(argv, + /// workingDirectory) when a second instance has been executed.argv is an Array of + /// the second instance's command line arguments, and workingDirectory is its + /// current working directory.Usually applications respond to this by making their + /// primary window focused and non-minimized.The callback is guaranteed to be + /// executed after the ready event of app gets emitted.This method returns false if + /// your process is the primary instance of the application and your app should + /// continue loading.And returns true if your process has sent its parameters to + /// another instance, and you should immediately quit.On macOS the system enforces + /// single instance automatically when users try to open a second instance of your + /// app in Finder, and the open-file and open-url events will be emitted for that. + /// However when users start your app in command line the system's single instance + /// mechanism will be bypassed and you have to use this method to ensure single + /// instance. + /// + /// Lambda with an array of the second instance’s command line arguments. + /// The second parameter is the working directory path. + /// This method returns false if your process is the primary instance of + /// the application and your app should continue loading. And returns true if your + /// process has sent its parameters to another instance, and you should immediately quit. public async static Task MakeSingleInstanceAsync(Action newInstanceOpened) { var taskCompletionSource = new TaskCompletionSource(); @@ -410,21 +686,42 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Releases all locks that were created by makeSingleInstance. This will allow + /// multiple instances of the application to once again run side by side. + /// public static void ReleaseSingleInstance() { _socket.Emit("appReleaseSingleInstance"); } + /// + /// Creates an NSUserActivity and sets it as the current activity. The activity is + /// eligible for Handoff to another device afterward. + /// + /// Uniquely identifies the activity. Maps to NSUserActivity.activityType. + /// App-specific state to store for use by another device. public static void SetUserActivity(string type, object userInfo) { _socket.Emit("appSetUserActivity", type, userInfo); } + /// + /// Creates an NSUserActivity and sets it as the current activity. The activity is + /// eligible for Handoff to another device afterward. + /// + /// Uniquely identifies the activity. Maps to NSUserActivity.activityType. + /// App-specific state to store for use by another device. + /// The webpage to load in a browser if no suitable app is installed on the resuming device. The scheme must be http or https. public static void SetUserActivity(string type, object userInfo, string webpageURL) { _socket.Emit("appSetUserActivity", type, userInfo, webpageURL); } + /// + /// The type of the currently running activity. + /// + /// public async static Task GetCurrentActivityTypeAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -440,11 +737,22 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Changes the Application User Model ID to id. + /// + /// public static void SetAppUserModelId(string id) { _socket.Emit("appSetAppUserModelId", id); } + /// + /// Imports the certificate in pkcs12 format into the platform certificate store. + /// callback is called with the result of import operation, a value of 0 indicates + /// success while any other value indicates failure according to chromium net_error_list. + /// + /// + /// Result of import. Value of 0 indicates success. public async static Task ImportCertificateAsync(ImportCertificateOptions options) { var taskCompletionSource = new TaskCompletionSource(); @@ -460,6 +768,10 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Memory and cpu usage statistics of all the processes associated with the app. + /// + /// public async static Task GetAppMetricsAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -477,6 +789,10 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// The Graphics Feature Status from chrome://gpu/. + /// + /// public async static Task GetGpuFeatureStatusAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -494,6 +810,14 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Sets the counter badge for current app. Setting the count to 0 will hide the + /// badge. On macOS it shows on the dock icon. On Linux it only works for Unity + /// launcher, Note: Unity launcher requires the existence of a.desktop file to + /// work, for more information please read Desktop Environment Integration. + /// + /// + /// Whether the call succeeded. public async static Task SetBadgeCountAsync(int count) { var taskCompletionSource = new TaskCompletionSource(); @@ -509,6 +833,10 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// The current value displayed in the counter badge. + /// + /// public async static Task GetBadgeCountAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -524,6 +852,10 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Whether the current desktop environment is Unity launcher. + /// + /// public async static Task IsUnityRunningAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -539,6 +871,12 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// If you provided path and args options to app.setLoginItemSettings then you need + /// to pass the same arguments here for openAtLogin to be set correctly. Note: This + /// API has no effect on MAS builds. + /// + /// public async static Task GetLoginItemSettingsAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -554,6 +892,13 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// If you provided path and args options to app.setLoginItemSettings then you need + /// to pass the same arguments here for openAtLogin to be set correctly. Note: This + /// API has no effect on MAS builds. + /// + /// + /// public async static Task GetLoginItemSettingsAsync(LoginItemSettingsOptions options) { var taskCompletionSource = new TaskCompletionSource(); @@ -569,11 +914,23 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Set the app's login item settings. To work with Electron's autoUpdater on + /// Windows, which uses Squirrel, you'll want to set the launch path to Update.exe, + /// and pass arguments that specify your application name. + /// + /// public static void SetLoginItemSettings(LoginSettings loginSettings) { _socket.Emit("appSetLoginItemSettings", JObject.FromObject(loginSettings, _jsonSerializer)); } + /// + /// This API will return true if the use of assistive technologies, + /// such as screen readers, has been detected. + /// See https://www.chromium.org/developers/design-documents/accessibility for more details. + /// + /// true if Chrome’s accessibility support is enabled, false otherwise. public async static Task IsAccessibilitySupportEnabledAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -589,31 +946,65 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Set the about panel options. This will override the values defined in the app's + /// .plist file. See the Apple docs for more details. + /// + /// public static void SetAboutPanelOptions(AboutPanelOptions options) { _socket.Emit("appSetAboutPanelOptions", JObject.FromObject(options, _jsonSerializer)); } + /// + /// Append a switch (with optional value) to Chromium's command line. Note: This + /// will not affect process.argv, and is mainly used by developers to control some + /// low-level Chromium behaviors. + /// + /// A command-line switch. public static void CommandLineAppendSwitch(string theSwtich) { _socket.Emit("appCommandLineAppendSwitch", theSwtich); } + /// + /// Append a switch (with optional value) to Chromium's command line. Note: This + /// will not affect process.argv, and is mainly used by developers to control some + /// low-level Chromium behaviors. + /// + /// A command-line switch. + /// A value for the given switch. public static void CommandLineAppendSwitch(string theSwtich, string value) { _socket.Emit("appCommandLineAppendSwitch", theSwtich, value); } + /// + /// Append an argument to Chromium's command line. The argument will be quoted + /// correctly.Note: This will not affect process.argv. + /// + /// The argument to append to the command line. public static void CommandLineAppendArgument(string value) { _socket.Emit("appCommandLineAppendArgument", value); } + /// + /// Enables mixed sandbox mode on the app. This method can only be called before app is ready. + /// public static void EnableMixedSandbox() { _socket.Emit("appEnableMixedSandbox"); } + /// + /// When critical is passed, the dock icon will bounce until either the application + /// becomes active or the request is canceled.When informational is passed, the + /// dock icon will bounce for one second.However, the request remains active until + /// either the application becomes active or the request is canceled. + /// + /// + /// public async static Task DockBounceAsync(DockBounceType type) { var taskCompletionSource = new TaskCompletionSource(); @@ -629,21 +1020,37 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Cancel the bounce of id. + /// + /// public static void DockCancelBounce(int id) { _socket.Emit("appDockCancelBounce", id); } + /// + /// Bounces the Downloads stack if the filePath is inside the Downloads folder. + /// + /// public static void DockDownloadFinished(string filePath) { _socket.Emit("appDockDownloadFinished", filePath); } + /// + /// Sets the string to be displayed in the dock’s badging area. + /// + /// public static void DockSetBadge(string text) { _socket.Emit("appDockSetBadge", text); } + /// + /// Gets the string to be displayed in the dock’s badging area. + /// + /// public async static Task DockGetBadgeAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -659,16 +1066,27 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + /// + /// Hides the dock icon. + /// public static void DockHide() { _socket.Emit("appDockHide"); } + /// + /// Shows the dock icon. + /// public static void DockShow() { _socket.Emit("appDockShow"); } + /// + /// Whether the dock icon is visible. The app.dock.show() call is asynchronous + /// so this method might not return true immediately after that call. + /// + /// public async static Task DockIsVisibleAsync() { var taskCompletionSource = new TaskCompletionSource(); @@ -685,16 +1103,27 @@ namespace ElectronNET.API } // TODO: Menu lösung muss gemacht werden und imeplementiert + /// + /// Sets the application's dock menu. + /// public static void DockSetMenu() { _socket.Emit("appDockSetMenu"); } + /// + /// Sets the image associated with this dock icon. + /// + /// public static void DockSetIcon(string image) { _socket.Emit("appDockSetIcon", image); } + /// + /// Sets the image associated with this dock icon. + /// + /// //public static void DockSetIcon(NativeImage image) //{ // _socket.Emit("appDockSetIcon", JObject.FromObject(image, _jsonSerializer)); diff --git a/ElectronNET.API/Entities/JumpListCategory.cs b/ElectronNET.API/Entities/JumpListCategory.cs index abf8c5c..e7ca486 100644 --- a/ElectronNET.API/Entities/JumpListCategory.cs +++ b/ElectronNET.API/Entities/JumpListCategory.cs @@ -4,8 +4,19 @@ namespace ElectronNET.API { public class JumpListCategory { + /// + /// Must be set if type is custom, otherwise it should be omitted. + /// public string Name { get; set; } = string.Empty; + + /// + /// Array of objects if type is tasks or custom, otherwise it should be omitted. + /// public JumpListItem[] Items { get; set; } = new JumpListItem[0]; + + /// + /// One of the following: "tasks" | "frequent" | "recent" | "custom" + /// public string Type { get; set; } = "tasks"; } } diff --git a/ElectronNET.API/Entities/JumpListItem.cs b/ElectronNET.API/Entities/JumpListItem.cs index f38562b..8d1ac63 100644 --- a/ElectronNET.API/Entities/JumpListItem.cs +++ b/ElectronNET.API/Entities/JumpListItem.cs @@ -2,13 +2,50 @@ { public class JumpListItem { + /// + /// The command line arguments when program is executed. Should only be set if type is task. + /// public string Args { get; set; } = string.Empty; + + /// + /// Description of the task (displayed in a tooltip). Should only be set if type is task. + /// public string Description { get; set; } = string.Empty; + + /// + /// The index of the icon in the resource file. If a resource file contains multiple + /// icons this value can be used to specify the zero-based index of the icon that + /// should be displayed for this task.If a resource file contains only one icon, + /// this property should be set to zero. + /// public int IconIndex { get; set; } = 0; + + /// + /// The absolute path to an icon to be displayed in a Jump List, which can be an + /// arbitrary resource file that contains an icon(e.g. .ico, .exe, .dll). You can + /// usually specify process.execPath to show the program icon. + /// public string IconPath { get; set; } = string.Empty; + + /// + /// Path of the file to open, should only be set if type is file. + /// public string Path { get; set; } = string.Empty; + + /// + /// Path of the program to execute, usually you should specify process.execPath + /// which opens the current program.Should only be set if type is task. + /// public string Program { get; set; } = string.Empty; + + /// + /// The text to be displayed for the item in the Jump List. Should only be set if type is task. + /// public string Title { get; set; } = string.Empty; + + /// + /// One of the following: "task" | "separator" | "file" + /// public string Type {get; set; } = string.Empty; } } diff --git a/ElectronNET.API/Entities/JumpListSettings.cs b/ElectronNET.API/Entities/JumpListSettings.cs index d4f3bc0..b7964cd 100644 --- a/ElectronNET.API/Entities/JumpListSettings.cs +++ b/ElectronNET.API/Entities/JumpListSettings.cs @@ -2,8 +2,14 @@ { public class JumpListSettings { + /// + /// The minimum number of items that will be shown in the Jump List (for a more detailed description of this value see the MSDN docs). + /// public int MinItems { get; set; } = 0; + /// + /// Array of JumpListItem objects that correspond to items that the user has explicitly removed from custom categories in the Jump List. These items must not be re-added to the Jump List in the next call to app.setJumpList(), Windows will not display any custom category that contains any of the removed items. + /// public JumpListItem[] RemovedItems { get; set; } = new JumpListItem[0]; } } diff --git a/ElectronNET.API/Entities/PathName.cs b/ElectronNET.API/Entities/PathName.cs index 48173d8..0435b4c 100644 --- a/ElectronNET.API/Entities/PathName.cs +++ b/ElectronNET.API/Entities/PathName.cs @@ -1,24 +1,76 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace ElectronNET.API.Entities +namespace ElectronNET.API.Entities { public enum PathName { + /// + /// User’s home directory. + /// home, + + /// + /// Per-user application data directory. + /// appData, + + /// + /// The directory for storing your app’s configuration files, + /// which by default it is the appData directory appended with your app’s name. + /// userData, + + /// + /// Temporary directory. + /// temp, + + /// + /// The current executable file. + /// exe, + + /// + /// The libchromiumcontent library. + /// module, + + /// + /// The current user’s Desktop directory. + /// desktop, + + /// + /// Directory for a user’s “My Documents”. + /// documents, + + /// + /// Directory for a user’s downloads. + /// downloads, + + /// + /// Directory for a user’s music. + /// music, + + /// + /// Directory for a user’s pictures. + /// pictures, + + /// + /// Directory for a user’s videos. + /// videos, + + /// + /// + /// logs, + + /// + /// Full path to the system version of the Pepper Flash plugin. + /// pepperFlashSystemPlugin } } diff --git a/ElectronNET.API/IpcMain.cs b/ElectronNET.API/IpcMain.cs index db3bdfc..cc6fa49 100644 --- a/ElectronNET.API/IpcMain.cs +++ b/ElectronNET.API/IpcMain.cs @@ -3,9 +3,9 @@ using Quobject.SocketIoClientDotNet.Client; namespace ElectronNET.API { - // - // Summary: - // Communicate asynchronously from the main process to renderer processes. + /// + /// Communicate asynchronously from the main process to renderer processes. + /// public class IpcMain { private Socket _socket; @@ -27,50 +27,35 @@ namespace ElectronNET.API _socket.On(channel, listener); } - // Summary: - // Adds a one time listener method for the event. This listener is invoked only - // the next time a message is sent to channel, after which it is removed. - // - // Parameters: - // channel: - // Channelname. - // - // listener: - // Callback Method. - // + /// + /// Adds a one time listener method for the event. This listener is invoked only + /// the next time a message is sent to channel, after which it is removed. + /// + /// Channelname. + /// Callback Method. public void Once(string channel, Action listener) { _socket.Emit("registerOnceIpcMainChannel", channel); _socket.On(channel, listener); } - // - // Summary: - // Removes listeners of the specified channel. - // - // Parameters: - // channel: - // Channelname. - // + /// + /// Removes listeners of the specified channel. + /// + /// Channelname. public void RemoveAllListeners(string channel) { _socket.Emit("removeAllListenersIpcMainChannel", channel); } - // - // Summary: - // Send a message to the renderer process asynchronously via channel, you can also send - // arbitrary arguments. Arguments will be serialized in JSON internally and hence - // no functions or prototype chain will be included. The renderer process handles it by - // listening for channel with ipcRenderer module. - // - // Parameters: - // channel: - // Channelname. - // - // data: - // Arguments data. - // + /// + /// Send a message to the renderer process asynchronously via channel, you can also send + /// arbitrary arguments. Arguments will be serialized in JSON internally and hence + /// no functions or prototype chain will be included. The renderer process handles it by + /// listening for channel with ipcRenderer module. + /// + /// Channelname. + /// Arguments data. public void Send(string channel, params object[] data) { _socket.Emit("sendToIpcRenderer", channel, data); diff --git a/ElectronNET.WebApp/Controllers/HomeController.cs b/ElectronNET.WebApp/Controllers/HomeController.cs index 1de15fa..fcc5116 100644 --- a/ElectronNET.WebApp/Controllers/HomeController.cs +++ b/ElectronNET.WebApp/Controllers/HomeController.cs @@ -29,7 +29,9 @@ namespace ElectronNET.WebApp.Controllers //var imagePath = Path.Combine(result, "Electron.png"); App.IpcMain.Send("GetPathComplete", result); - var image = await App.GetFileIconAsync(result); + + //var image = await App.GetFileIconAsync(result); + }); return View(); From 9abece156fa22abd109ed56427d3c1c1707e313c Mon Sep 17 00:00:00 2001 From: Gregor Biswanger Date: Sat, 14 Oct 2017 17:58:16 +0200 Subject: [PATCH 4/8] =?UTF-8?q?Create=20Electron-Class=20for=20API=C2=B4s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ElectronNET.API/App.cs | 432 +++++++++--------- ElectronNET.API/BridgeConnector.cs | 19 + ElectronNET.API/Electron.cs | 15 + ElectronNET.API/IpcMain.cs | 33 +- ElectronNET.API/WebHostBuilderExtensions.cs | 2 + .../Controllers/HomeController.cs | 16 +- ElectronNET.WebApp/Startup.cs | 2 +- 7 files changed, 281 insertions(+), 238 deletions(-) create mode 100644 ElectronNET.API/BridgeConnector.cs create mode 100644 ElectronNET.API/Electron.cs diff --git a/ElectronNET.API/App.cs b/ElectronNET.API/App.cs index 2192143..b203deb 100644 --- a/ElectronNET.API/App.cs +++ b/ElectronNET.API/App.cs @@ -2,50 +2,50 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; -using Quobject.SocketIoClientDotNet.Client; using System; using System.Threading.Tasks; namespace ElectronNET.API { - public static class App + public sealed class App { - /// - /// Communicate asynchronously from the main process to renderer processes. - /// - public static IpcMain IpcMain { get; private set; } + private static App _app; - private static Socket _socket; - private static JsonSerializer _jsonSerializer; + private App() { } - public static void OpenWindow(int width, int height, bool show) + public static App Instance { - _jsonSerializer = new JsonSerializer() + get { - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - _socket = IO.Socket("http://localhost:" + BridgeSettings.SocketPort); - _socket.On(Socket.EVENT_CONNECT, () => - { - Console.WriteLine("Verbunden!"); - - var browserWindowOptions = new BrowserWindowOptions() + if (_app == null) { - Height = height, - Width = width, - Show = show - }; + _app = new App(); + } - _socket.Emit("createBrowserWindow", JObject.FromObject(browserWindowOptions, _jsonSerializer)); - }); - - IpcMain = new IpcMain(_socket); + return _app; + } } - public static void CreateNotification(NotificationOptions notificationOptions) + private JsonSerializer _jsonSerializer = new JsonSerializer() { - _socket.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer)); + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + public void OpenWindow(int width, int height, bool show) + { + var browserWindowOptions = new BrowserWindowOptions() + { + Height = height, + Width = width, + Show = show + }; + + BridgeConnector.Socket.Emit("createBrowserWindow", JObject.FromObject(browserWindowOptions, _jsonSerializer)); + } + + public void CreateNotification(NotificationOptions notificationOptions) + { + BridgeConnector.Socket.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer)); } /// @@ -56,9 +56,9 @@ namespace ElectronNET.API /// that a window cancels the quitting by returning false in the beforeunload event /// handler. /// - public static void Quit() + public void Quit() { - _socket.Emit("appQuit"); + BridgeConnector.Socket.Emit("appQuit"); } /// @@ -66,9 +66,9 @@ namespace ElectronNET.API /// the before-quit and will-quit events will not be emitted. /// /// Exits immediately with exitCode. exitCode defaults to 0. - public static void Exit(int exitCode = 0) + public void Exit(int exitCode = 0) { - _socket.Emit("appExit", exitCode); + BridgeConnector.Socket.Emit("appExit", exitCode); } /// @@ -81,9 +81,9 @@ namespace ElectronNET.API /// make the app restart. When app.relaunch is called for multiple times, multiple /// instances will be started after current instance exited. /// - public static void Relaunch() + public void Relaunch() { - _socket.Emit("appRelaunch"); + BridgeConnector.Socket.Emit("appRelaunch"); } /// @@ -97,51 +97,51 @@ namespace ElectronNET.API /// instances will be started after current instance exited. /// /// - public static void Relaunch(RelaunchOptions relaunchOptions) + public void Relaunch(RelaunchOptions relaunchOptions) { - _socket.Emit("appRelaunch", JObject.FromObject(relaunchOptions, _jsonSerializer)); + BridgeConnector.Socket.Emit("appRelaunch", JObject.FromObject(relaunchOptions, _jsonSerializer)); } /// /// On Linux, focuses on the first visible window. On macOS, makes the application /// the active app.On Windows, focuses on the application's first window. /// - public static void Focus() + public void Focus() { - _socket.Emit("appFocus"); + BridgeConnector.Socket.Emit("appFocus"); } /// /// Hides all application windows without minimizing them. /// - public static void Hide() + public void Hide() { - _socket.Emit("appHide"); + BridgeConnector.Socket.Emit("appHide"); } /// /// Shows application windows after they were hidden. Does not automatically focus them. /// - public static void Show() + public void Show() { - _socket.Emit("appShow"); + BridgeConnector.Socket.Emit("appShow"); } /// /// The current application directory. /// /// - public async static Task GetAppPathAsync() + public async Task GetAppPathAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetAppPathCompleted", (path) => + BridgeConnector.Socket.On("appGetAppPathCompleted", (path) => { - _socket.Off("appGetAppPathCompleted"); + BridgeConnector.Socket.Off("appGetAppPathCompleted"); taskCompletionSource.SetResult(path.ToString()); }); - _socket.Emit("appGetAppPath"); + BridgeConnector.Socket.Emit("appGetAppPath"); return await taskCompletionSource.Task; } @@ -151,18 +151,18 @@ namespace ElectronNET.API /// /// /// A path to a special directory or file associated with name. - public async static Task GetPathAsync(PathName pathName) + public async Task GetPathAsync(PathName pathName) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetPathCompleted", (path) => + BridgeConnector.Socket.On("appGetPathCompleted", (path) => { - _socket.Off("appGetPathCompleted"); + BridgeConnector.Socket.Off("appGetPathCompleted"); taskCompletionSource.SetResult(path.ToString()); }); - _socket.Emit("appGetPath", pathName.ToString()); + BridgeConnector.Socket.Emit("appGetPath", pathName.ToString()); return await taskCompletionSource.Task; } @@ -178,9 +178,9 @@ namespace ElectronNET.API //{ // var taskCompletionSource = new TaskCompletionSource(); - // _socket.On("appGetFileIconCompleted", (results) => + // BridgeConnector.Socket.On("appGetFileIconCompleted", (results) => // { - // _socket.Off("appGetFileIconCompleted"); + // BridgeConnector.Socket.Off("appGetFileIconCompleted"); // byte[] test = ((JArray)results).Last.ToObject(); @@ -189,7 +189,7 @@ namespace ElectronNET.API // //NativeImage nativeImage = (NativeImage)result[1]; // //taskCompletionSource.SetResult(nativeImage); // }); - // _socket.Emit("appGetFileIcon", filePath); + // BridgeConnector.Socket.Emit("appGetFileIcon", filePath); // return await taskCompletionSource.Task; //} @@ -207,15 +207,15 @@ namespace ElectronNET.API //{ // var taskCompletionSource = new TaskCompletionSource(); - // _socket.On("appGetFileIconCompleted", (results) => + // BridgeConnector.Socket.On("appGetFileIconCompleted", (results) => // { - // _socket.Off("appGetFileIconCompleted"); + // BridgeConnector.Socket.Off("appGetFileIconCompleted"); // object[] result = results as object[]; // NativeImage nativeImage = (NativeImage)result[1]; // taskCompletionSource.SetResult(nativeImage); // }); - // _socket.Emit("appGetFileIcon", filePath, JObject.FromObject(fileIconOptions, _jsonSerializer)); + // BridgeConnector.Socket.Emit("appGetFileIcon", filePath, JObject.FromObject(fileIconOptions, _jsonSerializer)); // return await taskCompletionSource.Task; //} @@ -230,9 +230,9 @@ namespace ElectronNET.API /// /// /// - public static void SetPath(string name, string path) + public void SetPath(string name, string path) { - _socket.Emit("appSetPath", name, path); + BridgeConnector.Socket.Emit("appSetPath", name, path); } /// @@ -241,17 +241,17 @@ namespace ElectronNET.API /// the version of the current bundle or executable is returned. /// /// - public async static Task GetVersionAsync() + public async Task GetVersionAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetVersionCompleted", (version) => + BridgeConnector.Socket.On("appGetVersionCompleted", (version) => { - _socket.Off("appGetVersionCompleted"); + BridgeConnector.Socket.Off("appGetVersionCompleted"); taskCompletionSource.SetResult(version.ToString()); }); - _socket.Emit("appGetVersion"); + BridgeConnector.Socket.Emit("appGetVersion"); return await taskCompletionSource.Task; } @@ -263,17 +263,17 @@ namespace ElectronNET.API /// name by Electron. /// /// The current application’s name, which is the name in the application’s package.json file. - public async static Task GetNameAsync() + public async Task GetNameAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetNameCompleted", (name) => + BridgeConnector.Socket.On("appGetNameCompleted", (name) => { - _socket.Off("appGetNameCompleted"); + BridgeConnector.Socket.Off("appGetNameCompleted"); taskCompletionSource.SetResult(name.ToString()); }); - _socket.Emit("appGetName"); + BridgeConnector.Socket.Emit("appGetName"); return await taskCompletionSource.Task; } @@ -282,9 +282,9 @@ namespace ElectronNET.API /// Overrides the current application's name. /// /// Application's name - public static void SetName(string name) + public void SetName(string name) { - _socket.Emit("appSetName", name); + BridgeConnector.Socket.Emit("appSetName", name); } /// @@ -293,17 +293,17 @@ namespace ElectronNET.API /// folder.Note: On Windows you have to call it after the ready events gets emitted. /// /// - public async static Task GetLocaleAsync() + public async Task GetLocaleAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetLocaleCompleted", (locale) => + BridgeConnector.Socket.On("appGetLocaleCompleted", (locale) => { - _socket.Off("appGetLocaleCompleted"); + BridgeConnector.Socket.Off("appGetLocaleCompleted"); taskCompletionSource.SetResult(locale.ToString()); }); - _socket.Emit("appGetLocale"); + BridgeConnector.Socket.Emit("appGetLocale"); return await taskCompletionSource.Task; } @@ -314,17 +314,17 @@ namespace ElectronNET.API /// from dock menu. /// /// - public static void AddRecentDocument(string path) + public void AddRecentDocument(string path) { - _socket.Emit("appAddRecentDocument", path); + BridgeConnector.Socket.Emit("appAddRecentDocument", path); } /// /// Clears the recent documents list. /// - public static void ClearRecentDocuments() + public void ClearRecentDocuments() { - _socket.Emit("appClearRecentDocuments"); + BridgeConnector.Socket.Emit("appClearRecentDocuments"); } /// @@ -345,17 +345,17 @@ namespace ElectronNET.API /// If you want your app to handle electron:// links, /// call this method with electron as the parameter. /// Whether the call succeeded. - public async static Task SetAsDefaultProtocolClientAsync(string protocol) + public async Task SetAsDefaultProtocolClientAsync(string protocol) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appSetAsDefaultProtocolClientCompleted", (success) => + BridgeConnector.Socket.On("appSetAsDefaultProtocolClientCompleted", (success) => { - _socket.Off("appSetAsDefaultProtocolClientCompleted"); + BridgeConnector.Socket.Off("appSetAsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Emit("appSetAsDefaultProtocolClient", protocol); + BridgeConnector.Socket.Emit("appSetAsDefaultProtocolClient", protocol); return await taskCompletionSource.Task; } @@ -379,17 +379,17 @@ namespace ElectronNET.API /// call this method with electron as the parameter. /// Defaults to process.execPath /// Whether the call succeeded. - public async static Task SetAsDefaultProtocolClientAsync(string protocol, string path) + public async Task SetAsDefaultProtocolClientAsync(string protocol, string path) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appSetAsDefaultProtocolClientCompleted", (success) => + BridgeConnector.Socket.On("appSetAsDefaultProtocolClientCompleted", (success) => { - _socket.Off("appSetAsDefaultProtocolClientCompleted"); + BridgeConnector.Socket.Off("appSetAsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Emit("appSetAsDefaultProtocolClient", protocol, path); + BridgeConnector.Socket.Emit("appSetAsDefaultProtocolClient", protocol, path); return await taskCompletionSource.Task; } @@ -414,17 +414,17 @@ namespace ElectronNET.API /// Defaults to process.execPath /// Defaults to an empty array /// Whether the call succeeded. - public async static Task SetAsDefaultProtocolClientAsync(string protocol, string path, string[] args) + public async Task SetAsDefaultProtocolClientAsync(string protocol, string path, string[] args) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appSetAsDefaultProtocolClientCompleted", (success) => + BridgeConnector.Socket.On("appSetAsDefaultProtocolClientCompleted", (success) => { - _socket.Off("appSetAsDefaultProtocolClientCompleted"); + BridgeConnector.Socket.Off("appSetAsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Emit("appSetAsDefaultProtocolClient", protocol, path, args); + BridgeConnector.Socket.Emit("appSetAsDefaultProtocolClient", protocol, path, args); return await taskCompletionSource.Task; } @@ -435,17 +435,17 @@ namespace ElectronNET.API /// /// The name of your protocol, without ://. /// Whether the call succeeded. - public async static Task RemoveAsDefaultProtocolClientAsync(string protocol) + public async Task RemoveAsDefaultProtocolClientAsync(string protocol) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appRemoveAsDefaultProtocolClientCompleted", (success) => + BridgeConnector.Socket.On("appRemoveAsDefaultProtocolClientCompleted", (success) => { - _socket.Off("appRemoveAsDefaultProtocolClientCompleted"); + BridgeConnector.Socket.Off("appRemoveAsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Emit("appRemoveAsDefaultProtocolClient", protocol); + BridgeConnector.Socket.Emit("appRemoveAsDefaultProtocolClient", protocol); return await taskCompletionSource.Task; } @@ -457,17 +457,17 @@ namespace ElectronNET.API /// The name of your protocol, without ://. /// Defaults to process.execPath. /// Whether the call succeeded. - public async static Task RemoveAsDefaultProtocolClientAsync(string protocol, string path) + public async Task RemoveAsDefaultProtocolClientAsync(string protocol, string path) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appRemoveAsDefaultProtocolClientCompleted", (success) => + BridgeConnector.Socket.On("appRemoveAsDefaultProtocolClientCompleted", (success) => { - _socket.Off("appRemoveAsDefaultProtocolClientCompleted"); + BridgeConnector.Socket.Off("appRemoveAsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Emit("appRemoveAsDefaultProtocolClient", protocol, path); + BridgeConnector.Socket.Emit("appRemoveAsDefaultProtocolClient", protocol, path); return await taskCompletionSource.Task; } @@ -480,17 +480,17 @@ namespace ElectronNET.API /// Defaults to process.execPath. /// Defaults to an empty array. /// Whether the call succeeded. - public async static Task RemoveAsDefaultProtocolClientAsync(string protocol, string path, string[] args) + public async Task RemoveAsDefaultProtocolClientAsync(string protocol, string path, string[] args) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appRemoveAsDefaultProtocolClientCompleted", (success) => + BridgeConnector.Socket.On("appRemoveAsDefaultProtocolClientCompleted", (success) => { - _socket.Off("appRemoveAsDefaultProtocolClientCompleted"); + BridgeConnector.Socket.Off("appRemoveAsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Emit("appRemoveAsDefaultProtocolClient", protocol, path, args); + BridgeConnector.Socket.Emit("appRemoveAsDefaultProtocolClient", protocol, path, args); return await taskCompletionSource.Task; } @@ -506,17 +506,17 @@ namespace ElectronNET.API /// /// The name of your protocol, without ://. /// Returns Boolean - public async static Task IsDefaultProtocolClientAsync(string protocol) + public async Task IsDefaultProtocolClientAsync(string protocol) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appIsDefaultProtocolClientCompleted", (success) => + BridgeConnector.Socket.On("appIsDefaultProtocolClientCompleted", (success) => { - _socket.Off("appIsDefaultProtocolClientCompleted"); + BridgeConnector.Socket.Off("appIsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Emit("appIsDefaultProtocolClient", protocol); + BridgeConnector.Socket.Emit("appIsDefaultProtocolClient", protocol); return await taskCompletionSource.Task; } @@ -533,17 +533,17 @@ namespace ElectronNET.API /// The name of your protocol, without ://. /// Defaults to process.execPath. /// Returns Boolean - public async static Task IsDefaultProtocolClientAsync(string protocol, string path) + public async Task IsDefaultProtocolClientAsync(string protocol, string path) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appIsDefaultProtocolClientCompleted", (success) => + BridgeConnector.Socket.On("appIsDefaultProtocolClientCompleted", (success) => { - _socket.Off("appIsDefaultProtocolClientCompleted"); + BridgeConnector.Socket.Off("appIsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Emit("appIsDefaultProtocolClient", protocol, path); + BridgeConnector.Socket.Emit("appIsDefaultProtocolClient", protocol, path); return await taskCompletionSource.Task; } @@ -561,17 +561,17 @@ namespace ElectronNET.API /// Defaults to process.execPath. /// Defaults to an empty array. /// Returns Boolean - public async static Task IsDefaultProtocolClientAsync(string protocol, string path, string[] args) + public async Task IsDefaultProtocolClientAsync(string protocol, string path, string[] args) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appIsDefaultProtocolClientCompleted", (success) => + BridgeConnector.Socket.On("appIsDefaultProtocolClientCompleted", (success) => { - _socket.Off("appIsDefaultProtocolClientCompleted"); + BridgeConnector.Socket.Off("appIsDefaultProtocolClientCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Emit("appIsDefaultProtocolClient", protocol, path, args); + BridgeConnector.Socket.Emit("appIsDefaultProtocolClient", protocol, path, args); return await taskCompletionSource.Task; } @@ -583,17 +583,17 @@ namespace ElectronNET.API /// /// Array of Task objects. /// Whether the call succeeded. - public async static Task SetUserTasksAsync(UserTask[] userTasks) + public async Task SetUserTasksAsync(UserTask[] userTasks) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appSetUserTasksCompleted", (success) => + BridgeConnector.Socket.On("appSetUserTasksCompleted", (success) => { - _socket.Off("appSetUserTasksCompleted"); + BridgeConnector.Socket.Off("appSetUserTasksCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Emit("appSetUserTasks", JObject.FromObject(userTasks, _jsonSerializer)); + BridgeConnector.Socket.Emit("appSetUserTasks", JObject.FromObject(userTasks, _jsonSerializer)); return await taskCompletionSource.Task; } @@ -602,17 +602,17 @@ namespace ElectronNET.API /// Jump List settings for the application. /// /// - public async static Task GetJumpListSettingsAsync() + public async Task GetJumpListSettingsAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetJumpListSettingsCompleted", (success) => + BridgeConnector.Socket.On("appGetJumpListSettingsCompleted", (success) => { - _socket.Off("appGetJumpListSettingsCompleted"); + BridgeConnector.Socket.Off("appGetJumpListSettingsCompleted"); taskCompletionSource.SetResult(JObject.Parse(success.ToString()).ToObject()); }); - _socket.Emit("appGetJumpListSettings"); + BridgeConnector.Socket.Emit("appGetJumpListSettings"); return await taskCompletionSource.Task; } @@ -632,9 +632,9 @@ namespace ElectronNET.API /// obtained using app.getJumpListSettings(). /// /// - public static void SetJumpList(JumpListCategory[] jumpListCategories) + public void SetJumpList(JumpListCategory[] jumpListCategories) { - _socket.Emit("appSetJumpList", JObject.FromObject(jumpListCategories, _jsonSerializer)); + BridgeConnector.Socket.Emit("appSetJumpList", JObject.FromObject(jumpListCategories, _jsonSerializer)); } /// @@ -661,18 +661,18 @@ namespace ElectronNET.API /// This method returns false if your process is the primary instance of /// the application and your app should continue loading. And returns true if your /// process has sent its parameters to another instance, and you should immediately quit. - public async static Task MakeSingleInstanceAsync(Action newInstanceOpened) + public async Task MakeSingleInstanceAsync(Action newInstanceOpened) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appMakeSingleInstanceCompleted", (success) => + BridgeConnector.Socket.On("appMakeSingleInstanceCompleted", (success) => { - _socket.Off("appMakeSingleInstanceCompleted"); + BridgeConnector.Socket.Off("appMakeSingleInstanceCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Off("newInstanceOpened"); - _socket.On("newInstanceOpened", (result) => + BridgeConnector.Socket.Off("newInstanceOpened"); + BridgeConnector.Socket.On("newInstanceOpened", (result) => { JArray results = (JArray)result; string[] args = results.First.ToObject(); @@ -681,7 +681,7 @@ namespace ElectronNET.API newInstanceOpened(args, workdirectory); }); - _socket.Emit("appMakeSingleInstance"); + BridgeConnector.Socket.Emit("appMakeSingleInstance"); return await taskCompletionSource.Task; } @@ -690,9 +690,9 @@ namespace ElectronNET.API /// Releases all locks that were created by makeSingleInstance. This will allow /// multiple instances of the application to once again run side by side. /// - public static void ReleaseSingleInstance() + public void ReleaseSingleInstance() { - _socket.Emit("appReleaseSingleInstance"); + BridgeConnector.Socket.Emit("appReleaseSingleInstance"); } /// @@ -701,9 +701,9 @@ namespace ElectronNET.API /// /// Uniquely identifies the activity. Maps to NSUserActivity.activityType. /// App-specific state to store for use by another device. - public static void SetUserActivity(string type, object userInfo) + public void SetUserActivity(string type, object userInfo) { - _socket.Emit("appSetUserActivity", type, userInfo); + BridgeConnector.Socket.Emit("appSetUserActivity", type, userInfo); } /// @@ -713,26 +713,26 @@ namespace ElectronNET.API /// Uniquely identifies the activity. Maps to NSUserActivity.activityType. /// App-specific state to store for use by another device. /// The webpage to load in a browser if no suitable app is installed on the resuming device. The scheme must be http or https. - public static void SetUserActivity(string type, object userInfo, string webpageURL) + public void SetUserActivity(string type, object userInfo, string webpageURL) { - _socket.Emit("appSetUserActivity", type, userInfo, webpageURL); + BridgeConnector.Socket.Emit("appSetUserActivity", type, userInfo, webpageURL); } /// /// The type of the currently running activity. /// /// - public async static Task GetCurrentActivityTypeAsync() + public async Task GetCurrentActivityTypeAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetCurrentActivityTypeCompleted", (activityType) => + BridgeConnector.Socket.On("appGetCurrentActivityTypeCompleted", (activityType) => { - _socket.Off("appGetCurrentActivityTypeCompleted"); + BridgeConnector.Socket.Off("appGetCurrentActivityTypeCompleted"); taskCompletionSource.SetResult(activityType.ToString()); }); - _socket.Emit("appGetCurrentActivityType"); + BridgeConnector.Socket.Emit("appGetCurrentActivityType"); return await taskCompletionSource.Task; } @@ -741,9 +741,9 @@ namespace ElectronNET.API /// Changes the Application User Model ID to id. /// /// - public static void SetAppUserModelId(string id) + public void SetAppUserModelId(string id) { - _socket.Emit("appSetAppUserModelId", id); + BridgeConnector.Socket.Emit("appSetAppUserModelId", id); } /// @@ -753,17 +753,17 @@ namespace ElectronNET.API /// /// /// Result of import. Value of 0 indicates success. - public async static Task ImportCertificateAsync(ImportCertificateOptions options) + public async Task ImportCertificateAsync(ImportCertificateOptions options) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appImportCertificateCompleted", (result) => + BridgeConnector.Socket.On("appImportCertificateCompleted", (result) => { - _socket.Off("appImportCertificateCompleted"); + BridgeConnector.Socket.Off("appImportCertificateCompleted"); taskCompletionSource.SetResult((int)result); }); - _socket.Emit("appImportCertificate", JObject.FromObject(options, _jsonSerializer)); + BridgeConnector.Socket.Emit("appImportCertificate", JObject.FromObject(options, _jsonSerializer)); return await taskCompletionSource.Task; } @@ -772,19 +772,19 @@ namespace ElectronNET.API /// Memory and cpu usage statistics of all the processes associated with the app. /// /// - public async static Task GetAppMetricsAsync() + public async Task GetAppMetricsAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetAppMetricsCompleted", (result) => + BridgeConnector.Socket.On("appGetAppMetricsCompleted", (result) => { - _socket.Off("appGetAppMetricsCompleted"); + BridgeConnector.Socket.Off("appGetAppMetricsCompleted"); var processMetrics = ((JArray)result).ToObject(); taskCompletionSource.SetResult(processMetrics); }); - _socket.Emit("appGetAppMetrics"); + BridgeConnector.Socket.Emit("appGetAppMetrics"); return await taskCompletionSource.Task; } @@ -793,19 +793,19 @@ namespace ElectronNET.API /// The Graphics Feature Status from chrome://gpu/. /// /// - public async static Task GetGpuFeatureStatusAsync() + public async Task GetGpuFeatureStatusAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetGpuFeatureStatusCompleted", (result) => + BridgeConnector.Socket.On("appGetGpuFeatureStatusCompleted", (result) => { - _socket.Off("appGetGpuFeatureStatusCompleted"); + BridgeConnector.Socket.Off("appGetGpuFeatureStatusCompleted"); var gpuFeatureStatus = ((JObject)result).ToObject(); taskCompletionSource.SetResult(gpuFeatureStatus); }); - _socket.Emit("appGetGpuFeatureStatus"); + BridgeConnector.Socket.Emit("appGetGpuFeatureStatus"); return await taskCompletionSource.Task; } @@ -818,17 +818,17 @@ namespace ElectronNET.API /// /// /// Whether the call succeeded. - public async static Task SetBadgeCountAsync(int count) + public async Task SetBadgeCountAsync(int count) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appSetBadgeCountCompleted", (success) => + BridgeConnector.Socket.On("appSetBadgeCountCompleted", (success) => { - _socket.Off("appSetBadgeCountCompleted"); + BridgeConnector.Socket.Off("appSetBadgeCountCompleted"); taskCompletionSource.SetResult((bool)success); }); - _socket.Emit("appSetBadgeCount", count); + BridgeConnector.Socket.Emit("appSetBadgeCount", count); return await taskCompletionSource.Task; } @@ -837,17 +837,17 @@ namespace ElectronNET.API /// The current value displayed in the counter badge. /// /// - public async static Task GetBadgeCountAsync() + public async Task GetBadgeCountAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetBadgeCountCompleted", (count) => + BridgeConnector.Socket.On("appGetBadgeCountCompleted", (count) => { - _socket.Off("appGetBadgeCountCompleted"); + BridgeConnector.Socket.Off("appGetBadgeCountCompleted"); taskCompletionSource.SetResult((int)count); }); - _socket.Emit("appGetBadgeCount"); + BridgeConnector.Socket.Emit("appGetBadgeCount"); return await taskCompletionSource.Task; } @@ -856,17 +856,17 @@ namespace ElectronNET.API /// Whether the current desktop environment is Unity launcher. /// /// - public async static Task IsUnityRunningAsync() + public async Task IsUnityRunningAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appIsUnityRunningCompleted", (isUnityRunning) => + BridgeConnector.Socket.On("appIsUnityRunningCompleted", (isUnityRunning) => { - _socket.Off("appIsUnityRunningCompleted"); + BridgeConnector.Socket.Off("appIsUnityRunningCompleted"); taskCompletionSource.SetResult((bool)isUnityRunning); }); - _socket.Emit("appIsUnityRunning"); + BridgeConnector.Socket.Emit("appIsUnityRunning"); return await taskCompletionSource.Task; } @@ -877,17 +877,17 @@ namespace ElectronNET.API /// API has no effect on MAS builds. /// /// - public async static Task GetLoginItemSettingsAsync() + public async Task GetLoginItemSettingsAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetLoginItemSettingsCompleted", (loginItemSettings) => + BridgeConnector.Socket.On("appGetLoginItemSettingsCompleted", (loginItemSettings) => { - _socket.Off("appGetLoginItemSettingsCompleted"); + BridgeConnector.Socket.Off("appGetLoginItemSettingsCompleted"); taskCompletionSource.SetResult((LoginItemSettings)loginItemSettings); }); - _socket.Emit("appGetLoginItemSettings"); + BridgeConnector.Socket.Emit("appGetLoginItemSettings"); return await taskCompletionSource.Task; } @@ -899,17 +899,17 @@ namespace ElectronNET.API /// /// /// - public async static Task GetLoginItemSettingsAsync(LoginItemSettingsOptions options) + public async Task GetLoginItemSettingsAsync(LoginItemSettingsOptions options) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appGetLoginItemSettingsCompleted", (loginItemSettings) => + BridgeConnector.Socket.On("appGetLoginItemSettingsCompleted", (loginItemSettings) => { - _socket.Off("appGetLoginItemSettingsCompleted"); + BridgeConnector.Socket.Off("appGetLoginItemSettingsCompleted"); taskCompletionSource.SetResult((LoginItemSettings)loginItemSettings); }); - _socket.Emit("appGetLoginItemSettings", JObject.FromObject(options, _jsonSerializer)); + BridgeConnector.Socket.Emit("appGetLoginItemSettings", JObject.FromObject(options, _jsonSerializer)); return await taskCompletionSource.Task; } @@ -920,9 +920,9 @@ namespace ElectronNET.API /// and pass arguments that specify your application name. /// /// - public static void SetLoginItemSettings(LoginSettings loginSettings) + public void SetLoginItemSettings(LoginSettings loginSettings) { - _socket.Emit("appSetLoginItemSettings", JObject.FromObject(loginSettings, _jsonSerializer)); + BridgeConnector.Socket.Emit("appSetLoginItemSettings", JObject.FromObject(loginSettings, _jsonSerializer)); } /// @@ -931,17 +931,17 @@ namespace ElectronNET.API /// See https://www.chromium.org/developers/design-documents/accessibility for more details. /// /// true if Chrome’s accessibility support is enabled, false otherwise. - public async static Task IsAccessibilitySupportEnabledAsync() + public async Task IsAccessibilitySupportEnabledAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appIsAccessibilitySupportEnabledCompleted", (isAccessibilitySupportEnabled) => + BridgeConnector.Socket.On("appIsAccessibilitySupportEnabledCompleted", (isAccessibilitySupportEnabled) => { - _socket.Off("appIsAccessibilitySupportEnabledCompleted"); + BridgeConnector.Socket.Off("appIsAccessibilitySupportEnabledCompleted"); taskCompletionSource.SetResult((bool)isAccessibilitySupportEnabled); }); - _socket.Emit("appIsAccessibilitySupportEnabled"); + BridgeConnector.Socket.Emit("appIsAccessibilitySupportEnabled"); return await taskCompletionSource.Task; } @@ -951,9 +951,9 @@ namespace ElectronNET.API /// .plist file. See the Apple docs for more details. /// /// - public static void SetAboutPanelOptions(AboutPanelOptions options) + public void SetAboutPanelOptions(AboutPanelOptions options) { - _socket.Emit("appSetAboutPanelOptions", JObject.FromObject(options, _jsonSerializer)); + BridgeConnector.Socket.Emit("appSetAboutPanelOptions", JObject.FromObject(options, _jsonSerializer)); } /// @@ -962,9 +962,9 @@ namespace ElectronNET.API /// low-level Chromium behaviors. /// /// A command-line switch. - public static void CommandLineAppendSwitch(string theSwtich) + public void CommandLineAppendSwitch(string theSwtich) { - _socket.Emit("appCommandLineAppendSwitch", theSwtich); + BridgeConnector.Socket.Emit("appCommandLineAppendSwitch", theSwtich); } /// @@ -974,9 +974,9 @@ namespace ElectronNET.API /// /// A command-line switch. /// A value for the given switch. - public static void CommandLineAppendSwitch(string theSwtich, string value) + public void CommandLineAppendSwitch(string theSwtich, string value) { - _socket.Emit("appCommandLineAppendSwitch", theSwtich, value); + BridgeConnector.Socket.Emit("appCommandLineAppendSwitch", theSwtich, value); } /// @@ -984,17 +984,17 @@ namespace ElectronNET.API /// correctly.Note: This will not affect process.argv. /// /// The argument to append to the command line. - public static void CommandLineAppendArgument(string value) + public void CommandLineAppendArgument(string value) { - _socket.Emit("appCommandLineAppendArgument", value); + BridgeConnector.Socket.Emit("appCommandLineAppendArgument", value); } /// /// Enables mixed sandbox mode on the app. This method can only be called before app is ready. /// - public static void EnableMixedSandbox() + public void EnableMixedSandbox() { - _socket.Emit("appEnableMixedSandbox"); + BridgeConnector.Socket.Emit("appEnableMixedSandbox"); } /// @@ -1005,17 +1005,17 @@ namespace ElectronNET.API /// /// /// - public async static Task DockBounceAsync(DockBounceType type) + public async Task DockBounceAsync(DockBounceType type) { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appDockBounceCompleted", (id) => + BridgeConnector.Socket.On("appDockBounceCompleted", (id) => { - _socket.Off("appDockBounceCompleted"); + BridgeConnector.Socket.Off("appDockBounceCompleted"); taskCompletionSource.SetResult((int)id); }); - _socket.Emit("appDockBounce", type.ToString()); + BridgeConnector.Socket.Emit("appDockBounce", type.ToString()); return await taskCompletionSource.Task; } @@ -1024,44 +1024,44 @@ namespace ElectronNET.API /// Cancel the bounce of id. /// /// - public static void DockCancelBounce(int id) + public void DockCancelBounce(int id) { - _socket.Emit("appDockCancelBounce", id); + BridgeConnector.Socket.Emit("appDockCancelBounce", id); } /// /// Bounces the Downloads stack if the filePath is inside the Downloads folder. /// /// - public static void DockDownloadFinished(string filePath) + public void DockDownloadFinished(string filePath) { - _socket.Emit("appDockDownloadFinished", filePath); + BridgeConnector.Socket.Emit("appDockDownloadFinished", filePath); } /// /// Sets the string to be displayed in the dock’s badging area. /// /// - public static void DockSetBadge(string text) + public void DockSetBadge(string text) { - _socket.Emit("appDockSetBadge", text); + BridgeConnector.Socket.Emit("appDockSetBadge", text); } /// /// Gets the string to be displayed in the dock’s badging area. /// /// - public async static Task DockGetBadgeAsync() + public async Task DockGetBadgeAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appDockGetBadgeCompleted", (text) => + BridgeConnector.Socket.On("appDockGetBadgeCompleted", (text) => { - _socket.Off("appDockGetBadgeCompleted"); + BridgeConnector.Socket.Off("appDockGetBadgeCompleted"); taskCompletionSource.SetResult((string)text); }); - _socket.Emit("appDockGetBadge"); + BridgeConnector.Socket.Emit("appDockGetBadge"); return await taskCompletionSource.Task; } @@ -1069,17 +1069,17 @@ namespace ElectronNET.API /// /// Hides the dock icon. /// - public static void DockHide() + public void DockHide() { - _socket.Emit("appDockHide"); + BridgeConnector.Socket.Emit("appDockHide"); } /// /// Shows the dock icon. /// - public static void DockShow() + public void DockShow() { - _socket.Emit("appDockShow"); + BridgeConnector.Socket.Emit("appDockShow"); } /// @@ -1087,17 +1087,17 @@ namespace ElectronNET.API /// so this method might not return true immediately after that call. /// /// - public async static Task DockIsVisibleAsync() + public async Task DockIsVisibleAsync() { var taskCompletionSource = new TaskCompletionSource(); - _socket.On("appDockIsVisibleCompleted", (isVisible) => + BridgeConnector.Socket.On("appDockIsVisibleCompleted", (isVisible) => { - _socket.Off("appDockIsVisibleCompleted"); + BridgeConnector.Socket.Off("appDockIsVisibleCompleted"); taskCompletionSource.SetResult((bool)isVisible); }); - _socket.Emit("appDockIsVisible"); + BridgeConnector.Socket.Emit("appDockIsVisible"); return await taskCompletionSource.Task; } @@ -1106,18 +1106,18 @@ namespace ElectronNET.API /// /// Sets the application's dock menu. /// - public static void DockSetMenu() + public void DockSetMenu() { - _socket.Emit("appDockSetMenu"); + BridgeConnector.Socket.Emit("appDockSetMenu"); } /// /// Sets the image associated with this dock icon. /// /// - public static void DockSetIcon(string image) + public void DockSetIcon(string image) { - _socket.Emit("appDockSetIcon", image); + BridgeConnector.Socket.Emit("appDockSetIcon", image); } /// @@ -1126,7 +1126,7 @@ namespace ElectronNET.API /// //public static void DockSetIcon(NativeImage image) //{ - // _socket.Emit("appDockSetIcon", JObject.FromObject(image, _jsonSerializer)); + // BridgeConnector.Socket.Emit("appDockSetIcon", JObject.FromObject(image, _jsonSerializer)); //} } } diff --git a/ElectronNET.API/BridgeConnector.cs b/ElectronNET.API/BridgeConnector.cs new file mode 100644 index 0000000..bac492d --- /dev/null +++ b/ElectronNET.API/BridgeConnector.cs @@ -0,0 +1,19 @@ +using Quobject.SocketIoClientDotNet.Client; +using System; + +namespace ElectronNET.API +{ + internal static class BridgeConnector + { + public static Socket Socket; + + public static void StartConnection() + { + Socket = IO.Socket("http://localhost:" + BridgeSettings.SocketPort); + Socket.On(Socket.EVENT_CONNECT, () => + { + Console.WriteLine("BridgeConnector connected!"); + }); + } + } +} diff --git a/ElectronNET.API/Electron.cs b/ElectronNET.API/Electron.cs new file mode 100644 index 0000000..f7b854d --- /dev/null +++ b/ElectronNET.API/Electron.cs @@ -0,0 +1,15 @@ +namespace ElectronNET.API +{ + public static class Electron + { + /// + /// Communicate asynchronously from the main process to renderer processes. + /// + public static IpcMain IpcMain { get { return IpcMain.Instance; } } + + /// + /// Control your application's event lifecycle. + /// + public static App App { get { return App.Instance; } } + } +} diff --git a/ElectronNET.API/IpcMain.cs b/ElectronNET.API/IpcMain.cs index cc6fa49..7cf2577 100644 --- a/ElectronNET.API/IpcMain.cs +++ b/ElectronNET.API/IpcMain.cs @@ -1,20 +1,29 @@ using System; -using Quobject.SocketIoClientDotNet.Client; namespace ElectronNET.API { /// /// Communicate asynchronously from the main process to renderer processes. /// - public class IpcMain + public sealed class IpcMain { - private Socket _socket; + private static IpcMain _ipcMain; - public IpcMain(Socket socket) + private IpcMain() { } + + public static IpcMain Instance { - _socket = socket; + get + { + if(_ipcMain == null) + { + _ipcMain = new IpcMain(); + } + + return _ipcMain; + } } - + /// /// Listens to channel, when a new message arrives listener would be called with /// listener(event, args...). @@ -23,8 +32,8 @@ namespace ElectronNET.API /// Callback Method. public void On(string channel, Action listener) { - _socket.Emit("registerIpcMainChannel", channel); - _socket.On(channel, listener); + BridgeConnector.Socket.Emit("registerIpcMainChannel", channel); + BridgeConnector.Socket.On(channel, listener); } /// @@ -35,8 +44,8 @@ namespace ElectronNET.API /// Callback Method. public void Once(string channel, Action listener) { - _socket.Emit("registerOnceIpcMainChannel", channel); - _socket.On(channel, listener); + BridgeConnector.Socket.Emit("registerOnceIpcMainChannel", channel); + BridgeConnector.Socket.On(channel, listener); } /// @@ -45,7 +54,7 @@ namespace ElectronNET.API /// Channelname. public void RemoveAllListeners(string channel) { - _socket.Emit("removeAllListenersIpcMainChannel", channel); + BridgeConnector.Socket.Emit("removeAllListenersIpcMainChannel", channel); } /// @@ -58,7 +67,7 @@ namespace ElectronNET.API /// Arguments data. public void Send(string channel, params object[] data) { - _socket.Emit("sendToIpcRenderer", channel, data); + BridgeConnector.Socket.Emit("sendToIpcRenderer", channel, data); } } } \ No newline at end of file diff --git a/ElectronNET.API/WebHostBuilderExtensions.cs b/ElectronNET.API/WebHostBuilderExtensions.cs index f939a54..0409f6b 100644 --- a/ElectronNET.API/WebHostBuilderExtensions.cs +++ b/ElectronNET.API/WebHostBuilderExtensions.cs @@ -23,6 +23,8 @@ namespace ElectronNET.API { builder.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory) .UseUrls("http://0.0.0.0:" + BridgeSettings.WebPort); + + BridgeConnector.StartConnection(); } return builder; diff --git a/ElectronNET.WebApp/Controllers/HomeController.cs b/ElectronNET.WebApp/Controllers/HomeController.cs index fcc5116..536f1aa 100644 --- a/ElectronNET.WebApp/Controllers/HomeController.cs +++ b/ElectronNET.WebApp/Controllers/HomeController.cs @@ -1,8 +1,6 @@ using Microsoft.AspNetCore.Mvc; using ElectronNET.API; using ElectronNET.API.Entities; -using System; -using System.IO; namespace ElectronNET.WebApp.Controllers { @@ -10,24 +8,24 @@ namespace ElectronNET.WebApp.Controllers { public IActionResult Index() { - App.IpcMain.On("SayHello", (args) => { - App.CreateNotification(new NotificationOptions + Electron.IpcMain.On("SayHello", (args) => { + Electron.App.CreateNotification(new NotificationOptions { Title = "Hallo Robert", Body = "Nachricht von ASP.NET Core App" }); - App.IpcMain.Send("Goodbye", "Elephant!"); + Electron.IpcMain.Send("Goodbye", "Elephant!"); }); - App.IpcMain.On("GetPath", async (args) => + Electron.IpcMain.On("GetPath", async (args) => { - string pathName = await App.GetPathAsync(PathName.pictures); + string pathName = await Electron.App.GetPathAsync(PathName.pictures); //App.IpcMain.Send("GetPathComplete", pathName); - var result = await App.GetPathAsync(PathName.exe); + var result = await Electron.App.GetPathAsync(PathName.exe); //var imagePath = Path.Combine(result, "Electron.png"); - App.IpcMain.Send("GetPathComplete", result); + Electron.IpcMain.Send("GetPathComplete", result); //var image = await App.GetFileIconAsync(result); diff --git a/ElectronNET.WebApp/Startup.cs b/ElectronNET.WebApp/Startup.cs index 63b3d10..172cb3a 100644 --- a/ElectronNET.WebApp/Startup.cs +++ b/ElectronNET.WebApp/Startup.cs @@ -35,7 +35,7 @@ namespace ElectronNET.WebApp template: "{controller=Home}/{action=Index}/{id?}"); }); - App.OpenWindow(800, 600, true); + Electron.App.OpenWindow(800, 600, true); } } } From 90d0cc6189bd54d332863fac9fa2c49f26a1f9da Mon Sep 17 00:00:00 2001 From: Gregor Biswanger Date: Sat, 14 Oct 2017 19:57:49 +0200 Subject: [PATCH 5/8] implement the first App-API Events --- ElectronNET.API/App.cs | 253 +++++++++++++++++- ElectronNET.Host/api/app.js | 54 ++++ ElectronNET.Host/api/app.js.map | 2 +- ElectronNET.Host/api/app.ts | 54 ++++ .../Controllers/HomeController.cs | 10 +- ElectronNET.WebApp/Startup.cs | 1 + 6 files changed, 363 insertions(+), 11 deletions(-) diff --git a/ElectronNET.API/App.cs b/ElectronNET.API/App.cs index b203deb..715207c 100644 --- a/ElectronNET.API/App.cs +++ b/ElectronNET.API/App.cs @@ -9,7 +9,254 @@ namespace ElectronNET.API { public sealed class App { - private static App _app; + /// + /// Emitted when all windows have been closed. + /// + /// If you do not subscribe to this event and all windows are closed, + /// the default behavior is to quit the app; however, if you subscribe, + /// you control whether the app quits or not.If the user pressed Cmd + Q, + /// or the developer called app.quit(), Electron will first try to close + /// all the windows and then emit the will-quit event, and in this case the + /// window-all-closed event would not be emitted. + /// + public event Action WindowAllClosed + { + add + { + if (_windowAllClosed == null) + { + BridgeConnector.Socket.On("app-window-all-closed", () => + { + _windowAllClosed(); + }); + + BridgeConnector.Socket.Emit("register-app-window-all-closed-event"); + } + _windowAllClosed += value; + } + remove + { + _windowAllClosed -= value; + } + } + + private event Action _windowAllClosed; + + /// + /// Emitted before the application starts closing its windows. + /// + /// Note: If application quit was initiated by autoUpdater.quitAndInstall() then before-quit is emitted after + /// emitting close event on all windows and closing them. + /// + public event Action BeforeQuit + { + add + { + if (_beforeQuit == null) + { + BridgeConnector.Socket.On("app-before-quit", () => + { + _beforeQuit(); + }); + + BridgeConnector.Socket.Emit("register-app-before-quit-event"); + } + _beforeQuit += value; + } + remove + { + _beforeQuit -= value; + } + } + + private event Action _beforeQuit; + + /// + /// Emitted when all windows have been closed and the application will quit. + /// + /// See the description of the window-all-closed event for the differences between the will-quit and + /// window-all-closed events. + /// + public event Action WillQuit + { + add + { + if (_willQuit == null) + { + BridgeConnector.Socket.On("app-will-quit", () => + { + _willQuit(); + }); + + BridgeConnector.Socket.Emit("register-app-will-quit-event"); + } + _willQuit += value; + } + remove + { + _willQuit -= value; + } + } + + private event Action _willQuit; + + /// + /// Emitted when the application is quitting. + /// + public event Action Quitting + { + add + { + if (_quitting == null) + { + BridgeConnector.Socket.On("app-quit", () => + { + _quitting(); + }); + + BridgeConnector.Socket.Emit("register-app-quit-event"); + } + _quitting += value; + } + remove + { + _quitting -= value; + } + } + + private event Action _quitting; + + /// + /// Emitted when a BrowserWindow gets blurred. + /// + public event Action BrowserWindowBlur + { + add + { + if (_browserWindowBlur == null) + { + BridgeConnector.Socket.On("app-browser-window-blur", () => + { + _browserWindowBlur(); + }); + + BridgeConnector.Socket.Emit("register-app-browser-window-blur-event"); + } + _browserWindowBlur += value; + } + remove + { + _browserWindowBlur -= value; + } + } + + private event Action _browserWindowBlur; + + /// + /// Emitted when a BrowserWindow gets focused. + /// + public event Action BrowserWindowFocus + { + add + { + if (_browserWindowFocus == null) + { + BridgeConnector.Socket.On("app-browser-window-focus", () => + { + _browserWindowFocus(); + }); + + BridgeConnector.Socket.Emit("register-app-browser-window-focus-event"); + } + _browserWindowFocus += value; + } + remove + { + _browserWindowFocus -= value; + } + } + + private event Action _browserWindowFocus; + + /// + /// Emitted when a new BrowserWindow is created. + /// + public event Action BrowserWindowCreated + { + add + { + if (_browserWindowCreated == null) + { + BridgeConnector.Socket.On("app-browser-window-created", () => + { + _browserWindowCreated(); + }); + + BridgeConnector.Socket.Emit("register-app-browser-window-created-event"); + } + _browserWindowCreated += value; + } + remove + { + _browserWindowCreated -= value; + } + } + + private event Action _browserWindowCreated; + + /// + /// Emitted when a new webContents is created. + /// + public event Action WebContentsCreated + { + add + { + if (_webContentsCreated == null) + { + BridgeConnector.Socket.On("app-web-contents-created", () => + { + _webContentsCreated(); + }); + + BridgeConnector.Socket.Emit("register-app-web-contents-created-event"); + } + _webContentsCreated += value; + } + remove + { + _webContentsCreated -= value; + } + } + + private event Action _webContentsCreated; + + /// + /// Emitted when Chrome’s accessibility support changes. + /// This event fires when assistive technologies, such as screen readers, are enabled or disabled. + /// See https://www.chromium.org/developers/design-documents/accessibility for more details. + /// + public event Action AccessibilitySupportChanged + { + add + { + if (_accessibilitySupportChanged == null) + { + BridgeConnector.Socket.On("app-accessibility-support-changed", (state) => + { + _accessibilitySupportChanged((bool)state); + }); + + BridgeConnector.Socket.Emit("register-app-accessibility-support-changed-event"); + } + _accessibilitySupportChanged += value; + } + remove + { + _accessibilitySupportChanged -= value; + } + } + + private event Action _accessibilitySupportChanged; private App() { } @@ -26,11 +273,14 @@ namespace ElectronNET.API } } + private static App _app; + private JsonSerializer _jsonSerializer = new JsonSerializer() { ContractResolver = new CamelCasePropertyNamesContractResolver() }; + // TODO: Auslagern in eigenes Window-Management public void OpenWindow(int width, int height, bool show) { var browserWindowOptions = new BrowserWindowOptions() @@ -43,6 +293,7 @@ namespace ElectronNET.API BridgeConnector.Socket.Emit("createBrowserWindow", JObject.FromObject(browserWindowOptions, _jsonSerializer)); } + // TODO: Auslagern in eigenes Notification-API public void CreateNotification(NotificationOptions notificationOptions) { BridgeConnector.Socket.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer)); diff --git a/ElectronNET.Host/api/app.js b/ElectronNET.Host/api/app.js index aba7a4f..94ea0d5 100644 --- a/ElectronNET.Host/api/app.js +++ b/ElectronNET.Host/api/app.js @@ -1,6 +1,51 @@ "use strict"; exports.__esModule = true; module.exports = function (socket, app) { + socket.on('register-app-window-all-closed-event', function () { + app.on('window-all-closed', function () { + socket.emit('app-window-all-closed'); + }); + }); + socket.on('register-app-before-quit-event', function () { + app.on('before-quit', function () { + socket.emit('app-before-quit'); + }); + }); + socket.on('register-app-will-quit-event', function () { + app.on('will-quit', function () { + socket.emit('app-will-quit'); + }); + }); + socket.on('register-app-quit-event', function () { + app.on('quit', function () { + socket.emit('app-quit'); + }); + }); + socket.on('register-app-browser-window-blur-event', function () { + app.on('browser-window-blur', function () { + socket.emit('app-browser-window-blur'); + }); + }); + socket.on('register-app-browser-window-focus-event', function () { + app.on('browser-window-focus', function () { + socket.emit('app-browser-window-focus'); + }); + }); + socket.on('register-app-browser-window-created-event', function () { + app.on('browser-window-created', function () { + socket.emit('app-browser-window-created'); + }); + }); + socket.on('register-app-web-contents-created-event', function () { + app.on('web-contents-created', function () { + socket.emit('app-web-contents-created'); + }); + }); + socket.on('register-app-accessibility-support-changed-event', function () { + app.on('accessibility-support-changed', function (event, accessibilitySupportEnabled) { + socket.emit('app-accessibility-support-changed', accessibilitySupportEnabled); + }); + }); socket.on('appQuit', function () { app.quit(); }); @@ -28,6 +73,15 @@ module.exports = function (socket, app) { var path = app.getPath(name); socket.emit('appGetPathCompleted', path); }); + // const nativeImages = {}; + // function addNativeImage(nativeImage: Electron.NativeImage) { + // if(Object.keys(nativeImages).length === 0) { + // nativeImage['1'] = nativeImage; + // } else { + // let indexCount = Object.keys(nativeImages).length + 1; + // nativeImage[indexCount] = nativeImage; + // } + // } socket.on('appGetFileIcon', function (path, options) { if (options) { app.getFileIcon(path, options, function (error, nativeImage) { diff --git a/ElectronNET.Host/api/app.js.map b/ElectronNET.Host/api/app.js.map index 3e3c634..103379b 100644 --- a/ElectronNET.Host/api/app.js.map +++ b/ElectronNET.Host/api/app.js.map @@ -1 +1 @@ -{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/ElectronNET.Host/api/app.ts b/ElectronNET.Host/api/app.ts index 2490111..99fd2b6 100644 --- a/ElectronNET.Host/api/app.ts +++ b/ElectronNET.Host/api/app.ts @@ -1,6 +1,60 @@ import { nativeImage as NativeImage } from 'electron'; module.exports = (socket: SocketIO.Server, app: Electron.App) => { + + socket.on('register-app-window-all-closed-event', () => { + app.on('window-all-closed', () => { + socket.emit('app-window-all-closed'); + }); + }); + + socket.on('register-app-before-quit-event', () => { + app.on('before-quit', () => { + socket.emit('app-before-quit'); + }); + }); + + socket.on('register-app-will-quit-event', () => { + app.on('will-quit', () => { + socket.emit('app-will-quit'); + }); + }); + + socket.on('register-app-quit-event', () => { + app.on('quit', () => { + socket.emit('app-quit'); + }); + }); + + socket.on('register-app-browser-window-blur-event', () => { + app.on('browser-window-blur', () => { + socket.emit('app-browser-window-blur'); + }); + }); + + socket.on('register-app-browser-window-focus-event', () => { + app.on('browser-window-focus', () => { + socket.emit('app-browser-window-focus'); + }); + }); + + socket.on('register-app-browser-window-created-event', () => { + app.on('browser-window-created', () => { + socket.emit('app-browser-window-created'); + }); + }); + + socket.on('register-app-web-contents-created-event', () => { + app.on('web-contents-created', () => { + socket.emit('app-web-contents-created'); + }); + }); + + socket.on('register-app-accessibility-support-changed-event', () => { + app.on('accessibility-support-changed', (event, accessibilitySupportEnabled) => { + socket.emit('app-accessibility-support-changed', accessibilitySupportEnabled); + }); + }); socket.on('appQuit', () => { app.quit(); diff --git a/ElectronNET.WebApp/Controllers/HomeController.cs b/ElectronNET.WebApp/Controllers/HomeController.cs index 536f1aa..1c7bc7c 100644 --- a/ElectronNET.WebApp/Controllers/HomeController.cs +++ b/ElectronNET.WebApp/Controllers/HomeController.cs @@ -21,15 +21,7 @@ namespace ElectronNET.WebApp.Controllers Electron.IpcMain.On("GetPath", async (args) => { string pathName = await Electron.App.GetPathAsync(PathName.pictures); - //App.IpcMain.Send("GetPathComplete", pathName); - - var result = await Electron.App.GetPathAsync(PathName.exe); - //var imagePath = Path.Combine(result, "Electron.png"); - Electron.IpcMain.Send("GetPathComplete", result); - - - //var image = await App.GetFileIconAsync(result); - + Electron.IpcMain.Send("GetPathComplete", pathName); }); return View(); diff --git a/ElectronNET.WebApp/Startup.cs b/ElectronNET.WebApp/Startup.cs index 172cb3a..fa2b53a 100644 --- a/ElectronNET.WebApp/Startup.cs +++ b/ElectronNET.WebApp/Startup.cs @@ -1,4 +1,5 @@ using ElectronNET.API; +using ElectronNET.API.Entities; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; From 8f9a84cb0fd4ca2d06e72409060d5870819a4bca Mon Sep 17 00:00:00 2001 From: Gregor Biswanger Date: Sun, 15 Oct 2017 06:03:48 +0200 Subject: [PATCH 6/8] implement WindowManager, BrowserWindow-API and Menu-API --- ElectronNET.API/App.cs | 17 +- ElectronNET.API/BrowserWindow.cs | 16 ++ ElectronNET.API/Electron.cs | 10 + .../BrowserWindowConstructorOptions.cs | 244 ++++++++++++++++++ ElectronNET.API/Entities/MenuItem.cs | 72 ++++++ ElectronNET.API/Entities/WebPreferences.cs | 186 +++++++++++++ ElectronNET.API/IpcMain.cs | 4 +- ElectronNET.API/Menu.cs | 92 +++++++ ElectronNET.API/WindowManager.cs | 67 +++++ ElectronNET.CLI/Commands/BuildCommand.cs | 2 + .../Commands/StartElectronCommand.cs | 2 + ElectronNET.CLI/ElectronNET.CLI.csproj | 5 + ElectronNET.Host/api/browserWindows.js | 43 +++ ElectronNET.Host/api/browserWindows.js.map | 1 + ElectronNET.Host/api/browserWindows.ts | 47 ++++ ElectronNET.Host/main.js | 44 ++-- .../Controllers/HomeController.cs | 5 + ElectronNET.WebApp/Startup.cs | 24 +- 18 files changed, 847 insertions(+), 34 deletions(-) create mode 100644 ElectronNET.API/BrowserWindow.cs create mode 100644 ElectronNET.API/Entities/BrowserWindowConstructorOptions.cs create mode 100644 ElectronNET.API/Entities/MenuItem.cs create mode 100644 ElectronNET.API/Entities/WebPreferences.cs create mode 100644 ElectronNET.API/Menu.cs create mode 100644 ElectronNET.API/WindowManager.cs create mode 100644 ElectronNET.Host/api/browserWindows.js create mode 100644 ElectronNET.Host/api/browserWindows.js.map create mode 100644 ElectronNET.Host/api/browserWindows.ts diff --git a/ElectronNET.API/App.cs b/ElectronNET.API/App.cs index 715207c..4d4c86a 100644 --- a/ElectronNET.API/App.cs +++ b/ElectronNET.API/App.cs @@ -258,9 +258,9 @@ namespace ElectronNET.API private event Action _accessibilitySupportChanged; - private App() { } + internal App() { } - public static App Instance + internal static App Instance { get { @@ -280,19 +280,6 @@ namespace ElectronNET.API ContractResolver = new CamelCasePropertyNamesContractResolver() }; - // TODO: Auslagern in eigenes Window-Management - public void OpenWindow(int width, int height, bool show) - { - var browserWindowOptions = new BrowserWindowOptions() - { - Height = height, - Width = width, - Show = show - }; - - BridgeConnector.Socket.Emit("createBrowserWindow", JObject.FromObject(browserWindowOptions, _jsonSerializer)); - } - // TODO: Auslagern in eigenes Notification-API public void CreateNotification(NotificationOptions notificationOptions) { diff --git a/ElectronNET.API/BrowserWindow.cs b/ElectronNET.API/BrowserWindow.cs new file mode 100644 index 0000000..55fdaa8 --- /dev/null +++ b/ElectronNET.API/BrowserWindow.cs @@ -0,0 +1,16 @@ +namespace ElectronNET.API +{ + public class BrowserWindow + { + public int Id { get; private set; } + + internal BrowserWindow(int id) { + Id = id; + } + + public void Minimize() + { + BridgeConnector.Socket.Emit("browserWindow-minimize", Id); + } + } +} diff --git a/ElectronNET.API/Electron.cs b/ElectronNET.API/Electron.cs index f7b854d..0a551b0 100644 --- a/ElectronNET.API/Electron.cs +++ b/ElectronNET.API/Electron.cs @@ -11,5 +11,15 @@ /// Control your application's event lifecycle. /// public static App App { get { return App.Instance; } } + + /// + /// Control your windows. + /// + public static WindowManager WindowManager { get { return WindowManager.Instance; } } + + /// + /// Create native application menus and context menus. + /// + public static Menu Menu { get { return Menu.Instance; } } } } diff --git a/ElectronNET.API/Entities/BrowserWindowConstructorOptions.cs b/ElectronNET.API/Entities/BrowserWindowConstructorOptions.cs new file mode 100644 index 0000000..2e6f805 --- /dev/null +++ b/ElectronNET.API/Entities/BrowserWindowConstructorOptions.cs @@ -0,0 +1,244 @@ +namespace ElectronNET.API.Entities +{ + public class BrowserWindowConstructorOptions + { + /// + /// Window's width in pixels. Default is 800. + /// + public int Width { get; set; } + + /// + /// Window's height in pixels. Default is 600. + /// + public int Height { get; set; } + + /// + /// ( if y is used) Window's left offset from screen. Default is to center the + /// window. + /// + public int X { get; set; } + + /// + /// ( if x is used) Window's top offset from screen. Default is to center the + /// window. + /// + public int Y { get; set; } + + /// + /// The width and height would be used as web page's size, which means the actual + /// window's size will include window frame's size and be slightly larger.Default + /// is false. + /// + public bool UseContentSize { get; set; } + + /// + /// Show window in the center of the screen. + /// + public bool Center { get; set; } + + /// + /// Window's minimum width. Default is 0. + /// + public int MinWidth { get; set; } + + /// + /// Window's minimum height. Default is 0. + /// + public int MinHeight { get; set; } + + /// + /// Window's maximum width. Default is no limit. + /// + public int MaxWidth { get; set; } + + /// + /// Window's maximum height. Default is no limit. + /// + public int MaxHeight { get; set; } + + /// + /// Whether window is resizable. Default is true. + /// + public bool Resizable { get; set; } + + /// + /// Whether window is movable. This is not implemented on Linux. Default is true. + /// + public bool Movable { get; set; } + + /// + /// Whether window is minimizable. This is not implemented on Linux. Default is true. + /// + public bool Minimizable { get; set; } + + /// + /// Whether window is maximizable. This is not implemented on Linux. Default is true. + /// + public bool Maximizable { get; set; } + + /// + /// Whether window is closable. This is not implemented on Linux. Default is true. + /// + public bool Closable { get; set; } + + /// + /// Whether the window can be focused. Default is true. On Windows setting + /// focusable: false also implies setting skipTaskbar: true. On Linux setting + /// focusable: false makes the window stop interacting with wm, so the window will + /// always stay on top in all workspaces. + /// + public bool Focusable { get; set; } + + /// + /// Whether the window should always stay on top of other windows. Default is false. + /// + public bool AlwaysOnTop { get; set; } + + /// + /// Whether the window should show in fullscreen. When explicitly set to false the + /// fullscreen button will be hidden or disabled on macOS.Default is false. + /// + public bool Fullscreen { get; set; } + + /// + /// Whether the window can be put into fullscreen mode. On macOS, also whether the + /// maximize/zoom button should toggle full screen mode or maximize window.Default + /// is true. + /// + public bool Fullscreenable { get; set; } + + /// + /// Whether to show the window in taskbar. Default is false. + /// + public bool SkipTaskbar { get; set; } + + /// + /// The kiosk mode. Default is false. + /// + public bool Kiosk { get; set; } + + /// + /// Default window title. Default is "Electron.NET". + /// + public string Title { get; set; } = "Electron.NET"; + + /// + /// The window icon. On Windows it is recommended to use ICO icons to get best + /// visual effects, you can also leave it undefined so the executable's icon will be used. + /// + public string Icon { get; set; } + + /// + /// Whether window should be shown when created. Default is true. + /// + public bool Show { get; set; } + + /// + /// Specify false to create a . Default is true. + /// + public bool Frame { get; set; } + + /// + /// Whether this is a modal window. This only works when the window is a child + /// window.Default is false. + /// + public bool Modal { get; set; } + + /// + /// Whether the web view accepts a single mouse-down event that simultaneously + /// activates the window.Default is false. + /// + public bool AcceptFirstMouse { get; set; } + + /// + /// Whether to hide cursor when typing. Default is false. + /// + public bool DisableAutoHideCursor { get; set; } + + /// + /// Auto hide the menu bar unless the Alt key is pressed. Default is false. + /// + public bool AutoHideMenuBar { get; set; } + + /// + /// Enable the window to be resized larger than screen. Default is false. + /// + public bool EnableLargerThanScreen { get; set; } + + /// + /// Window's background color as Hexadecimal value, like #66CD00 or #FFF or + /// #80FFFFFF (alpha is supported). Default is #FFF (white). + /// + public string BackgroundColor { get; set; } + + /// + /// Whether window should have a shadow. This is only implemented on macOS. Default + /// is true. + /// + public bool HasShadow { get; set; } + + /// + /// Forces using dark theme for the window, only works on some GTK+3 desktop + /// environments.Default is false. + /// + public bool DarkTheme { get; set; } + + /// + /// Makes the window . Default is false. + /// + public bool Transparent { get; set; } + + /// + /// The type of window, default is normal window. See more about this below. + /// + public string Type { get; set; } + + /// + /// The style of window title bar. Default is default. Possible values are: + /// 'default' | 'hidden' | 'hidden-inset' | 'hiddenInset' | 'customButtonsOnHover' + /// + public string TitleBarStyle { get; set; } + + /// + /// Shows the title in the tile bar in full screen mode on macOS for all + /// titleBarStyle options.Default is false. + /// + public bool FullscreenWindowTitle { get; set; } + + /// + /// Use WS_THICKFRAME style for frameless windows on Windows, which adds standard + /// window frame.Setting it to false will remove window shadow and window + /// animations.Default is true. + /// + public bool ThickFrame { get; set; } + + /// + /// Add a type of vibrancy effect to the window, only on macOS. Can be + /// appearance-based, light, dark, titlebar, selection, menu, popover, sidebar, + /// medium-light or ultra-dark. + /// + public string Vibrancy { get; set; } + + /// + /// Controls the behavior on macOS when option-clicking the green stoplight button + /// on the toolbar or by clicking the Window > Zoom menu item.If true, the window + /// will grow to the preferred width of the web page when zoomed, false will cause + /// it to zoom to the width of the screen.This will also affect the behavior when + /// calling maximize() directly.Default is false. + /// + public bool ZoomToPageWidth { get; set; } + + /// + /// Tab group name, allows opening the window as a native tab on macOS 10.12+. + /// Windows with the same tabbing identifier will be grouped together.This also + /// adds a native new tab button to your window's tab bar and allows your app and + /// window to receive the new-window-for-tab event. + /// + public string TabbingIdentifier { get; set; } + + /// + /// Settings of web page's features. + /// + public WebPreferences WebPreferences { get; set; } + } +} diff --git a/ElectronNET.API/Entities/MenuItem.cs b/ElectronNET.API/Entities/MenuItem.cs new file mode 100644 index 0000000..667993b --- /dev/null +++ b/ElectronNET.API/Entities/MenuItem.cs @@ -0,0 +1,72 @@ +using Newtonsoft.Json; +using System; + +namespace ElectronNET.API.Entities +{ + public class MenuItem + { + /// + /// Will be called with click(menuItem, browserWindow, event) when the menu item is + /// clicked. + /// + [JsonIgnore] + public Action Click { get; set; } + + /// + /// Define the action of the menu item, when specified the click property will be + /// ignored. + /// + public string Role { get; set; } + + /// + /// Can be normal, separator, submenu, checkbox or radio. + /// + public string Type { get; set; } + + + public string Label { get; set; } + + + public string Sublabel { get; set; } + + + public string Accelerator { get; set; } + + + public string Icon { get; set; } + + /// + /// If false, the menu item will be greyed out and unclickable. + /// + public bool Enabled { get; set; } + + /// + /// If false, the menu item will be entirely hidden. + /// + public bool Visible { get; set; } + + /// + /// Should only be specified for checkbox or radio type menu items. + /// + public bool Checked { get; set; } + + /// + /// Should be specified for submenu type menu items. If submenu is specified, the + /// type: 'submenu' can be omitted.If the value is not a Menu then it will be + /// automatically converted to one using Menu.buildFromTemplate. + /// + public MenuItem[] Submenu { get; set; } + + /// + /// Unique within a single menu. If defined then it can be used as a reference to + /// this item by the position attribute. + /// + public string Id { get; internal set; } + + /// + /// This field allows fine-grained definition of the specific location within a + /// given menu. + /// + public string Position { get; set; } + } +} diff --git a/ElectronNET.API/Entities/WebPreferences.cs b/ElectronNET.API/Entities/WebPreferences.cs new file mode 100644 index 0000000..8f4e738 --- /dev/null +++ b/ElectronNET.API/Entities/WebPreferences.cs @@ -0,0 +1,186 @@ +namespace ElectronNET.API.Entities +{ + public class WebPreferences + { + /// + /// Whether to enable DevTools. If it is set to false, can not use + /// BrowserWindow.webContents.openDevTools() to open DevTools.Default is true. + /// + public bool DevTools { get; set; } + + /// + /// Whether node integration is enabled. Default is true. + /// + public bool NodeIntegration { get; set; } + + /// + /// Whether node integration is enabled in web workers. Default is false. + /// + public bool NodeIntegrationInWorker { get; set; } + + /// + /// Specifies a script that will be loaded before other scripts run in the page. + /// This script will always have access to node APIs no matter whether node + /// integration is turned on or off.The value should be the absolute file path to + /// the script. When node integration is turned off, the preload script can + /// reintroduce Node global symbols back to the global scope. + /// + public string Preload { get; set; } + + /// + /// If set, this will sandbox the renderer associated with the window, making it + /// compatible with the Chromium OS-level sandbox and disabling the Node.js engine. + /// This is not the same as the nodeIntegration option and the APIs available to the + /// preload script are more limited. Read more about the option.This option is + /// currently experimental and may change or be removed in future Electron releases. + /// + public bool Sandbox { get; set; } + + /// + /// Sets the session used by the page according to the session's partition string. + /// If partition starts with persist:, the page will use a persistent session + /// available to all pages in the app with the same partition.If there is no + /// persist: prefix, the page will use an in-memory session. By assigning the same + /// partition, multiple pages can share the same session.Default is the default + /// session. + /// + public string Partition { get; set; } + + /// + /// The default zoom factor of the page, 3.0 represents 300%. Default is 1.0. + /// + public int ZoomFactor { get; set; } + + /// + /// Enables JavaScript support. Default is true. + /// + public bool Javascript { get; set; } + + /// + /// When false, it will disable the same-origin policy (usually using testing + /// websites by people), and set allowRunningInsecureContent to true if this options + /// has not been set by user.Default is true. + /// + public bool WebSecurity { get; set; } + + /// + /// Allow an https page to run JavaScript, CSS or plugins from http URLs. Default is + /// false. + /// + public bool AllowRunningInsecureContent { get; set; } + + /// + /// Enables image support. Default is true. + /// + public bool Images { get; set; } + + /// + /// Make TextArea elements resizable. Default is true. + /// + public bool TextAreasAreResizable { get; set; } + + /// + /// Enables WebGL support. Default is true. + /// + public bool Webgl { get; set; } + + /// + /// Enables WebAudio support. Default is true. + /// + public bool Webaudio { get; set; } + + /// + /// Whether plugins should be enabled. Default is false. + /// + public bool Plugins { get; set; } + + /// + /// Enables Chromium's experimental features. Default is false. + /// + public bool ExperimentalFeatures { get; set; } + + /// + /// Enables Chromium's experimental canvas features. Default is false. + /// + public bool ExperimentalCanvasFeatures { get; set; } + + /// + /// Enables scroll bounce (rubber banding) effect on macOS. Default is false. + /// + public bool ScrollBounce { get; set; } + + /// + /// A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to + /// enable.The full list of supported feature strings can be found in the file. + /// + public string BlinkFeatures { get; set; } + + /// + /// A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to + /// disable.The full list of supported feature strings can be found in the file. + /// + public string DisableBlinkFeatures { get; set; } + + /// + /// Defaults to 16. + /// + public int DefaultFontSize { get; set; } + + /// + /// Defaults to 13. + /// + public int DefaultMonospaceFontSize { get; set; } + + /// + /// Defaults to 0. + /// + public int MinimumFontSize { get; set; } + + /// + /// Defaults to ISO-8859-1. + /// + public string DefaultEncoding { get; set; } + + /// + /// Whether to throttle animations and timers when the page becomes background. This + /// also affects the[Page Visibility API][#page-visibility]. Defaults to true. + /// + public bool BackgroundThrottling { get; set; } + + /// + /// Whether to enable offscreen rendering for the browser window. Defaults to false. + /// + public bool Offscreen { get; set; } + + /// + /// Whether to run Electron APIs and the specified preload script in a separate + /// JavaScript context.Defaults to false. The context that the preload script runs + /// in will still have full access to the document and window globals but it will + /// use its own set of JavaScript builtins (Array, Object, JSON, etc.) and will be + /// isolated from any changes made to the global environment by the loaded page.The + /// Electron API will only be available in the preload script and not the loaded + /// page. This option should be used when loading potentially untrusted remote + /// content to ensure the loaded content cannot tamper with the preload script and + /// any Electron APIs being used. This option uses the same technique used by . You + /// can access this context in the dev tools by selecting the 'Electron Isolated + /// Context' entry in the combo box at the top of the Console tab. This option is + /// currently experimental and may change or be removed in future Electron releases. + /// + public bool ContextIsolation { get; set; } + + /// + /// Whether to use native window.open(). Defaults to false. This option is currently experimental. + /// + public bool NativeWindowOpen { get; set; } + + /// + /// Whether to enable the . Defaults to the value of the nodeIntegration option. The + /// preload script configured for the will have node integration enabled + /// when it is executed so you should ensure remote/untrusted content is not able to + /// create a tag with a possibly malicious preload script.You can use the + /// will-attach-webview event on to strip away the preload script and to validate or + /// alter the's initial settings. + /// + public bool WebviewTag { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/IpcMain.cs b/ElectronNET.API/IpcMain.cs index 7cf2577..5f3dc62 100644 --- a/ElectronNET.API/IpcMain.cs +++ b/ElectronNET.API/IpcMain.cs @@ -9,9 +9,9 @@ namespace ElectronNET.API { private static IpcMain _ipcMain; - private IpcMain() { } + internal IpcMain() { } - public static IpcMain Instance + internal static IpcMain Instance { get { diff --git a/ElectronNET.API/Menu.cs b/ElectronNET.API/Menu.cs new file mode 100644 index 0000000..4a82028 --- /dev/null +++ b/ElectronNET.API/Menu.cs @@ -0,0 +1,92 @@ +using ElectronNET.API.Entities; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; +using System; +using System.Linq; + +namespace ElectronNET.API +{ + public sealed class Menu + { + private static Menu _menu; + + internal Menu() { } + + internal static Menu Instance + { + get + { + if (_menu == null) + { + _menu = new Menu(); + } + + return _menu; + } + } + + public IReadOnlyCollection Items { get { return _items.AsReadOnly(); } } + private List _items = new List(); + + public void SetApplicationMenu(MenuItem[] menuItems) + { + AddMenuItemsId(menuItems); + BridgeConnector.Socket.Emit("menu-setApplicationMenu", JArray.FromObject(menuItems, _jsonSerializer)); + _items.AddRange(menuItems); + + BridgeConnector.Socket.On("menuItemClicked", (id) => { + MenuItem menuItem = GetMenuItem(_items, id.ToString()); + menuItem?.Click(); + }); + } + + private void AddMenuItemsId(MenuItem[] menuItems) + { + for (int index = 0; index < menuItems.Length; index++) + { + var menuItem = menuItems[index]; + if(menuItem?.Submenu?.Length > 0) + { + AddMenuItemsId(menuItem.Submenu); + } + + if(string.IsNullOrEmpty(menuItem.Role)) + { + menuItem.Id = Guid.NewGuid().ToString(); + } + } + } + + private MenuItem GetMenuItem(List menuItems, string id) + { + MenuItem result = new MenuItem(); + + foreach (var item in menuItems) + { + if(item.Id == id) + { + result = item; + } + else if(item?.Submenu?.Length > 0) + { + var menuItem = GetMenuItem(item.Submenu.ToList(), id); + if(menuItem.Id == id) + { + result = menuItem; + } + } + } + + return result; + } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; + } +} diff --git a/ElectronNET.API/WindowManager.cs b/ElectronNET.API/WindowManager.cs new file mode 100644 index 0000000..9622ef4 --- /dev/null +++ b/ElectronNET.API/WindowManager.cs @@ -0,0 +1,67 @@ +using ElectronNET.API.Entities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ElectronNET.API +{ + public sealed class WindowManager + { + private static WindowManager _windowManager; + + internal WindowManager() { } + + internal static WindowManager Instance + { + get + { + if (_windowManager == null) + { + _windowManager = new WindowManager(); + } + + return _windowManager; + } + } + + public IReadOnlyCollection BrowserWindows { get { return _browserWindows.AsReadOnly(); } } + private List _browserWindows = new List(); + + public async Task CreateWindowAsync(string loadUrl = "http://localhost") + { + return await CreateWindowAsync(new BrowserWindowConstructorOptions(), loadUrl); + } + + public Task CreateWindowAsync(BrowserWindowConstructorOptions options, string loadUrl = "http://localhost") + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("BrowserWindowCreated", (id) => + { + string windowId = id.ToString(); + BrowserWindow browserWindow = new BrowserWindow(int.Parse(windowId)); + _browserWindows.Add(browserWindow); + + taskCompletionSource.SetResult(browserWindow); + }); + + if (loadUrl.ToUpper() == "HTTP://LOCALHOST") + { + loadUrl = $"{loadUrl}:{BridgeSettings.WebPort}"; + } + + BridgeConnector.Socket.Emit("createBrowserWindow", JObject.FromObject(options, _jsonSerializer), loadUrl); + + return taskCompletionSource.Task; + } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; + } +} diff --git a/ElectronNET.CLI/Commands/BuildCommand.cs b/ElectronNET.CLI/Commands/BuildCommand.cs index c240da9..7e3b366 100644 --- a/ElectronNET.CLI/Commands/BuildCommand.cs +++ b/ElectronNET.CLI/Commands/BuildCommand.cs @@ -40,6 +40,8 @@ namespace ElectronNET.CLI.Commands Directory.CreateDirectory(hostApiFolder); } EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "ipc.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "app.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "browserWindows.js", "api."); Console.WriteLine("Start npm install..."); ProcessHelper.CmdExecute("npm install", tempPath); diff --git a/ElectronNET.CLI/Commands/StartElectronCommand.cs b/ElectronNET.CLI/Commands/StartElectronCommand.cs index c0e4995..8dd5159 100644 --- a/ElectronNET.CLI/Commands/StartElectronCommand.cs +++ b/ElectronNET.CLI/Commands/StartElectronCommand.cs @@ -59,6 +59,8 @@ namespace ElectronNET.CLI.Commands Directory.CreateDirectory(hostApiFolder); } EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "ipc.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "app.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "browserWindows.js", "api."); Console.WriteLine("Start npm install..."); ProcessHelper.CmdExecute("npm install", tempPath); diff --git a/ElectronNET.CLI/ElectronNET.CLI.csproj b/ElectronNET.CLI/ElectronNET.CLI.csproj index 3a7efa6..b530400 100644 --- a/ElectronNET.CLI/ElectronNET.CLI.csproj +++ b/ElectronNET.CLI/ElectronNET.CLI.csproj @@ -34,4 +34,9 @@ + + + + + diff --git a/ElectronNET.Host/api/browserWindows.js b/ElectronNET.Host/api/browserWindows.js new file mode 100644 index 0000000..85b812c --- /dev/null +++ b/ElectronNET.Host/api/browserWindows.js @@ -0,0 +1,43 @@ +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +var windows = []; +var ipc; +module.exports = function (socket) { + socket.on('createBrowserWindow', function (options, loadUrl) { + var window = new electron_1.BrowserWindow(options); + window.on('closed', function (sender) { + for (var index = 0; index < windows.length; index++) { + var windowItem = windows[index]; + try { + windowItem.id; + } + catch (error) { + if (error.message === 'Object has been destroyed') { + windows.splice(index, 1); + } + } + } + }); + if (ipc == undefined) { + ipc = require('./ipc')(socket, window); + } + if (loadUrl) { + window.loadURL(loadUrl); + } + windows.push(window); + socket.emit('BrowserWindowCreated', window.id); + }); + socket.on('browserWindow-minimize', function (id) { + getWindowById(id).minimize(); + }); + function getWindowById(id) { + for (var index = 0; index < windows.length; index++) { + var element = windows[index]; + if (element.id == id) { + return element; + } + } + } +}; +//# sourceMappingURL=browserWindows.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/browserWindows.js.map b/ElectronNET.Host/api/browserWindows.js.map new file mode 100644 index 0000000..f1c28ff --- /dev/null +++ b/ElectronNET.Host/api/browserWindows.js.map @@ -0,0 +1 @@ +{"version":3,"file":"browserWindows.js","sourceRoot":"","sources":["browserWindows.ts"],"names":[],"mappings":";;AAAA,qCAAyC;AACzC,IAAI,OAAO,GAA6B,EAAE,CAAA;AAC1C,IAAI,GAAG,CAAC;AAER,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,UAAC,OAAO,EAAE,OAAO;QAC9C,IAAI,MAAM,GAAG,IAAI,wBAAa,CAAC,OAAO,CAAC,CAAC;QAExC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAC,MAAM;YACvB,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;gBAClD,IAAI,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;gBAChC,IAAI,CAAC;oBACD,UAAU,CAAC,EAAE,CAAC;gBAClB,CAAC;gBAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;oBACb,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,2BAA2B,CAAC,CAAC,CAAC;wBAChD,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;oBAC7B,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC;YACnB,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAED,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACV,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,UAAC,EAAE;QACnC,aAAa,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,uBAAuB,EAAU;QAC7B,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YAClD,IAAI,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7B,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBACnB,MAAM,CAAC,OAAO,CAAC;YACnB,CAAC;QACL,CAAC;IACL,CAAC;AACL,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/browserWindows.ts b/ElectronNET.Host/api/browserWindows.ts new file mode 100644 index 0000000..105b9ed --- /dev/null +++ b/ElectronNET.Host/api/browserWindows.ts @@ -0,0 +1,47 @@ +import { BrowserWindow } from "electron"; +let windows: Electron.BrowserWindow[] = [] +let ipc; + +module.exports = (socket: SocketIO.Server) => { + socket.on('createBrowserWindow', (options, loadUrl) => { + let window = new BrowserWindow(options); + + window.on('closed', (sender) => { + for (var index = 0; index < windows.length; index++) { + var windowItem = windows[index]; + try { + windowItem.id; + } catch (error) { + if (error.message === 'Object has been destroyed') { + windows.splice(index, 1); + } + } + } + }); + + // TODO: IPC Lösung für mehrere Fenster finden + if (ipc == undefined) { + ipc = require('./ipc')(socket, window); + } + + if (loadUrl) { + window.loadURL(loadUrl); + } + + windows.push(window); + socket.emit('BrowserWindowCreated', window.id); + }); + + socket.on('browserWindow-minimize', (id) => { + getWindowById(id).minimize(); + }); + + function getWindowById(id: number): Electron.BrowserWindow { + for (var index = 0; index < windows.length; index++) { + var element = windows[index]; + if (element.id == id) { + return element; + } + } + } +} \ No newline at end of file diff --git a/ElectronNET.Host/main.js b/ElectronNET.Host/main.js index e22ac47..0fcca2e 100644 --- a/ElectronNET.Host/main.js +++ b/ElectronNET.Host/main.js @@ -1,9 +1,9 @@ -const { app, BrowserWindow, Notification } = require('electron'); +const { app, Notification, Menu } = require('electron'); const fs = require('fs'); const path = require('path'); const process = require('child_process').spawn; const portfinder = require('detect-port'); -let io, window, apiProcess, loadURL, ipc, appApi; +let io, browserWindows, apiProcess, loadURL, appApi; app.on('ready', () => { portfinder(8000, (error, port) => { @@ -18,17 +18,16 @@ function startSocketApiBridge(port) { io.on('connection', (socket) => { console.log('ASP.NET Core Application connected...'); appApi = require('./api/app')(socket, app); + browserWindows = require('./api/browserWindows')(socket); - socket.on('createBrowserWindow', (options) => { - window = new BrowserWindow(options); - window.loadURL(loadURL); + socket.on('menu-setApplicationMenu', (menuItems) => { + const menu = Menu.buildFromTemplate(menuItems); - window.on('closed', function () { - mainWindow = null; - apiProcess = null; + addMenuItemClickConnector(menu.items, (id) => { + socket.emit("menuItemClicked", id); }); - ipc = require('./api/ipc')(socket, window); + Menu.setApplicationMenu(menu); }); socket.on('createNotification', (options) => { @@ -39,6 +38,19 @@ function startSocketApiBridge(port) { }); } +function addMenuItemClickConnector(menuItems, callback) { + menuItems.forEach((item) => { + if(item.submenu && item.submenu.items.length > 0) { + addMenuItemClickConnector(item.submenu.items, callback); + } + + if("id" in item && item.id) { + item.click = () => { callback(item.id); }; + } + }); +} + + function startAspCoreBackend(electronPort) { portfinder(8000, (error, electronWebPort) => { loadURL = `http://localhost:${electronWebPort}` @@ -68,10 +80,10 @@ app.on('window-all-closed', () => { } }); -app.on('activate', () => { - // On macOS it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (win === null) { - createWindow(); - } -}); \ No newline at end of file +// app.on('activate', () => { +// // On macOS it's common to re-create a window in the app when the +// // dock icon is clicked and there are no other windows open. +// if (window === null) { +// createWindow(); +// } +// }); \ No newline at end of file diff --git a/ElectronNET.WebApp/Controllers/HomeController.cs b/ElectronNET.WebApp/Controllers/HomeController.cs index 1c7bc7c..3dea985 100644 --- a/ElectronNET.WebApp/Controllers/HomeController.cs +++ b/ElectronNET.WebApp/Controllers/HomeController.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using ElectronNET.API; using ElectronNET.API.Entities; +using System.Linq; namespace ElectronNET.WebApp.Controllers { @@ -22,7 +23,11 @@ namespace ElectronNET.WebApp.Controllers { string pathName = await Electron.App.GetPathAsync(PathName.pictures); Electron.IpcMain.Send("GetPathComplete", pathName); + + Electron.WindowManager.BrowserWindows.First().Minimize(); + await Electron.WindowManager.CreateWindowAsync("http://www.google.de"); }); + return View(); } diff --git a/ElectronNET.WebApp/Startup.cs b/ElectronNET.WebApp/Startup.cs index fa2b53a..a1648ec 100644 --- a/ElectronNET.WebApp/Startup.cs +++ b/ElectronNET.WebApp/Startup.cs @@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using System.Linq; +using System.Threading; namespace ElectronNET.WebApp { @@ -36,7 +38,27 @@ namespace ElectronNET.WebApp template: "{controller=Home}/{action=Index}/{id?}"); }); - Electron.App.OpenWindow(800, 600, true); + Bootstrap(); + } + + public async void Bootstrap() + { + Electron.Menu.SetApplicationMenu(new MenuItem[] { + new MenuItem { + Label = "Datei", + Submenu = new MenuItem[] { + new MenuItem { + Label = "Beenden", + Click = () => + { + Electron.App.Exit(); + } + } + } + } + }); + + var browserWindow = await Electron.WindowManager.CreateWindowAsync(); } } } From 08b88e3adf8d26da42b9941f923b34b43b2cb2e8 Mon Sep 17 00:00:00 2001 From: Gregor Biswanger Date: Sun, 15 Oct 2017 17:03:07 +0200 Subject: [PATCH 7/8] implement first dialog-, notification-, tray- and menu-api functions --- ElectronNET.API/App.cs | 6 -- ElectronNET.API/Dialog.cs | 89 ++++++++++++++++++ ElectronNET.API/Electron.cs | 15 +++ ElectronNET.API/Entities/MessageBoxOptions.cs | 84 +++++++++++++++++ ElectronNET.API/Entities/MessageBoxResult.cs | 9 ++ .../Entities/NotificationOptions.cs | 45 +++++++++ ElectronNET.API/Notification.cs | 39 ++++++++ ElectronNET.API/Tray.cs | 39 ++++++++ ElectronNET.CLI/Commands/BuildCommand.cs | 4 + .../Commands/StartElectronCommand.cs | 4 + ElectronNET.CLI/ElectronNET.CLI.csproj | 7 ++ ElectronNET.Host/api/browserWindows.js | 1 + ElectronNET.Host/api/browserWindows.js.map | 2 +- ElectronNET.Host/api/dialog.js | 19 ++++ ElectronNET.Host/api/dialog.js.map | 1 + ElectronNET.Host/api/dialog.ts | 17 ++++ ElectronNET.Host/api/menu.js | 23 +++++ ElectronNET.Host/api/menu.js.map | 1 + ElectronNET.Host/api/menu.ts | 25 +++++ ElectronNET.Host/api/notification.js | 10 ++ ElectronNET.Host/api/notification.js.map | 1 + ElectronNET.Host/api/notification.ts | 8 ++ ElectronNET.Host/api/tray.js | 14 +++ ElectronNET.Host/api/tray.js.map | 1 + ElectronNET.Host/api/tray.ts | 14 +++ ElectronNET.Host/main.js | 37 ++------ ElectronNET.WebApp/Assets/electron.ico | Bin 0 -> 279958 bytes ElectronNET.WebApp/Assets/electron_32x32.png | Bin 0 -> 1281 bytes .../Controllers/HomeController.cs | 6 +- ElectronNET.WebApp/ElectronNET.WebApp.csproj | 10 ++ ElectronNET.WebApp/Startup.cs | 24 ++++- ElectronNET.WebApp/Views/Home/Index.cshtml | 1 - 32 files changed, 511 insertions(+), 45 deletions(-) create mode 100644 ElectronNET.API/Dialog.cs create mode 100644 ElectronNET.API/Entities/MessageBoxOptions.cs create mode 100644 ElectronNET.API/Entities/MessageBoxResult.cs create mode 100644 ElectronNET.API/Notification.cs create mode 100644 ElectronNET.API/Tray.cs create mode 100644 ElectronNET.Host/api/dialog.js create mode 100644 ElectronNET.Host/api/dialog.js.map create mode 100644 ElectronNET.Host/api/dialog.ts create mode 100644 ElectronNET.Host/api/menu.js create mode 100644 ElectronNET.Host/api/menu.js.map create mode 100644 ElectronNET.Host/api/menu.ts create mode 100644 ElectronNET.Host/api/notification.js create mode 100644 ElectronNET.Host/api/notification.js.map create mode 100644 ElectronNET.Host/api/notification.ts create mode 100644 ElectronNET.Host/api/tray.js create mode 100644 ElectronNET.Host/api/tray.js.map create mode 100644 ElectronNET.Host/api/tray.ts create mode 100644 ElectronNET.WebApp/Assets/electron.ico create mode 100644 ElectronNET.WebApp/Assets/electron_32x32.png diff --git a/ElectronNET.API/App.cs b/ElectronNET.API/App.cs index 4d4c86a..e4849ad 100644 --- a/ElectronNET.API/App.cs +++ b/ElectronNET.API/App.cs @@ -280,12 +280,6 @@ namespace ElectronNET.API ContractResolver = new CamelCasePropertyNamesContractResolver() }; - // TODO: Auslagern in eigenes Notification-API - public void CreateNotification(NotificationOptions notificationOptions) - { - BridgeConnector.Socket.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer)); - } - /// /// Try to close all windows. The before-quit event will be emitted first. If all /// windows are successfully closed, the will-quit event will be emitted and by diff --git a/ElectronNET.API/Dialog.cs b/ElectronNET.API/Dialog.cs new file mode 100644 index 0000000..f47afac --- /dev/null +++ b/ElectronNET.API/Dialog.cs @@ -0,0 +1,89 @@ +using ElectronNET.API.Entities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using System; +using System.Threading.Tasks; + +namespace ElectronNET.API +{ + public sealed class Dialog + { + private static Dialog _dialog; + + internal Dialog() { } + + internal static Dialog Instance + { + get + { + if (_dialog == null) + { + _dialog = new Dialog(); + } + + return _dialog; + } + } + + /// + /// Shows a message box, it will block the process until the message box is closed. + /// It returns the index of the clicked button. The browserWindow argument allows + /// the dialog to attach itself to a parent window, making it modal. If a callback + /// is passed, the dialog will not block the process.The API call will be + /// asynchronous and the result will be passed via callback(response). + /// + /// + /// The API call will be asynchronous and the result will be passed via MessageBoxResult. + public async Task ShowMessageBoxAsync(MessageBoxOptions messageBoxOptions) + { + return await ShowMessageBoxAsync(null, messageBoxOptions); + } + + /// + /// Shows a message box, it will block the process until the message box is closed. + /// It returns the index of the clicked button. If a callback + /// is passed, the dialog will not block the process. + /// + /// The browserWindow argument allows the dialog to attach itself to a parent window, making it modal. + /// + /// The API call will be asynchronous and the result will be passed via MessageBoxResult. + public Task ShowMessageBoxAsync(BrowserWindow browserWindow, MessageBoxOptions messageBoxOptions) + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("showMessageBoxComplete", (args) => + { + BridgeConnector.Socket.Off("showMessageBoxComplete"); + + var result = ((JArray)args); + + taskCompletionSource.SetResult(new MessageBoxResult + { + Response = (int)result.First, + CheckboxChecked = (bool)result.Last + }); + + }); + + if (browserWindow == null) + { + BridgeConnector.Socket.Emit("showMessageBox", JObject.FromObject(messageBoxOptions, _jsonSerializer)); + } else + { + BridgeConnector.Socket.Emit("showMessageBox", + JObject.FromObject(browserWindow, _jsonSerializer), + JObject.FromObject(messageBoxOptions, _jsonSerializer)); + } + + return taskCompletionSource.Task; + } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; + } +} diff --git a/ElectronNET.API/Electron.cs b/ElectronNET.API/Electron.cs index 0a551b0..6002677 100644 --- a/ElectronNET.API/Electron.cs +++ b/ElectronNET.API/Electron.cs @@ -21,5 +21,20 @@ /// Create native application menus and context menus. /// public static Menu Menu { get { return Menu.Instance; } } + + /// + /// Display native system dialogs for opening and saving files, alerting, etc. + /// + public static Dialog Dialog { get { return Dialog.Instance; } } + + /// + /// Create OS desktop notifications + /// + public static Notification Notification { get { return Notification.Instance; } } + + /// + /// Add icons and context menus to the system’s notification area. + /// + public static Tray Tray { get { return Tray.Instance; } } } } diff --git a/ElectronNET.API/Entities/MessageBoxOptions.cs b/ElectronNET.API/Entities/MessageBoxOptions.cs new file mode 100644 index 0000000..960471c --- /dev/null +++ b/ElectronNET.API/Entities/MessageBoxOptions.cs @@ -0,0 +1,84 @@ +namespace ElectronNET.API.Entities +{ + public class MessageBoxOptions + { + /// + /// Can be "none", "info", "error", "question" or "warning". On Windows, "question" + /// displays the same icon as "info", unless you set an icon using the "icon" + /// option.On macOS, both "warning" and "error" display the same warning icon. + /// + public string Type { get; set; } + + /// + /// Array of texts for buttons. On Windows, an empty array will result in one button + /// labeled "OK". + /// + public string[] Buttons { get; set; } + + /// + /// Index of the button in the buttons array which will be selected by default when + /// the message box opens. + /// + public int DefaultId { get; set; } + + /// + /// Title of the message box, some platforms will not show it. + /// + public string Title { get; set; } + + /// + /// Content of the message box. + /// + public string Message { get; set; } + + /// + /// Extra information of the message. + /// + public string Detail { get; set; } + + /// + /// If provided, the message box will include a checkbox with the given label. The + /// checkbox state can be inspected only when using callback. + /// + public string CheckboxLabel { get; set; } + + /// + /// Initial checked state of the checkbox. false by default. + /// + public bool CheckboxChecked { get; set; } + + public string Icon { get; set; } + + /// + /// The index of the button to be used to cancel the dialog, via the Esc key. By + /// default this is assigned to the first button with "cancel" or "no" as the label. + /// If no such labeled buttons exist and this option is not set, 0 will be used as + /// the return value or callback response. This option is ignored on Windows. + /// + public int CancelId { get; set; } + + /// + /// On Windows Electron will try to figure out which one of the buttons are common + /// buttons(like "Cancel" or "Yes"), and show the others as command links in the + /// dialog.This can make the dialog appear in the style of modern Windows apps. If + /// you don't like this behavior, you can set noLink to true. + /// + public bool NoLink { get; set; } + + /// + /// Normalize the keyboard access keys across platforms. Default is false. Enabling + /// this assumes & is used in the button labels for the placement of the keyboard + /// shortcut access key and labels will be converted so they work correctly on each + /// platform, & characters are removed on macOS, converted to _ on Linux, and left + /// untouched on Windows.For example, a button label of Vie&w will be converted to + /// Vie_w on Linux and View on macOS and can be selected via Alt-W on Windows and + /// Linux. + /// + public bool NormalizeAccessKeys { get; set; } + + public MessageBoxOptions(string message) + { + Message = message; + } + } +} diff --git a/ElectronNET.API/Entities/MessageBoxResult.cs b/ElectronNET.API/Entities/MessageBoxResult.cs new file mode 100644 index 0000000..20a19b6 --- /dev/null +++ b/ElectronNET.API/Entities/MessageBoxResult.cs @@ -0,0 +1,9 @@ +namespace ElectronNET.API.Entities +{ + public class MessageBoxResult + { + public int Response { get; set; } + + public bool CheckboxChecked { get; set; } + } +} diff --git a/ElectronNET.API/Entities/NotificationOptions.cs b/ElectronNET.API/Entities/NotificationOptions.cs index b58e954..efa127f 100644 --- a/ElectronNET.API/Entities/NotificationOptions.cs +++ b/ElectronNET.API/Entities/NotificationOptions.cs @@ -2,7 +2,52 @@ { public class NotificationOptions { + /// + /// A title for the notification, which will be shown at the top of the notification + /// window when it is shown + /// public string Title { get; set; } + + /// + /// The body text of the notification, which will be displayed below the title or + /// subtitle + /// public string Body { get; set; } + + /// + /// A subtitle for the notification, which will be displayed below the title. + /// + public string Subtitle { get; set; } + + /// + /// Whether or not to emit an OS notification noise when showing the notification + /// + public bool Silent { get; set; } + + /// + /// An icon to use in the notification + /// + public string Icon { get; set; } + + /// + /// Whether or not to add an inline reply option to the notification. + /// + public bool HasReply { get; set; } + + /// + /// The placeholder to write in the inline reply input field. + /// + public string ReplyPlaceholder { get; set; } + + /// + /// The name of the sound file to play when the notification is shown. + /// + public string Sound { get; set; } + + public NotificationOptions(string title, string body) + { + Title = title; + Body = body; + } } } diff --git a/ElectronNET.API/Notification.cs b/ElectronNET.API/Notification.cs new file mode 100644 index 0000000..ad396d0 --- /dev/null +++ b/ElectronNET.API/Notification.cs @@ -0,0 +1,39 @@ +using ElectronNET.API.Entities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +namespace ElectronNET.API +{ + public sealed class Notification + { + private static Notification _notification; + + internal Notification() { } + + internal static Notification Instance + { + get + { + if (_notification == null) + { + _notification = new Notification(); + } + + return _notification; + } + } + + public void Show(NotificationOptions notificationOptions) + { + BridgeConnector.Socket.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer)); + } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; + } +} diff --git a/ElectronNET.API/Tray.cs b/ElectronNET.API/Tray.cs new file mode 100644 index 0000000..417812e --- /dev/null +++ b/ElectronNET.API/Tray.cs @@ -0,0 +1,39 @@ +using ElectronNET.API.Entities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +namespace ElectronNET.API +{ + public sealed class Tray + { + private static Tray _tray; + + internal Tray() { } + + internal static Tray Instance + { + get + { + if (_tray == null) + { + _tray = new Tray(); + } + + return _tray; + } + } + + public void Show(string image, MenuItem[] menuItems) + { + BridgeConnector.Socket.Emit("create-tray", image, JArray.FromObject(menuItems, _jsonSerializer)); + } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; + } +} diff --git a/ElectronNET.CLI/Commands/BuildCommand.cs b/ElectronNET.CLI/Commands/BuildCommand.cs index 7e3b366..66503b9 100644 --- a/ElectronNET.CLI/Commands/BuildCommand.cs +++ b/ElectronNET.CLI/Commands/BuildCommand.cs @@ -42,6 +42,10 @@ namespace ElectronNET.CLI.Commands EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "ipc.js", "api."); EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "app.js", "api."); EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "browserWindows.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "dialog.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "menu.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "notification.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "tray.js", "api."); Console.WriteLine("Start npm install..."); ProcessHelper.CmdExecute("npm install", tempPath); diff --git a/ElectronNET.CLI/Commands/StartElectronCommand.cs b/ElectronNET.CLI/Commands/StartElectronCommand.cs index 8dd5159..474e1da 100644 --- a/ElectronNET.CLI/Commands/StartElectronCommand.cs +++ b/ElectronNET.CLI/Commands/StartElectronCommand.cs @@ -61,6 +61,10 @@ namespace ElectronNET.CLI.Commands EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "ipc.js", "api."); EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "app.js", "api."); EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "browserWindows.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "dialog.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "menu.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "notification.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "tray.js", "api."); Console.WriteLine("Start npm install..."); ProcessHelper.CmdExecute("npm install", tempPath); diff --git a/ElectronNET.CLI/ElectronNET.CLI.csproj b/ElectronNET.CLI/ElectronNET.CLI.csproj index b530400..994d6f5 100644 --- a/ElectronNET.CLI/ElectronNET.CLI.csproj +++ b/ElectronNET.CLI/ElectronNET.CLI.csproj @@ -39,4 +39,11 @@ + + + + + + + diff --git a/ElectronNET.Host/api/browserWindows.js b/ElectronNET.Host/api/browserWindows.js index 85b812c..39efbb2 100644 --- a/ElectronNET.Host/api/browserWindows.js +++ b/ElectronNET.Host/api/browserWindows.js @@ -19,6 +19,7 @@ module.exports = function (socket) { } } }); + // TODO: IPC Lösung für mehrere Fenster finden if (ipc == undefined) { ipc = require('./ipc')(socket, window); } diff --git a/ElectronNET.Host/api/browserWindows.js.map b/ElectronNET.Host/api/browserWindows.js.map index f1c28ff..3e67861 100644 --- a/ElectronNET.Host/api/browserWindows.js.map +++ b/ElectronNET.Host/api/browserWindows.js.map @@ -1 +1 @@ -{"version":3,"file":"browserWindows.js","sourceRoot":"","sources":["browserWindows.ts"],"names":[],"mappings":";;AAAA,qCAAyC;AACzC,IAAI,OAAO,GAA6B,EAAE,CAAA;AAC1C,IAAI,GAAG,CAAC;AAER,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,UAAC,OAAO,EAAE,OAAO;QAC9C,IAAI,MAAM,GAAG,IAAI,wBAAa,CAAC,OAAO,CAAC,CAAC;QAExC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAC,MAAM;YACvB,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;gBAClD,IAAI,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;gBAChC,IAAI,CAAC;oBACD,UAAU,CAAC,EAAE,CAAC;gBAClB,CAAC;gBAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;oBACb,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,2BAA2B,CAAC,CAAC,CAAC;wBAChD,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;oBAC7B,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC;YACnB,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAED,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACV,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,UAAC,EAAE;QACnC,aAAa,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,uBAAuB,EAAU;QAC7B,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YAClD,IAAI,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7B,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBACnB,MAAM,CAAC,OAAO,CAAC;YACnB,CAAC;QACL,CAAC;IACL,CAAC;AACL,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"browserWindows.js","sourceRoot":"","sources":["browserWindows.ts"],"names":[],"mappings":";;AAAA,qCAAyC;AACzC,IAAI,OAAO,GAA6B,EAAE,CAAA;AAC1C,IAAI,GAAG,CAAC;AAER,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,UAAC,OAAO,EAAE,OAAO;QAC9C,IAAI,MAAM,GAAG,IAAI,wBAAa,CAAC,OAAO,CAAC,CAAC;QAExC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAC,MAAM;YACvB,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;gBAClD,IAAI,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;gBAChC,IAAI,CAAC;oBACD,UAAU,CAAC,EAAE,CAAC;gBAClB,CAAC;gBAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;oBACb,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,2BAA2B,CAAC,CAAC,CAAC;wBAChD,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;oBAC7B,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,+CAA+C;QAC/C,EAAE,CAAC,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC;YACnB,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAED,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACV,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,UAAC,EAAE;QACnC,aAAa,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,uBAAuB,EAAU;QAC7B,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YAClD,IAAI,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7B,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBACnB,MAAM,CAAC,OAAO,CAAC;YACnB,CAAC;QACL,CAAC;IACL,CAAC;AACL,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/dialog.js b/ElectronNET.Host/api/dialog.js new file mode 100644 index 0000000..e3f106e --- /dev/null +++ b/ElectronNET.Host/api/dialog.js @@ -0,0 +1,19 @@ +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +module.exports = function (socket) { + socket.on('showMessageBox', function (browserWindow, options) { + if ("id" in browserWindow) { + var window = electron_1.BrowserWindow.fromId(browserWindow.id); + electron_1.dialog.showMessageBox(window, options, function (response, checkboxChecked) { + socket.emit('showMessageBoxComplete', response, checkboxChecked); + }); + } + else { + electron_1.dialog.showMessageBox(browserWindow, function (response, checkboxChecked) { + socket.emit('showMessageBoxComplete', response, checkboxChecked); + }); + } + }); +}; +//# sourceMappingURL=dialog.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/dialog.js.map b/ElectronNET.Host/api/dialog.js.map new file mode 100644 index 0000000..f7a9d8d --- /dev/null +++ b/ElectronNET.Host/api/dialog.js.map @@ -0,0 +1 @@ +{"version":3,"file":"dialog.js","sourceRoot":"","sources":["dialog.ts"],"names":[],"mappings":";;AAAA,qCAAiD;AAEjD,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,UAAC,aAAa,EAAE,OAAO;QAC/C,EAAE,CAAA,CAAC,IAAI,IAAI,aAAa,CAAC,CAAC,CAAC;YACvB,IAAI,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAEpD,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC7D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;YACrE,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,IAAI,CAAC,CAAC;YACJ,iBAAM,CAAC,cAAc,CAAC,aAAa,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC3D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;YACrE,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/dialog.ts b/ElectronNET.Host/api/dialog.ts new file mode 100644 index 0000000..c445e51 --- /dev/null +++ b/ElectronNET.Host/api/dialog.ts @@ -0,0 +1,17 @@ +import { BrowserWindow, dialog } from "electron"; + +module.exports = (socket: SocketIO.Server) => { + socket.on('showMessageBox', (browserWindow, options) => { + if("id" in browserWindow) { + var window = BrowserWindow.fromId(browserWindow.id); + + dialog.showMessageBox(window, options, (response, checkboxChecked) => { + socket.emit('showMessageBoxComplete', response, checkboxChecked); + }); + } else { + dialog.showMessageBox(browserWindow, (response, checkboxChecked) => { + socket.emit('showMessageBoxComplete', response, checkboxChecked); + }); + } + }); +} \ No newline at end of file diff --git a/ElectronNET.Host/api/menu.js b/ElectronNET.Host/api/menu.js new file mode 100644 index 0000000..4eb7eb3 --- /dev/null +++ b/ElectronNET.Host/api/menu.js @@ -0,0 +1,23 @@ +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +module.exports = function (socket) { + socket.on('menu-setApplicationMenu', function (menuItems) { + var menu = electron_1.Menu.buildFromTemplate(menuItems); + addMenuItemClickConnector(menu.items, function (id) { + socket.emit("menuItemClicked", id); + }); + electron_1.Menu.setApplicationMenu(menu); + }); + function addMenuItemClickConnector(menuItems, callback) { + menuItems.forEach(function (item) { + if (item.submenu && item.submenu.items.length > 0) { + addMenuItemClickConnector(item.submenu.items, callback); + } + if ("id" in item && item.id) { + item.click = function () { callback(item.id); }; + } + }); + } +}; +//# sourceMappingURL=menu.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/menu.js.map b/ElectronNET.Host/api/menu.js.map new file mode 100644 index 0000000..94adcce --- /dev/null +++ b/ElectronNET.Host/api/menu.js.map @@ -0,0 +1 @@ +{"version":3,"file":"menu.js","sourceRoot":"","sources":["menu.ts"],"names":[],"mappings":";;AAAA,qCAAgC;AAEhC,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,UAAC,SAAS;QAC3C,IAAM,IAAI,GAAG,eAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAE/C,yBAAyB,CAAC,IAAI,CAAC,KAAK,EAAE,UAAC,EAAE;YACrC,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,eAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,mCAAmC,SAAS,EAAE,QAAQ;QAClD,SAAS,CAAC,OAAO,CAAC,UAAC,IAAI;YACnB,EAAE,CAAA,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC/C,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC5D,CAAC;YAED,EAAE,CAAA,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACzB,IAAI,CAAC,KAAK,GAAG,cAAQ,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9C,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;AACL,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/menu.ts b/ElectronNET.Host/api/menu.ts new file mode 100644 index 0000000..1d96344 --- /dev/null +++ b/ElectronNET.Host/api/menu.ts @@ -0,0 +1,25 @@ +import { Menu } from "electron"; + +module.exports = (socket: SocketIO.Server) => { + socket.on('menu-setApplicationMenu', (menuItems) => { + const menu = Menu.buildFromTemplate(menuItems); + + addMenuItemClickConnector(menu.items, (id) => { + socket.emit("menuItemClicked", id); + }); + + Menu.setApplicationMenu(menu); + }); + + function addMenuItemClickConnector(menuItems, callback) { + menuItems.forEach((item) => { + if(item.submenu && item.submenu.items.length > 0) { + addMenuItemClickConnector(item.submenu.items, callback); + } + + if("id" in item && item.id) { + item.click = () => { callback(item.id); }; + } + }); + } +} \ No newline at end of file diff --git a/ElectronNET.Host/api/notification.js b/ElectronNET.Host/api/notification.js new file mode 100644 index 0000000..8ce83a7 --- /dev/null +++ b/ElectronNET.Host/api/notification.js @@ -0,0 +1,10 @@ +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +module.exports = function (socket) { + socket.on('createNotification', function (options) { + var notification = new electron_1.Notification(options); + notification.show(); + }); +}; +//# sourceMappingURL=notification.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/notification.js.map b/ElectronNET.Host/api/notification.js.map new file mode 100644 index 0000000..b7d3ab1 --- /dev/null +++ b/ElectronNET.Host/api/notification.js.map @@ -0,0 +1 @@ +{"version":3,"file":"notification.js","sourceRoot":"","sources":["notification.ts"],"names":[],"mappings":";;AAAA,qCAAwC;AAExC,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,UAAC,OAAO;QACpC,IAAM,YAAY,GAAG,IAAI,uBAAY,CAAC,OAAO,CAAC,CAAC;QAC/C,YAAY,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/notification.ts b/ElectronNET.Host/api/notification.ts new file mode 100644 index 0000000..2264373 --- /dev/null +++ b/ElectronNET.Host/api/notification.ts @@ -0,0 +1,8 @@ +import { Notification } from "electron"; + +module.exports = (socket: SocketIO.Server) => { + socket.on('createNotification', (options) => { + const notification = new Notification(options); + notification.show(); + }); +} \ No newline at end of file diff --git a/ElectronNET.Host/api/tray.js b/ElectronNET.Host/api/tray.js new file mode 100644 index 0000000..8cc6e97 --- /dev/null +++ b/ElectronNET.Host/api/tray.js @@ -0,0 +1,14 @@ +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +var path = require('path'); +var tray; +module.exports = function (socket) { + socket.on('create-tray', function (image, menuItems) { + var menu = electron_1.Menu.buildFromTemplate(menuItems); + var imagePath = path.join(__dirname.replace('api', ''), 'bin', image); + tray = new electron_1.Tray(imagePath); + tray.setContextMenu(menu); + }); +}; +//# sourceMappingURL=tray.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/tray.js.map b/ElectronNET.Host/api/tray.js.map new file mode 100644 index 0000000..6ceee68 --- /dev/null +++ b/ElectronNET.Host/api/tray.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tray.js","sourceRoot":"","sources":["tray.ts"],"names":[],"mappings":";;AAAA,qCAAsC;AACtC,IAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC7B,IAAI,IAAI,CAAC;AAET,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,UAAC,KAAK,EAAE,SAAS;QACtC,IAAM,IAAI,GAAG,eAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAE/C,IAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAExE,IAAI,GAAG,IAAI,eAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/tray.ts b/ElectronNET.Host/api/tray.ts new file mode 100644 index 0000000..a0ec1c8 --- /dev/null +++ b/ElectronNET.Host/api/tray.ts @@ -0,0 +1,14 @@ +import { Menu, Tray } from "electron"; +const path = require('path'); +let tray; + +module.exports = (socket: SocketIO.Server) => { + socket.on('create-tray', (image, menuItems) => { + const menu = Menu.buildFromTemplate(menuItems); + + const imagePath = path.join(__dirname.replace('api', ''), 'bin', image); + + tray = new Tray(imagePath); + tray.setContextMenu(menu); + }); +} \ No newline at end of file diff --git a/ElectronNET.Host/main.js b/ElectronNET.Host/main.js index 0fcca2e..4c01f71 100644 --- a/ElectronNET.Host/main.js +++ b/ElectronNET.Host/main.js @@ -1,9 +1,9 @@ -const { app, Notification, Menu } = require('electron'); +const { app } = require('electron'); const fs = require('fs'); const path = require('path'); const process = require('child_process').spawn; const portfinder = require('detect-port'); -let io, browserWindows, apiProcess, loadURL, appApi; +let io, browserWindows, apiProcess, loadURL, appApi, menu, dialog, notification, tray; app.on('ready', () => { portfinder(8000, (error, port) => { @@ -17,36 +17,13 @@ function startSocketApiBridge(port) { io.on('connection', (socket) => { console.log('ASP.NET Core Application connected...'); + appApi = require('./api/app')(socket, app); browserWindows = require('./api/browserWindows')(socket); - - socket.on('menu-setApplicationMenu', (menuItems) => { - const menu = Menu.buildFromTemplate(menuItems); - - addMenuItemClickConnector(menu.items, (id) => { - socket.emit("menuItemClicked", id); - }); - - Menu.setApplicationMenu(menu); - }); - - socket.on('createNotification', (options) => { - const notification = new Notification(options); - notification.show(); - }); - - }); -} - -function addMenuItemClickConnector(menuItems, callback) { - menuItems.forEach((item) => { - if(item.submenu && item.submenu.items.length > 0) { - addMenuItemClickConnector(item.submenu.items, callback); - } - - if("id" in item && item.id) { - item.click = () => { callback(item.id); }; - } + menu = require('./api/menu')(socket); + dialog = require('./api/dialog')(socket); + notification = require('./api/notification')(socket); + tray = require('./api/tray')(socket); }); } diff --git a/ElectronNET.WebApp/Assets/electron.ico b/ElectronNET.WebApp/Assets/electron.ico new file mode 100644 index 0000000000000000000000000000000000000000..3a104492d2e1a089e2e03b0eba36a53c94ccd009 GIT binary patch literal 279958 zcmZQzU}Rup00Bk@2?h-XW`;Bd28MRLz_7ZgMa{ppTWw&py$BCpaHRffq@AmE&<{(FfcGfX$0kD zq(6#ALtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU z1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0q zLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$( zGz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!n zMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ON zU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU|>VQ(NGT$8d$fD z+CCZrGz$Seu*smr4`yJ$cHGerq71t%9*v_?qaiTZLI6_ELtF)CI~(ge8ymooK9~y@ zF)@I!os9J1is1~1A*1AI2n@LpfE4M@#`>&>G7Uzh*;vi#qdW&gh{`v*q< zzb^m&YQgO@eLGiI_SD9PyIEUlX<4XiI2!7?m>4)2K|2l*lZQ%ZjJk8=gaABcIvVOh z%6wB*buUZf#=@|Zds;p}pZD+U@_*k~{QI!v*XxBpUoQCh3XC9(mkWNrT=4tN!v7za z{`;}=@2912AI{#rrm85}*Ir-OLPHbMH9)pv&Kg}J&0q+blTaoC6Ls0Rmg2*AfDA+2<4EiD%lg9Q__zP?)U|I4x;FQF+P zp6q{s8aa#pe_rQxD}TKP zmEONzFZ}gp;V)Pk{`YCw|8Fb4Jezm%Q2Vy!0Y@*c3mnKV$Wr!rg1CA6%U9 z_v6z4UzY#~!3Rvg{l02(F**EMiw z4Cb&Jb=xot0Z6KYl>9Cx24-sNH95iGUM~3eVF{$U0&VcTnE&tN(!U><9@tP@l;mTt zuVbOEVWp*IudnN3V&Gn-c}|ZmC;v^cm4mm9Mo}s0qQ4dh{{L?)?wy|y<700%ngxtdW(kK`G8@d^ftUQ|>KZMj5kKB8`txq_kC*d*y;=DC z%|gi7=KpUiuAk|v$Oy2}(Kb`la5B;Z4`UiY`~@)qPJ(BhPAO`+tAE@Xy;t;MuE1zuthlz5hNg{qS^dV_}%N8ffMhobsWww~$iW5qGWa zXb8#_kV*lPYb-T2?e%q6%*g%!W!bMci$Fs|Z$RB@P($d;vU?XNhPhc=Ya_bD5Er63 zdz3pG0+fdUBxylPesgt=^l-P2Pv-swHG39-x~SlBp8ubgKEF9FGt%8mOY0WIc) zBzuqph*;hXu?137KaW)ezdfJ#|J(9QM>}0j4ZsWM;C6vB z5y(2+8EsT{Gz7>90mvkmt*&;UgT;eOlm35N3hD4d208zKTK4Gjq-Y;Ib2SZDQ%dJ* zAsGQeIvX1pE34Jy1bu(C;P+c_EdZWG1~r7fuh_b@%tl)q?j490ICqpW8UmDu0Ju#8 zDpws0bR7+J4{vMy|7`_0@q=b{f4rCvO8i$RM|j#=YG^_dKcd(tw;YC~WC-bMYG9(G z)>It!^X=l_(1s9b`NWTx3;usuKB*jzA;8gG9a{2Jp7L)=I4P|snH_& zfvXC^!v>saP717!R1MQc4l^Zpp64ghS0tMwE|((oDmxW zko50ttZ%2M<85tn_xyzapO(Tqp`hl+=Ve{hF=ndj)N1x%4{D?&ZL6cbYfaVvZ_9sx z=LLShSp*sm`nF>I!eR>z=o&d{RY2GsLvD+1)U=@$0+7TH>GoP^Xs(%C2y6C(r=q~K zv1{iQS!jR;`5|6|Xo0Y)NWzCkAvw|0()i}7-v6JM{Q%DngZ31>TKMPvlFG~gGqurH z5U8g{#lRoIMuCRzL8JW|8hNpvKfo;?$UGOQ$o;(R>hUf&Gehv2Oh}Uh)KH;Oa6xhw zq!nbLp_vlm{OQ>|$Z*i_H=wmf|35Che_?{RjVZJh1l~$dBgc#yJM2RMlHQz*K&$#) zObjj`=>)B6elZ`?>-_Zw)a=iQaI-+^b<(!vfj9}01u-qv|8+TNg%5ZU4kV?`>Pe&js3Rl`K$`cChI(iB zw?P&NfpX=`1^+)Sy>zn2Ru?fHK%?*(HFkK10C>~|bUu%*u1=t%`J?Mo|Gr=R^VI^- zc+1NL|35Fga;($E#K6Ho7ZMKeWC`KYk_0ueAXR{smS%FW^QY(Yf4yDw{rUW_Pv`#s zvi$Ucb}KDSS~`HXb=;`S2RsBIi5Zf{%+%BuP0of7_=9?yuNVG)x40N}ydAP)fu1gh z^vccE)j@UBw-x`sE&u;x<&O_bsIQ!4r#XnzxX1hS$-ft_mEGvf`%K%C6u%v~>5QqY*lMoxhq@JCwPJKb> z-t{%R)>M_H`B`gg!O}LUOAfBcK)rYf11yC_93l%gkbb_Pda#TdNO=fA(g39JH&@qK zIV&GD*#zzOF8KRy@wpqlM!gsy{)c}wYHX-nmRaz zgJi)A@C;l`up?Jf0|>af79a`0+1S8J zTPw=T_SLZU5{rYay26BRWxHFZl(O>1p!J3Sppu6F?) zMhY?n63dVPfwLhDG!j`2n$#$lq7Z%P|pk+vgK~5I&{tlUu?)mXvC8>VpS%LLMVeOUCt>sY-#o;yip_Ms7<(UEb3En9o zE)kx#es-n~2B58HW@_qInwqw{+71Swt(xF13}`hSC<8(q2|t?-%mrmn@X#5E0T!WV zq>LN`;pGXO0Xs7pa(FT*RN+!^2DQQmGRGR|VDaki3~=EOZuvi-|Nr~Sc@r|t)WC&5 ztOY_XOX21~yaP$+$ZT*E1>}A3Il4M#YU(B`YBr#=AoRVhO_G9~8Vf_`_h)QbR(5b( z!}-GLL3Qp7-aJ%$a(NX ziXgJc>LI#DN#aAm1#})VXqeGPN84IU%Lg@3#WC{6R$`EAIjV$gnM z@S$5jUd%^Bpfk8Yr)+_?u>Je6>?vqrMMjv6KrN6EgOHH31Wi=b z+|3O`oGs)09MU7)BE4(_oh%#;^^BEOA*TYu6F)>3*?|Vq4&H%iudnN2ZglNL4`_i6 z_|PiQI=8PYmQBw^UR~n^R!p{aNE-FPZ3caCBOjCnz$GfE1@2&=Yp$+gtgPm2tRLoP zU7a1YY5mtn0~kTOEMG7D{dUp6PfP#*T=nzKqPv$SZCX;673J<|pl70@ zW)E%`IT`7jtAjchUREZdu2u;Fj%nep5gs-H4(4`xI>ySXkXplW^zdF1mkUCAVYYgp z14GinTo+BrzIdqP^OL!M-Yx$7VacyIi{9Rwd1PzD)V8Eh7b`P0b;w9KDD{$P5mE|7 zlu@A7KI$6PIl+*`4@x}X%~oHY&WrW2x6#pt*Y9ZBN#(*F3`zWuBOoBXR~v0@$n5~0 z7RE)%zO#GNPwZ^^{A4bui2l3`bbRzj&^fx_pU;CFllTjs&cV~u5I6sXBx&$5bU$8z zj#va8wD^2Jf`rrvUX?o8mOn@+Iwql zt%IBEL92aUf>!&0#{9pp+_9o}HeKsfwDFmR6{% zRYPIeuGN)yFHipdasem=zAguCw0{LUAQ4iwL)QF&PJ{sMg+Yk_1gGBLZx;P~zvTbt zrJ#n;w-x`tul)aW)&F0s|NjP~U#tKBS_MKsR{s080(27lCs1PvQW5+B*#bGL7gS|{ z1i^Xa*XxC#VE?-O>hbQ@(ukd_tA4&)407AYr4VO8@*OC4zOVTAb;Z3)6Whw8Y_zq( z6#=xlNOri95!6FS3nW-Ti64A^a%P14<13T@e_#3Q4QOT`veyF=yrAfK1v+Wr`}6sK z-Y@?DedX~TjsAA#;F1}%>lR+xkm`Mi?KV2vG2V9X9?$vxb`hjp1*NFBi%L`dpgmvk z9d@J|j_fK(vIiIV2CgOswz@jz>KfMCT7ixhZRL@>R#&~cJsniYe_r}r}6*iWl*<45BP;vA3tA#jxGK10(55M|F6q8FD`WjU4@_n z?lgf~caXRyBLIi17D#|Vnmy*~8o6;^AD_$#wZ58If)wE>=D^rmm)-j?tss{!8R@&58~NIrB?UP*6@@LDlyzu(!_(_iKxdZzSP5D91m5fn z8V3WFKj6a{e}a=eC?df7k-vkRSV*x0T9WnFR`5L;KB4&i)Lz?#bx{w)U@DLMdMl9UjrYbvd+4S5;S0;lF<^=~kqzZ;O3_^lB z4lfsgYJ?vvL9z37`ET%+Ot4B&lNA&@KUS`qoo@|3qyk(NK+7oNLvk3againrQEjcI zwR>F+s7!kfYIuXj?p}gU{P_QM`ID=Y4{vSQy{_iu?&c2TKz~C# zyoo|;h=DVpft8k4tgrp2$8%tj3o7y6Kb#%uX=|;mMN-cf<}YYmL*}X=OBBM~th;Mt z&+c#g0Y2OO->0QNU(WyWaz0v1161U`Sp;s7EdT#)#oN0xj_zz)JT<2(D=^5(($PrI zN=wT`8I_hP}HcZ>ghT6X{Zg#DZAcCV{Gx4-Sn^Le1y zf!7Y;`)>YzTw0bMV6G0`n+6XYWCx5)HY6?}qb`L>zQ17S+klQc10SvM;nD0#&51$I zmbSXup!F?=da=ItYvvXG{s0=>29+~!Kx23RJ}kL^c~YRGxveg=sYy~GgVR5#A06y$ z_44-gfA5!k1Fa~Y_y5{`Thf`np!A>*7#70?i8$H$%b=l6@fOky>bIn5wF;oSFCk(^7Dg z3{TN3Z+*@GPs>34CeR`9pvwCH=VccTbvPR8 zIT*n0f-+EDGGtmTRCl(uw$_$qW&gjg03D$TZc_ezxA@)tS=rGZMoOxXR1KMnx6%SF z^y+Vj|My`Dq|5|$#9qw*|9wSAWt54s8hk4{DUJk12WZ)kmgcOkR8S-Q7D zD>=y7N=plpj3H?QtFg#Z5H@6P$Q*o)U3o^pzI8R_O)l{scFB zKm#lvm;V2{{Lj0^SB`WpoRpmx=jCc@08Uz&c6LnZ`4du?7V`1gLvt9vuk z!d#7&Kv!Ej8-pqiM?*bJO-)Np&6P9rAqNG33MlZ9)UUUT^5eX~YqO!l6gaFNQCR?# z>YR+grz=>QJijpw+}i@R>OoUZpO$u1MjI-sxS2t(#D&BNsFpX@H&@fxyrc}&Q3Wr~ z1T}%auGqD@$_{*aD9K?98r1=Xyn}(RgMn^+LFk^fRqN&#CI&iz6F;cg07-9HO@=rO zuD}^QD{88$=4oZzSrvWZNaxS@i~oOH0okYvDndagMT7hA;LGp+zF++R=gM!d7T!49 zzj#V^Mx?ukxsi#Anx!V_!WYO)E4-}^whJ0=ki?JdBydFrYRKiqdVYVq`2Uw>zh5u> z|9kai@JWe~9s#7aiL47N8)CSNiGii2W=g2bhevZDt3e=1`sbU41qt4!s%ownu>Kf`J=a7<4L(wh)zXnB2#qgot;`6w@2{X|X@U~}=Vez<_rk*p zK1l>gV=mx1g0wK#FVE)v24A!C^VP!t@0UEfJjuht$X;L93H1tTf}sjNmKId#K^AmD z3NgsSNQjMa63r-ZY6N8*2*VY88mF0>x|fx4e?$D8bN%2e@Rq?#cu3+-+g!-7Cfdhl2lq$Gsi@c47($ES0T?`~-;i|}_a zw@}wG1GkjG{ZNoCaPuKq4`K_%YCI$){hKJOEt#4FX|6yl{r`E{>Ny3N;fcp6h`pfn zudLeNl<@!e>hI6zL56q!|6Db(IT6yI1)Bg02Z(yeFqe(4c94@LcBdbRuVf4iqL}yM`45lK#!rHQFm8|HInzplgFZFIzl0+fq{#Uhl*G4o?Yox;m%! zw}DQA0=K1qy;=12)q*fLYuH2p$pHm78EzL=1~_Gc3-V-cnbMl{@Y2Np z-C3WbH>NI{lARvzYNMlVp`l@`s{`4Gf@&QoQGqKa zkX@)!I7J}gX{o7MnHBi^9q6P)X!`%M?BelmNbLqrs5ni4TLktUXu#H7O?^&Z`v0G+ zAYC(1Q}frV=CTN5WmU)&1Cka{K>=An>t}EF_{tLisHyV;w1eRPm*syxF1>QJYeGZ3 zudS)Mx(4LnEJ$|-Vi%-k4sip-7Sc)3MKgAKIzBe0ukOwSEsc8xx={7^n?;|V%*}{! zM>?}AwoPwP#KR%ib zS-1kK(7!CZd<0bfJLrQZ!XPT~+ddLxA?<%_ZLRchx9_0gGEnCiH0$(f>GiXHuBM>V zrNHGCD0tz?1Jq8|&`6JP`vz)7gS9}~|CeCxf6CUvVGVhBl!4t0Y8qK+XxQoMRAdHT zI@0;?^D@xYjo=Hcp=JK7g+D+mh!+0;zT(^Kg-5nERAmRbnu3n81($)K0TU#zfl5x0 zy|5`f5Ss#D*y-tj#vx%7d7xEHKUVfP#+$0Bxqy#nC&GMCIRI(@G#0_z|DenG{(oL} zbXxS(7+dBR%ZwO{|xH*K+lK&zTybD{SP0!BEkWLE2$xF2qabO+w1Fi zSsFjRJ{2_e51!Qml^CCw_1482DM7}6K)p7IXCVcqxw^)VRTYp<8T<^)?JFxF>(9X| zp@xIm6on+bK!#)i7ZU@>eyf~V&!gKLK$nwZT=)x_z54%k`L~w~cCD_=jC8lr(Kc7t zfNZLQI0<4JBn!aV5GhJXPznSe7Tr}H13DA}GCl?#f!VXR8hkn&u^|I7%1TQsDah%~ z{aMK4Kfm8CE=uwNuTD2|2Im5Zn_W!}?e%pW4D>D>>VOY{f@1UQ@+DKUjg?2of3PP- zD=n>!i%UTJ4Z-mR+H&%4@rOrq3KG2y6;-UYVGECq^daN_)7q24ZA;Mp6le+aZAEKY zB)Cxu8DS&o#3TtkTM@M*aBWS^#A*c zPcP>0SXG%8?rNn4+PVT6#)PDHh+SYav4;jk0U0FNOi-7^LPH}h%oQ@14Vf$d_kQuS z8&f?kjls1KQT9Uo4q0J)`anB)S;&0IDDM9cOCDdD9Ovg?q@-#GZqk6f2Hq54t*y0o zUJ>#Fa?q-ww~Ml)J-~y>kk}#00c3>0(9(j4FLjOl1n(c<6)%vq1UXv& z*g-ab+3D&e2RUzDUIy9=08+dVG!YM;DZYDgl0WziaQIR~3WEY3`$!EQ(3Ojmni5~% zo$>$M3P`?#xE9pS1Rux@D)V2=-?FqUHPpolJQ)O;OGS1J++<`yYOx`PI~eG>nHpXK zkB>nHpg;$OJ3iZ(Nh}>x{?^Y z?#R(VFDKUX@YZ^8%Lmjh1&tShPYgS|zm>8DTEjEHKq3nq$oiI=n%h@a{Qt4?8+gk( zD83-;e}1fd{b1I)Bb_IAHeET{^Wn){$d!nY^~sQg_y5}p&^hGbDR_vP6ecH#ArR69 zyh_Rmk40Xl9G)aL??X#IS- z;LrOde?KpKbYtq71MMevHeWm2_Z1utkP!*+OfjhU{pZ7y!bBfPe;IBjE=xzCI3)es z>gt5LT0Ocl`Tu9o;%7*?3K>87^KS9~&&&ROUiSa$nu8|Sp_Wt24P%;CL>OC`| z3~QV}-05X$0vYy%#24A&33rSWctxePww8^yR%21vlWS8z`*FZ~Y$1`0$e3T3Uq9K? zP!MXb58AE^nE--~XX=AaZh>S0BGN6{&PLS;Dg>O3K?{fC{2V~*(7^kAL7mXg%dVg9 z^|3JpO+tas??TmxrHug(5NBi1hTp23pl{$+sh~OzLSx@nVLqJoAsVGRewG${rh3b_ZRbjLSr118GnIS zFTcJwb5cv9o0%bauPdnI2@g_G%BNqNM^C!oNhn=UOXGXzCx8bEK&|mVZx?-hG$%R4 z1v0S+_cGkgSQ+rxft*NCn(FuZ?#%x`R)P{aq$>*@4ytL1Sk=q)zdtF54G zp2;p@Ahv^JAC!hcn?ba++A5-6-2?S=L7NT1IT768SpbgQWxw7o+PtJR)D^S`-(DYd zR21BHBGVPvI`ni)6+yUn?DTZNmnE(E@d6Z#psCyME2?vYL9+lRpm7+uN<#L64yk~Y z4Cd+@QC_y&SCoH$1!_?J`>^EC`^CTCE&ly>5okLAWC!KfKfN>K@Rs_{>X-lr3sY5;^B-U)Kn*0x zUWi^$YhO*>-`*V56aNg_lLZ>dhZp-V=Kp@T`2W`x7msunB>GrtYJyLoLiBLqS%65Z zX{QW4b!!N^FRv#JymSY&r3jM#=k%ppYQPtk5EWMN!6HaA)ka6#N>ei<(tY8itm~(G zUp|=i{_&iTPv^aUIQ#L9sk_%yH57(=S{g&{0f&bPB!p?_<>6$qt*-XTolS^dz}rP% zp3Q43iwt$K^0G3q1T0`~BGMii!3);{D%n8i`RiF|XqKn@Ke;*?G{gj6oc#Os z!r!2QR#2ns|JUUo9?hQGn&e~zIv^1|yaQSq4KZiHi+$t-08ak~rmAW+Il+)>6f{r< z9*R7&rQTj2blNAfdV*~5?if&J0fiNKWgzH~77a}Y1HAxy^Q1th^l;a3cWXCOL(ql= zbq%CC5vq%z6{8B^Az-bowQpTDBK^Nv^yT^d%t&`*Wi`lZ6UZzuB?%8%uZsz2&Af|= z!SWe-Ki)6?`w`UIhg3SCWb&I=2ntRB+i2N}>=heiH6qi22UOp!=ch^gyRigZ3Tk zf=)X@o9P==#R7ic4IWw0bnwDrNc#tp4*$GeRF@ZGp`igeTMzD3NQ}U_L^2?{AS7sH zLro*b$Ns{>_W$2kKwA4COTpXFKy8L^D_-B7(Nz^~4?ar)mhz!#24V=2_R>um#8nOk zdafq=*U$BXCZr(aY3~-ldonk|!^Tz@G^GwvMv|*AwSenE#NnSLnKr5zH3T4IcluVE znxGZz;N@GO68IJ9a;whDC=(Sm_?la?3l>NWf=e(^@nxo_UY{TG>i$g7R1xy57O1tq z=>M1H2RGG)d)Sz&szJ0sw8NXVsP2bmia{a>Imr6hjwbNZ1<;(uuQv;Syk1zE;%A`= zn#+d!g;WP42R?)iAtB~K*kl_HF=RxN(DZMqsR>%)^L0672oyZdziLKjs-dC^JpEG= zK9GJKKQ*-`+2QkpPy8pi{`}B0~gvJD0U2Pj3ZO9o8kT`{qlsJGsdLii_bc@Gl z=uMH3rTF~~@usTkuHYdc`nY7&7GgpGlCB`fL6l_#{QriW{#R~WQ2|~+MM)V8%3qMa zp@oKKprgf!-Oc}htOTtP12+#q#Uto^r-lE&tvG+MJt@c;HpB!PErfUh;z$U4(2@{G zf=28$G(ihNAxn|pfVU$4T(x>mfsqpW?v+97myzQzaJ>mStj1DPGv43vD`nFOstWA)|lZg)*NP2*fkU`G0FxLl{K)Vzn6I-BJw%4G!`@bKS{Qj_b>9ibB z;sG}j;7uKfcH%7^K$Q@uIvay_uV=@4f=(GiY{gx9XmcH8m)rolaMWt7ApmJ(+v(}} z*_quu)eB!k{`<|MZ_npPdf8foPa^>@Fd()ffFuTp$&iD)8;im~hwFY?3UBlLd<|Z# z@^$&kJ2PssgW!WakSqz$0$ANV2n8Wtb~e_x*4B#fu=(7~KM*Lf5#na>DhM5ad-I1ECqiZcKnG=fSn}lRQCE!gfpfx<8HAUb9J$}7i^!M|!l{534jP$LwwZS8tpq>Ie3sBdC^fC?P1qTCN zH#0-fX*}RfYM^G`n?+xr%}We)vIMv8z!e-wCB0llLfJJkodhncL31}LA||l9hgk4~Cy0;}zqPiOr-d=-VE^wcKr0nrfev1Stl;|hamnWw^E)b| zEi|CBmT-*_1|BPhgp{qWHfR;lchF(cpqdoCL_a6a3$`F1G=)7RJTjsj2|6p%Q2)lM zUP#3angw_{|Np0D4TWLgSpd+044(7}k%E-`R$7|DPL>ysbVBwEL&`u%;{X3;+4CFI za^pNfi&em<8Nf9TgV2W8q$_6T!&d=;ju-g0qNyYTT$3UekPHL=4g)X1r{h7xFB+N) zCS^jp0H6i`;6pxlt*Nrn(T4jJl3d|j2m>MrN&FTX8qwZ%w=YZpCI090f4v3`C_xkd z_Z4Rkwuiadz%TZKYlN6H)JaJCH&RrY+LnX}MsWE*qca7(3L4ZJf%tl;dv@5kAJQTP z%>t-_HUEqWnC<(D zJ?pC7%s}h4;0G;&8~mUoF+2hjlKxFq)j+#IJ`80NLvP|J(AFv+^NBG;o#hWF|8vRQUyRf3u!Vz6F=y5XpPhm7tkps;2AFP zCSuTl=byKWf4o`PSrKKbst(r((FNfSGZLJ^L8o=a`q;mI06M@5Jd6NZOnB{dpDp+% z@nPoWVdfoYV*?9yjV04^LESgdxhRW%zFPSA{o)t5rv*A%Ku&vuXBJ3tXQ2UV^FO;W z6?Bgn=%fP389|^m{NG+LY%B~lQ32g34NvxP?X(U$NG8Rw4%EiO5Ts6X6rBD+*DLtj znLoKT{V({0;h(P-{`KIUDKUyE^3`=q!{4p!EM?$>W>T{A|s@$00(4 z0?l5CtKc0X2m=x+Xevj!vO?2S)lct@Iiivu{3o##Bhj3kTe6?isET$+*}f|b5-T_6Fv7X zPPlZaWA)s^q7*-S@D^IyxzI*O`^?@JX!-}AB=h0PoU|~|*}QN|A#R3{ppqYa)Nodm z$I99HYvvZ_#CchPt|Nh-(+=?(MDZv|yAXgU9$Q`QU?dr5zXY-=3_Omv^!C|4 z$gMh{J?xsApq}#Y)!(1Z`~H0X-}j4uy<6N=6b>o*AyEb)Y3E=}lR;?-e0W-MvhS@k zegD5M|MzA2zmH2ncl&-^3cAtzmxjDArn#%7eYu()5sIC*48?-xeh*z{QJ$K zug~Y@$9Y+3VA%x;DR3+_G)j_vLFajXU-|Fr@?Y;4x0Xc?Y)F#l_hFz593%SX>gt`< z(eOz8^%}HY<^Q)8OQ+?6s{lhA9qk||iwlQ4{{L9{|NDxMPv$lig@G3KfbLw=b27x< z(W6oLKoT${@mpwUww6Zx0-s_Ey8QITe9#&^@Ckw6!R-IvS3bWvEeYwaRY;SGT5hw} z*4nwM5)pK77X5g&pfuIb0(K`g+#-mxos9JD^>mNzYWn|U<=3ZkzdfJ#=iTCOuNLOT zdn4T-L)Y+xyKtm2;4$K8sAsRQdlGyREo43yas>3ZR||4tJj~TK+)P1Pz{}dCzA&t- zHYUc`9@aa69$!Y^paTtTLXNY`j`sNWYQdkkpzVi{RUF_c2~ZCOvFZ+g=}ZcH-4zUH?CUHV*%W zY@GbC4@HPi*PDEiGJ$U(T^R?xCrXZpOr zH#>m)n7S6~8W!pr;AjGsQPfIBcwGqUIl>jdQw4aONB7);HqZhj*mx|Y{{kZyfpWy_ zMca`l`7NW}_| z7i8_DY+8f>B&b2{Qhm_Pzc;`)Kf<#BDDiw=+EEEw*Z_}Eh?apmhZ)>w0o`^te?kVL z;SD}Y6q4ZKB&hrcZ_fSs5_Fb+VUn+@D)gQ#h!-dbPf+>?&mn59o?U>P{{OvS+)x+_ zIt2ywIy{Jt5XW0;YNm&~eRw;PH|X z;r0c#90IgE?B#+#?-qZ2GB+{M(GqD)V*uj?vi8_mS#{fra>%ePX5o*P1wc)ppR1Zm zBjD+u;uM8Q|DXv(*m+{0&CVZ|w3L8G#xT!Qf&>~QsLj;W8wx@}V{WfN6K9aizy7{o z{1!a<2pX_L93lj=i(&^8l?+CvEy(d=s;Um!)A4mV%$2kq0+>nD4>t)W){!_^N+ zaKY36&efHm#Y^DEKcuLKCwn*pA`Uuh@yE*6a`-SK<%t25CL!hjvgvv7Q{Z6fKLT9- z8$tpKIg}tG&@7;;zG!kbX!j8Ka6D*Ge_eihPYYyp8ln-R1;QRBX%PZwp>3zDb8vGV zXsH9_7zaoP?c0h2o9pcLbs^D#Owz(hc+7+(9#d8IWz%!v2@jIa;mIDtfCxfYW`15) zn&waX`TV$Y3L^clo`cf<`TKrxLqQlg{X<7`(EI`pZIH2$F8uMG&5$lUq}vY4s^3=3 z=u8=%5kLz!8gY?RGUP@@8y)RX7pv#DrvLv4nec^o&{iy+4w?}F9}@ve0FaBbY2+|m z#zNA+rKV;@M!-MN!b{Nco0v@?NOFQ~%=!0z$@5#&B0Ov$_q{rTZVQ6AACwMpS%g;{ zs@+OUbNvGJ^Mq@2gU!@5;N2yx=0N(wHagnjZq_euP5%d;TL+&Q16o!4>Dj#0P{eIA zSWOreq*e$(;=;)YG&JF0pyzIGWUj7Joa_s}I39Eh6(n_mDtGYR+0<)Alac(O3C~_% z*WKI*be$78lfsMoU#~%f{}3*u7Wf7p|Aek+0yV11uoP1Z)EQP50;&uxrob5xG=L1bw!S7O7`%cSnvp?cL0^}j-`@tVDWE1mw4j+Y%B3U(AVCUA z{GbczbhHB;EL_2xTwF{I&DGTV>*N1_Sq9nM3c5A*6?muQ>xC5=0mxYZVh$xPAyF@M z&ca+>qrM;%eCQBp-!UY$LyCRKB0bO$?(_NoJ}i0jXm+HhEqF~DDAkc@9cJQ3h*)cD zZC_D=NdIp@*OHc{`QuOj$aB!3<%OD>>lYS7I|6S&we63W^Z$QY-d7)I23uGQaxsn8QDt~S0FwAYwXnPOp0!nvE={_4sG~A7z*<|&#T0bJ<=S~gpm+g~2tgXs zf8Q_u_;_v};*=Xu0Y%R&0JRHz`J=U#)}o1^#e2Wsg66p(7m`Ak*;zCWJ_+N$$r zVO4e@cy$3Z@k33ZM&hy7*4n+M3ZDK!eb|=^ij#f8)?sh|!=nRSyo1uSot}=5jmft)&GDa6`S5yIY`H06fMHDw;kneRF?SN~jBREQ1tNBSjI85pc=}Tc_t}q-U+I zHMu46{gb)>f2{oXVF_p}?wdt_-Y)w0Y1#iDD<52%RG0+1Pz+w=f;ECt9bt=bdj~%8 zZLO`fe?u*D`v3Z3em-b150>?BXf{Gz0_o0}t80{}2mE=z7*^YYR&@OTzH;ND68HjX zxEIjO8Re1{0?<~3sj6CET^uOcKb!aS)q<~2=l=h;eDTyAOHEBzQv+LFoj^wmP|<{R z)B~u2`f1tI>r?BHkp(KB}7DF^IvJ#%%9gaF5-({gW}?)&s?-q)84K0KatZhza< zwq$>MGstN?5G`Q!lqP;;Lu_=kPwZ@hmfPU<&!3*n%Z_qK4i;qfXl$6P^uR-@nxM-8 zzb%K4=t2&SPYZX2ERcpNrl$MR+%rPB@Q}3E*Y&nB0UhG{aVd0d|BLzmzpa=zA=6S5 zwB`Y__&LVM{`swG|35B;%-2JgtbAF1_rk<*cWdOhhMP?5kc3zSZbE=IvRP?qTBvKd znHdH+n1{Jr``Vg;4r5nU2k*mzwnHFVsOm618y)S-M`8P4UW4|(ynQ%3A;1y5L<(vz zK^x(%&@eaamv?6Td%qa55dG`&{Tpj-bhN;Rfa+@+_+n&bC`bT8s$DZR^(n1Mpi_Xr zXW@c}O%{HCwJ;;n-9kgt88lh~N?qpa8kv#q?;p*E57YgEtSkGn?B2zR5gs-Jm<1s9 zj*E!_Wbv4-u8xh4wymy?g8|6p5Lq}0PDNCUZpi+}TNgp?_Mfo*k1z1?D)0|NeB0~UDW;z>#cJ&51SvON36Am>%zr= zm$6}85~uwURC$st()G{E>`{{PR*9$%jl@9%(AMuGIg zqZO+Is3-_Y;?64khq{60ar!|1JYu#)6)rXFn@X*bpFY&SKwPvKQ6s{WwN7z zt^?^~mGE+=wJZ|ep@xiQ{d%)73*4um(&AOBg##sFI3RlAi2*WybnpBG$P_B5@CToQ zyKX@dWWWL9JVzr?N`|mqOhAXfwUtNxdb=2;9#lYqYk}vWEb#oswA4@+$l674feZ>{ zxOET{h7Acx|28_>QC@bh?$7%3e(|qYpmV_fe_DF|bT51xKJosAS7Z(bx-KRLmyUJ8 zvH*A>=GB#2Y)R5;FuK<&3$yMqOSA=->{}^;Mv<>(4vT;=?#U2hid} z@Sz;=)C-CoBT!iisRdfgBH&rz*PDgE!TTlueO&tH;jGeBKNIj8WAHsn(7FTD4MSZ7 zoc=*qd#8oEet10R&)db2$&vq`mY&<+O7{7Okf>MbSV zKS9Ini@^;c(2|26FXsPwzvSD?1#M-Kkb_6SwZQPq0+57iuC7s<>h}XYEe=ippO@|5 zP;0FX8LrWH#65$68Sn7Y#z9}#-NNYh**0ssA+*2fW(ME zBF;iXGdvuIZz<;tLDRvR*q~W6sQXGNq?$pqj z)0YlfJpX(?q$U6V%kp#J1y`WkVZfypmK2Mac-40?0qwx5&kz0zT3`d27g+QgwE6{f z4B-DS%T~?GbI{kd(E;801vd({LK(sV3rhcHhK7nN(>qc?!3f@i080N~mrw6Ny8pt6 zwDb?@Z8;koSZixXd)d9bI}VCVanCqehifVZ+j$3?#_zjuB@YN#vhq@ZEb76KK#keg&dD;9pB+<%uF?~ObT zOeiF=SAvj5q1D+z|6%vbgNl?d%dQ^lf*eW)HWu0=1GBN4Gb)7~0^m~1z(iSXQd1&q zcnv&i_j%co?TwHH56J5AvO#G8QVUpWYKFL2UOCnUS|$hXP5%bZCV+ZvAD4c5I)mGYCUREYYw>SL%zT)Sr1)xQYkYjRRfNlr)^^FET$Iq7we!Kz=uAbc09N}SWuBHxIoB-F0?8w2-1{b+{;4Z3Ggs07y zXY>BN1s${nYM*>scH>01kBzCFK2g^wn}D5EODjSWy+5j66nshJSq`1Kid%LV^FE`c5*1kMHEbu24h+?i3AA7ZYKave6QHWKePTBwBh7Zj2jnprU( z|2{1RRd%nzv$Wq=9@<=oyikX12SADxD=p2eDECiKq4$e{YU+jz&TZhtx05#`@-J z>iO|r&q23Cf|jyFsu)O*_|N;re?Kf)H7noE)DSX`hm`n1g&vy8gOCgHETn(hQ5p6B z6KH=ZbbRLLsw{0jUU~d240{DT3`3~{gP*QW&}H1+UinR z#gG%xr21kosRwmeA+ZLUSOIt0K(Y3G{{Nq==Juu=DXLHsYmoQ?Et4<=tvWVSQxA8y zIlI5@|BsbFz^5@oHs}6)xd3!W>h~2_j&`MlIGd@dLkc>GcBH&8cw+}r-hwY3&|Eq# z2Rc3jx%~I@vPsPe)E%F+(a}DCs2zEK&F5$Ha$-E;UF^Y>#s+V!!9xjjUcq9-c?F;= ztKKhZD#CYO4O}mN267s9F)^^#)^as9Tsyz$@291I-Y@3m-H@#)-Y z?a7XYdXT7uCw@pK!f)4r$%4n{oxmeM=IR;;H`PJL=OJ_GKi_~(envhek8*cfYin&? zRt_Jm1Km;hZgE2amh%-Uw`(MrV6ClnWJ^6fJb%3bUE@-c;s+m6qa+aENf*)&7lq8Y{RZzfKDWOuc}P_RkZca|>6J6R@R0odcF~8Yb7Ot% ziJn8jUs^)zYb#C7#Z$6jxeTM{hUVm!L`eDvwO2uF z)K^YzO9J&W%tq5c{xSqpR?il+U$qn7p8WG}@w-Q}lY*Q`+OJA5W+BlBX(~h7LQaNy zizjFQe!t`|Y+ML3ItpqRfhvOK=k~Q`M!K7+soUx4Am7_K*m4164$)Lqy(%jZbb2#* zW(qQlc5+v^mnK2x$ROwI zf|hl?S^zpz<;(J~FBhzsndfhBZl;Rp7ei74ge1mZTB`x4f6&UGsU6Axf35lsUPB8S z-Tk&=(UffP78P&^Kza%!M-W&qY|jxm{cC`x0N~>lFXsRMv2t2_vXK%+;}x_Hr9o>x z=)S^zt?=^y_nSpup3l#Z_o8C^2ifnCbPFLNqp!YpW*Zll`~g)2pjD&bwh(y!=mlsI z_5W`x9$lH-Ru*Zer(>lFIzk7s;}2p2ghV!Q$3bPqjf?jAsUt+lldY($*b1WJBy z7uDnhW8POqEk{D45>g$wm>AgV=~$?1RAdA^xHJjWBLHn11f~4nphM5VcWQrJ`uFp) z<2#$u!a)n?Y;{2^s3Exk;v|TH5cYtOkgcOPPxZoA{{Mcn=*!c&X<@FAE)2x8l(`L> z(X6$!)+60713H5B%=ak z^R?bA`u1W0cozWn`^E4?!AO>ZBtOWkOQUbXcW&MI;$hJqC1`)&!kaHJ7 z6@jjfg@#6XhX3s|egD5L2OZ!Enj={F3$|Y3_Z!e*jBoDEoY|G?V`FNjrViN~4(?Mx z6Enn2nmQZVaB%(w9gEsj90o7^LDL!ES03D24>~c)7_@*2Sqph=h`$^RK$k^6yfGC# z{|mY%=FhvuuOG~c^|80s(S~dUCeQqlrVA3DkQR7eg7?4A%RuKfy#|;6pO)S@(M|UH zA5udS;u3H|(uY(89+pP4d(%IH&t3cp>I*}U0sr|5bTQ(ecZ>gjTYm52q|S zRpeX%Dv-h10aP83>Jl>4LuRW@Rn#^tECH1v;8UtVwbhs93npeFpNUO|;mBGb7FlR$ zb1$-q=qZQ-OW(E4X^TMLxYJ}=w4tjtOay1yQxc$6eA1VE)FXk&$rHt2dI@I9x{ zRkdH2FQ1VIK3jlJTeRRkL`b4?F#(+=Vy&%Jn-_BX{DlACR{VJfS~3AT&-~3I$ZF~z zFBkm#yzI~CWmk@OHx-A0Hz2Fo8|Xp~d_y)6VkU860?O~isDzmBVghRXBnLace=zIM z+eJS?XI?G*`)={;yEA-jOdySaVobxM2JU#sP1&Hv;V1B5^_xYIQP$?t@X_`Ecmfc& z6sVB^IlyN^Lp*%q9=!i?@v}QKLS3w2bNA3(fZHfylMRt3K&pEuBhaQ-Z!6>3-Dz*{ z&;0*w1-w%LSw06@O84*6(*K{AT|Uy;UJ>PC0lKONK1Kv?3qjoms$a+*e#ho)Xy}=$ zs5KUcLvBfimL6Z0AKX|AzZngiUZ|4@Nc6WM%UI?<*Ee%mlA0f!5&k30p|H z3U;2JlM!g`sGXjUnVNd2tJRvhg&&{I`~PjlFVGk%v{L{XZ~6Ih!M`ud{(f0@=lq08 zEs23n7N)9dpe5W!po1x3ra@B(bvK-3xB>@lpp6sqnS$L90@8? zhOSC*8qjw(F|Y#d0(1KKWbSWJBNo)tg!HYh9PM&9H?#w}RrdI*C)fd;n=(hPPo(zn#qw9(NnO!7Ult>G7F zICKTHE`Y422A?|zx-$iwL4JQ&a_LCtq~^q6XGQ3s9l)W%<@+Wk`iT3CSK3o)G^+90NZ2O+7c(^Zmowp#C*z z%^PUj*Y7urzP_BF5#a{E%LbwgV!|j%QV2kT4s_?5wdwtf6QQSIL1vv^f;Ot}SXtp@ z1iIM-vJMksI7AnOO=}X87$A)SP#JClD#I-_G;DOVi;{hgY_0$PX5s&@&?z(co&rdw z0QD2UEc^du`TK{nwy&tD$qn|lGB#6Fx7N}EWolE0`9T0y+m$0u`P_l$rBs6pit=tk^R@G(ks z?VTY9)bLexf)`}^|M{>4)V2hVw}X-pI13!x-UwQUsSY};0kZZ6t`W|lLx6yf zjDxrt)HQZ8(znpiu+`PciuTyPs`A6rx&Oa|M@qq4Ea8nJ$S@J;F2C<9et%qg`+Wb3 znRyuzZXOm!W~%CDY8sG(Nu7-ikopLq-Zv!2K~g*1ci_wj>XKM!XkDNsbnQ5_Qhq-F|JUWG z_Ov({=%VSuWy`2I{t$o#Bj^wt3k}VgU8$f79en>0sB8HeH0%2R>+(leCzqxBTWV@r zYeQC`feK-8l#yJ;<8cqnF$go!MBrQq15zbfYHC_(Y9qDDS$?bJ}v$KW%-|Xi*KInSu{DjDA@;e$FaHw*lVDPBv(^Ih_MiNf)hM? zy9l%tQ$us%C|k^8e?m?=R-V$27jbnE&_VlArGu zZ(Uvy?QLhKrVi4FBmc=57}G*$s;YZf7>q9U zfPxX8Bq(7(q7_0yD%LGa%l>}{oz(zsPQO_QJ*(mC^7jvBt)5em5D3~MX0EOQIou4K z5ukYi;u3iL62pLKfsoGN0Z$hb16NZ6gJNU5ud zL3K{h-t{$K!3~CgpO^i5wE&)@;TZvPNbkQ-OaFoU2(RzVIJCKLdPhoroR^oSv5gL> z5dv9g2Wj;|G9zTgMP)_+V&v!50`O5J(?eYlN0DH793{RWu5~dnu+!5qSJ$x8)bzGC z>8_3et)u?B{LhCa$dwmpTH({u_mAgfM|+s7A(nb#2@_I!9ME7K)dJgL1PV`MeS3Xf zTV3syv-AHWO}T>FpP-2+&;f@3zpwcCbl#z@^$kT~VeZyeTAC&*YF64>c6y*|7M+ar zosB`M*A?8mgj~f4Ze4@YGd$iQDIC(+fH%DD!DpmfYHFIPshg@oYZ*vF0!J1oor4*O zv;=Z9I0hlgAa#L*fu5z8e>n zJiR&X&!=VJwTSb<%Mn3`13*^&{r|e+>5ZvbQSQjIT@c5hg~lkC>=1y2CuH!!(NND? zORKjw?(?&GplKISTL?5S0N$1YDoB67UG(qsvj1O~Kff{c;HJ769m%;dp1!uGkdYz_ zbqy0`H4_yzGc|P!4NWU8O)D)eYi$s+($cch(zMjnG*{O!RaG}uRy9>sv((hI)zx-1 z(sMI4jPS52P4S!15I?gsH7m;9N=plJ(FUaK#pOH5I%|*ue<}~xFL~ z%)WG}WAoCoIeqCZWswz`frW`a>EUjPfll%Mjxj#=(cX6P{*FmO&S_z;d2wFl83A<# zp}qBSi>736Us-W>U+a_WQ$IbQ|MShle;=3r`?T!$yTy}Rk|2dDe3TxKXCYMpB&R@p z0clM+80dofESj2r_GT?55j$5`KD##M|JUXJzkpI5B&Gd$0bf%An*IPyrM+D6A$bbLDBki6=?Y4^}FK$hHeskKZJ2T$hpY`$a98j|P zxb)x8mH&RN`u}~!zpu;xe_HnU{gU6{^J9O4o9ll+E`9lMc9@%$4e96mLA(IT3y?uY zXJdWvY9b8_4UJG&>+0O#ohvJz-kJtFgYM_be_xk_yaTEf7J!-`pn*;BMP}fk;Ac0c zMtj>q#&%qc^{<`k`TuDtNEu`{_WO#3ld?=y)ZERDEHyO?6Mb%;>;Lq44yc#?Vaflm zEB^gl{r~5xf8SStIvO9B{(ZL?vWoTli}|3SgAGzaG94u4e}BDj`>M*MU}wl#@yHzz zvLkm?BQYU>oN}Cv4IB*g%+%CFTr3w%%zSxk+W&7W{(V{sDx;o5;||m{0FO~XF1-Z} zuz-rq`H&vmpSO$tzFYkF-Qs`mK?uxV1V1GC`*V;o$m*RRFF}_jfyOGqrh%6eFZlX= zUQv<{d?PI}?uV-ZH;VKjT!^P2)q#r%=++N#=N)uTdwH7wlBwBe_qKj{HV>3mKQH_D zX&JPq@O=K)r*lDj4bJv^SsFVT>6@u()Z_+(nopn%0vi1P{dUo(XY-PRoNRQo?esv4 z37_7a`v1pDh(~_B1eFiS-UOBNFue#h&p|D*e;=3p|GNC=n?(mV*5<^3PGPdr(t?Z* zLEH}^AwC)!+NP>%!7i4Qni4J^>-zqBA*ew6ybN;57bwjmrC!MT zWauT@h{OzWadeRgh%!|MRk^cV`AVSU}np5XX?=Yk0c|QXM!N zf|~J=bOkw;4t#!sL9CxcQ%S^@C8bwS_rAVA>+i>T|MzX>_h<7U9)wK8A$t>&uVEp!;5WFj_y6arUmup- zxiDerw4Bs1SJ1Hl8XDlfJan8BGCEC$-$%7j9s-a~F?g4*wz&pqMP*^4&%(*s7Z0`n zds4XDlhY zuN&{@&{Q10aB|kN8F@uXK9H+AthBT;BHVr`9W`?nT4sGR88x|IyJJR{^+LX^v z=KTM(?BDOz|9-Ce|7``RkNJ7o|4+;Qe_Hzg<5Cd$2~2{=jz9{2fZP8+SN(mr_|xM# zPi{=Te5&WrwuTim@;a-cBiwC4<3EPb`7e0trNpxkLm=J(n*%Z&A_yC#1$huMK53$& zW~!oQuC4(d)YZ4r(%QYY8Z;mVT8akVf%SFynSHG&BUFZZyVg|w|F`z*Aq~bNky)>}=Y%zIx;0lBLsgr+22dRYX-}1Y|_IMS9x0fUgWN zSJ$x60Il#BFJsoJopWckdzc@r|HwkNk$M3tuaB?dbA+nL*FYn!R5 zLl#}w>w^YG!2`Ak4%hE(wPf{t#b)TnkIyh!w5iSc52lkRWAX!BNI&2n^m3fQFESI zT0FZs?JsDPJ7^IUq}zMxNC%{82T940`~qQ*lA|Foq(cCbn8AfVXu@~>{9^bCKj2Ba z1%E#-smKgKT8%kcun*pHY)B_X`a2#e@f(<{YgA?i{sJ#ag3RQCw%>kTzH?P2WJNT@ zc@QlS_9!_T0z)?hASoHr@&T=LcD8(aW7_`@;2pSc7J>E%ykGqK-pp`!8`$A^qb2`n z$v?P405VSjPRXF*S)>jrbR-9V_5^DL`N&3Fd;f+S&~E4#^C8!hK??r~jS0xzUbuSv z#*WI4h5)TYfWo8*(GG6*8PyH0%4DmqaiR*AplQ@ z5C(Ww8nnpM(GWE62AOC>TUrCI7eM7a#LW z|7dn%pd)xA7-U)IXyXSFfRJDuCFvgm@TBWxqz^el#8N}k0)#Xi4RpOMjqP-`Ex-$k zth6*C#XEd*4Z?t%;A(1Mrlye{?f&`MJjfOR$lB~5uRzlAiC)9-%%S! zLjWEEkVFU}os9HAyM4_K^Ao&R%*Z>vzwOHDzFQY2TsqlvY-iJ~uGHiZ7dt)BMN7yl z5FmOXCBK=PdYqrbvl~;P=iq@?JAjT~__kuh!eUEJO|V8tu@50f3&7D37_bn4CtdIk z9UXgporZ$YYsb4mTWdcr1MNxxt-f9g+EW19p#VBg>d>aT>?n5&4Gp+na8}ZDH8nI- zQ%??d1||MaOF`R#-+)>^pv3=m`PE}x9_B{&`k+H}A^mHxt^xD-sO_U608hZ+{ft^( zR>r&5R{#3~KKu>5jTf}<<|Sw|FLb*N_@Ju)-&TBkvv5{-DrBuGBp98I^^KKPb7MVU z-)6%Ecr$%_#SZQlPR`h{a=#3Wsqj`TILja!mosB^&lLH+rE+6Uq z{~dHD1M-e?(9jKRyB4G+4?4vHd|vM6#ifw_><$LH7U~*}g`uAw&-wRZ$@dq~3y6Qb znE&s?lD7|Mry||tJDT_*CB`T@NJ0R7^PRzo-Oc~Mul)WTwCfg<&LO$r&%4Ec-Y)@N z9|k^J2Op>L)kkkli`NaA5=JR=~`!Q9QvFx=gy zvoh-1iEjA)aiF7q!0n>{pO*du-KYr8JK$RnzCEA+|I^Yp_h%I(c+>L|%s~~xqmCK; zAppL=USsjp9LN#{_;D+L-YuTemJB&!(N0eXe4MF{i7IHf{*INEklpJ@HN`?umI0qY z1v=F3#r*%@Ry?^eH6_FazPxjEz-RC$h7smM(C`arr?8RU&9i;~KP&+q#r7I}fBl!` zD`w=GsHj199fF7JK@-8QriQk<+RjG$=MJ<%#`Ym+@<3K~g7!1MT=4S^=*Yp7yPCt@ zZIBP!9ARNQki#0IK}}2ufY)b&?#NCJb@}iVbZsBx=()e|mpr{a-OtX!+-3qlg(s!=tgAwZuHfb@LL z)io+I0wH%VLJIKzpO@`fTWzHUnx#cs*Z^4qWUH$k;$rpm+7wWg0l6>lHK_gb|I^az zCwg+@Jk8ZW^J4IEe)@Q4)Rxf@ASndEg}I@riduVlBZIZHZr&FPfYMxf=*= zpdM0;gDVdGQ@fjydjbF6FP_qp=xCq|TiyU#CXJ?@Bwh|7$;^x(!Ya&uDiL>`2%h6CeWX^i#|M@?Qd^xsj2B^208^1 zq7p*V)EA?MkA?twApmLrSZIJw8vh2Gt_7Xg4@&=^mR>mA3AyST(`4{?sdl87?fVC_ z{=8ibYWBZc@c+Y-hnFS>Izo@wh3Fbh{E%=MB?o5+K$5(I0)WPpAjk3y zPG66@YBU7Un&nnnT1U1u{QtZR=_;0`x6kwi*qcKRt#vg69l8j)kkMF4H9yYt%kz1l zVIT117if9g_Z8DSl8uy9A!1#z9kBZx(`DL7$gh zKh>KZ?O_ML%+yd(#Y9=n)zqN5B>dgO*|6)RL6;Hye!KYXquFu34&YlXq21oW@BL91 zj_?qGq<_dgyw1k@=MT0+rhq{O`)g1Z`1f(?kJk&2Y;Bm=pE<29dCAnA%SSr@eOme( z)L90dVfE|vBGAJAA1jy5$QxbcFv645kn=ty{lj{}8hLSE-#|;(K=Z$lEC8Mk22JMv z`?c!d_m%&Dtp+WAdj-2+=?&gt%p6g{IO^G<8v>Bz3Lz~u zHG67ezJYhC{{UY(2U!jZS>yvs_HPz}mi>b-GXDQ<#nbCkvZFn~S9(F4{Sb48ZfK0U zd<2F7eB9F6Sl>)dy&%!&`iY*uUzUNcY6ag!_7l7j@+Wu~)ql5o9e>c ztig#Nx<+U;@sGg7GhDo^2VUx<=WMJGx+%{{zq>l-#LlJ<4`=`X4!YGBv?KW2ivM4h zKf5+%&)VwJR6lENEgPND>!e4!y~8F1KvP$cNihclJyTURdwtzFKZl|e-?s9oiA@O& zMPa$|-k>sHQxh^T0AJxYTJjHnX7@WdIz0_rLJMFt^q3Z zjX}38fnwYcWDBw`kRZmYp;5un5Ev>U0PhPxQnfQUsY4G!Gyx@h$X*zTEd0jOq2jwy zcaDYtF(Cj>-q7X{xL*J&=h4RY!D^u5#F#m%W;6tbRtSK1?1EA_q*8#BXc=K>d1}

$l2Jy*;pUMHU)8=jPxN2sA%J;QIHUT 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 literal 0 HcmV?d00001 diff --git a/ElectronNET.WebApp/Assets/electron_32x32.png b/ElectronNET.WebApp/Assets/electron_32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..125dde6e575ca5da10d077cbd7b6bd394dc0b053 GIT binary patch literal 1281 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4rT@h2A3sW#~2uxrU&?hxau1@=@~j27(445 zJ8NoKXsDZOX<2zE6!@o?Y3tY+n79OFlvl0UU9oaUaBi)#yuP8Co2s&LLeqrY=}Y43 zdS*R%>);osXXL17;HYQdkTr2Z_LRl$k(u4cui1J=YH3@mshAj=x*D0e>FC<(=-H_% z8A-N@fJ?s&kt)XFI?HU?i-yh#Fp?vw~xVql* z72D$K`*aN)G&L=Y7pyN`vf0r;-qgm&%+}Y^IoLHktzqktoT*C!GRjI8t<%+aa0*N; zoVT`O#SU#Lj>UFynWpq>&4bANQ%9d`a-+U-Avr=8v)Wq7mZu0?oNzKwFn?eif zRF#Yq8vE2#OpVOlqbk}v4_^u^Y%nl&tysC;B_u^h*G@ytJffsIy?2&@sf&S$v%0ET zPQGZQF|zPz-Fwp5(ldY7N?koWZEb5^efy-=De?7v z&AX0Sxdb}}B~`E8o8C9KYR#UeohM>zx>LJm$_T4uPhOlgX`xqqUi0o#*6v~bXYb6o z^E$SsC!uMgg+oBYwxi8Ek2(g#N0zlI$?7IFPl|8oH@5WfNh~Osvr17$H@$aG=EMcz z#ZBsJW-5wCG1cvLoA(yYTWjwVt)gI1x_F~UbXI6Vy_IW-v4uzFs+~TGg{^zf46Sf63yF=JtNgyN`Rt<+kiOZeZ-<7Lji06x6u=SZHB`rE^eBO?Tg^OWyH$ zjoS|gW>wW~KI9gWp`mUOlvPo^W`}P|ap~esm8*8-&0G;t($cW?kb6|7wOeRH;{+S` zFbDrQ6Du#5kmSxIm)j1U4@fT$%&h1*bTP4cQe0hM`+@V3Wvy1Op-HV%Q#xn3S37TD zU|>@9ba4!+xV3lE^>ATFiKENktb51dwP9Uu`5jq9CaD~i1f7j;od;b_gsR^+O*o?W zt?!1kR>Hxiz2R^6e*bn{B7W0s;io4b*UsL#-}3&>a=Q=fSp}Ikn8_F0v~3WJUlAOx zQqRs2udlbSuDQit=!1EJXU>fkg~EL%tOm!_R?KW+%(=HHb@eqP2Nfw!d6B+tfu?6R zZ}WU>aVqlg^fdXdx08c|tV>e1ZSkACyX5V(IqPnoWhqNZDJ%c>V&&IUZ{B*ny1YI9 zKO^(q0|JlOdCyI1Oz$wS)<~&3XQ1?iPky1W^1S?%qfu_v$0z4L=pSlu@* zSn0r(%**L#MADZ2&~15C5OF=nZ>F*Ndte?5|y6QN*J($q3=_j{D+zt=Za2>t4 zO;?%?E=&_up0dB@=&8`vl8tN)KTNZ)e-v@}{foD5LC&2mJ6*co8Esi=QvKS@X^Ycx zx7yp~C#+Rd)*alI{e9k?M|&%snnV~{-rmk_3SaYDE2sjBEyE!7bFnwI76 z{j9uBtE}SYEnK)pSlvHVdsj^9ubxL%v#az3Bo{85yT9P$q?(^y6Y8#3zpeVpyY#`c zwYQfV^|c7RyK<-W{_g(=jbP0l+XkKRa7vc literal 0 HcmV?d00001 diff --git a/ElectronNET.WebApp/Controllers/HomeController.cs b/ElectronNET.WebApp/Controllers/HomeController.cs index 3dea985..5f2146c 100644 --- a/ElectronNET.WebApp/Controllers/HomeController.cs +++ b/ElectronNET.WebApp/Controllers/HomeController.cs @@ -10,11 +10,7 @@ namespace ElectronNET.WebApp.Controllers public IActionResult Index() { Electron.IpcMain.On("SayHello", (args) => { - Electron.App.CreateNotification(new NotificationOptions - { - Title = "Hallo Robert", - Body = "Nachricht von ASP.NET Core App" - }); + Electron.Notification.Show(new NotificationOptions("Hallo Robert","Nachricht von ASP.NET Core App")); Electron.IpcMain.Send("Goodbye", "Elephant!"); }); diff --git a/ElectronNET.WebApp/ElectronNET.WebApp.csproj b/ElectronNET.WebApp/ElectronNET.WebApp.csproj index 33d154c..afa9da2 100644 --- a/ElectronNET.WebApp/ElectronNET.WebApp.csproj +++ b/ElectronNET.WebApp/ElectronNET.WebApp.csproj @@ -6,6 +6,7 @@ + @@ -24,4 +25,13 @@ + + + + PreserveNewest + + + PreserveNewest + + diff --git a/ElectronNET.WebApp/Startup.cs b/ElectronNET.WebApp/Startup.cs index a1648ec..8ad87aa 100644 --- a/ElectronNET.WebApp/Startup.cs +++ b/ElectronNET.WebApp/Startup.cs @@ -45,20 +45,40 @@ namespace ElectronNET.WebApp { Electron.Menu.SetApplicationMenu(new MenuItem[] { new MenuItem { - Label = "Datei", + Label = "File", Submenu = new MenuItem[] { new MenuItem { - Label = "Beenden", + Label = "Exit", Click = () => { Electron.App.Exit(); } } } + }, + new MenuItem + { + Label = "About", + Click = async () => { + await Electron.Dialog.ShowMessageBoxAsync(new MessageBoxOptions("(c) 2017 Gregor Biswanger & Robert Muehsig") { + Title = "About us...", + Type = "info" + }); + } } }); var browserWindow = await Electron.WindowManager.CreateWindowAsync(); + + Electron.Tray.Show("/Assets/electron_32x32.png", new MenuItem[] { + new MenuItem { + Label = "Exit", + Click = () => + { + Electron.App.Exit(); + } + } + }); } } } diff --git a/ElectronNET.WebApp/Views/Home/Index.cshtml b/ElectronNET.WebApp/Views/Home/Index.cshtml index 22197ef..75b5ee5 100644 --- a/ElectronNET.WebApp/Views/Home/Index.cshtml +++ b/ElectronNET.WebApp/Views/Home/Index.cshtml @@ -3,7 +3,6 @@ - Home

Hello from ASP.NET Core MVC!

From a4aa1dfae3e361fb2cc6922a755c43134a341806 Mon Sep 17 00:00:00 2001 From: Gregor Biswanger Date: Sun, 15 Oct 2017 21:39:52 +0200 Subject: [PATCH 8/8] implement BrowserWindow-API functions --- ElectronNET.API/BrowserWindow.cs | 598 +++++++++++++++++- .../BrowserWindowConstructorOptions.cs | 244 ------- .../Entities/BrowserWindowOptions.cs | 235 +++++++ ElectronNET.API/Entities/Rectangle.cs | 10 + ElectronNET.API/Entities/Size.cs | 9 + .../Extensions/MenuItemExtensions.cs | 53 ++ ElectronNET.API/IpcMain.cs | 17 +- ElectronNET.API/Menu.cs | 45 +- ElectronNET.API/Tray.cs | 12 + ElectronNET.API/WindowManager.cs | 6 +- ElectronNET.Host/api/browserWindows.js | 66 +- ElectronNET.Host/api/browserWindows.js.map | 2 +- ElectronNET.Host/api/browserWindows.ts | 200 +++++- ElectronNET.Host/api/dialog.js | 4 +- ElectronNET.Host/api/dialog.js.map | 2 +- ElectronNET.Host/api/dialog.ts | 4 +- ElectronNET.Host/api/ipc.js | 19 +- ElectronNET.Host/api/ipc.js.map | 2 +- ElectronNET.Host/api/ipc.ts | 8 +- ElectronNET.Host/api/tray.js | 13 + ElectronNET.Host/api/tray.js.map | 2 +- ElectronNET.Host/api/tray.ts | 16 + ElectronNET.Host/main.js | 4 +- .../Controllers/HomeController.cs | 15 +- ElectronNET.WebApp/Startup.cs | 17 +- 25 files changed, 1261 insertions(+), 342 deletions(-) delete mode 100644 ElectronNET.API/Entities/BrowserWindowConstructorOptions.cs create mode 100644 ElectronNET.API/Entities/Rectangle.cs create mode 100644 ElectronNET.API/Entities/Size.cs create mode 100644 ElectronNET.API/Extensions/MenuItemExtensions.cs diff --git a/ElectronNET.API/BrowserWindow.cs b/ElectronNET.API/BrowserWindow.cs index 55fdaa8..790f0f0 100644 --- a/ElectronNET.API/BrowserWindow.cs +++ b/ElectronNET.API/BrowserWindow.cs @@ -1,4 +1,10 @@ -namespace ElectronNET.API +using ElectronNET.API.Entities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using System.Threading.Tasks; + +namespace ElectronNET.API { public class BrowserWindow { @@ -8,9 +14,599 @@ Id = id; } + /// + /// Force closing the window, the unload and beforeunload event won’t be + /// emitted for the web page, and close event will also not be emitted + /// for this window, but it guarantees the closed event will be emitted. + /// + public void Destroy() + { + BridgeConnector.Socket.Emit("browserWindow-destroy", Id); + } + + /// + /// Try to close the window. This has the same effect as a user manually + /// clicking the close button of the window. The web page may cancel the close though. + /// + public void Close() + { + BridgeConnector.Socket.Emit("browserWindow-close", Id); + } + + /// + /// Focuses on the window. + /// + public void Focus() + { + BridgeConnector.Socket.Emit("browserWindow-focus", Id); + } + + /// + /// Removes focus from the window. + /// + public void Blur() + { + BridgeConnector.Socket.Emit("browserWindow-blur", Id); + } + + /// + /// Whether the window is focused. + /// + /// + public Task IsFocusedAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isFocused-completed", (isFocused) => { + BridgeConnector.Socket.Off("browserWindow-isFocused-completed"); + + taskCompletionSource.SetResult((bool)isFocused); + }); + + BridgeConnector.Socket.Emit("browserWindow-isFocused", Id); + + return taskCompletionSource.Task; + } + + /// + /// Whether the window is destroyed. + /// + /// + public Task IsDestroyedAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isDestroyed-completed", (isDestroyed) => { + BridgeConnector.Socket.Off("browserWindow-isDestroyed-completed"); + + taskCompletionSource.SetResult((bool)isDestroyed); + }); + + BridgeConnector.Socket.Emit("browserWindow-isDestroyed", Id); + + return taskCompletionSource.Task; + } + + /// + /// Shows and gives focus to the window. + /// + public void Show() + { + BridgeConnector.Socket.Emit("browserWindow-show", Id); + } + + /// + /// Shows the window but doesn’t focus on it. + /// + public void ShowInactive() + { + BridgeConnector.Socket.Emit("browserWindow-showInactive", Id); + } + + /// + /// Hides the window. + /// + public void Hide() + { + BridgeConnector.Socket.Emit("browserWindow-hide", Id); + } + + /// + /// Whether the window is visible to the user. + /// + /// + public Task IsVisibleAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isVisible-completed", (isVisible) => { + BridgeConnector.Socket.Off("browserWindow-isVisible-completed"); + + taskCompletionSource.SetResult((bool)isVisible); + }); + + BridgeConnector.Socket.Emit("browserWindow-isVisible", Id); + + return taskCompletionSource.Task; + } + + /// + /// Whether current window is a modal window. + /// + /// + public Task IsModalAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isModal-completed", (isModal) => { + BridgeConnector.Socket.Off("browserWindow-isModal-completed"); + + taskCompletionSource.SetResult((bool)isModal); + }); + + BridgeConnector.Socket.Emit("browserWindow-isModal", Id); + + return taskCompletionSource.Task; + } + + /// + /// Maximizes the window. This will also show (but not focus) the window if it isn’t being displayed already. + /// + public void Maximize() + { + BridgeConnector.Socket.Emit("browserWindow-maximize", Id); + } + + /// + /// Unmaximizes the window. + /// + public void Unmaximize() + { + BridgeConnector.Socket.Emit("browserWindow-unmaximize", Id); + } + + /// + /// Whether the window is maximized. + /// + /// + public Task IsMaximizedAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isMaximized-completed", (isMaximized) => { + BridgeConnector.Socket.Off("browserWindow-isMaximized-completed"); + + taskCompletionSource.SetResult((bool)isMaximized); + }); + + BridgeConnector.Socket.Emit("browserWindow-isMaximized", Id); + + return taskCompletionSource.Task; + } + + /// + /// Minimizes the window. On some platforms the minimized window will be shown in the Dock. + /// public void Minimize() { BridgeConnector.Socket.Emit("browserWindow-minimize", Id); } + + /// + /// Restores the window from minimized state to its previous state. + /// + public void Restore() + { + BridgeConnector.Socket.Emit("browserWindow-restore", Id); + } + + /// + /// Whether the window is minimized. + /// + /// + public Task IsMinimizedAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isMinimized-completed", (isMinimized) => { + BridgeConnector.Socket.Off("browserWindow-isMinimized-completed"); + + taskCompletionSource.SetResult((bool)isMinimized); + }); + + BridgeConnector.Socket.Emit("browserWindow-isMinimized", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets whether the window should be in fullscreen mode. + /// + public void SetFullScreen(bool flag) + { + BridgeConnector.Socket.Emit("browserWindow-setFullScreen", Id, flag); + } + + /// + /// Whether the window is in fullscreen mode. + /// + /// + public Task IsFullScreenAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isFullScreen-completed", (isFullScreen) => { + BridgeConnector.Socket.Off("browserWindow-isFullScreen-completed"); + + taskCompletionSource.SetResult((bool)isFullScreen); + }); + + BridgeConnector.Socket.Emit("browserWindow-isFullScreen", Id); + + return taskCompletionSource.Task; + } + + /// + /// This will make a window maintain an aspect ratio. The extra size allows a developer to have space, + /// specified in pixels, not included within the aspect ratio calculations. This API already takes into + /// account the difference between a window’s size and its content size. + /// + /// Consider a normal window with an HD video player and associated controls.Perhaps there are 15 pixels + /// of controls on the left edge, 25 pixels of controls on the right edge and 50 pixels of controls below + /// the player. In order to maintain a 16:9 aspect ratio (standard aspect ratio for HD @1920x1080) within + /// the player itself we would call this function with arguments of 16/9 and[40, 50]. The second argument + /// doesn’t care where the extra width and height are within the content view–only that they exist. Just + /// sum any extra width and height areas you have within the overall content view. + /// + /// The aspect ratio to maintain for some portion of the content view. + /// The extra size not to be included while maintaining the aspect ratio. + public void SetAspectRatio(int aspectRatio, Size extraSize) + { + BridgeConnector.Socket.Emit("browserWindow-setAspectRatio", Id, aspectRatio, JObject.FromObject(extraSize, _jsonSerializer)); + } + + /// + /// Uses Quick Look to preview a file at a given path. + /// + /// The absolute path to the file to preview with QuickLook. This is important as + /// Quick Look uses the file name and file extension on the path to determine the content type of the + /// file to open. + public void PreviewFile(string path) + { + BridgeConnector.Socket.Emit("browserWindow-previewFile", Id, path); + } + + /// + /// Uses Quick Look to preview a file at a given path. + /// + /// The absolute path to the file to preview with QuickLook. This is important as + /// Quick Look uses the file name and file extension on the path to determine the content type of the + /// file to open. + /// The name of the file to display on the Quick Look modal view. This is + /// purely visual and does not affect the content type of the file. Defaults to path. + public void PreviewFile(string path, string displayname) + { + BridgeConnector.Socket.Emit("browserWindow-previewFile", Id, path, displayname); + } + + /// + /// Closes the currently open Quick Look panel. + /// + public void CloseFilePreview() + { + BridgeConnector.Socket.Emit("browserWindow-closeFilePreview", Id); + } + + /// + /// Resizes and moves the window to the supplied bounds + /// + /// + public void SetBounds(Rectangle bounds) + { + BridgeConnector.Socket.Emit("browserWindow-setBounds", Id, JObject.FromObject(bounds, _jsonSerializer)); + } + + /// + /// Resizes and moves the window to the supplied bounds + /// + /// + /// + public void SetBounds(Rectangle bounds, bool animate) + { + BridgeConnector.Socket.Emit("browserWindow-setBounds", Id, JObject.FromObject(bounds, _jsonSerializer), animate); + } + + public Task GetBoundsAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getBounds-completed", (getBounds) => { + BridgeConnector.Socket.Off("browserWindow-getBounds-completed"); + + taskCompletionSource.SetResult(((JObject)getBounds).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getBounds", Id); + + return taskCompletionSource.Task; + } + + /// + /// Resizes and moves the window’s client area (e.g. the web page) to the supplied bounds. + /// + /// + public void SetContentBounds(Rectangle bounds) + { + BridgeConnector.Socket.Emit("browserWindow-setContentBounds", Id, JObject.FromObject(bounds, _jsonSerializer)); + } + + /// + /// Resizes and moves the window’s client area (e.g. the web page) to the supplied bounds. + /// + /// + /// + public void SetContentBounds(Rectangle bounds, bool animate) + { + BridgeConnector.Socket.Emit("browserWindow-setContentBounds", Id, JObject.FromObject(bounds, _jsonSerializer), animate); + } + + public Task GetContentBoundsAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getContentBounds-completed", (getContentBounds) => { + BridgeConnector.Socket.Off("browserWindow-getContentBounds-completed"); + + taskCompletionSource.SetResult(((JObject)getContentBounds).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getContentBounds", Id); + + return taskCompletionSource.Task; + } + + /// + /// Resizes the window to width and height. + /// + /// + /// + /// + public void SetSize(int width, int height) + { + BridgeConnector.Socket.Emit("browserWindow-setSize", Id, width, height); + } + + /// + /// Resizes the window to width and height. + /// + /// + /// + /// + public void SetSize(int width, int height, bool animate) + { + BridgeConnector.Socket.Emit("browserWindow-setSize", Id, width, height, animate); + } + + /// + /// Contains the window’s width and height. + /// + /// + public Task GetSizeAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getSize-completed", (size) => { + BridgeConnector.Socket.Off("browserWindow-getSize-completed"); + + taskCompletionSource.SetResult(((JArray)size).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getSize", Id); + + return taskCompletionSource.Task; + } + + /// + /// Resizes the window’s client area (e.g. the web page) to width and height. + /// + /// + /// + /// + public void SetContentSize(int width, int height) + { + BridgeConnector.Socket.Emit("browserWindow-setContentSize", Id, width, height); + } + + /// + /// Resizes the window’s client area (e.g. the web page) to width and height. + /// + /// + /// + /// + public void SetContentSize(int width, int height, bool animate) + { + BridgeConnector.Socket.Emit("browserWindow-setContentSize", Id, width, height, animate); + } + + /// + /// Contains the window’s client area’s width and height. + /// + /// + public Task GetContentSizeAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getContentSize-completed", (size) => { + BridgeConnector.Socket.Off("browserWindow-getContentSize-completed"); + + taskCompletionSource.SetResult(((JArray)size).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getContentSize", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets the minimum size of window to width and height. + /// + /// + /// + public void SetMinimumSize(int width, int height) + { + BridgeConnector.Socket.Emit("browserWindow-setMinimumSize", Id, width, height); + } + + /// + /// Contains the window’s minimum width and height. + /// + /// + public Task GetMinimumSizeAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getMinimumSize-completed", (size) => { + BridgeConnector.Socket.Off("browserWindow-getMinimumSize-completed"); + + taskCompletionSource.SetResult(((JArray)size).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getMinimumSize", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets the maximum size of window to width and height. + /// + /// + /// + public void SetMaximumSize(int width, int height) + { + BridgeConnector.Socket.Emit("browserWindow-setMaximumSize", Id, width, height); + } + + /// + /// Contains the window’s maximum width and height. + /// + /// + public Task GetMaximumSizeAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getMaximumSize-completed", (size) => { + BridgeConnector.Socket.Off("browserWindow-getMaximumSize-completed"); + + taskCompletionSource.SetResult(((JArray)size).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getMaximumSize", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets whether the window can be manually resized by user. + /// + /// + public void SetResizable(bool resizable) + { + BridgeConnector.Socket.Emit("browserWindow-setResizable", Id, resizable); + } + + /// + /// Whether the window can be manually resized by user. + /// + /// + public Task IsResizableAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isResizable-completed", (resizable) => { + BridgeConnector.Socket.Off("browserWindow-isResizable-completed"); + + taskCompletionSource.SetResult((bool)resizable); + }); + + BridgeConnector.Socket.Emit("browserWindow-isResizable", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets whether the window can be moved by user. On Linux does nothing. + /// + /// + public void SetMovable(bool movable) + { + BridgeConnector.Socket.Emit("browserWindow-setMovable", Id, movable); + } + + /// + /// Whether the window can be moved by user. + /// + /// On Linux always returns true. + /// + /// On Linux always returns true. + public Task IsMovableAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isMovable-completed", (movable) => { + BridgeConnector.Socket.Off("browserWindow-isMovable-completed"); + + taskCompletionSource.SetResult((bool)movable); + }); + + BridgeConnector.Socket.Emit("browserWindow-isMovable", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets whether the window can be manually minimized by user. On Linux does nothing. + /// + /// + public void SetMinimizable(bool minimizable) + { + BridgeConnector.Socket.Emit("browserWindow-setMinimizable", Id, minimizable); + } + + /// + /// Whether the window can be manually minimized by user. + /// + /// On Linux always returns true. + /// + /// On Linux always returns true. + public Task IsMinimizableAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isMinimizable-completed", (minimizable) => { + BridgeConnector.Socket.Off("browserWindow-isMinimizable-completed"); + + taskCompletionSource.SetResult((bool)minimizable); + }); + + BridgeConnector.Socket.Emit("browserWindow-isMinimizable", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets whether the window can be manually maximized by user. On Linux does nothing. + /// + /// + public void SetMaximizable(bool maximizable) + { + BridgeConnector.Socket.Emit("browserWindow-setMaximizable", Id, maximizable); + } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; } } diff --git a/ElectronNET.API/Entities/BrowserWindowConstructorOptions.cs b/ElectronNET.API/Entities/BrowserWindowConstructorOptions.cs deleted file mode 100644 index 2e6f805..0000000 --- a/ElectronNET.API/Entities/BrowserWindowConstructorOptions.cs +++ /dev/null @@ -1,244 +0,0 @@ -namespace ElectronNET.API.Entities -{ - public class BrowserWindowConstructorOptions - { - /// - /// Window's width in pixels. Default is 800. - /// - public int Width { get; set; } - - /// - /// Window's height in pixels. Default is 600. - /// - public int Height { get; set; } - - /// - /// ( if y is used) Window's left offset from screen. Default is to center the - /// window. - /// - public int X { get; set; } - - /// - /// ( if x is used) Window's top offset from screen. Default is to center the - /// window. - /// - public int Y { get; set; } - - /// - /// The width and height would be used as web page's size, which means the actual - /// window's size will include window frame's size and be slightly larger.Default - /// is false. - /// - public bool UseContentSize { get; set; } - - /// - /// Show window in the center of the screen. - /// - public bool Center { get; set; } - - /// - /// Window's minimum width. Default is 0. - /// - public int MinWidth { get; set; } - - /// - /// Window's minimum height. Default is 0. - /// - public int MinHeight { get; set; } - - /// - /// Window's maximum width. Default is no limit. - /// - public int MaxWidth { get; set; } - - /// - /// Window's maximum height. Default is no limit. - /// - public int MaxHeight { get; set; } - - /// - /// Whether window is resizable. Default is true. - /// - public bool Resizable { get; set; } - - /// - /// Whether window is movable. This is not implemented on Linux. Default is true. - /// - public bool Movable { get; set; } - - /// - /// Whether window is minimizable. This is not implemented on Linux. Default is true. - /// - public bool Minimizable { get; set; } - - /// - /// Whether window is maximizable. This is not implemented on Linux. Default is true. - /// - public bool Maximizable { get; set; } - - /// - /// Whether window is closable. This is not implemented on Linux. Default is true. - /// - public bool Closable { get; set; } - - /// - /// Whether the window can be focused. Default is true. On Windows setting - /// focusable: false also implies setting skipTaskbar: true. On Linux setting - /// focusable: false makes the window stop interacting with wm, so the window will - /// always stay on top in all workspaces. - /// - public bool Focusable { get; set; } - - /// - /// Whether the window should always stay on top of other windows. Default is false. - /// - public bool AlwaysOnTop { get; set; } - - /// - /// Whether the window should show in fullscreen. When explicitly set to false the - /// fullscreen button will be hidden or disabled on macOS.Default is false. - /// - public bool Fullscreen { get; set; } - - /// - /// Whether the window can be put into fullscreen mode. On macOS, also whether the - /// maximize/zoom button should toggle full screen mode or maximize window.Default - /// is true. - /// - public bool Fullscreenable { get; set; } - - /// - /// Whether to show the window in taskbar. Default is false. - /// - public bool SkipTaskbar { get; set; } - - /// - /// The kiosk mode. Default is false. - /// - public bool Kiosk { get; set; } - - /// - /// Default window title. Default is "Electron.NET". - /// - public string Title { get; set; } = "Electron.NET"; - - /// - /// The window icon. On Windows it is recommended to use ICO icons to get best - /// visual effects, you can also leave it undefined so the executable's icon will be used. - /// - public string Icon { get; set; } - - /// - /// Whether window should be shown when created. Default is true. - /// - public bool Show { get; set; } - - /// - /// Specify false to create a . Default is true. - /// - public bool Frame { get; set; } - - /// - /// Whether this is a modal window. This only works when the window is a child - /// window.Default is false. - /// - public bool Modal { get; set; } - - /// - /// Whether the web view accepts a single mouse-down event that simultaneously - /// activates the window.Default is false. - /// - public bool AcceptFirstMouse { get; set; } - - /// - /// Whether to hide cursor when typing. Default is false. - /// - public bool DisableAutoHideCursor { get; set; } - - /// - /// Auto hide the menu bar unless the Alt key is pressed. Default is false. - /// - public bool AutoHideMenuBar { get; set; } - - /// - /// Enable the window to be resized larger than screen. Default is false. - /// - public bool EnableLargerThanScreen { get; set; } - - /// - /// Window's background color as Hexadecimal value, like #66CD00 or #FFF or - /// #80FFFFFF (alpha is supported). Default is #FFF (white). - /// - public string BackgroundColor { get; set; } - - /// - /// Whether window should have a shadow. This is only implemented on macOS. Default - /// is true. - /// - public bool HasShadow { get; set; } - - /// - /// Forces using dark theme for the window, only works on some GTK+3 desktop - /// environments.Default is false. - /// - public bool DarkTheme { get; set; } - - /// - /// Makes the window . Default is false. - /// - public bool Transparent { get; set; } - - /// - /// The type of window, default is normal window. See more about this below. - /// - public string Type { get; set; } - - /// - /// The style of window title bar. Default is default. Possible values are: - /// 'default' | 'hidden' | 'hidden-inset' | 'hiddenInset' | 'customButtonsOnHover' - /// - public string TitleBarStyle { get; set; } - - /// - /// Shows the title in the tile bar in full screen mode on macOS for all - /// titleBarStyle options.Default is false. - /// - public bool FullscreenWindowTitle { get; set; } - - /// - /// Use WS_THICKFRAME style for frameless windows on Windows, which adds standard - /// window frame.Setting it to false will remove window shadow and window - /// animations.Default is true. - /// - public bool ThickFrame { get; set; } - - /// - /// Add a type of vibrancy effect to the window, only on macOS. Can be - /// appearance-based, light, dark, titlebar, selection, menu, popover, sidebar, - /// medium-light or ultra-dark. - /// - public string Vibrancy { get; set; } - - /// - /// Controls the behavior on macOS when option-clicking the green stoplight button - /// on the toolbar or by clicking the Window > Zoom menu item.If true, the window - /// will grow to the preferred width of the web page when zoomed, false will cause - /// it to zoom to the width of the screen.This will also affect the behavior when - /// calling maximize() directly.Default is false. - /// - public bool ZoomToPageWidth { get; set; } - - /// - /// Tab group name, allows opening the window as a native tab on macOS 10.12+. - /// Windows with the same tabbing identifier will be grouped together.This also - /// adds a native new tab button to your window's tab bar and allows your app and - /// window to receive the new-window-for-tab event. - /// - public string TabbingIdentifier { get; set; } - - /// - /// Settings of web page's features. - /// - public WebPreferences WebPreferences { get; set; } - } -} diff --git a/ElectronNET.API/Entities/BrowserWindowOptions.cs b/ElectronNET.API/Entities/BrowserWindowOptions.cs index 8a817e7..9e6ed33 100644 --- a/ElectronNET.API/Entities/BrowserWindowOptions.cs +++ b/ElectronNET.API/Entities/BrowserWindowOptions.cs @@ -2,8 +2,243 @@ { public class BrowserWindowOptions { + /// + /// Window's width in pixels. Default is 800. + /// public int Width { get; set; } + + /// + /// Window's height in pixels. Default is 600. + /// public int Height { get; set; } + + /// + /// ( if y is used) Window's left offset from screen. Default is to center the + /// window. + /// + public int X { get; set; } + + /// + /// ( if x is used) Window's top offset from screen. Default is to center the + /// window. + /// + public int Y { get; set; } + + /// + /// The width and height would be used as web page's size, which means the actual + /// window's size will include window frame's size and be slightly larger.Default + /// is false. + /// + public bool UseContentSize { get; set; } + + /// + /// Show window in the center of the screen. + /// + public bool Center { get; set; } + + /// + /// Window's minimum width. Default is 0. + /// + public int MinWidth { get; set; } + + /// + /// Window's minimum height. Default is 0. + /// + public int MinHeight { get; set; } + + /// + /// Window's maximum width. Default is no limit. + /// + public int MaxWidth { get; set; } + + /// + /// Window's maximum height. Default is no limit. + /// + public int MaxHeight { get; set; } + + /// + /// Whether window is resizable. Default is true. + /// + public bool Resizable { get; set; } + + /// + /// Whether window is movable. This is not implemented on Linux. Default is true. + /// + public bool Movable { get; set; } + + /// + /// Whether window is minimizable. This is not implemented on Linux. Default is true. + /// + public bool Minimizable { get; set; } + + /// + /// Whether window is maximizable. This is not implemented on Linux. Default is true. + /// + public bool Maximizable { get; set; } + + /// + /// Whether window is closable. This is not implemented on Linux. Default is true. + /// + public bool Closable { get; set; } + + /// + /// Whether the window can be focused. Default is true. On Windows setting + /// focusable: false also implies setting skipTaskbar: true. On Linux setting + /// focusable: false makes the window stop interacting with wm, so the window will + /// always stay on top in all workspaces. + /// + public bool Focusable { get; set; } + + /// + /// Whether the window should always stay on top of other windows. Default is false. + /// + public bool AlwaysOnTop { get; set; } + + /// + /// Whether the window should show in fullscreen. When explicitly set to false the + /// fullscreen button will be hidden or disabled on macOS.Default is false. + /// + public bool Fullscreen { get; set; } + + /// + /// Whether the window can be put into fullscreen mode. On macOS, also whether the + /// maximize/zoom button should toggle full screen mode or maximize window.Default + /// is true. + /// + public bool Fullscreenable { get; set; } + + /// + /// Whether to show the window in taskbar. Default is false. + /// + public bool SkipTaskbar { get; set; } + + /// + /// The kiosk mode. Default is false. + /// + public bool Kiosk { get; set; } + + /// + /// Default window title. Default is "Electron.NET". + /// + public string Title { get; set; } = "Electron.NET"; + + /// + /// The window icon. On Windows it is recommended to use ICO icons to get best + /// visual effects, you can also leave it undefined so the executable's icon will be used. + /// + public string Icon { get; set; } + + /// + /// Whether window should be shown when created. Default is true. + /// public bool Show { get; set; } + + /// + /// Specify false to create a . Default is true. + /// + public bool Frame { get; set; } + + /// + /// Whether this is a modal window. This only works when the window is a child + /// window.Default is false. + /// + public bool Modal { get; set; } + + /// + /// Whether the web view accepts a single mouse-down event that simultaneously + /// activates the window.Default is false. + /// + public bool AcceptFirstMouse { get; set; } + + /// + /// Whether to hide cursor when typing. Default is false. + /// + public bool DisableAutoHideCursor { get; set; } + + /// + /// Auto hide the menu bar unless the Alt key is pressed. Default is false. + /// + public bool AutoHideMenuBar { get; set; } + + /// + /// Enable the window to be resized larger than screen. Default is false. + /// + public bool EnableLargerThanScreen { get; set; } + + /// + /// Window's background color as Hexadecimal value, like #66CD00 or #FFF or + /// #80FFFFFF (alpha is supported). Default is #FFF (white). + /// + public string BackgroundColor { get; set; } + + /// + /// Whether window should have a shadow. This is only implemented on macOS. Default + /// is true. + /// + public bool HasShadow { get; set; } + + /// + /// Forces using dark theme for the window, only works on some GTK+3 desktop + /// environments.Default is false. + /// + public bool DarkTheme { get; set; } + + /// + /// Makes the window . Default is false. + /// + public bool Transparent { get; set; } + + /// + /// The type of window, default is normal window. + /// + public string Type { get; set; } + + /// + /// The style of window title bar. Default is default. Possible values are: + /// 'default' | 'hidden' | 'hidden-inset' | 'hiddenInset' | 'customButtonsOnHover' + /// + public string TitleBarStyle { get; set; } + + /// + /// Shows the title in the tile bar in full screen mode on macOS for all + /// titleBarStyle options.Default is false. + /// + public bool FullscreenWindowTitle { get; set; } + + /// + /// Use WS_THICKFRAME style for frameless windows on Windows, which adds standard + /// window frame.Setting it to false will remove window shadow and window + /// animations.Default is true. + /// + public bool ThickFrame { get; set; } + + /// + /// Add a type of vibrancy effect to the window, only on macOS. Can be + /// appearance-based, light, dark, titlebar, selection, menu, popover, sidebar, + /// medium-light or ultra-dark. + /// + public string Vibrancy { get; set; } + + /// + /// Controls the behavior on macOS when option-clicking the green stoplight button + /// on the toolbar or by clicking the Window > Zoom menu item.If true, the window + /// will grow to the preferred width of the web page when zoomed, false will cause + /// it to zoom to the width of the screen.This will also affect the behavior when + /// calling maximize() directly.Default is false. + /// + public bool ZoomToPageWidth { get; set; } + + /// + /// Tab group name, allows opening the window as a native tab on macOS 10.12+. + /// Windows with the same tabbing identifier will be grouped together.This also + /// adds a native new tab button to your window's tab bar and allows your app and + /// window to receive the new-window-for-tab event. + /// + public string TabbingIdentifier { get; set; } + + /// + /// Settings of web page's features. + /// + public WebPreferences WebPreferences { get; set; } } } diff --git a/ElectronNET.API/Entities/Rectangle.cs b/ElectronNET.API/Entities/Rectangle.cs new file mode 100644 index 0000000..7e1a8eb --- /dev/null +++ b/ElectronNET.API/Entities/Rectangle.cs @@ -0,0 +1,10 @@ +namespace ElectronNET.API.Entities +{ + public class Rectangle + { + public int X { get; set; } + public int Y { get; set; } + public int Width { get; set; } + public int Height { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/Size.cs b/ElectronNET.API/Entities/Size.cs new file mode 100644 index 0000000..459428a --- /dev/null +++ b/ElectronNET.API/Entities/Size.cs @@ -0,0 +1,9 @@ +namespace ElectronNET.API.Entities +{ + public class Size + { + public int Width { get; set; } + + public int Height { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Extensions/MenuItemExtensions.cs b/ElectronNET.API/Extensions/MenuItemExtensions.cs new file mode 100644 index 0000000..ba5f8b5 --- /dev/null +++ b/ElectronNET.API/Extensions/MenuItemExtensions.cs @@ -0,0 +1,53 @@ +using ElectronNET.API.Entities; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ElectronNET.API.Extensions +{ + internal static class MenuItemExtensions + { + public static MenuItem[] AddMenuItemsId(this MenuItem[] menuItems) + { + for (int index = 0; index < menuItems.Length; index++) + { + var menuItem = menuItems[index]; + if (menuItem?.Submenu?.Length > 0) + { + AddMenuItemsId(menuItem.Submenu); + } + + if (string.IsNullOrEmpty(menuItem.Role) && + string.IsNullOrEmpty(menuItem.Id)) + { + menuItem.Id = Guid.NewGuid().ToString(); + } + } + + return menuItems; + } + + public static MenuItem GetMenuItem(this List menuItems, string id) + { + MenuItem result = new MenuItem(); + + foreach (var item in menuItems) + { + if (item.Id == id) + { + result = item; + } + else if (item?.Submenu?.Length > 0) + { + var menuItem = GetMenuItem(item.Submenu.ToList(), id); + if (menuItem.Id == id) + { + result = menuItem; + } + } + } + + return result; + } + } +} diff --git a/ElectronNET.API/IpcMain.cs b/ElectronNET.API/IpcMain.cs index 5f3dc62..b0f56f9 100644 --- a/ElectronNET.API/IpcMain.cs +++ b/ElectronNET.API/IpcMain.cs @@ -1,4 +1,7 @@ -using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using System; namespace ElectronNET.API { @@ -63,11 +66,19 @@ namespace ElectronNET.API /// no functions or prototype chain will be included. The renderer process handles it by /// listening for channel with ipcRenderer module. ///
+ /// BrowserWindow with channel. /// Channelname. /// Arguments data. - public void Send(string channel, params object[] data) + public void Send(BrowserWindow browserWindow, string channel, params object[] data) { - BridgeConnector.Socket.Emit("sendToIpcRenderer", channel, data); + BridgeConnector.Socket.Emit("sendToIpcRenderer", JObject.FromObject(browserWindow, _jsonSerializer), channel, data); } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; } } \ No newline at end of file diff --git a/ElectronNET.API/Menu.cs b/ElectronNET.API/Menu.cs index 4a82028..cfd1af5 100644 --- a/ElectronNET.API/Menu.cs +++ b/ElectronNET.API/Menu.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json.Serialization; using System.Collections.Generic; using System; using System.Linq; +using ElectronNET.API.Extensions; namespace ElectronNET.API { @@ -32,56 +33,16 @@ namespace ElectronNET.API public void SetApplicationMenu(MenuItem[] menuItems) { - AddMenuItemsId(menuItems); + menuItems.AddMenuItemsId(); BridgeConnector.Socket.Emit("menu-setApplicationMenu", JArray.FromObject(menuItems, _jsonSerializer)); _items.AddRange(menuItems); BridgeConnector.Socket.On("menuItemClicked", (id) => { - MenuItem menuItem = GetMenuItem(_items, id.ToString()); + MenuItem menuItem = _items.GetMenuItem(id.ToString()); menuItem?.Click(); }); } - private void AddMenuItemsId(MenuItem[] menuItems) - { - for (int index = 0; index < menuItems.Length; index++) - { - var menuItem = menuItems[index]; - if(menuItem?.Submenu?.Length > 0) - { - AddMenuItemsId(menuItem.Submenu); - } - - if(string.IsNullOrEmpty(menuItem.Role)) - { - menuItem.Id = Guid.NewGuid().ToString(); - } - } - } - - private MenuItem GetMenuItem(List menuItems, string id) - { - MenuItem result = new MenuItem(); - - foreach (var item in menuItems) - { - if(item.Id == id) - { - result = item; - } - else if(item?.Submenu?.Length > 0) - { - var menuItem = GetMenuItem(item.Submenu.ToList(), id); - if(menuItem.Id == id) - { - result = menuItem; - } - } - } - - return result; - } - private JsonSerializer _jsonSerializer = new JsonSerializer() { ContractResolver = new CamelCasePropertyNamesContractResolver(), diff --git a/ElectronNET.API/Tray.cs b/ElectronNET.API/Tray.cs index 417812e..01599d9 100644 --- a/ElectronNET.API/Tray.cs +++ b/ElectronNET.API/Tray.cs @@ -1,7 +1,9 @@ using ElectronNET.API.Entities; +using ElectronNET.API.Extensions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; +using System.Collections.Generic; namespace ElectronNET.API { @@ -24,9 +26,19 @@ namespace ElectronNET.API } } + public IReadOnlyCollection Items { get { return _items.AsReadOnly(); } } + private List _items = new List(); + public void Show(string image, MenuItem[] menuItems) { + menuItems.AddMenuItemsId(); BridgeConnector.Socket.Emit("create-tray", image, JArray.FromObject(menuItems, _jsonSerializer)); + _items.AddRange(menuItems); + + BridgeConnector.Socket.On("trayMenuItemClicked", (id) => { + MenuItem menuItem = _items.GetMenuItem(id.ToString()); + menuItem?.Click(); + }); } private JsonSerializer _jsonSerializer = new JsonSerializer() diff --git a/ElectronNET.API/WindowManager.cs b/ElectronNET.API/WindowManager.cs index 9622ef4..2103041 100644 --- a/ElectronNET.API/WindowManager.cs +++ b/ElectronNET.API/WindowManager.cs @@ -31,15 +31,17 @@ namespace ElectronNET.API public async Task CreateWindowAsync(string loadUrl = "http://localhost") { - return await CreateWindowAsync(new BrowserWindowConstructorOptions(), loadUrl); + return await CreateWindowAsync(new BrowserWindowOptions(), loadUrl); } - public Task CreateWindowAsync(BrowserWindowConstructorOptions options, string loadUrl = "http://localhost") + public Task CreateWindowAsync(BrowserWindowOptions options, string loadUrl = "http://localhost") { var taskCompletionSource = new TaskCompletionSource(); BridgeConnector.Socket.On("BrowserWindowCreated", (id) => { + BridgeConnector.Socket.Off("BrowserWindowCreated"); + string windowId = id.ToString(); BrowserWindow browserWindow = new BrowserWindow(int.Parse(windowId)); _browserWindows.Add(browserWindow); diff --git a/ElectronNET.Host/api/browserWindows.js b/ElectronNET.Host/api/browserWindows.js index 39efbb2..9ff87d5 100644 --- a/ElectronNET.Host/api/browserWindows.js +++ b/ElectronNET.Host/api/browserWindows.js @@ -2,7 +2,6 @@ exports.__esModule = true; var electron_1 = require("electron"); var windows = []; -var ipc; module.exports = function (socket) { socket.on('createBrowserWindow', function (options, loadUrl) { var window = new electron_1.BrowserWindow(options); @@ -19,19 +18,76 @@ module.exports = function (socket) { } } }); - // TODO: IPC Lösung für mehrere Fenster finden - if (ipc == undefined) { - ipc = require('./ipc')(socket, window); - } if (loadUrl) { window.loadURL(loadUrl); } windows.push(window); socket.emit('BrowserWindowCreated', window.id); }); + socket.on('browserWindow-destroy', function (id) { + getWindowById(id).destroy(); + }); + socket.on('browserWindow-close', function (id) { + getWindowById(id).close(); + }); + socket.on('browserWindow-focus', function (id) { + getWindowById(id).focus(); + }); + socket.on('browserWindow-blur', function (id) { + getWindowById(id).blur(); + }); + socket.on('browserWindow-isFocused', function (id) { + var isFocused = getWindowById(id).isFocused(); + socket.emit('browserWindow-isFocused-completed', isFocused); + }); + socket.on('browserWindow-isDestroyed', function (id) { + var isDestroyed = getWindowById(id).isDestroyed(); + socket.emit('browserWindow-isDestroyed-completed', isDestroyed); + }); + socket.on('browserWindow-show', function (id) { + getWindowById(id).show(); + }); + socket.on('browserWindow-showInactive', function (id) { + getWindowById(id).showInactive(); + }); + socket.on('browserWindow-hide', function (id) { + getWindowById(id).hide(); + }); + socket.on('browserWindow-isVisible', function (id) { + var isVisible = getWindowById(id).isVisible(); + socket.emit('browserWindow-isVisible-completed', isVisible); + }); + socket.on('browserWindow-isModal', function (id) { + var isModal = getWindowById(id).isModal(); + socket.emit('browserWindow-isModal-completed', isModal); + }); + socket.on('browserWindow-maximize', function (id) { + getWindowById(id).maximize(); + }); + socket.on('browserWindow-unmaximize', function (id) { + getWindowById(id).unmaximize(); + }); + socket.on('browserWindow-isMaximized', function (id) { + var isMaximized = getWindowById(id).isMaximized(); + socket.emit('browserWindow-isMaximized-completed', isMaximized); + }); socket.on('browserWindow-minimize', function (id) { getWindowById(id).minimize(); }); + socket.on('browserWindow-restore', function (id) { + getWindowById(id).restore(); + }); + socket.on('browserWindow-isMinimized', function (id) { + var isMinimized = getWindowById(id).isMinimized(); + socket.emit('browserWindow-isMinimized-completed', isMinimized); + }); + socket.on('browserWindow-setFullScreen', function (id, fullscreen) { + getWindowById(id).setFullScreen(fullscreen); + }); + socket.on('browserWindow-isFullScreen', function (id) { + var isFullScreen = getWindowById(id).isFullScreen(); + socket.emit('browserWindow-isFullScreen-completed', isFullScreen); + }); function getWindowById(id) { for (var index = 0; index < windows.length; index++) { var element = windows[index]; diff --git a/ElectronNET.Host/api/browserWindows.js.map b/ElectronNET.Host/api/browserWindows.js.map index 3e67861..b819a5b 100644 --- a/ElectronNET.Host/api/browserWindows.js.map +++ b/ElectronNET.Host/api/browserWindows.js.map @@ -1 +1 @@ -{"version":3,"file":"browserWindows.js","sourceRoot":"","sources":["browserWindows.ts"],"names":[],"mappings":";;AAAA,qCAAyC;AACzC,IAAI,OAAO,GAA6B,EAAE,CAAA;AAC1C,IAAI,GAAG,CAAC;AAER,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,UAAC,OAAO,EAAE,OAAO;QAC9C,IAAI,MAAM,GAAG,IAAI,wBAAa,CAAC,OAAO,CAAC,CAAC;QAExC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAC,MAAM;YACvB,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;gBAClD,IAAI,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;gBAChC,IAAI,CAAC;oBACD,UAAU,CAAC,EAAE,CAAC;gBAClB,CAAC;gBAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;oBACb,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,2BAA2B,CAAC,CAAC,CAAC;wBAChD,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;oBAC7B,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,+CAA+C;QAC/C,EAAE,CAAC,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC;YACnB,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAED,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACV,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,UAAC,EAAE;QACnC,aAAa,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,uBAAuB,EAAU;QAC7B,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YAClD,IAAI,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7B,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBACnB,MAAM,CAAC,OAAO,CAAC;YACnB,CAAC;QACL,CAAC;IACL,CAAC;AACL,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"browserWindows.js","sourceRoot":"","sources":["browserWindows.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/ElectronNET.Host/api/browserWindows.ts b/ElectronNET.Host/api/browserWindows.ts index 105b9ed..0614241 100644 --- a/ElectronNET.Host/api/browserWindows.ts +++ b/ElectronNET.Host/api/browserWindows.ts @@ -1,6 +1,5 @@ import { BrowserWindow } from "electron"; -let windows: Electron.BrowserWindow[] = [] -let ipc; +const windows: Electron.BrowserWindow[] = [] module.exports = (socket: SocketIO.Server) => { socket.on('createBrowserWindow', (options, loadUrl) => { @@ -19,11 +18,6 @@ module.exports = (socket: SocketIO.Server) => { } }); - // TODO: IPC Lösung für mehrere Fenster finden - if (ipc == undefined) { - ipc = require('./ipc')(socket, window); - } - if (loadUrl) { window.loadURL(loadUrl); } @@ -32,10 +26,202 @@ module.exports = (socket: SocketIO.Server) => { socket.emit('BrowserWindowCreated', window.id); }); + socket.on('browserWindow-destroy', (id) => { + getWindowById(id).destroy(); + }); + + socket.on('browserWindow-close', (id) => { + getWindowById(id).close(); + }); + + socket.on('browserWindow-focus', (id) => { + getWindowById(id).focus(); + }); + + socket.on('browserWindow-blur', (id) => { + getWindowById(id).blur(); + }); + + socket.on('browserWindow-isFocused', (id) => { + const isFocused = getWindowById(id).isFocused(); + + socket.emit('browserWindow-isFocused-completed', isFocused); + }); + + socket.on('browserWindow-isDestroyed', (id) => { + const isDestroyed = getWindowById(id).isDestroyed(); + + socket.emit('browserWindow-isDestroyed-completed', isDestroyed); + }); + + socket.on('browserWindow-show', (id) => { + getWindowById(id).show(); + }); + + socket.on('browserWindow-showInactive', (id) => { + getWindowById(id).showInactive(); + }); + + socket.on('browserWindow-hide', (id) => { + getWindowById(id).hide(); + }); + + socket.on('browserWindow-isVisible', (id) => { + const isVisible = getWindowById(id).isVisible(); + + socket.emit('browserWindow-isVisible-completed', isVisible); + }); + + socket.on('browserWindow-isModal', (id) => { + const isModal = getWindowById(id).isModal(); + + socket.emit('browserWindow-isModal-completed', isModal); + }); + + socket.on('browserWindow-maximize', (id) => { + getWindowById(id).maximize(); + }); + + socket.on('browserWindow-unmaximize', (id) => { + getWindowById(id).unmaximize(); + }); + + socket.on('browserWindow-isMaximized', (id) => { + const isMaximized = getWindowById(id).isMaximized(); + + socket.emit('browserWindow-isMaximized-completed', isMaximized); + }); + socket.on('browserWindow-minimize', (id) => { getWindowById(id).minimize(); }); + socket.on('browserWindow-restore', (id) => { + getWindowById(id).restore(); + }); + + socket.on('browserWindow-isMinimized', (id) => { + const isMinimized = getWindowById(id).isMinimized(); + + socket.emit('browserWindow-isMinimized-completed', isMinimized); + }); + + socket.on('browserWindow-setFullScreen', (id, fullscreen) => { + getWindowById(id).setFullScreen(fullscreen); + }); + + socket.on('browserWindow-isFullScreen', (id) => { + const isFullScreen = getWindowById(id).isFullScreen(); + + socket.emit('browserWindow-isFullScreen-completed', isFullScreen); + }); + + socket.on('browserWindow-setAspectRatio', (id, aspectRatio, extraSize) => { + getWindowById(id).setAspectRatio(aspectRatio, extraSize); + }); + + socket.on('browserWindow-previewFile', (id, path, displayname) => { + getWindowById(id).previewFile(path, displayname); + }); + + socket.on('browserWindow-closeFilePreview', (id) => { + getWindowById(id).closeFilePreview(); + }); + + socket.on('browserWindow-setBounds', (id, bounds, animate) => { + getWindowById(id).setBounds(bounds, animate); + }); + + socket.on('browserWindow-getBounds', (id) => { + const rectangle = getWindowById(id).getBounds(); + + socket.emit('browserWindow-getBounds-completed', rectangle); + }); + + socket.on('browserWindow-setContentBounds', (id, bounds, animate) => { + getWindowById(id).setContentBounds(bounds, animate); + }); + + socket.on('browserWindow-getContentBounds', (id) => { + const rectangle = getWindowById(id).getContentBounds(); + + socket.emit('browserWindow-getContentBounds-completed', rectangle); + }); + + socket.on('browserWindow-setSize', (id, width, height, animate) => { + getWindowById(id).setSize(width, height, animate); + }); + + socket.on('browserWindow-getSize', (id) => { + const size = getWindowById(id).getSize(); + + socket.emit('browserWindow-getSize-completed', size); + }); + + socket.on('browserWindow-setContentSize', (id, width, height, animate) => { + getWindowById(id).setContentSize(width, height, animate); + }); + + socket.on('browserWindow-getContentSize', (id) => { + const size = getWindowById(id).getContentSize(); + + socket.emit('browserWindow-getContentSize-completed', size); + }); + + socket.on('browserWindow-setMinimumSize', (id, width, height) => { + getWindowById(id).setMinimumSize(width, height); + }); + + socket.on('browserWindow-getMinimumSize', (id) => { + const size = getWindowById(id).getMinimumSize(); + + socket.emit('browserWindow-getMinimumSize-completed', size); + }); + + socket.on('browserWindow-setMaximumSize', (id, width, height) => { + getWindowById(id).setMaximumSize(width, height); + }); + + socket.on('browserWindow-getMaximumSize', (id) => { + const size = getWindowById(id).getMaximumSize(); + + socket.emit('browserWindow-getMaximumSize-completed', size); + }); + + socket.on('browserWindow-setResizable', (id, resizable) => { + getWindowById(id).setResizable(resizable); + }); + + socket.on('browserWindow-isResizable', (id) => { + const resizable = getWindowById(id).isResizable(); + + socket.emit('browserWindow-isResizable-completed', resizable); + }); + + socket.on('browserWindow-setMovable', (id, movable) => { + getWindowById(id).setMovable(movable); + }); + + socket.on('browserWindow-isMovable', (id) => { + const movable = getWindowById(id).isMovable(); + + socket.emit('browserWindow-isMovable-completed', movable); + }); + + socket.on('browserWindow-setMinimizable', (id, minimizable) => { + getWindowById(id).setMinimizable(minimizable); + }); + + socket.on('browserWindow-isMinimizable', (id) => { + const minimizable = getWindowById(id).isMinimizable(); + + socket.emit('browserWindow-isMinimizable-completed', minimizable); + }); + + socket.on('browserWindow-setMaximizable', (id, maximizable) => { + getWindowById(id).setMaximizable(maximizable); + }); + function getWindowById(id: number): Electron.BrowserWindow { for (var index = 0; index < windows.length; index++) { var element = windows[index]; diff --git a/ElectronNET.Host/api/dialog.js b/ElectronNET.Host/api/dialog.js index e3f106e..7d0a88a 100644 --- a/ElectronNET.Host/api/dialog.js +++ b/ElectronNET.Host/api/dialog.js @@ -6,12 +6,12 @@ module.exports = function (socket) { if ("id" in browserWindow) { var window = electron_1.BrowserWindow.fromId(browserWindow.id); electron_1.dialog.showMessageBox(window, options, function (response, checkboxChecked) { - socket.emit('showMessageBoxComplete', response, checkboxChecked); + socket.emit('showMessageBoxComplete', [response, checkboxChecked]); }); } else { electron_1.dialog.showMessageBox(browserWindow, function (response, checkboxChecked) { - socket.emit('showMessageBoxComplete', response, checkboxChecked); + socket.emit('showMessageBoxComplete', [response, checkboxChecked]); }); } }); diff --git a/ElectronNET.Host/api/dialog.js.map b/ElectronNET.Host/api/dialog.js.map index f7a9d8d..672e716 100644 --- a/ElectronNET.Host/api/dialog.js.map +++ b/ElectronNET.Host/api/dialog.js.map @@ -1 +1 @@ -{"version":3,"file":"dialog.js","sourceRoot":"","sources":["dialog.ts"],"names":[],"mappings":";;AAAA,qCAAiD;AAEjD,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,UAAC,aAAa,EAAE,OAAO;QAC/C,EAAE,CAAA,CAAC,IAAI,IAAI,aAAa,CAAC,CAAC,CAAC;YACvB,IAAI,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAEpD,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC7D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;YACrE,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,IAAI,CAAC,CAAC;YACJ,iBAAM,CAAC,cAAc,CAAC,aAAa,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC3D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;YACrE,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"dialog.js","sourceRoot":"","sources":["dialog.ts"],"names":[],"mappings":";;AAAA,qCAAiD;AAEjD,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,UAAC,aAAa,EAAE,OAAO;QAC/C,EAAE,CAAA,CAAC,IAAI,IAAI,aAAa,CAAC,CAAC,CAAC;YACvB,IAAI,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAEpD,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC7D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,IAAI,CAAC,CAAC;YACJ,iBAAM,CAAC,cAAc,CAAC,aAAa,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC3D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/dialog.ts b/ElectronNET.Host/api/dialog.ts index c445e51..24a03c2 100644 --- a/ElectronNET.Host/api/dialog.ts +++ b/ElectronNET.Host/api/dialog.ts @@ -6,11 +6,11 @@ module.exports = (socket: SocketIO.Server) => { var window = BrowserWindow.fromId(browserWindow.id); dialog.showMessageBox(window, options, (response, checkboxChecked) => { - socket.emit('showMessageBoxComplete', response, checkboxChecked); + socket.emit('showMessageBoxComplete', [response, checkboxChecked]); }); } else { dialog.showMessageBox(browserWindow, (response, checkboxChecked) => { - socket.emit('showMessageBoxComplete', response, checkboxChecked); + socket.emit('showMessageBoxComplete', [response, checkboxChecked]); }); } }); diff --git a/ElectronNET.Host/api/ipc.js b/ElectronNET.Host/api/ipc.js index 80535a6..81d465d 100644 --- a/ElectronNET.Host/api/ipc.js +++ b/ElectronNET.Host/api/ipc.js @@ -1,23 +1,26 @@ -var ipcMain = require('electron').ipcMain; -module.exports = function (socket, window) { +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +module.exports = function (socket) { socket.on('registerIpcMainChannel', function (channel) { - ipcMain.on(channel, function (event, args) { + electron_1.ipcMain.on(channel, function (event, args) { socket.emit(channel, [event.preventDefault(), args]); }); }); socket.on('registerOnceIpcMainChannel', function (channel) { - ipcMain.once(channel, function (event, args) { + electron_1.ipcMain.once(channel, function (event, args) { socket.emit(channel, [event.preventDefault(), args]); }); }); socket.on('removeAllListenersIpcMainChannel', function (channel) { - ipcMain.removeAllListeners(channel); + electron_1.ipcMain.removeAllListeners(channel); }); - socket.on('sendToIpcRenderer', function (channel) { + socket.on('sendToIpcRenderer', function (browserWindow, channel) { var data = []; - for (var _i = 1; _i < arguments.length; _i++) { - data[_i - 1] = arguments[_i]; + for (var _i = 2; _i < arguments.length; _i++) { + data[_i - 2] = arguments[_i]; } + var window = electron_1.BrowserWindow.fromId(browserWindow.id); if (window) { window.webContents.send(channel, data); } diff --git a/ElectronNET.Host/api/ipc.js.map b/ElectronNET.Host/api/ipc.js.map index 36547f4..dea2f4c 100644 --- a/ElectronNET.Host/api/ipc.js.map +++ b/ElectronNET.Host/api/ipc.js.map @@ -1 +1 @@ -{"version":3,"file":"ipc.js","sourceRoot":"","sources":["ipc.ts"],"names":[],"mappings":"AAAQ,IAAA,qCAAO,CAAyB;AAExC,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB,EAAE,MAAM;IAC7C,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,UAAC,OAAO;QACxC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,UAAC,KAAK,EAAE,IAAI;YAC5B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,4BAA4B,EAAE,UAAC,OAAO;QAC5C,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,UAAC,KAAK,EAAE,IAAI;YAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,kCAAkC,EAAE,UAAC,OAAO;QAClD,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,mBAAmB,EAAE,UAAC,OAAO;QAAE,cAAO;aAAP,UAAO,EAAP,qBAAO,EAAP,IAAO;YAAP,6BAAO;;QAC5C,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YACT,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC3C,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"ipc.js","sourceRoot":"","sources":["ipc.ts"],"names":[],"mappings":";;AAAA,qCAAkD;AAElD,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,UAAC,OAAO;QACxC,kBAAO,CAAC,EAAE,CAAC,OAAO,EAAE,UAAC,KAAK,EAAE,IAAI;YAC5B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,4BAA4B,EAAE,UAAC,OAAO;QAC5C,kBAAO,CAAC,IAAI,CAAC,OAAO,EAAE,UAAC,KAAK,EAAE,IAAI;YAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,kCAAkC,EAAE,UAAC,OAAO;QAClD,kBAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,mBAAmB,EAAE,UAAC,aAAa,EAAE,OAAO;QAAE,cAAO;aAAP,UAAO,EAAP,qBAAO,EAAP,IAAO;YAAP,6BAAO;;QAC3D,IAAM,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QAEtD,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YACT,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC3C,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/ipc.ts b/ElectronNET.Host/api/ipc.ts index 7f65581..b3a7b2b 100644 --- a/ElectronNET.Host/api/ipc.ts +++ b/ElectronNET.Host/api/ipc.ts @@ -1,6 +1,6 @@ -const { ipcMain } = require('electron'); +import { ipcMain, BrowserWindow } from 'electron'; -module.exports = (socket: SocketIO.Server, window) => { +module.exports = (socket: SocketIO.Server) => { socket.on('registerIpcMainChannel', (channel) => { ipcMain.on(channel, (event, args) => { socket.emit(channel, [event.preventDefault(), args]); @@ -17,7 +17,9 @@ module.exports = (socket: SocketIO.Server, window) => { ipcMain.removeAllListeners(channel); }); - socket.on('sendToIpcRenderer', (channel, ...data) => { + socket.on('sendToIpcRenderer', (browserWindow, channel, ...data) => { + const window = BrowserWindow.fromId(browserWindow.id); + if (window) { window.webContents.send(channel, data); } diff --git a/ElectronNET.Host/api/tray.js b/ElectronNET.Host/api/tray.js index 8cc6e97..1a9a53f 100644 --- a/ElectronNET.Host/api/tray.js +++ b/ElectronNET.Host/api/tray.js @@ -6,9 +6,22 @@ var tray; module.exports = function (socket) { socket.on('create-tray', function (image, menuItems) { var menu = electron_1.Menu.buildFromTemplate(menuItems); + addMenuItemClickConnector(menu.items, function (id) { + socket.emit("trayMenuItemClicked", id); + }); var imagePath = path.join(__dirname.replace('api', ''), 'bin', image); tray = new electron_1.Tray(imagePath); tray.setContextMenu(menu); }); + function addMenuItemClickConnector(menuItems, callback) { + menuItems.forEach(function (item) { + if (item.submenu && item.submenu.items.length > 0) { + addMenuItemClickConnector(item.submenu.items, callback); + } + if ("id" in item && item.id) { + item.click = function () { callback(item.id); }; + } + }); + } }; //# sourceMappingURL=tray.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/tray.js.map b/ElectronNET.Host/api/tray.js.map index 6ceee68..e28487a 100644 --- a/ElectronNET.Host/api/tray.js.map +++ b/ElectronNET.Host/api/tray.js.map @@ -1 +1 @@ -{"version":3,"file":"tray.js","sourceRoot":"","sources":["tray.ts"],"names":[],"mappings":";;AAAA,qCAAsC;AACtC,IAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC7B,IAAI,IAAI,CAAC;AAET,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,UAAC,KAAK,EAAE,SAAS;QACtC,IAAM,IAAI,GAAG,eAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAE/C,IAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAExE,IAAI,GAAG,IAAI,eAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"tray.js","sourceRoot":"","sources":["tray.ts"],"names":[],"mappings":";;AAAA,qCAAsC;AACtC,IAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC7B,IAAI,IAAI,CAAC;AAET,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,UAAC,KAAK,EAAE,SAAS;QACtC,IAAM,IAAI,GAAG,eAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAE/C,yBAAyB,CAAC,IAAI,CAAC,KAAK,EAAE,UAAC,EAAE;YACrC,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,IAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAExE,IAAI,GAAG,IAAI,eAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,mCAAmC,SAAS,EAAE,QAAQ;QAClD,SAAS,CAAC,OAAO,CAAC,UAAC,IAAI;YACnB,EAAE,CAAA,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC/C,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC5D,CAAC;YAED,EAAE,CAAA,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACzB,IAAI,CAAC,KAAK,GAAG,cAAQ,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9C,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;AACL,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/tray.ts b/ElectronNET.Host/api/tray.ts index a0ec1c8..01b8150 100644 --- a/ElectronNET.Host/api/tray.ts +++ b/ElectronNET.Host/api/tray.ts @@ -6,9 +6,25 @@ module.exports = (socket: SocketIO.Server) => { socket.on('create-tray', (image, menuItems) => { const menu = Menu.buildFromTemplate(menuItems); + addMenuItemClickConnector(menu.items, (id) => { + socket.emit("trayMenuItemClicked", id); + }); + const imagePath = path.join(__dirname.replace('api', ''), 'bin', image); tray = new Tray(imagePath); tray.setContextMenu(menu); }); + + function addMenuItemClickConnector(menuItems, callback) { + menuItems.forEach((item) => { + if(item.submenu && item.submenu.items.length > 0) { + addMenuItemClickConnector(item.submenu.items, callback); + } + + if("id" in item && item.id) { + item.click = () => { callback(item.id); }; + } + }); + } } \ No newline at end of file diff --git a/ElectronNET.Host/main.js b/ElectronNET.Host/main.js index 4c01f71..0e766de 100644 --- a/ElectronNET.Host/main.js +++ b/ElectronNET.Host/main.js @@ -3,7 +3,7 @@ const fs = require('fs'); const path = require('path'); const process = require('child_process').spawn; const portfinder = require('detect-port'); -let io, browserWindows, apiProcess, loadURL, appApi, menu, dialog, notification, tray; +let io, browserWindows, ipc, apiProcess, loadURL, appApi, menu, dialog, notification, tray; app.on('ready', () => { portfinder(8000, (error, port) => { @@ -20,6 +20,7 @@ function startSocketApiBridge(port) { appApi = require('./api/app')(socket, app); browserWindows = require('./api/browserWindows')(socket); + ipc = require('./api/ipc')(socket); menu = require('./api/menu')(socket); dialog = require('./api/dialog')(socket); notification = require('./api/notification')(socket); @@ -27,7 +28,6 @@ function startSocketApiBridge(port) { }); } - function startAspCoreBackend(electronPort) { portfinder(8000, (error, electronWebPort) => { loadURL = `http://localhost:${electronWebPort}` diff --git a/ElectronNET.WebApp/Controllers/HomeController.cs b/ElectronNET.WebApp/Controllers/HomeController.cs index 5f2146c..2690c75 100644 --- a/ElectronNET.WebApp/Controllers/HomeController.cs +++ b/ElectronNET.WebApp/Controllers/HomeController.cs @@ -12,16 +12,21 @@ namespace ElectronNET.WebApp.Controllers Electron.IpcMain.On("SayHello", (args) => { Electron.Notification.Show(new NotificationOptions("Hallo Robert","Nachricht von ASP.NET Core App")); - Electron.IpcMain.Send("Goodbye", "Elephant!"); + Electron.IpcMain.Send(Electron.WindowManager.BrowserWindows.First(), "Goodbye", "Elephant!"); }); Electron.IpcMain.On("GetPath", async (args) => { - string pathName = await Electron.App.GetPathAsync(PathName.pictures); - Electron.IpcMain.Send("GetPathComplete", pathName); + var currentBrowserWindow = Electron.WindowManager.BrowserWindows.First(); - Electron.WindowManager.BrowserWindows.First().Minimize(); - await Electron.WindowManager.CreateWindowAsync("http://www.google.de"); + string pathName = await Electron.App.GetPathAsync(PathName.pictures); + Electron.IpcMain.Send(currentBrowserWindow, "GetPathComplete", pathName); + + currentBrowserWindow.Minimize(); + await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { + Title = "My second Window", + AutoHideMenuBar = true + },"http://www.google.de"); }); diff --git a/ElectronNET.WebApp/Startup.cs b/ElectronNET.WebApp/Startup.cs index 8ad87aa..c7c3b49 100644 --- a/ElectronNET.WebApp/Startup.cs +++ b/ElectronNET.WebApp/Startup.cs @@ -43,7 +43,7 @@ namespace ElectronNET.WebApp public async void Bootstrap() { - Electron.Menu.SetApplicationMenu(new MenuItem[] { + var menuItems = new MenuItem[] { new MenuItem { Label = "File", Submenu = new MenuItem[] { @@ -66,19 +66,12 @@ namespace ElectronNET.WebApp }); } } - }); + }; + + Electron.Menu.SetApplicationMenu(menuItems); + Electron.Tray.Show("/Assets/electron_32x32.png", menuItems); var browserWindow = await Electron.WindowManager.CreateWindowAsync(); - - Electron.Tray.Show("/Assets/electron_32x32.png", new MenuItem[] { - new MenuItem { - Label = "Exit", - Click = () => - { - Electron.App.Exit(); - } - } - }); } } }