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 0000000..3a10449 Binary files /dev/null and b/ElectronNET.WebApp/Assets/electron.ico differ diff --git a/ElectronNET.WebApp/Assets/electron_32x32.png b/ElectronNET.WebApp/Assets/electron_32x32.png new file mode 100644 index 0000000..125dde6 Binary files /dev/null and b/ElectronNET.WebApp/Assets/electron_32x32.png differ 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!