mirror of
https://github.com/ElectronNET/Electron.NET.git
synced 2026-04-29 01:22:00 +00:00
implement Demo App sections: Dialogs, Menu, Tray, Shell, CrashHang, Notification, Shortcuts etc. Fix some API bugs and implement GlobalShortcut-, Shell- and WebContents-API.
This commit is contained in:
@@ -156,7 +156,7 @@ namespace ElectronNET.API
|
||||
{
|
||||
if (_unresponsive == null)
|
||||
{
|
||||
BridgeConnector.Socket.On("browserWindow-unresponsive", () =>
|
||||
BridgeConnector.Socket.On("browserWindow-unresponsive" + Id, () =>
|
||||
{
|
||||
_unresponsive();
|
||||
});
|
||||
@@ -182,7 +182,7 @@ namespace ElectronNET.API
|
||||
{
|
||||
if (_responsive == null)
|
||||
{
|
||||
BridgeConnector.Socket.On("browserWindow-responsive", () =>
|
||||
BridgeConnector.Socket.On("browserWindow-responsive" + Id, () =>
|
||||
{
|
||||
_responsive();
|
||||
});
|
||||
@@ -807,6 +807,7 @@ namespace ElectronNET.API
|
||||
|
||||
internal BrowserWindow(int id) {
|
||||
Id = id;
|
||||
WebContents = new WebContents(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2148,6 +2149,11 @@ namespace ElectronNET.API
|
||||
BridgeConnector.Socket.Emit("browserWindowSetVibrancy", Id, type.GetDescription());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Render and control web pages.
|
||||
/// </summary>
|
||||
public WebContents WebContents { get; internal set; }
|
||||
|
||||
private JsonSerializer _jsonSerializer = new JsonSerializer()
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
|
||||
@@ -36,5 +36,15 @@
|
||||
/// Add icons and context menus to the system’s notification area.
|
||||
/// </summary>
|
||||
public static Tray Tray { get { return Tray.Instance; } }
|
||||
|
||||
/// <summary>
|
||||
/// Detect keyboard events when the application does not have keyboard focus.
|
||||
/// </summary>
|
||||
public static GlobalShortcut GlobalShortcut { get { return GlobalShortcut.Instance; } }
|
||||
|
||||
/// <summary>
|
||||
/// Manage files and URLs using their default applications.
|
||||
/// </summary>
|
||||
public static Shell Shell { get { return Shell.Instance; } }
|
||||
}
|
||||
}
|
||||
|
||||
15
ElectronNET.API/Entities/DevToolsMode.cs
Normal file
15
ElectronNET.API/Entities/DevToolsMode.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace ElectronNET.API.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Opens the devtools with specified dock state, can be right, bottom, undocked,
|
||||
/// detach.Defaults to last used dock state.In undocked mode it's possible to dock
|
||||
/// back.In detach mode it's not.
|
||||
/// </summary>
|
||||
public enum DevToolsMode
|
||||
{
|
||||
right,
|
||||
bottom,
|
||||
undocked,
|
||||
detach
|
||||
}
|
||||
}
|
||||
7
ElectronNET.API/Entities/Error.cs
Normal file
7
ElectronNET.API/Entities/Error.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace ElectronNET.API.Entities
|
||||
{
|
||||
public class Error
|
||||
{
|
||||
public string Stack { get; set; }
|
||||
}
|
||||
}
|
||||
16
ElectronNET.API/Entities/OpenDevToolsOptions.cs
Normal file
16
ElectronNET.API/Entities/OpenDevToolsOptions.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace ElectronNET.API.Entities
|
||||
{
|
||||
public class OpenDevToolsOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Opens the devtools with specified dock state, can be right, bottom, undocked,
|
||||
/// detach.Defaults to last used dock state.In undocked mode it's possible to dock
|
||||
/// back.In detach mode it's not.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public DevToolsMode Mode { get; set; }
|
||||
}
|
||||
}
|
||||
13
ElectronNET.API/Entities/OpenExternalOptions.cs
Normal file
13
ElectronNET.API/Entities/OpenExternalOptions.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ElectronNET.API.Entities
|
||||
{
|
||||
public class OpenExternalOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// true to bring the opened application to the foreground. The default is true.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
public bool Activate { get; set; } = true;
|
||||
}
|
||||
}
|
||||
41
ElectronNET.API/Entities/ShortcutDetails.cs
Normal file
41
ElectronNET.API/Entities/ShortcutDetails.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
namespace ElectronNET.API
|
||||
{
|
||||
public class ShortcutDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// The Application User Model ID. Default is empty.
|
||||
/// </summary>
|
||||
public string AppUserModelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The arguments to be applied to target when launching from this shortcut. Default is empty.
|
||||
/// </summary>
|
||||
public string Args { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The working directory. Default is empty.
|
||||
/// </summary>
|
||||
public string Cwd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The description of the shortcut. Default is empty.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path to the icon, can be a DLL or EXE. icon and iconIndex have to be set
|
||||
/// together.Default is empty, which uses the target's icon.
|
||||
/// </summary>
|
||||
public string Icon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The resource ID of icon when icon is a DLL or EXE. Default is 0.
|
||||
/// </summary>
|
||||
public int IconIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The target to launch from this shortcut.
|
||||
/// </summary>
|
||||
public string Target { get; set; }
|
||||
}
|
||||
}
|
||||
20
ElectronNET.API/Entities/ShortcutLinkOperation.cs
Normal file
20
ElectronNET.API/Entities/ShortcutLinkOperation.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace ElectronNET.API
|
||||
{
|
||||
public enum ShortcutLinkOperation
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new shortcut, overwriting if necessary.
|
||||
/// </summary>
|
||||
create,
|
||||
|
||||
/// <summary>
|
||||
/// Updates specified properties only on an existing shortcut.
|
||||
/// </summary>
|
||||
update,
|
||||
|
||||
/// <summary>
|
||||
/// Overwrites an existing shortcut, fails if the shortcut doesn’t exist.
|
||||
/// </summary>
|
||||
replace
|
||||
}
|
||||
}
|
||||
98
ElectronNET.API/GlobalShortcut.cs
Normal file
98
ElectronNET.API/GlobalShortcut.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ElectronNET.API
|
||||
{
|
||||
/// <summary>
|
||||
/// Detect keyboard events when the application does not have keyboard focus.
|
||||
/// </summary>
|
||||
public sealed class GlobalShortcut
|
||||
{
|
||||
private static GlobalShortcut _globalShortcut;
|
||||
|
||||
internal GlobalShortcut() { }
|
||||
|
||||
internal static GlobalShortcut Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_globalShortcut == null)
|
||||
{
|
||||
_globalShortcut = new GlobalShortcut();
|
||||
}
|
||||
|
||||
return _globalShortcut;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, Action> _shortcuts = new Dictionary<string, Action>();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a global shortcut of accelerator.
|
||||
/// The callback is called when the registered shortcut is pressed by the user.
|
||||
///
|
||||
/// When the accelerator is already taken by other applications, this call will
|
||||
/// silently fail.This behavior is intended by operating systems, since they don’t
|
||||
/// want applications to fight for global shortcuts.
|
||||
/// </summary>
|
||||
public void Register(string accelerator, Action function)
|
||||
{
|
||||
if (!_shortcuts.ContainsKey(accelerator))
|
||||
{
|
||||
_shortcuts.Add(accelerator, function);
|
||||
|
||||
BridgeConnector.Socket.Off("globalShortcut-pressed");
|
||||
BridgeConnector.Socket.On("globalShortcut-pressed", (shortcut) =>
|
||||
{
|
||||
if (_shortcuts.ContainsKey(shortcut.ToString()))
|
||||
{
|
||||
_shortcuts[shortcut.ToString()]();
|
||||
}
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Emit("globalShortcut-register", accelerator);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the accelerator is already taken by other applications,
|
||||
/// this call will still return false. This behavior is intended by operating systems,
|
||||
/// since they don’t want applications to fight for global shortcuts.
|
||||
/// </summary>
|
||||
/// <returns>Whether this application has registered accelerator.</returns>
|
||||
public Task<bool> IsRegisteredAsync(string accelerator)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
|
||||
BridgeConnector.Socket.On("globalShortcut-isRegisteredCompleted", (isRegistered) =>
|
||||
{
|
||||
BridgeConnector.Socket.Off("globalShortcut-isRegisteredCompleted");
|
||||
|
||||
taskCompletionSource.SetResult((bool)isRegistered);
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Emit("globalShortcut-isRegistered", accelerator);
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters the global shortcut of accelerator.
|
||||
/// </summary>
|
||||
public void Unregister(string accelerator)
|
||||
{
|
||||
_shortcuts.Remove(accelerator);
|
||||
BridgeConnector.Socket.Emit("globalShortcut-unregister", accelerator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters all of the global shortcuts.
|
||||
/// </summary>
|
||||
public void UnregisterAll()
|
||||
{
|
||||
_shortcuts.Clear();
|
||||
BridgeConnector.Socket.Emit("globalShortcut-unregisterAll");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,25 @@ namespace ElectronNET.API
|
||||
BridgeConnector.Socket.On(channel, listener);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to the renderer process synchronously via channel,
|
||||
/// you can also send arbitrary arguments.
|
||||
///
|
||||
/// Note: Sending a synchronous message will block the whole renderer process,
|
||||
/// unless you know what you are doing you should never use it.
|
||||
/// </summary>
|
||||
/// <param name="channel"></param>
|
||||
/// <param name="listener"></param>
|
||||
public void OnSync(string channel, Func<object, object> listener)
|
||||
{
|
||||
BridgeConnector.Socket.Emit("registerSyncIpcMainChannel", channel);
|
||||
BridgeConnector.Socket.On(channel, (args) => {
|
||||
var result = listener(args);
|
||||
|
||||
BridgeConnector.Socket.Emit(channel + "Sync", result);
|
||||
});
|
||||
}
|
||||
|
||||
/// <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.
|
||||
|
||||
@@ -4,6 +4,9 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System.Collections.Generic;
|
||||
using ElectronNET.API.Extensions;
|
||||
using System.Linq;
|
||||
using System.Collections.ObjectModel;
|
||||
using System;
|
||||
|
||||
namespace ElectronNET.API
|
||||
{
|
||||
@@ -26,22 +29,55 @@ namespace ElectronNET.API
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<MenuItem> Items { get { return _items.AsReadOnly(); } }
|
||||
private List<MenuItem> _items = new List<MenuItem>();
|
||||
public IReadOnlyCollection<MenuItem> MenuItems { get { return _menuItems.AsReadOnly(); } }
|
||||
private List<MenuItem> _menuItems = new List<MenuItem>();
|
||||
|
||||
public void SetApplicationMenu(MenuItem[] menuItems)
|
||||
{
|
||||
_menuItems.Clear();
|
||||
|
||||
menuItems.AddMenuItemsId();
|
||||
BridgeConnector.Socket.Emit("menu-setApplicationMenu", JArray.FromObject(menuItems, _jsonSerializer));
|
||||
_items.AddRange(menuItems);
|
||||
_menuItems.AddRange(menuItems);
|
||||
|
||||
BridgeConnector.Socket.Off("menuItemClicked");
|
||||
BridgeConnector.Socket.On("menuItemClicked", (id) => {
|
||||
MenuItem menuItem = _items.GetMenuItem(id.ToString());
|
||||
menuItem?.Click();
|
||||
MenuItem menuItem = _menuItems.GetMenuItem(id.ToString());
|
||||
menuItem.Click?.Invoke();
|
||||
});
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<int, ReadOnlyCollection<MenuItem>> ContextMenuItems { get; internal set; }
|
||||
private Dictionary<int, List<MenuItem>> _contextMenuItems = new Dictionary<int, List<MenuItem>>();
|
||||
|
||||
public void SetContextMenu(BrowserWindow browserWindow, MenuItem[] menuItems)
|
||||
{
|
||||
if (!_contextMenuItems.ContainsKey(browserWindow.Id))
|
||||
{
|
||||
menuItems.AddMenuItemsId();
|
||||
BridgeConnector.Socket.Emit("menu-setContextMenu", browserWindow.Id, JArray.FromObject(menuItems, _jsonSerializer));
|
||||
_contextMenuItems.Add(browserWindow.Id, menuItems.ToList());
|
||||
|
||||
var x = _contextMenuItems.ToDictionary(kv => kv.Key, kv => kv.Value.AsReadOnly());
|
||||
ContextMenuItems = new ReadOnlyDictionary<int, ReadOnlyCollection<MenuItem>>(x);
|
||||
|
||||
BridgeConnector.Socket.Off("contextMenuItemClicked");
|
||||
BridgeConnector.Socket.On("contextMenuItemClicked", (results) =>
|
||||
{
|
||||
var id = ((JArray)results).First.ToString();
|
||||
var browserWindowId = (int)((JArray)results).Last;
|
||||
|
||||
MenuItem menuItem = _contextMenuItems[browserWindowId].GetMenuItem(id);
|
||||
menuItem.Click?.Invoke();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void ContextMenuPopup(BrowserWindow browserWindow)
|
||||
{
|
||||
BridgeConnector.Socket.Emit("menu-contextMenuPopup", browserWindow.Id);
|
||||
}
|
||||
|
||||
private JsonSerializer _jsonSerializer = new JsonSerializer()
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
|
||||
244
ElectronNET.API/Shell.cs
Normal file
244
ElectronNET.API/Shell.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
using ElectronNET.API.Entities;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ElectronNET.API
|
||||
{
|
||||
/// <summary>
|
||||
/// Manage files and URLs using their default applications.
|
||||
/// </summary>
|
||||
public sealed class Shell
|
||||
{
|
||||
private static Shell _shell;
|
||||
|
||||
internal Shell() { }
|
||||
|
||||
internal static Shell Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_shell == null)
|
||||
{
|
||||
_shell = new Shell();
|
||||
}
|
||||
|
||||
return _shell;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the given file in a file manager. If possible, select the file.
|
||||
/// </summary>
|
||||
/// <param name="fullPath"></param>
|
||||
/// <returns>Whether the item was successfully shown.</returns>
|
||||
public Task<bool> ShowItemInFolderAsync(string fullPath)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
|
||||
BridgeConnector.Socket.On("shell-showItemInFolderCompleted", (success) =>
|
||||
{
|
||||
BridgeConnector.Socket.Off("shell-showItemInFolderCompleted");
|
||||
|
||||
taskCompletionSource.SetResult((bool)success);
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Emit("shell-showItemInFolder", fullPath);
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the given file in the desktop’s default manner.
|
||||
/// </summary>
|
||||
/// <param name="fullPath"></param>
|
||||
/// <returns>Whether the item was successfully opened.</returns>
|
||||
public Task<bool> OpenItemAsync(string fullPath)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
|
||||
BridgeConnector.Socket.On("shell-openItemCompleted", (success) =>
|
||||
{
|
||||
BridgeConnector.Socket.Off("shell-openItemCompleted");
|
||||
|
||||
taskCompletionSource.SetResult((bool)success);
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Emit("shell-openItem", fullPath);
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the given external protocol URL in the desktop’s default manner.
|
||||
/// (For example, mailto: URLs in the user’s default mail agent).
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns>Whether an application was available to open the URL.
|
||||
/// If callback is specified, always returns true.</returns>
|
||||
public Task<bool> OpenExternalAsync(string url)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
|
||||
BridgeConnector.Socket.On("shell-openExternalCompleted", (success) =>
|
||||
{
|
||||
BridgeConnector.Socket.Off("shell-openExternalCompleted");
|
||||
|
||||
taskCompletionSource.SetResult((bool)success);
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Emit("shell-openExternal", url);
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the given external protocol URL in the desktop’s default manner.
|
||||
/// (For example, mailto: URLs in the user’s default mail agent).
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="options">macOS only</param>
|
||||
/// <returns>Whether an application was available to open the URL.
|
||||
/// If callback is specified, always returns true.</returns>
|
||||
public Task<bool> OpenExternalAsync(string url, OpenExternalOptions options)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
|
||||
BridgeConnector.Socket.On("shell-openExternalCompleted", (success) =>
|
||||
{
|
||||
BridgeConnector.Socket.Off("shell-openExternalCompleted");
|
||||
|
||||
taskCompletionSource.SetResult((bool)success);
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Emit("shell-openExternal", url, JObject.FromObject(options, _jsonSerializer));
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the given external protocol URL in the desktop’s default manner.
|
||||
/// (For example, mailto: URLs in the user’s default mail agent).
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="options">macOS only</param>
|
||||
/// <param name="action">macOS only</param>
|
||||
/// <returns>Whether an application was available to open the URL.
|
||||
/// If callback is specified, always returns true.</returns>
|
||||
public Task<bool> OpenExternalAsync(string url, OpenExternalOptions options, Action<Error> action)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
|
||||
BridgeConnector.Socket.On("shell-openExternalCompleted", (success) =>
|
||||
{
|
||||
BridgeConnector.Socket.Off("shell-openExternalCompleted");
|
||||
|
||||
taskCompletionSource.SetResult((bool)success);
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Off("shell-openExternalCallback");
|
||||
BridgeConnector.Socket.On("shell-openExternalCallback", (args) => {
|
||||
var urlKey = ((JArray)args).First.ToString();
|
||||
var error = ((JArray)args).Last.ToObject<Error>();
|
||||
|
||||
if(_openExternalCallbacks.ContainsKey(urlKey))
|
||||
{
|
||||
_openExternalCallbacks[urlKey](error);
|
||||
}
|
||||
});
|
||||
|
||||
_openExternalCallbacks.Add(url, action);
|
||||
|
||||
BridgeConnector.Socket.Emit("shell-openExternal", url, JObject.FromObject(options, _jsonSerializer), true);
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
private Dictionary<string, Action<Error>> _openExternalCallbacks = new Dictionary<string, Action<Error>>();
|
||||
|
||||
/// <summary>
|
||||
/// Move the given file to trash and returns a boolean status for the operation.
|
||||
/// </summary>
|
||||
/// <param name="fullPath"></param>
|
||||
/// <returns> Whether the item was successfully moved to the trash.</returns>
|
||||
public Task<bool> MoveItemToTrashAsync(string fullPath)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
|
||||
BridgeConnector.Socket.On("shell-moveItemToTrashCompleted", (success) =>
|
||||
{
|
||||
BridgeConnector.Socket.Off("shell-moveItemToTrashCompleted");
|
||||
|
||||
taskCompletionSource.SetResult((bool)success);
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Emit("shell-moveItemToTrash", fullPath);
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play the beep sound.
|
||||
/// </summary>
|
||||
public void Beep()
|
||||
{
|
||||
BridgeConnector.Socket.Emit("shell-beep");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates or updates a shortcut link at shortcutPath.
|
||||
/// </summary>
|
||||
/// <param name="shortcutPath"></param>
|
||||
/// <param name="operation"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns>Whether the shortcut was created successfully.</returns>
|
||||
public Task<bool> WriteShortcutLinkAsync(string shortcutPath, ShortcutLinkOperation operation, ShortcutDetails options)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
|
||||
BridgeConnector.Socket.On("shell-writeShortcutLinkCompleted", (success) =>
|
||||
{
|
||||
BridgeConnector.Socket.Off("shell-writeShortcutLinkCompleted");
|
||||
|
||||
taskCompletionSource.SetResult((bool)success);
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Emit("shell-writeShortcutLink", shortcutPath, operation.ToString(), JObject.FromObject(options, _jsonSerializer));
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the shortcut link at shortcutPath.
|
||||
///
|
||||
/// An exception will be thrown when any error happens.
|
||||
/// </summary>
|
||||
/// <param name="shortcutPath"></param>
|
||||
/// <returns></returns>
|
||||
public Task<ShortcutDetails> ReadShortcutLinkAsync(string shortcutPath)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<ShortcutDetails>();
|
||||
|
||||
BridgeConnector.Socket.On("shell-readShortcutLinkCompleted", (shortcutDetails) =>
|
||||
{
|
||||
BridgeConnector.Socket.Off("shell-readShortcutLinkCompleted");
|
||||
|
||||
taskCompletionSource.SetResult((ShortcutDetails)shortcutDetails);
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Emit("shell-readShortcutLink", shortcutPath);
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
private JsonSerializer _jsonSerializer = new JsonSerializer()
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
DefaultValueHandling = DefaultValueHandling.Ignore
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -199,6 +199,11 @@ namespace ElectronNET.API
|
||||
public IReadOnlyCollection<MenuItem> Items { get { return _items.AsReadOnly(); } }
|
||||
private List<MenuItem> _items = new List<MenuItem>();
|
||||
|
||||
public void Show(string image, MenuItem menuItem)
|
||||
{
|
||||
Show(image, new MenuItem[] { menuItem });
|
||||
}
|
||||
|
||||
public void Show(string image, MenuItem[] menuItems)
|
||||
{
|
||||
menuItems.AddMenuItemsId();
|
||||
@@ -218,6 +223,7 @@ namespace ElectronNET.API
|
||||
public void Destroy()
|
||||
{
|
||||
BridgeConnector.Socket.Emit("tray-destroy");
|
||||
_items.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
99
ElectronNET.API/WebContents.cs
Normal file
99
ElectronNET.API/WebContents.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using ElectronNET.API.Entities;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ElectronNET.API
|
||||
{
|
||||
/// <summary>
|
||||
/// Render and control web pages.
|
||||
/// </summary>
|
||||
public class WebContents
|
||||
{
|
||||
public int Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Emitted when the renderer process crashes or is killed.
|
||||
/// </summary>
|
||||
public event Action<bool> OnCrashed
|
||||
{
|
||||
add
|
||||
{
|
||||
if (_crashed == null)
|
||||
{
|
||||
BridgeConnector.Socket.On("webContents-crashed" + Id, (killed) =>
|
||||
{
|
||||
_crashed((bool)killed);
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Emit("register-webContents-crashed", Id);
|
||||
}
|
||||
_crashed += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
_crashed -= value;
|
||||
}
|
||||
}
|
||||
|
||||
private event Action<bool> _crashed;
|
||||
|
||||
/// <summary>
|
||||
/// Emitted when the navigation is done, i.e. the spinner of the tab has
|
||||
/// stopped spinning, and the onload event was dispatched.
|
||||
/// </summary>
|
||||
public event Action OnDidFinishLoad
|
||||
{
|
||||
add
|
||||
{
|
||||
if (_didFinishLoad == null)
|
||||
{
|
||||
BridgeConnector.Socket.On("webContents-didFinishLoad" + Id, () =>
|
||||
{
|
||||
_didFinishLoad();
|
||||
});
|
||||
|
||||
BridgeConnector.Socket.Emit("register-webContents-didFinishLoad", Id);
|
||||
}
|
||||
_didFinishLoad += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
_didFinishLoad -= value;
|
||||
}
|
||||
}
|
||||
|
||||
private event Action _didFinishLoad;
|
||||
|
||||
internal WebContents(int id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the devtools.
|
||||
/// </summary>
|
||||
public void OpenDevTools()
|
||||
{
|
||||
BridgeConnector.Socket.Emit("webContentsOpenDevTools", Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the devtools.
|
||||
/// </summary>
|
||||
/// <param name="openDevToolsOptions"></param>
|
||||
public void OpenDevTools(OpenDevToolsOptions openDevToolsOptions)
|
||||
{
|
||||
BridgeConnector.Socket.Emit("webContentsOpenDevTools", Id, JObject.FromObject(openDevToolsOptions, _jsonSerializer));
|
||||
}
|
||||
|
||||
private JsonSerializer _jsonSerializer = new JsonSerializer()
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
DefaultValueHandling = DefaultValueHandling.Ignore
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,9 @@ namespace ElectronNET.CLI.Commands
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "menu.js", "api.");
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "notification.js", "api.");
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "tray.js", "api.");
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "webContents.js", "api.");
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "globalShortcut.js", "api.");
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "shell.js", "api.");
|
||||
|
||||
Console.WriteLine("Start npm install...");
|
||||
ProcessHelper.CmdExecute("npm install", tempPath);
|
||||
|
||||
@@ -65,6 +65,9 @@ namespace ElectronNET.CLI.Commands
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "menu.js", "api.");
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "notification.js", "api.");
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "tray.js", "api.");
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "webContents.js", "api.");
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "globalShortcut.js", "api.");
|
||||
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "shell.js", "api.");
|
||||
|
||||
Console.WriteLine("Start npm install...");
|
||||
ProcessHelper.CmdExecute("npm install", tempPath);
|
||||
|
||||
@@ -68,6 +68,12 @@ This package contains the dotnet tooling to electronize your application.</Descr
|
||||
<EmbeddedResource Include="..\ElectronNET.Host\api\notification.js" Link="ElectronHost\api\notification.js" />
|
||||
<EmbeddedResource Include="..\ElectronNET.Host\api\tray.js" Link="ElectronHost\api\tray.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\ElectronNET.Host\api\globalShortcut.js" Link="ElectronHost\api\globalShortcut.js" />
|
||||
<None Include="..\ElectronNET.Host\api\shell.js" Link="ElectronHost\api\shell.js" />
|
||||
<None Include="..\ElectronNET.Host\api\webContents.js" Link="ElectronHost\api\webContents.js" />
|
||||
</ItemGroup>
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(OS)' == 'Windows_NT'">
|
||||
<Exec Command="$(ProjectDir)devCleanup.cmd" IgnoreExitCode="true" />
|
||||
</Target>
|
||||
|
||||
@@ -31,12 +31,12 @@ module.exports = function (socket) {
|
||||
});
|
||||
socket.on('register-browserWindow-unresponsive', function (id) {
|
||||
getWindowById(id).on('unresponsive', function () {
|
||||
socket.emit('browserWindow-unresponsive');
|
||||
socket.emit('browserWindow-unresponsive' + id);
|
||||
});
|
||||
});
|
||||
socket.on('register-browserWindow-responsive', function (id) {
|
||||
getWindowById(id).on('responsive', function () {
|
||||
socket.emit('browserWindow-responsive');
|
||||
socket.emit('browserWindow-responsive' + id);
|
||||
});
|
||||
});
|
||||
socket.on('register-browserWindow-blur', function (id) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,6 +3,7 @@ const path = require('path');
|
||||
const windows: Electron.BrowserWindow[] = []
|
||||
|
||||
module.exports = (socket: SocketIO.Server) => {
|
||||
|
||||
socket.on('register-browserWindow-ready-to-show', (id) => {
|
||||
getWindowById(id).on('ready-to-show', () => {
|
||||
socket.emit('browserWindow-ready-to-show');
|
||||
@@ -35,13 +36,13 @@ module.exports = (socket: SocketIO.Server) => {
|
||||
|
||||
socket.on('register-browserWindow-unresponsive', (id) => {
|
||||
getWindowById(id).on('unresponsive', () => {
|
||||
socket.emit('browserWindow-unresponsive');
|
||||
socket.emit('browserWindow-unresponsive' + id);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-browserWindow-responsive', (id) => {
|
||||
getWindowById(id).on('responsive', () => {
|
||||
socket.emit('browserWindow-responsive');
|
||||
socket.emit('browserWindow-responsive' + id);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
24
ElectronNET.Host/api/globalShortcut.js
Normal file
24
ElectronNET.Host/api/globalShortcut.js
Normal file
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var electron_1 = require("electron");
|
||||
module.exports = function (socket) {
|
||||
socket.on('globalShortcut-register', function (accelerator) {
|
||||
electron_1.globalShortcut.register(accelerator, function () {
|
||||
socket.emit('globalShortcut-pressed', accelerator);
|
||||
});
|
||||
});
|
||||
socket.on('globalShortcut-isRegistered', function (accelerator) {
|
||||
var isRegistered = electron_1.globalShortcut.isRegistered(accelerator);
|
||||
socket.emit('globalShortcut-isRegisteredCompleted', isRegistered);
|
||||
});
|
||||
socket.on('globalShortcut-unregister', function (accelerator) {
|
||||
electron_1.globalShortcut.unregister(accelerator);
|
||||
});
|
||||
socket.on('globalShortcut-unregisterAll', function () {
|
||||
try {
|
||||
electron_1.globalShortcut.unregisterAll();
|
||||
}
|
||||
catch (error) { }
|
||||
});
|
||||
};
|
||||
//# sourceMappingURL=globalShortcut.js.map
|
||||
1
ElectronNET.Host/api/globalShortcut.js.map
Normal file
1
ElectronNET.Host/api/globalShortcut.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"globalShortcut.js","sourceRoot":"","sources":["globalShortcut.ts"],"names":[],"mappings":";;AAAA,qCAA0C;AAE1C,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,UAAC,WAAW;QAC7C,yBAAc,CAAC,QAAQ,CAAC,WAAW,EAAE;YACjC,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,WAAW,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,6BAA6B,EAAE,UAAC,WAAW;QACjD,IAAM,YAAY,GAAG,yBAAc,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QAE9D,MAAM,CAAC,IAAI,CAAC,sCAAsC,EAAE,YAAY,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,2BAA2B,EAAE,UAAC,WAAW;QAC/C,yBAAc,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,8BAA8B,EAAE;QACtC,IAAI,CAAC;YACD,yBAAc,CAAC,aAAa,EAAE,CAAC;QACnC,CAAC;QAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"}
|
||||
25
ElectronNET.Host/api/globalShortcut.ts
Normal file
25
ElectronNET.Host/api/globalShortcut.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { globalShortcut } from "electron";
|
||||
|
||||
module.exports = (socket: SocketIO.Server) => {
|
||||
socket.on('globalShortcut-register', (accelerator) => {
|
||||
globalShortcut.register(accelerator, () => {
|
||||
socket.emit('globalShortcut-pressed', accelerator);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('globalShortcut-isRegistered', (accelerator) => {
|
||||
const isRegistered = globalShortcut.isRegistered(accelerator);
|
||||
|
||||
socket.emit('globalShortcut-isRegisteredCompleted', isRegistered);
|
||||
});
|
||||
|
||||
socket.on('globalShortcut-unregister', (accelerator) => {
|
||||
globalShortcut.unregister(accelerator);
|
||||
});
|
||||
|
||||
socket.on('globalShortcut-unregisterAll', () => {
|
||||
try {
|
||||
globalShortcut.unregisterAll();
|
||||
} catch (error) { }
|
||||
});
|
||||
}
|
||||
@@ -7,6 +7,16 @@ module.exports = function (socket) {
|
||||
socket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
socket.on('registerSyncIpcMainChannel', function (channel) {
|
||||
electron_1.ipcMain.on(channel, function (event, args) {
|
||||
var x = socket;
|
||||
x.removeAllListeners(channel + 'Sync');
|
||||
socket.on(channel + 'Sync', function (result) {
|
||||
event.returnValue = result;
|
||||
});
|
||||
socket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
socket.on('registerOnceIpcMainChannel', function (channel) {
|
||||
electron_1.ipcMain.once(channel, function (event, args) {
|
||||
socket.emit(channel, [event.preventDefault(), args]);
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"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"}
|
||||
{"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,EAAE,CAAC,OAAO,EAAE,UAAC,KAAK,EAAE,IAAI;YAC5B,IAAI,CAAC,GAAQ,MAAM,CAAC;YACpB,CAAC,CAAC,kBAAkB,CAAC,OAAO,GAAG,MAAM,CAAC,CAAC;YACvC,MAAM,CAAC,EAAE,CAAC,OAAO,GAAG,MAAM,EAAE,UAAC,MAAM;gBAC/B,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC;YAC/B,CAAC,CAAC,CAAC;YAEH,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"}
|
||||
@@ -7,6 +7,18 @@ module.exports = (socket: SocketIO.Server) => {
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('registerSyncIpcMainChannel', (channel) => {
|
||||
ipcMain.on(channel, (event, args) => {
|
||||
let x = <any>socket;
|
||||
x.removeAllListeners(channel + 'Sync');
|
||||
socket.on(channel + 'Sync', (result) => {
|
||||
event.returnValue = result;
|
||||
});
|
||||
|
||||
socket.emit(channel, [event.preventDefault(), args]);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('registerOnceIpcMainChannel', (channel) => {
|
||||
ipcMain.once(channel, (event, args) => {
|
||||
socket.emit(channel, [event.preventDefault(), args]);
|
||||
|
||||
@@ -1,7 +1,36 @@
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var electron_1 = require("electron");
|
||||
var contextMenuItems = [];
|
||||
module.exports = function (socket) {
|
||||
socket.on('menu-setContextMenu', function (browserWindowId, menuItems) {
|
||||
var menu = electron_1.Menu.buildFromTemplate(menuItems);
|
||||
addContextMenuItemClickConnector(menu.items, browserWindowId, function (id, browserWindowId) {
|
||||
socket.emit("contextMenuItemClicked", [id, browserWindowId]);
|
||||
});
|
||||
contextMenuItems.push({
|
||||
menu: menu,
|
||||
browserWindowId: browserWindowId
|
||||
});
|
||||
});
|
||||
function addContextMenuItemClickConnector(menuItems, browserWindowId, callback) {
|
||||
menuItems.forEach(function (item) {
|
||||
if (item.submenu && item.submenu.items.length > 0) {
|
||||
addContextMenuItemClickConnector(item.submenu.items, browserWindowId, callback);
|
||||
}
|
||||
if ("id" in item && item.id) {
|
||||
item.click = function () { callback(item.id, browserWindowId); };
|
||||
}
|
||||
});
|
||||
}
|
||||
socket.on('menu-contextMenuPopup', function (browserWindowId) {
|
||||
contextMenuItems.forEach(function (x) {
|
||||
if (x.browserWindowId === browserWindowId) {
|
||||
var browserWindow = electron_1.BrowserWindow.fromId(browserWindowId);
|
||||
x.menu.popup(browserWindow);
|
||||
}
|
||||
});
|
||||
});
|
||||
socket.on('menu-setApplicationMenu', function (menuItems) {
|
||||
var menu = electron_1.Menu.buildFromTemplate(menuItems);
|
||||
addMenuItemClickConnector(menu.items, function (id) {
|
||||
|
||||
@@ -1 +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"}
|
||||
{"version":3,"file":"menu.js","sourceRoot":"","sources":["menu.ts"],"names":[],"mappings":";;AAAA,qCAA+C;AAC/C,IAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,UAAC,eAAe,EAAE,SAAS;QACxD,IAAM,IAAI,GAAG,eAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAE/C,gCAAgC,CAAC,IAAI,CAAC,KAAK,EAAE,eAAe,EAAE,UAAC,EAAE,EAAE,eAAe;YAC9E,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;QACjE,CAAC,CAAC,CAAC;QAEH,gBAAgB,CAAC,IAAI,CAAC;YAClB,IAAI,EAAE,IAAI;YACV,eAAe,EAAE,eAAe;SACnC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,0CAA0C,SAAS,EAAE,eAAe,EAAE,QAAQ;QAC1E,SAAS,CAAC,OAAO,CAAC,UAAC,IAAI;YACnB,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;gBAChD,gCAAgC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;YACpF,CAAC;YAED,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC1B,IAAI,CAAC,KAAK,GAAG,cAAQ,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/D,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAED,MAAM,CAAC,EAAE,CAAC,uBAAuB,EAAE,UAAC,eAAe;QAC/C,gBAAgB,CAAC,OAAO,CAAC,UAAA,CAAC;YACtB,EAAE,CAAA,CAAC,CAAC,CAAC,eAAe,KAAK,eAAe,CAAC,CAAC,CAAC;gBACvC,IAAI,aAAa,GAAG,wBAAa,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;gBAC1D,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YAChC,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,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,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;gBAChD,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC5D,CAAC;YAED,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC1B,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"}
|
||||
@@ -1,6 +1,41 @@
|
||||
import { Menu } from "electron";
|
||||
import { Menu, BrowserWindow } from "electron";
|
||||
const contextMenuItems = [];
|
||||
|
||||
module.exports = (socket: SocketIO.Server) => {
|
||||
socket.on('menu-setContextMenu', (browserWindowId, menuItems) => {
|
||||
const menu = Menu.buildFromTemplate(menuItems);
|
||||
|
||||
addContextMenuItemClickConnector(menu.items, browserWindowId, (id, browserWindowId) => {
|
||||
socket.emit("contextMenuItemClicked", [id, browserWindowId]);
|
||||
});
|
||||
|
||||
contextMenuItems.push({
|
||||
menu: menu,
|
||||
browserWindowId: browserWindowId
|
||||
});
|
||||
});
|
||||
|
||||
function addContextMenuItemClickConnector(menuItems, browserWindowId, callback) {
|
||||
menuItems.forEach((item) => {
|
||||
if (item.submenu && item.submenu.items.length > 0) {
|
||||
addContextMenuItemClickConnector(item.submenu.items, browserWindowId, callback);
|
||||
}
|
||||
|
||||
if ("id" in item && item.id) {
|
||||
item.click = () => { callback(item.id, browserWindowId); };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
socket.on('menu-contextMenuPopup', (browserWindowId) => {
|
||||
contextMenuItems.forEach(x => {
|
||||
if(x.browserWindowId === browserWindowId) {
|
||||
let browserWindow = BrowserWindow.fromId(browserWindowId);
|
||||
x.menu.popup(browserWindow);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('menu-setApplicationMenu', (menuItems) => {
|
||||
const menu = Menu.buildFromTemplate(menuItems);
|
||||
|
||||
@@ -13,11 +48,11 @@ module.exports = (socket: SocketIO.Server) => {
|
||||
|
||||
function addMenuItemClickConnector(menuItems, callback) {
|
||||
menuItems.forEach((item) => {
|
||||
if(item.submenu && item.submenu.items.length > 0) {
|
||||
if (item.submenu && item.submenu.items.length > 0) {
|
||||
addMenuItemClickConnector(item.submenu.items, callback);
|
||||
}
|
||||
|
||||
if("id" in item && item.id) {
|
||||
|
||||
if ("id" in item && item.id) {
|
||||
item.click = () => { callback(item.id); };
|
||||
}
|
||||
});
|
||||
|
||||
44
ElectronNET.Host/api/shell.js
Normal file
44
ElectronNET.Host/api/shell.js
Normal file
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var electron_1 = require("electron");
|
||||
module.exports = function (socket) {
|
||||
socket.on('shell-showItemInFolder', function (fullPath) {
|
||||
var success = electron_1.shell.showItemInFolder(fullPath);
|
||||
socket.emit('shell-showItemInFolderCompleted', success);
|
||||
});
|
||||
socket.on('shell-openItem', function (fullPath) {
|
||||
var success = electron_1.shell.openItem(fullPath);
|
||||
socket.emit('shell-openItemCompleted', success);
|
||||
});
|
||||
socket.on('shell-openExternal', function (url, options, callback) {
|
||||
var success = false;
|
||||
if (options && callback) {
|
||||
success = electron_1.shell.openExternal(url, options, function (error) {
|
||||
socket.emit('shell-openExternalCallback', [url, error]);
|
||||
});
|
||||
}
|
||||
else if (options) {
|
||||
success = electron_1.shell.openExternal(url, options);
|
||||
}
|
||||
else {
|
||||
success = electron_1.shell.openExternal(url);
|
||||
}
|
||||
socket.emit('shell-openExternalCompleted', success);
|
||||
});
|
||||
socket.on('shell-moveItemToTrash', function (fullPath) {
|
||||
var success = electron_1.shell.moveItemToTrash(fullPath);
|
||||
socket.emit('shell-moveItemToTrashCompleted', success);
|
||||
});
|
||||
socket.on('shell-beep', function () {
|
||||
electron_1.shell.beep();
|
||||
});
|
||||
socket.on('shell-writeShortcutLink', function (shortcutPath, operation, options) {
|
||||
var success = electron_1.shell.writeShortcutLink(shortcutPath, operation, options);
|
||||
socket.emit('shell-writeShortcutLinkCompleted', success);
|
||||
});
|
||||
socket.on('shell-readShortcutLink', function (shortcutPath) {
|
||||
var shortcutDetails = electron_1.shell.readShortcutLink(shortcutPath);
|
||||
socket.emit('shell-readShortcutLinkCompleted', shortcutDetails);
|
||||
});
|
||||
};
|
||||
//# sourceMappingURL=shell.js.map
|
||||
1
ElectronNET.Host/api/shell.js.map
Normal file
1
ElectronNET.Host/api/shell.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"shell.js","sourceRoot":"","sources":["shell.ts"],"names":[],"mappings":";;AAAA,qCAAiC;AAEjC,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,UAAC,QAAQ;QACzC,IAAM,OAAO,GAAG,gBAAK,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAEjD,MAAM,CAAC,IAAI,CAAC,iCAAiC,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,UAAC,QAAQ;QACjC,IAAM,OAAO,GAAG,gBAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAEzC,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,UAAC,GAAG,EAAE,OAAO,EAAE,QAAQ;QACnD,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,EAAE,CAAC,CAAC,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC;YACtB,OAAO,GAAG,gBAAK,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,UAAC,KAAK;gBAC7C,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;YAC5D,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACjB,OAAO,GAAG,gBAAK,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QAAC,IAAI,CAAC,CAAC;YACJ,OAAO,GAAG,gBAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,6BAA6B,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,uBAAuB,EAAE,UAAC,QAAQ;QACxC,IAAM,OAAO,GAAG,gBAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QAEhD,MAAM,CAAC,IAAI,CAAC,gCAAgC,EAAE,OAAO,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE;QACpB,gBAAK,CAAC,IAAI,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,UAAC,YAAY,EAAE,SAAS,EAAE,OAAO;QAClE,IAAM,OAAO,GAAG,gBAAK,CAAC,iBAAiB,CAAC,YAAY,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAE1E,MAAM,CAAC,IAAI,CAAC,kCAAkC,EAAE,OAAO,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,UAAC,YAAY;QAC7C,IAAM,eAAe,GAAG,gBAAK,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;QAE7D,MAAM,CAAC,IAAI,CAAC,iCAAiC,EAAE,eAAe,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"}
|
||||
53
ElectronNET.Host/api/shell.ts
Normal file
53
ElectronNET.Host/api/shell.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { shell } from "electron";
|
||||
|
||||
module.exports = (socket: SocketIO.Server) => {
|
||||
socket.on('shell-showItemInFolder', (fullPath) => {
|
||||
const success = shell.showItemInFolder(fullPath);
|
||||
|
||||
socket.emit('shell-showItemInFolderCompleted', success);
|
||||
});
|
||||
|
||||
socket.on('shell-openItem', (fullPath) => {
|
||||
const success = shell.openItem(fullPath);
|
||||
|
||||
socket.emit('shell-openItemCompleted', success);
|
||||
});
|
||||
|
||||
socket.on('shell-openExternal', (url, options, callback) => {
|
||||
let success = false;
|
||||
|
||||
if (options && callback) {
|
||||
success = shell.openExternal(url, options, (error) => {
|
||||
socket.emit('shell-openExternalCallback', [url, error]);
|
||||
});
|
||||
} else if (options) {
|
||||
success = shell.openExternal(url, options);
|
||||
} else {
|
||||
success = shell.openExternal(url);
|
||||
}
|
||||
|
||||
socket.emit('shell-openExternalCompleted', success);
|
||||
});
|
||||
|
||||
socket.on('shell-moveItemToTrash', (fullPath) => {
|
||||
const success = shell.moveItemToTrash(fullPath);
|
||||
|
||||
socket.emit('shell-moveItemToTrashCompleted', success);
|
||||
});
|
||||
|
||||
socket.on('shell-beep', () => {
|
||||
shell.beep();
|
||||
});
|
||||
|
||||
socket.on('shell-writeShortcutLink', (shortcutPath, operation, options) => {
|
||||
const success = shell.writeShortcutLink(shortcutPath, operation, options);
|
||||
|
||||
socket.emit('shell-writeShortcutLinkCompleted', success);
|
||||
});
|
||||
|
||||
socket.on('shell-readShortcutLink', (shortcutPath) => {
|
||||
const shortcutDetails = shell.readShortcutLink(shortcutPath);
|
||||
|
||||
socket.emit('shell-readShortcutLinkCompleted', shortcutDetails);
|
||||
});
|
||||
}
|
||||
31
ElectronNET.Host/api/webContents.js
Normal file
31
ElectronNET.Host/api/webContents.js
Normal file
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var electron_1 = require("electron");
|
||||
module.exports = function (socket) {
|
||||
socket.on('register-webContents-crashed', function (id) {
|
||||
var browserWindow = getWindowById(id);
|
||||
browserWindow.webContents.removeAllListeners('crashed');
|
||||
browserWindow.webContents.on('crashed', function (event, killed) {
|
||||
socket.emit('webContents-crashed' + id, killed);
|
||||
});
|
||||
});
|
||||
socket.on('register-webContents-didFinishLoad', function (id) {
|
||||
var browserWindow = getWindowById(id);
|
||||
browserWindow.webContents.removeAllListeners('did-finish-load');
|
||||
browserWindow.webContents.on('did-finish-load', function () {
|
||||
socket.emit('webContents-didFinishLoad' + id);
|
||||
});
|
||||
});
|
||||
socket.on('webContentsOpenDevTools', function (id, options) {
|
||||
if (options) {
|
||||
getWindowById(id).webContents.openDevTools(options);
|
||||
}
|
||||
else {
|
||||
getWindowById(id).webContents.openDevTools();
|
||||
}
|
||||
});
|
||||
function getWindowById(id) {
|
||||
return electron_1.BrowserWindow.fromId(id);
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=webContents.js.map
|
||||
1
ElectronNET.Host/api/webContents.js.map
Normal file
1
ElectronNET.Host/api/webContents.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"webContents.js","sourceRoot":"","sources":["webContents.ts"],"names":[],"mappings":";;AAAA,qCAAyC;AACzC,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,8BAA8B,EAAE,UAAC,EAAE;QACzC,IAAI,aAAa,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;QAEtC,aAAa,CAAC,WAAW,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;QACxD,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,UAAC,KAAK,EAAE,MAAM;YAClD,MAAM,CAAC,IAAI,CAAC,qBAAqB,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,oCAAoC,EAAE,UAAC,EAAE;QAC/C,IAAI,aAAa,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;QAEtC,aAAa,CAAC,WAAW,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,CAAC;QAChE,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC,iBAAiB,EAAE;YAC5C,MAAM,CAAC,IAAI,CAAC,2BAA2B,GAAG,EAAE,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,yBAAyB,EAAE,UAAC,EAAE,EAAE,OAAO;QAC7C,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACV,aAAa,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QACxD,CAAC;QAAC,IAAI,CAAC,CAAC;YACJ,aAAa,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC;QACjD,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,uBAAuB,EAAU;QAC7B,MAAM,CAAC,wBAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;AACL,CAAC,CAAC"}
|
||||
32
ElectronNET.Host/api/webContents.ts
Normal file
32
ElectronNET.Host/api/webContents.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { BrowserWindow } from 'electron';
|
||||
module.exports = (socket: SocketIO.Server) => {
|
||||
socket.on('register-webContents-crashed', (id) => {
|
||||
var browserWindow = getWindowById(id);
|
||||
|
||||
browserWindow.webContents.removeAllListeners('crashed');
|
||||
browserWindow.webContents.on('crashed', (event, killed) => {
|
||||
socket.emit('webContents-crashed' + id, killed);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('register-webContents-didFinishLoad', (id) => {
|
||||
var browserWindow = getWindowById(id);
|
||||
|
||||
browserWindow.webContents.removeAllListeners('did-finish-load');
|
||||
browserWindow.webContents.on('did-finish-load', () => {
|
||||
socket.emit('webContents-didFinishLoad' + id);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('webContentsOpenDevTools', (id, options) => {
|
||||
if (options) {
|
||||
getWindowById(id).webContents.openDevTools(options);
|
||||
} else {
|
||||
getWindowById(id).webContents.openDevTools();
|
||||
}
|
||||
});
|
||||
|
||||
function getWindowById(id: number): Electron.BrowserWindow {
|
||||
return BrowserWindow.fromId(id);
|
||||
}
|
||||
};
|
||||
@@ -3,7 +3,9 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const process = require('child_process').spawn;
|
||||
const portfinder = require('detect-port');
|
||||
let io, browserWindows, ipc, apiProcess, loadURL, appApi, menu, dialog, notification, tray;
|
||||
let io, browserWindows, ipc, apiProcess, loadURL;
|
||||
let appApi, menu, dialog, notification, tray, webContents;
|
||||
let globalShortcut, shell;
|
||||
|
||||
app.on('ready', () => {
|
||||
portfinder(8000, (error, port) => {
|
||||
@@ -25,6 +27,9 @@ function startSocketApiBridge(port) {
|
||||
dialog = require('./api/dialog')(socket);
|
||||
notification = require('./api/notification')(socket);
|
||||
tray = require('./api/tray')(socket);
|
||||
webContents = require('./api/webContents')(socket);
|
||||
globalShortcut = require('./api/globalShortcut')(socket);
|
||||
shell = require('./api/shell')(socket);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
76
ElectronNET.WebApp/Controllers/CrashHangController.cs
Normal file
76
ElectronNET.WebApp/Controllers/CrashHangController.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ElectronNET.API;
|
||||
using ElectronNET.API.Entities;
|
||||
|
||||
namespace ElectronNET.WebApp.Controllers
|
||||
{
|
||||
public class CrashHangController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
Electron.IpcMain.On("process-crash", async (args) =>
|
||||
{
|
||||
string viewPath = $"http://localhost:{BridgeSettings.WebPort}/crashhang/processcrash";
|
||||
|
||||
var browserWindow = await Electron.WindowManager.CreateWindowAsync(viewPath);
|
||||
browserWindow.WebContents.OnCrashed += async (killed) =>
|
||||
{
|
||||
var options = new MessageBoxOptions("This process has crashed.")
|
||||
{
|
||||
Type = MessageBoxType.info,
|
||||
Title = "Renderer Process Crashed",
|
||||
Buttons = new string[] { "Reload", "Close" }
|
||||
};
|
||||
var result = await Electron.Dialog.ShowMessageBoxAsync(options);
|
||||
|
||||
if (result.Response == 0)
|
||||
{
|
||||
browserWindow.Reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
browserWindow.Close();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Electron.IpcMain.On("process-hang", async (args) =>
|
||||
{
|
||||
string viewPath = $"http://localhost:{BridgeSettings.WebPort}/crashhang/processhang";
|
||||
|
||||
var browserWindow = await Electron.WindowManager.CreateWindowAsync(viewPath);
|
||||
browserWindow.OnUnresponsive += async () =>
|
||||
{
|
||||
var options = new MessageBoxOptions("This process is hanging.")
|
||||
{
|
||||
Type = MessageBoxType.info,
|
||||
Title = "Renderer Process Hanging",
|
||||
Buttons = new string[] { "Reload", "Close" }
|
||||
};
|
||||
var result = await Electron.Dialog.ShowMessageBoxAsync(options);
|
||||
|
||||
if (result.Response == 0)
|
||||
{
|
||||
browserWindow.Reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
browserWindow.Close();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult ProcessCrash()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult ProcessHang()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
67
ElectronNET.WebApp/Controllers/DialogsController.cs
Normal file
67
ElectronNET.WebApp/Controllers/DialogsController.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ElectronNET.API;
|
||||
using ElectronNET.API.Entities;
|
||||
|
||||
namespace ElectronNET.WebApp.Controllers
|
||||
{
|
||||
public class DialogsController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
Electron.IpcMain.On("select-directory", async (args) => {
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
var options = new OpenDialogOptions {
|
||||
Properties = new OpenDialogProperty[] {
|
||||
OpenDialogProperty.openFile,
|
||||
OpenDialogProperty.openDirectory
|
||||
}
|
||||
};
|
||||
|
||||
string[] files = await Electron.Dialog.ShowOpenDialogAsync(mainWindow, options);
|
||||
Electron.IpcMain.Send(mainWindow, "select-directory-reply", files);
|
||||
});
|
||||
|
||||
Electron.IpcMain.On("error-dialog", (args) =>
|
||||
{
|
||||
Electron.Dialog.ShowErrorBox("An Error Message", "Demonstrating an error message.");
|
||||
});
|
||||
|
||||
Electron.IpcMain.On("information-dialog", async (args) =>
|
||||
{
|
||||
var options = new MessageBoxOptions("This is an information dialog. Isn't it nice?")
|
||||
{
|
||||
Type = MessageBoxType.info,
|
||||
Title = "Information",
|
||||
Buttons = new string[] { "Yes", "No" }
|
||||
};
|
||||
|
||||
var result = await Electron.Dialog.ShowMessageBoxAsync(options);
|
||||
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
Electron.IpcMain.Send(mainWindow, "information-dialog-reply", result.Response);
|
||||
});
|
||||
|
||||
Electron.IpcMain.On("save-dialog", async (args) =>
|
||||
{
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
var options = new SaveDialogOptions
|
||||
{
|
||||
Title = "Save an Image",
|
||||
Filters = new FileFilter[]
|
||||
{
|
||||
new FileFilter { Name = "Images", Extensions = new string[] {"jpg", "png", "gif" } }
|
||||
}
|
||||
};
|
||||
|
||||
var result = await Electron.Dialog.ShowSaveDialogAsync(mainWindow, options);
|
||||
Electron.IpcMain.Send(mainWindow, "save-dialog-reply", result);
|
||||
});
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,119 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ElectronNET.API;
|
||||
using ElectronNET.API.Entities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Linq;
|
||||
|
||||
namespace ElectronNET.WebApp.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
{
|
||||
var menu = new MenuItem[] {
|
||||
new MenuItem { Label = "Edit", Submenu = new MenuItem[] {
|
||||
new MenuItem { Label = "Undo", Accelerator = "CmdOrCtrl+Z", Role = MenuRole.undo },
|
||||
new MenuItem { Label = "Redo", Accelerator = "Shift+CmdOrCtrl+Z", Role = MenuRole.redo },
|
||||
new MenuItem { Type = MenuType.separator },
|
||||
new MenuItem { Label = "Cut", Accelerator = "CmdOrCtrl+X", Role = MenuRole.cut },
|
||||
new MenuItem { Label = "Copy", Accelerator = "CmdOrCtrl+C", Role = MenuRole.copy },
|
||||
new MenuItem { Label = "Paste", Accelerator = "CmdOrCtrl+V", Role = MenuRole.paste },
|
||||
new MenuItem { Label = "Select All", Accelerator = "CmdOrCtrl+A", Role = MenuRole.selectall }
|
||||
}
|
||||
},
|
||||
new MenuItem { Label = "View", Submenu = new MenuItem[] {
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Reload",
|
||||
Accelerator = "CmdOrCtrl+R",
|
||||
Click = () =>
|
||||
{
|
||||
// on reload, start fresh and close any old
|
||||
// open secondary windows
|
||||
Electron.WindowManager.BrowserWindows.ToList().ForEach(browserWindow => {
|
||||
if(browserWindow.Id != 1)
|
||||
{
|
||||
browserWindow.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
browserWindow.Reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Toggle Full Screen",
|
||||
Accelerator = "CmdOrCtrl+F",
|
||||
Click = async () =>
|
||||
{
|
||||
bool isFullScreen = await Electron.WindowManager.BrowserWindows.First().IsFullScreenAsync();
|
||||
Electron.WindowManager.BrowserWindows.First().SetFullScreen(!isFullScreen);
|
||||
}
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Open Developer Tools",
|
||||
Accelerator = "CmdOrCtrl+I",
|
||||
Click = () => Electron.WindowManager.BrowserWindows.First().WebContents.OpenDevTools()
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Type = MenuType.separator
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "App Menu Demo",
|
||||
Click = async () => {
|
||||
var options = new MessageBoxOptions("This demo is for the Menu section, showing how to create a clickable menu item in the application menu.");
|
||||
options.Type = MessageBoxType.info;
|
||||
options.Title = "Application Menu Demo";
|
||||
await Electron.Dialog.ShowMessageBoxAsync(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new MenuItem { Label = "Window", Role = MenuRole.window, Submenu = new MenuItem[] {
|
||||
new MenuItem { Label = "Minimize", Accelerator = "CmdOrCtrl+M", Role = MenuRole.minimize },
|
||||
new MenuItem { Label = "Close", Accelerator = "CmdOrCtrl+W", Role = MenuRole.close }
|
||||
}
|
||||
},
|
||||
new MenuItem { Label = "Help", Role = MenuRole.help, Submenu = new MenuItem[] {
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Learn More",
|
||||
Click = async () => await Electron.Shell.OpenExternalAsync("https://github.com/ElectronNET")
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Electron.Menu.SetApplicationMenu(menu);
|
||||
|
||||
CreateContextMenu();
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
private void CreateContextMenu()
|
||||
{
|
||||
var menu = new MenuItem[]
|
||||
{
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Hello",
|
||||
Click = async () => await Electron.Dialog.ShowMessageBoxAsync("Electron.NET rocks!")
|
||||
},
|
||||
new MenuItem { Type = MenuType.separator },
|
||||
new MenuItem { Label = "Electron.NET", Type = MenuType.checkbox, Checked = true }
|
||||
};
|
||||
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
Electron.Menu.SetContextMenu(mainWindow, menu);
|
||||
|
||||
Electron.IpcMain.On("show-context-menu", (args) => {
|
||||
Electron.Menu.ContextMenuPopup(mainWindow);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
25
ElectronNET.WebApp/Controllers/IpcController.cs
Normal file
25
ElectronNET.WebApp/Controllers/IpcController.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ElectronNET.API;
|
||||
using System.Linq;
|
||||
|
||||
namespace ElectronNET.WebApp.Controllers
|
||||
{
|
||||
public class IpcController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
Electron.IpcMain.On("async-msg", (args) =>
|
||||
{
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
Electron.IpcMain.Send(mainWindow, "asynchronous-reply", "pong");
|
||||
});
|
||||
|
||||
Electron.IpcMain.OnSync("sync-msg", (args) =>
|
||||
{
|
||||
return "pong";
|
||||
});
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
67
ElectronNET.WebApp/Controllers/ManageWindowsController.cs
Normal file
67
ElectronNET.WebApp/Controllers/ManageWindowsController.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ElectronNET.API;
|
||||
using ElectronNET.API.Entities;
|
||||
|
||||
namespace ElectronNET.WebApp.Controllers
|
||||
{
|
||||
public class WindowsController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
string viewPath = $"http://localhost:{BridgeSettings.WebPort}/windows/demowindow";
|
||||
|
||||
Electron.IpcMain.On("new-window", async (args) => {
|
||||
|
||||
await Electron.WindowManager.CreateWindowAsync(viewPath);
|
||||
|
||||
});
|
||||
|
||||
Electron.IpcMain.On("manage-window", async (args) => {
|
||||
|
||||
var browserWindow = await Electron.WindowManager.CreateWindowAsync(viewPath);
|
||||
browserWindow.OnMove += UpdateReply;
|
||||
browserWindow.OnResize += UpdateReply;
|
||||
});
|
||||
|
||||
Electron.IpcMain.On("listen-to-window", async (args) => {
|
||||
var mainBrowserWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
|
||||
var browserWindow = await Electron.WindowManager.CreateWindowAsync(viewPath);
|
||||
browserWindow.OnFocus += () => Electron.IpcMain.Send(mainBrowserWindow, "listen-to-window-focus");
|
||||
browserWindow.OnBlur += () => Electron.IpcMain.Send(mainBrowserWindow, "listen-to-window-blur");
|
||||
|
||||
Electron.IpcMain.On("listen-to-window-set-focus", (x) => browserWindow.Focus());
|
||||
});
|
||||
|
||||
Electron.IpcMain.On("frameless-window", async (args) => {
|
||||
var options = new BrowserWindowOptions
|
||||
{
|
||||
Frame = false
|
||||
};
|
||||
await Electron.WindowManager.CreateWindowAsync(options, viewPath);
|
||||
});
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
private async void UpdateReply()
|
||||
{
|
||||
var browserWindow = Electron.WindowManager.BrowserWindows.Last();
|
||||
var size = await browserWindow.GetSizeAsync();
|
||||
var position = await browserWindow.GetPositionAsync();
|
||||
string message = $"Size: {size[0]},{size[1]} Position: {position[0]},{position[1]}";
|
||||
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
Electron.IpcMain.Send(mainWindow, "manage-window-reply", message);
|
||||
}
|
||||
|
||||
public IActionResult DemoWindow()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
16
ElectronNET.WebApp/Controllers/MenusController.cs
Normal file
16
ElectronNET.WebApp/Controllers/MenusController.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ElectronNET.WebApp.Controllers
|
||||
{
|
||||
public class MenusController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
40
ElectronNET.WebApp/Controllers/NotificationsController.cs
Normal file
40
ElectronNET.WebApp/Controllers/NotificationsController.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ElectronNET.API;
|
||||
using ElectronNET.API.Entities;
|
||||
|
||||
namespace ElectronNET.WebApp.Controllers
|
||||
{
|
||||
public class NotificationsController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
Electron.IpcMain.On("basic-noti", (args) => {
|
||||
|
||||
var options = new NotificationOptions("Basic Notification", "Short message part")
|
||||
{
|
||||
OnClick = async () => await Electron.Dialog.ShowMessageBoxAsync("Notification clicked")
|
||||
};
|
||||
|
||||
Electron.Notification.Show(options);
|
||||
|
||||
});
|
||||
|
||||
Electron.IpcMain.On("advanced-noti", (args) => {
|
||||
|
||||
var options = new NotificationOptions("Notification with image", "Short message plus a custom image")
|
||||
{
|
||||
OnClick = async () => await Electron.Dialog.ShowMessageBoxAsync("Notification clicked"),
|
||||
Icon = "/assets/img/programming.png"
|
||||
};
|
||||
|
||||
Electron.Notification.Show(options);
|
||||
});
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
27
ElectronNET.WebApp/Controllers/ShellController.cs
Normal file
27
ElectronNET.WebApp/Controllers/ShellController.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using ElectronNET.API;
|
||||
using ElectronNET.API.Entities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ElectronNET.WebApp.Controllers
|
||||
{
|
||||
public class ShellController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
Electron.IpcMain.On("open-file-manager", async (args) => {
|
||||
|
||||
string path = await Electron.App.GetPathAsync(PathName.home);
|
||||
await Electron.Shell.ShowItemInFolderAsync(path);
|
||||
|
||||
});
|
||||
|
||||
Electron.IpcMain.On("open-ex-links", async (args) => {
|
||||
|
||||
await Electron.Shell.OpenExternalAsync("https://github.com/ElectronNET");
|
||||
|
||||
});
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
26
ElectronNET.WebApp/Controllers/ShortcutsController.cs
Normal file
26
ElectronNET.WebApp/Controllers/ShortcutsController.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using ElectronNET.API;
|
||||
using ElectronNET.API.Entities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ElectronNET.WebApp.Controllers
|
||||
{
|
||||
public class ShortcutsController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
Electron.GlobalShortcut.Register("CommandOrControl+Alt+K", async () => {
|
||||
var options = new MessageBoxOptions("You pressed the registered global shortcut keybinding.")
|
||||
{
|
||||
Type = MessageBoxType.info,
|
||||
Title = "Success!"
|
||||
};
|
||||
|
||||
await Electron.Dialog.ShowMessageBoxAsync(options);
|
||||
});
|
||||
|
||||
Electron.App.WillQuit += () => Electron.GlobalShortcut.UnregisterAll();
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
34
ElectronNET.WebApp/Controllers/TrayController.cs
Normal file
34
ElectronNET.WebApp/Controllers/TrayController.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ElectronNET.API;
|
||||
using ElectronNET.API.Entities;
|
||||
|
||||
namespace ElectronNET.WebApp.Controllers
|
||||
{
|
||||
public class TrayController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
Electron.IpcMain.On("put-in-tray", (args) => {
|
||||
|
||||
if (Electron.Tray.Items.Count == 0)
|
||||
{
|
||||
var menu = new MenuItem
|
||||
{
|
||||
Label = "Remove",
|
||||
Click = () => Electron.Tray.Destroy()
|
||||
};
|
||||
|
||||
Electron.Tray.Show("/Assets/electron_32x32.png", menu);
|
||||
Electron.Tray.SetToolTip("Electron Demo in the tray.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Electron.Tray.Destroy();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ElectronNET.API;
|
||||
using ElectronNET.API.Entities;
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Controllers\ManageWindowsController.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Remove="Views\Windows\HandleErrorCrashes.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Assets\" />
|
||||
<Folder Include="wwwroot\assets\" />
|
||||
|
||||
113
ElectronNET.WebApp/Views/CrashHang/Index.cshtml
Normal file
113
ElectronNET.WebApp/Views/CrashHang/Index.cshtml
Normal file
@@ -0,0 +1,113 @@
|
||||
<template class="task-template">
|
||||
<section id="crash-hang-section" class="section js-section u-category-windows">
|
||||
<header class="section-header">
|
||||
<div class="section-wrapper">
|
||||
<h1>
|
||||
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-windows"></use></svg>
|
||||
Handling Window Crashes and Hangs
|
||||
</h1>
|
||||
<h3>The <code>Electron.WindowManager</code> will emit events when the renderer process crashes or hangs. You can listen for these events and give users the chance to reload, wait or close that window.</h3>
|
||||
|
||||
<p>You find the sample source code in <code>Controllers\CrashHangController.cs</code>.</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="new-window-crashes-demo-toggle" class="js-container-target demo-toggle-button">Relaunch window after the process crashes
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="process-crash">View Demo</button>
|
||||
</div>
|
||||
<p>In this demo we create a new window and provide a link that will force a crash using <code>process.crash()</code>.</p>
|
||||
<p>The window is listening for the crash event and when the event occurs it prompts the user with two options: reload or close.</p>
|
||||
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="csharp">var browserWindow = await Electron.WindowManager.CreateWindowAsync(viewPath);
|
||||
browserWindow.WebContents.OnCrashed += async (killed) => {
|
||||
var options = new MessageBoxOptions("This process has crashed.") {
|
||||
Type = MessageBoxType.info,
|
||||
Title = "Renderer Process Crashed",
|
||||
Buttons = new string[] { "Reload", "Close" }
|
||||
};
|
||||
var result = await Electron.Dialog.ShowMessageBoxAsync(options);
|
||||
|
||||
if (result.Response == 0)
|
||||
{
|
||||
browserWindow.Reload();
|
||||
} else
|
||||
{
|
||||
browserWindow.Close();
|
||||
}
|
||||
};</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="new-window-hangs-demo-toggle" class="js-container-target demo-toggle-button">Relaunch window after the process hangs
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="process-hang">View Demo</button>
|
||||
</div>
|
||||
<p>In this demo we create a new window and provide a link that will force the process to hang using <code>process.hang()</code>.</p>
|
||||
<p>The window is listening for the process to become officially unresponsive (this can take up to 30 seconds). When this event occurs it prompts the user with two options: reload or close.</p>
|
||||
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="csharp">var browserWindow = await Electron.WindowManager.CreateWindowAsync();
|
||||
browserWindow.OnUnresponsive += async () => {
|
||||
var options = new MessageBoxOptions("This process is hanging.")
|
||||
{
|
||||
Type = MessageBoxType.info,
|
||||
Title = "Renderer Process Hanging",
|
||||
Buttons = new string[] { "Reload", "Close" }
|
||||
};
|
||||
var result = await Electron.Dialog.ShowMessageBoxAsync(options);
|
||||
|
||||
if (result.Response == 0)
|
||||
{
|
||||
browserWindow.Reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
browserWindow.Close();
|
||||
}
|
||||
};</code></pre>
|
||||
|
||||
<div class="demo-protip">
|
||||
<h2>ProTip</h2>
|
||||
<strong>Wait for the process to become responsive again.</strong>
|
||||
<p>A third option in the case of a process that is hanging is to wait and see if the problem resolves, allowing the process to become responsive again. To do this, use the <code>BrowserWindow</code> event 'OnResponsive' as shown below.</p>
|
||||
<pre><code class="csharp">browserWindow.OnResponsive += () =>
|
||||
{
|
||||
// Do something when the window is responsive again
|
||||
};</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
(function(){
|
||||
const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("process-crash").addEventListener("click", () => {
|
||||
ipcRenderer.send("process-crash");
|
||||
});
|
||||
|
||||
document.getElementById("process-hang").addEventListener("click", () => {
|
||||
ipcRenderer.send("process-hang");
|
||||
});
|
||||
|
||||
}());
|
||||
</script>
|
||||
|
||||
</section>
|
||||
</template>
|
||||
24
ElectronNET.WebApp/Views/CrashHang/ProcessCrash.cshtml
Normal file
24
ElectronNET.WebApp/Views/CrashHang/ProcessCrash.cshtml
Normal file
@@ -0,0 +1,24 @@
|
||||
<style>
|
||||
body {
|
||||
padding: 20px;
|
||||
font-family: system, -apple-system, '.SFNSText-Regular', 'SF UI Text', 'Lucida Grande', 'Segoe UI', Ubuntu, Cantarell, sans-serif;
|
||||
color: #fff;
|
||||
background-color: #8aba87;
|
||||
text-align: center;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
#crash {
|
||||
color: white;
|
||||
opacity: 0.7;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 12px;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>Click the text below to crash and then reload this process.</p>
|
||||
<a id="crash" href="javascript:process.crash()">Crash this process</a>
|
||||
29
ElectronNET.WebApp/Views/CrashHang/ProcessHang.cshtml
Normal file
29
ElectronNET.WebApp/Views/CrashHang/ProcessHang.cshtml
Normal file
@@ -0,0 +1,29 @@
|
||||
<style>
|
||||
body {
|
||||
padding: 20px;
|
||||
font-family: system, -apple-system, '.SFNSText-Regular', 'SF UI Text', 'Lucida Grande', 'Segoe UI', Ubuntu, Cantarell, sans-serif;
|
||||
color: #fff;
|
||||
background-color: #8aba87;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
#crash {
|
||||
color: white;
|
||||
opacity: 0.7;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 12px;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>Click the text below to hang and then reload this process.</p>
|
||||
<small>(This will take up to 30 seconds.)</small>
|
||||
|
||||
<a id="crash" href="javascript:process.hang()">Hang this process</a>
|
||||
222
ElectronNET.WebApp/Views/Dialogs/Index.cshtml
Normal file
222
ElectronNET.WebApp/Views/Dialogs/Index.cshtml
Normal file
@@ -0,0 +1,222 @@
|
||||
<template class="task-template">
|
||||
<section id="dialogs-section" class="section js-section u-category-native-ui">
|
||||
<header class="section-header">
|
||||
<div class="section-wrapper">
|
||||
<h1>
|
||||
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-native-ui"></use></svg>
|
||||
Use system dialogs
|
||||
</h1>
|
||||
<h3>The <code>Electron.Dialog</code> in Electron.NET allows you to use native system dialogs for opening files or directories, saving a file or displaying informational messages.</h3>
|
||||
|
||||
<p>This is a main process module because this process is more efficient with native utilities and it allows the call to happen without interupting the visible elements in your page's renderer process.</p>
|
||||
|
||||
<p>You find the sample source code in <code>Controllers\DialogsController.cs</code>.</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="open-file-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Open a File or Directory
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="select-directory">View Demo</button>
|
||||
<span class="demo-response" id="selected-file"></span>
|
||||
</div>
|
||||
<p>In this demo, the <code>ipcRenderer</code> is used to send a message from the renderer process instructing the main process to launch the open file (or directory) dialog. If a file is selected, the main process can send that information back to the renderer process.</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="language-js">const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("select-directory").addEventListener("click", () => {
|
||||
ipcRenderer.send("select-directory");
|
||||
});
|
||||
|
||||
ipcRenderer.on("select-directory-reply", (sender, path) => {
|
||||
document.getElementById("selected-file").innerText = `You selected: ${path}`;;
|
||||
});</code></pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre><code class="csharp">Electron.IpcMain.On("select-directory", async (args) => {
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
var options = new OpenDialogOptions {
|
||||
Properties = new OpenDialogProperty[] {
|
||||
OpenDialogProperty.openFile,
|
||||
OpenDialogProperty.openDirectory
|
||||
}
|
||||
};
|
||||
|
||||
string[] files = await Electron.Dialog.ShowOpenDialogAsync(mainWindow, options);
|
||||
Electron.IpcMain.Send(mainWindow, "select-directory-reply", files);
|
||||
});</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="error-dialog-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Error Dialog
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button id="error-dialog" class="demo-button">View Demo</button>
|
||||
</div>
|
||||
<p>In this demo, the <code>ipcRenderer</code> is used to send a message from the renderer process instructing the main process to launch the error dialog.</p>
|
||||
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="language-js">const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("error-dialog").addEventListener("click", () => {
|
||||
ipcRenderer.send("error-dialog");
|
||||
});</code></pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre><code class="csharp">Electron.IpcMain.On("error-dialog", (args) =>
|
||||
{
|
||||
Electron.Dialog.ShowErrorBox("An Error Message", "Demonstrating an error message.");
|
||||
});</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="information-dialog-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Information Dialog
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="information-dialog">View Demo</button>
|
||||
<span class="demo-response" id="info-selection"></span>
|
||||
</div>
|
||||
<p>In this demo, the <code>ipcRenderer</code> is used to send a message from the renderer process instructing the main process to launch the information dialog. Options may be provided for responses which can then be relayed back to the renderer process.</p>
|
||||
|
||||
<p>Note: The <code>title</code> property is not displayed in macOS.</p>
|
||||
|
||||
<p>An information dialog can contain an icon, your choice of buttons, title and message.</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="javascript">document.getElementById("information-dialog").addEventListener("click", () => {
|
||||
ipcRenderer.send("information-dialog");
|
||||
});
|
||||
|
||||
ipcRenderer.on("information-dialog-reply", (sender, index) => {
|
||||
let message = 'You selected ';
|
||||
|
||||
if(index == 0) {
|
||||
message += 'yes.'
|
||||
} else {
|
||||
message += 'no.'
|
||||
}
|
||||
|
||||
document.getElementById("info-selection").innerText = message;
|
||||
});</code></pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre><code class="csharp">Electron.IpcMain.On("information-dialog", async (args) =>
|
||||
{
|
||||
var options = new MessageBoxOptions("This is an information dialog. Isn't it nice?")
|
||||
{
|
||||
Type = MessageBoxType.info,
|
||||
Title = "Information",
|
||||
Buttons = new string[] { "Yes", "No" }
|
||||
};
|
||||
|
||||
var result = await Electron.Dialog.ShowMessageBoxAsync(options);
|
||||
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
Electron.IpcMain.Send(mainWindow, "information-dialog-reply", result.Response);
|
||||
});</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="save-dialog-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Save Dialog
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="save-dialog">View Demo</button>
|
||||
<span class="demo-response" id="file-saved"></span>
|
||||
</div>
|
||||
<p>In this demo, the <code>ipcRenderer</code> is used to send a message from the renderer process instructing the main process to launch the save dialog. It returns the path selected by the user which can be relayed back to the renderer process.</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="javascript">const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("save-dialog").addEventListener("click", () => {
|
||||
ipcRenderer.send("save-dialog");
|
||||
});
|
||||
|
||||
ipcRenderer.on("save-dialog-reply", (sender, path) => {
|
||||
if (!path) path = 'No path';
|
||||
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`;
|
||||
});</code></pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre><code class="csharp">Electron.IpcMain.On("save-dialog", async (args) =>
|
||||
{
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
var options = new SaveDialogOptions
|
||||
{
|
||||
Title = "Save an Image",
|
||||
Filters = new FileFilter[]
|
||||
{
|
||||
new FileFilter { Name = "Images", Extensions = new string[] {"jpg", "png", "gif" } }
|
||||
}
|
||||
};
|
||||
|
||||
var result = await Electron.Dialog.ShowSaveDialogAsync(mainWindow, options);
|
||||
Electron.IpcMain.Send(mainWindow, "save-dialog-reply", result);
|
||||
});</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("select-directory").addEventListener("click", () => {
|
||||
ipcRenderer.send("select-directory");
|
||||
});
|
||||
|
||||
ipcRenderer.on("select-directory-reply", (sender, path) => {
|
||||
document.getElementById("selected-file").innerText = `You selected: ${path}`;
|
||||
});
|
||||
|
||||
document.getElementById("error-dialog").addEventListener("click", () => {
|
||||
ipcRenderer.send("error-dialog");
|
||||
});
|
||||
|
||||
document.getElementById("information-dialog").addEventListener("click", () => {
|
||||
ipcRenderer.send("information-dialog");
|
||||
});
|
||||
|
||||
ipcRenderer.on("information-dialog-reply", (sender, index) => {
|
||||
let message = 'You selected ';
|
||||
|
||||
if(index == 0) {
|
||||
message += 'yes.'
|
||||
} else {
|
||||
message += 'no.'
|
||||
}
|
||||
|
||||
document.getElementById("info-selection").innerText = message;
|
||||
});
|
||||
|
||||
document.getElementById("save-dialog").addEventListener("click", () => {
|
||||
ipcRenderer.send("save-dialog");
|
||||
});
|
||||
|
||||
ipcRenderer.on("save-dialog-reply", (sender, path) => {
|
||||
if (!path) path = 'No path';
|
||||
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`;
|
||||
});
|
||||
|
||||
}());
|
||||
</script>
|
||||
|
||||
</section>
|
||||
</template>
|
||||
@@ -14,6 +14,14 @@
|
||||
|
||||
<link rel="import" href="about">
|
||||
<link rel="import" href="windows">
|
||||
<link rel="import" href="crashhang">
|
||||
<link rel="import" href="menus">
|
||||
<link rel="import" href="shortcuts">
|
||||
<link rel="import" href="shell">
|
||||
<link rel="import" href="notifications">
|
||||
<link rel="import" href="dialogs">
|
||||
<link rel="import" href="tray">
|
||||
<link rel="import" href="ipc">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
104
ElectronNET.WebApp/Views/Ipc/Index.cshtml
Normal file
104
ElectronNET.WebApp/Views/Ipc/Index.cshtml
Normal file
@@ -0,0 +1,104 @@
|
||||
<template class="task-template">
|
||||
<section id="ipc-section" class="section js-section u-category-communication">
|
||||
<header class="communication">
|
||||
<div class="section-wrapper">
|
||||
<h1>
|
||||
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-communication"></use></svg>
|
||||
Communication between processes
|
||||
</h1>
|
||||
<h3>The <code>ipc</code> (inter-process communication) module allows you to send and receive synchronous and asynchronous messages between the main and renderer processes.</h3>
|
||||
|
||||
<p>There is a version of this module available for both processes: <code>Electron.IpcMain</code> and <code>ipcRenderer</code>.</p>
|
||||
<p>You find the sample source code in <code>Controllers\IpcController.cs</code>.</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="async-msg-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Asynchronous messages
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="async-msg">Ping</button>
|
||||
<span class="demo-response" id="async-reply"></span>
|
||||
</div>
|
||||
<p>Using <code>ipc</code> to send messages between processes asynchronously is the preferred method since it will return when finished without blocking other operations in the same process.</p>
|
||||
|
||||
<p>This example sends a "ping" from this process (renderer) to the main process. The main process then replies with "pong".</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="javascript">const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("async-msg").addEventListener("click", () => {
|
||||
ipcRenderer.send("async-msg", 'ping');
|
||||
});
|
||||
|
||||
ipcRenderer.on('asynchronous-reply', (event, arg) => {
|
||||
const message = `Asynchronous message reply: ${arg}`;
|
||||
document.getElementById('async-reply').innerHTML = message;
|
||||
});</code></pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre><code class="csharp">Electron.IpcMain.On("async-msg", (args) =>
|
||||
{
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
Electron.IpcMain.Send(mainWindow, "asynchronous-reply", "pong");
|
||||
});</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="sync-msg-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Synchronous messages
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="sync-msg">Ping</button>
|
||||
<span class="demo-response" id="sync-reply"></span>
|
||||
</div>
|
||||
<p>You can use the <code>ipc</code> module to send synchronous messages between processes as well, but note that the synchronous nature of this method means that it <b>will block</b> other operations while completing its task.</p>
|
||||
<p>This example sends a synchronous message, "ping", from this process (renderer) to the main process. The main process then replies with "pong".</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="javascript">const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("sync-msg").addEventListener("click", () => {
|
||||
const reply = ipcRenderer.sendSync("sync-msg", "ping");
|
||||
const message = `Synchronous message reply: ${reply}`;
|
||||
document.getElementById('sync-reply').innerHTML = message;
|
||||
});</code></pre>
|
||||
<h5>Main Process</h5>
|
||||
<pre><code class="csharp">Electron.IpcMain.OnSync("sync-msg", (args) =>
|
||||
{
|
||||
return "pong";
|
||||
});</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("async-msg").addEventListener("click", () => {
|
||||
ipcRenderer.send("async-msg", 'ping');
|
||||
});
|
||||
|
||||
ipcRenderer.on('asynchronous-reply', (event, arg) => {
|
||||
const message = `Asynchronous message reply: ${arg}`;
|
||||
document.getElementById('async-reply').innerHTML = message;
|
||||
});
|
||||
|
||||
document.getElementById("sync-msg").addEventListener("click", () => {
|
||||
const reply = ipcRenderer.sendSync("sync-msg", "ping");
|
||||
const message = `Synchronous message reply: ${reply}`;
|
||||
document.getElementById('sync-reply').innerHTML = message;
|
||||
});
|
||||
|
||||
}());
|
||||
</script>
|
||||
|
||||
</section>
|
||||
</template>
|
||||
189
ElectronNET.WebApp/Views/Menus/Index.cshtml
Normal file
189
ElectronNET.WebApp/Views/Menus/Index.cshtml
Normal file
@@ -0,0 +1,189 @@
|
||||
<template class="task-template">
|
||||
<section id="menus-section" class="section js-section u-category-menu">
|
||||
<header class="section-header">
|
||||
<div class="section-wrapper">
|
||||
<h1>
|
||||
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-menu"></use></svg>
|
||||
Customize Menus
|
||||
</h1>
|
||||
<h3>The <code>Electron.Menu</code> and <code>MenuItem</code> can be used to create custom native menus.</h3>
|
||||
|
||||
<p>There are two kinds of menus: the application (top) menu and context (right-click) menu.</p>
|
||||
|
||||
<p>You find the sample source code in <code>Controllers\HomeController.cs</code>.</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="application-menu-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Create an application menu
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<p>The <code>Electron.Menu</code> and <code>MenuItem</code> allow you to customize your application menu. If you don't set any menu, Electron will generate a minimal menu for your app by default.</p>
|
||||
|
||||
<p>This app uses the code below to set the application menu. If you click the 'View' option in the application menu and then the 'App Menu Demo', you'll see an information box displayed.</p>
|
||||
<h5>Main Process</h5>
|
||||
<pre><code class="csharp">var menu = new MenuItem[] {
|
||||
new MenuItem { Label = "Edit", Submenu = new MenuItem[] {
|
||||
new MenuItem { Label = "Undo", Accelerator = "CmdOrCtrl+Z", Role = MenuRole.undo },
|
||||
new MenuItem { Label = "Redo", Accelerator = "Shift+CmdOrCtrl+Z", Role = MenuRole.redo },
|
||||
new MenuItem { Type = MenuType.separator },
|
||||
new MenuItem { Label = "Cut", Accelerator = "CmdOrCtrl+X", Role = MenuRole.cut },
|
||||
new MenuItem { Label = "Copy", Accelerator = "CmdOrCtrl+C", Role = MenuRole.copy },
|
||||
new MenuItem { Label = "Paste", Accelerator = "CmdOrCtrl+V", Role = MenuRole.paste },
|
||||
new MenuItem { Label = "Select All", Accelerator = "CmdOrCtrl+A", Role = MenuRole.selectall }
|
||||
}
|
||||
},
|
||||
new MenuItem { Label = "View", Submenu = new MenuItem[] {
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Reload",
|
||||
Accelerator = "CmdOrCtrl+R",
|
||||
Click = () =>
|
||||
{
|
||||
// on reload, start fresh and close any old
|
||||
// open secondary windows
|
||||
Electron.WindowManager.BrowserWindows.ToList().ForEach(browserWindow => {
|
||||
if(browserWindow.Id != 1)
|
||||
{
|
||||
browserWindow.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
browserWindow.Reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Toggle Full Screen",
|
||||
Accelerator = "CmdOrCtrl+F",
|
||||
Click = async () =>
|
||||
{
|
||||
bool isFullScreen = await Electron.WindowManager.BrowserWindows.First().IsFullScreenAsync();
|
||||
Electron.WindowManager.BrowserWindows.First().SetFullScreen(!isFullScreen);
|
||||
}
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Open Developer Tools",
|
||||
Accelerator = "CmdOrCtrl+I",
|
||||
Click = () => Electron.WindowManager.BrowserWindows.First().WebContents.OpenDevTools()
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Type = MenuType.separator
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Label = "App Menu Demo",
|
||||
Click = async () => {
|
||||
var options = new MessageBoxOptions("This demo is for the Menu section, showing how to create a clickable menu item in the application menu.");
|
||||
options.Type = MessageBoxType.info;
|
||||
options.Title = "Application Menu Demo";
|
||||
await Electron.Dialog.ShowMessageBoxAsync(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new MenuItem { Label = "Window", Role = MenuRole.window, Submenu = new MenuItem[] {
|
||||
new MenuItem { Label = "Minimize", Accelerator = "CmdOrCtrl+M", Role = MenuRole.minimize },
|
||||
new MenuItem { Label = "Close", Accelerator = "CmdOrCtrl+W", Role = MenuRole.close }
|
||||
}
|
||||
},
|
||||
new MenuItem { Label = "Help", Role = MenuRole.help, Submenu = new MenuItem[] {
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Learn More",
|
||||
Click = async () => await Electron.Shell.OpenExternalAsync("https://github.com/ElectronNET")
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Electron.Menu.SetApplicationMenu(menu);</code></pre>
|
||||
|
||||
<div class="demo-protip">
|
||||
<h2>ProTip</h2>
|
||||
<strong>Know operating system menu differences.</strong>
|
||||
<p>When designing an app for multiple operating systems it's important to be mindful of the ways application menu conventions differ on each operating system.</p>
|
||||
<p>For instance, on Windows, accelerators are set with an <code>&</code>. Naming conventions also vary, like between "Settings" or "Preferences". Below are resources for learning operating system specific standards.</p>
|
||||
<ul>
|
||||
<li><a href="https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/MenuBarMenus.html#//apple_ref/doc/uid/20000957-CH29-SW1">macOS<span class="u-visible-to-screen-reader">(opens in new window)</span></a></li>
|
||||
<li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb226797(v=vs.85).aspx">Windows<span class="u-visible-to-screen-reader">(opens in new window)</span></a></li>
|
||||
<li><a href="https://developer.gnome.org/hig/stable/menu-bars.html.en">Linux<span class="u-visible-to-screen-reader">(opens in new window)</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="context-menu-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Create a context menu
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="context-menu">View Demo</button>
|
||||
</div>
|
||||
<p>A context, or right-click, menu can be created with the <code>Electron.Menu.SetContextMenu()</code> and <code>MenuItem</code> as well. You can right-click anywhere in this app or click the demo button to see an example context menu.</p>
|
||||
|
||||
<p>In this demo we use the <code>ipcRenderer</code> module to show the context menu when explicitly calling it from the renderer process.</p>
|
||||
<p>See the full <a href="http://electron.atom.io/docs/api/web-contents/#event-context-menu">context-menu event documentation</a> for all the available properties.</p>
|
||||
<h5>Main Process</h5>
|
||||
<pre><code class="csharp">var menu = new MenuItem[]
|
||||
{
|
||||
new MenuItem
|
||||
{
|
||||
Label = "Hello",
|
||||
Click = async () => await Electron.Dialog.ShowMessageBoxAsync("Electron.NET rocks!")
|
||||
},
|
||||
new MenuItem { Type = MenuType.separator },
|
||||
new MenuItem { Label = "Electron.NET", Type = MenuType.checkbox, Checked = true }
|
||||
};
|
||||
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
Electron.Menu.SetContextMenu(mainWindow, menu);
|
||||
|
||||
Electron.IpcMain.On("show-context-menu", (args) => {
|
||||
Electron.Menu.ContextMenuPopup(mainWindow);
|
||||
});</code></pre>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="javascript">const { ipcRenderer } = require("electron");
|
||||
|
||||
window.addEventListener('contextmenu', (e) => {
|
||||
e.preventDefault()
|
||||
ipcRenderer.send('show-context-menu');
|
||||
}, false);</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function(){
|
||||
const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("process-crash").addEventListener("click", () => {
|
||||
ipcRenderer.send("process-crash");
|
||||
});
|
||||
|
||||
const contextMenuBtn = document.getElementById('context-menu')
|
||||
contextMenuBtn.addEventListener('click', function () {
|
||||
ipcRenderer.send('show-context-menu');
|
||||
})
|
||||
|
||||
window.addEventListener('contextmenu', (e) => {
|
||||
e.preventDefault()
|
||||
ipcRenderer.send('show-context-menu');
|
||||
}, false);
|
||||
|
||||
}());
|
||||
</script>
|
||||
|
||||
</section>
|
||||
</template>
|
||||
78
ElectronNET.WebApp/Views/Notifications/Index.cshtml
Normal file
78
ElectronNET.WebApp/Views/Notifications/Index.cshtml
Normal file
@@ -0,0 +1,78 @@
|
||||
<template class="task-template">
|
||||
<section id="notifications-section" class="section js-section u-category-native-ui">
|
||||
<header class="notifications">
|
||||
<div class="section-wrapper">
|
||||
<h1>
|
||||
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-notification"></use></svg>
|
||||
Desktop notifications
|
||||
</h1>
|
||||
<h3>The <code>Electron.Notification</code> in Electron.NET allows you to add basic desktop notifications.</h3>
|
||||
|
||||
<p>Electron conveniently allows developers to send notifications with the <a href="https://notifications.spec.whatwg.org/">HTML5 Notification API</a>, using the currently running operating system’s native notification APIs to display it.</p>
|
||||
|
||||
<p><b>Note:</b> Since this is an HTML5 API it is only available in the renderer process.</p>
|
||||
|
||||
<p>You find the sample source code in <code>Controllers\NotificationsController.cs</code>.</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="basic-notification-demo-toggle" class="js-container-target demo-toggle-button">Basic notification
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win 7+, macOS, Linux (that supports libnotify)<span class="demo-meta-divider">|</span> Process: Renderer</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="basic-noti">View demo</button>
|
||||
</div>
|
||||
<p>This demo demonstrates a basic notification. Text only.</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="csharp">var options = new NotificationOptions("Basic Notification", "Short message part")
|
||||
{
|
||||
OnClick = async () => await Electron.Dialog.ShowMessageBoxAsync("Notification clicked")
|
||||
};
|
||||
|
||||
Electron.Notification.Show(options);</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="advanced-notification-demo-toggle" class="js-container-target demo-toggle-button">Notification with image
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win 7+, macOS, Linux (that supports libnotify) <span class="demo-meta-divider">|</span> Process: Renderer</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="advanced-noti">View demo</button>
|
||||
</div>
|
||||
<p>This demo demonstrates a basic notification. Both text and a image</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="csharp">var options = new NotificationOptions("Notification with image", "Short message plus a custom image")
|
||||
{
|
||||
OnClick = async () => await Electron.Dialog.ShowMessageBoxAsync("Notification clicked"),
|
||||
Icon = "/assets/img/programming.png"
|
||||
};
|
||||
|
||||
Electron.Notification.Show(options);</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function(){
|
||||
const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("basic-noti").addEventListener("click", () => {
|
||||
ipcRenderer.send("basic-noti");
|
||||
});
|
||||
|
||||
document.getElementById("advanced-noti").addEventListener("click", () => {
|
||||
ipcRenderer.send("advanced-noti");
|
||||
});
|
||||
|
||||
}());
|
||||
</script>
|
||||
|
||||
</section>
|
||||
</template>
|
||||
71
ElectronNET.WebApp/Views/Shell/Index.cshtml
Normal file
71
ElectronNET.WebApp/Views/Shell/Index.cshtml
Normal file
@@ -0,0 +1,71 @@
|
||||
<template class="task-template">
|
||||
<section id="ex-links-file-manager-section" class="section js-section u-category-native-ui">
|
||||
<header class="section-header">
|
||||
<div class="section-wrapper">
|
||||
<h1>
|
||||
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-native-ui"></use></svg>
|
||||
Open external links and the file manager
|
||||
</h1>
|
||||
<h3>The <code>Electron.Shell</code> in Electron.NET allows you to access certain native elements like the file manager and default web browser.</h3>
|
||||
|
||||
<p>This module works in both the main and renderer process.</p>
|
||||
|
||||
<p>You find the sample source code in <code>Controllers\ShellController.cs</code>.</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="open-file-manager-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Open Path in File Manager
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="open-file-manager">View Demo</button>
|
||||
</div>
|
||||
<p>This demonstrates using the <code>Electron.Shell</code> to open the system file manager at a particular location.</p>
|
||||
<p>Clicking the demo button will open your file manager at the root.</p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="csharp">string path = await Electron.App.GetPathAsync(PathName.home);
|
||||
await Electron.Shell.ShowItemInFolderAsync(path);</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="open-ex-links-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Open External Links
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="open-ex-links">View Demo</button>
|
||||
</div>
|
||||
<p>If you do not want your app to open website links <em>within</em> the app, you can use the <code>Electron.Shell</code> to open them externally. When clicked, the links will open outside of your app and in the user's default web browser.</p>
|
||||
<p>When the demo button is clicked, the electron website will open in your browser.<p>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="csharp">await Electron.Shell.OpenExternalAsync("https://github.com/ElectronNET");</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("open-file-manager").addEventListener("click", () => {
|
||||
ipcRenderer.send("open-file-manager");
|
||||
});
|
||||
|
||||
document.getElementById("open-ex-links").addEventListener("click", () => {
|
||||
ipcRenderer.send("open-ex-links");
|
||||
});
|
||||
|
||||
}());
|
||||
</script>
|
||||
|
||||
|
||||
</section>
|
||||
</template>
|
||||
74
ElectronNET.WebApp/Views/Shortcuts/Index.cshtml
Normal file
74
ElectronNET.WebApp/Views/Shortcuts/Index.cshtml
Normal file
@@ -0,0 +1,74 @@
|
||||
<template class="task-template">
|
||||
<section id="shortcuts-section" class="section js-section u-category-menu">
|
||||
<header class="section-header">
|
||||
<div class="section-wrapper">
|
||||
<h1>
|
||||
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-menu"></use></svg>
|
||||
Keyboard Shortcuts
|
||||
</h1>
|
||||
|
||||
<h3>The <code>Electron.GlobalShortcut</code> and <code>MenuItem</code> can be used to define keyboard shortcuts.</h3>
|
||||
|
||||
<p>
|
||||
In Electron.NET, keyboard shortcuts are called accelerators.
|
||||
They can be assigned to actions in your application's Menu,
|
||||
or they can be assigned globally so they'll be triggered even when
|
||||
your app doesn't have keyboard focus.
|
||||
</p>
|
||||
|
||||
<p>You find the sample source code in <code>Controllers\ShortcutsController.cs</code>.</p>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="shortcuts-demo-toggle" class="js-container-target demo-toggle-button">Register a global keyboard shortcut
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<p>
|
||||
To try this demo, press <kbd class="normalize-to-platform">CommandOrControl+Alt+K</kbd> on your keyboard.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Global shortcuts are detected even when the app doesn't have
|
||||
keyboard focus.
|
||||
</p>
|
||||
|
||||
<h5>Main Process</h5>
|
||||
<pre><code class="csharp">Electron.GlobalShortcut.Register("CommandOrControl+Alt+K", async () => {
|
||||
var options = new MessageBoxOptions("You pressed the registered global shortcut keybinding.")
|
||||
{
|
||||
Type = MessageBoxType.info,
|
||||
Title = "Success!"
|
||||
};
|
||||
|
||||
await Electron.Dialog.ShowMessageBoxAsync(options);
|
||||
});
|
||||
|
||||
Electron.App.WillQuit += () => Electron.GlobalShortcut.UnregisterAll();</code></pre>
|
||||
|
||||
<div class="demo-protip">
|
||||
<h2>ProTip</h2>
|
||||
<strong>Avoid overriding system-wide keyboard shortcuts.</strong>
|
||||
<p>
|
||||
When registering global shortcuts, it's important to be aware of
|
||||
existing defaults in the target operating system, so as not to
|
||||
override any existing behaviors. For an overview of each
|
||||
operating system's keyboard shortcuts, view these documents:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li><a class="u-exlink" href="https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/Keyboard.html">macOS</a></li>
|
||||
<li><a class="u-exlink" href="http://windows.microsoft.com/en-us/windows-10/keyboard-shortcuts">Windows</a></li>
|
||||
<li><a class="u-exlink" href="https://developer.gnome.org/hig/stable/keyboard-input.html.en">Linux</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</template>
|
||||
100
ElectronNET.WebApp/Views/Tray/Index.cshtml
Normal file
100
ElectronNET.WebApp/Views/Tray/Index.cshtml
Normal file
@@ -0,0 +1,100 @@
|
||||
<template class="task-template">
|
||||
<section id="tray-section" class="section js-section u-category-native-ui">
|
||||
<header class="section-header">
|
||||
<div class="section-wrapper">
|
||||
<h1>
|
||||
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-native-ui"></use></svg>
|
||||
Tray
|
||||
</h1>
|
||||
<h3>The <code>Electron.Tray</code> allows you to create an icon in the operating system's notification area.</h3>
|
||||
<p>This icon can also have a context menu attached.</p>
|
||||
|
||||
<p>You find the sample source code in <code>Controllers\TrayController.cs</code>.</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="tray-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Tray
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux | Process: Main</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="put-in-tray">View Demo</button>
|
||||
<span class="demo-response" id="tray-countdown"></span>
|
||||
</div>
|
||||
<p>The demo button sends a message to the main process using the <code>ipcRenderer</code>. In the main process the app is told to place an icon, with a context menu, in the tray.</p>
|
||||
|
||||
<p>In this example the tray icon can be removed by clicking 'Remove' in the context menu or selecting the demo button again.</p>
|
||||
<h5>Main Process</h5>
|
||||
<pre><code class="csharp">Electron.IpcMain.On("put-in-tray", (args) => {
|
||||
|
||||
if (Electron.Tray.Items.Count == 0)
|
||||
{
|
||||
var menu = new MenuItem
|
||||
{
|
||||
Label = "Remove",
|
||||
Click = () => Electron.Tray.Destroy()
|
||||
};
|
||||
|
||||
Electron.Tray.Show("/Assets/electron_32x32.png", menu);
|
||||
Electron.Tray.SetToolTip("Electron Demo in the tray.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Electron.Tray.Destroy();
|
||||
}
|
||||
|
||||
});</code></pre>
|
||||
<h5>Renderer Process</h5>
|
||||
<pre><code class="javascript">const { ipcRenderer } = require("electron");
|
||||
|
||||
let trayOn = false;
|
||||
document.getElementById("put-in-tray").addEventListener("click", () => {
|
||||
ipcRenderer.send("put-in-tray");
|
||||
|
||||
let message = '';
|
||||
|
||||
if(trayOn) {
|
||||
trayOn = false;
|
||||
} else {
|
||||
trayOn = true;
|
||||
message = 'Click demo again to remove.'
|
||||
}
|
||||
|
||||
document.getElementById('tray-countdown').innerHTML = message;</code></pre>
|
||||
|
||||
<div class="demo-protip">
|
||||
<h2>ProTip</h2>
|
||||
<strong>Tray support in Linux.</strong>
|
||||
<p>On Linux distributions that only have app indicator support, users will need to install <code>libappindicator1</code> to make the tray icon work. See the <a href="http://electron.atom.io/docs/api/tray">full API documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> for more details about using Tray on Linux.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
const { ipcRenderer } = require("electron");
|
||||
|
||||
let trayOn = false;
|
||||
document.getElementById("put-in-tray").addEventListener("click", () => {
|
||||
ipcRenderer.send("put-in-tray");
|
||||
|
||||
let message = '';
|
||||
|
||||
if(trayOn) {
|
||||
trayOn = false;
|
||||
} else {
|
||||
trayOn = true;
|
||||
message = 'Click demo again to remove.'
|
||||
}
|
||||
|
||||
document.getElementById('tray-countdown').innerHTML = message;
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -156,44 +156,45 @@ await Electron.WindowManager.CreateWindowAsync(options);</code></pre>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { ipcRenderer } = require("electron");
|
||||
(function(){
|
||||
const { ipcRenderer } = require("electron");
|
||||
|
||||
document.getElementById("new-window").addEventListener("click", () => {
|
||||
ipcRenderer.send("new-window");
|
||||
});
|
||||
|
||||
document.getElementById("new-window").addEventListener("click", () => {
|
||||
ipcRenderer.send("new-window");
|
||||
});
|
||||
document.getElementById("manage-window").addEventListener("click", () => {
|
||||
ipcRenderer.send("manage-window");
|
||||
});
|
||||
|
||||
document.getElementById("manage-window").addEventListener("click", () => {
|
||||
ipcRenderer.send("manage-window");
|
||||
});
|
||||
ipcRenderer.on("manage-window-reply", (sender, data) => {
|
||||
document.getElementById("manage-window-reply").innerText = data;
|
||||
});
|
||||
|
||||
document.getElementById("listen-to-window").addEventListener("click", () => {
|
||||
ipcRenderer.send("listen-to-window");
|
||||
});
|
||||
|
||||
ipcRenderer.on("manage-window-reply", (sender, data) => {
|
||||
document.getElementById("manage-window-reply").innerText = data;
|
||||
});
|
||||
|
||||
document.getElementById("listen-to-window").addEventListener("click", () => {
|
||||
ipcRenderer.send("listen-to-window");
|
||||
});
|
||||
document.getElementById('focus-on-modal-window').addEventListener("click", () => {
|
||||
ipcRenderer.send("listen-to-window-set-focus");
|
||||
});
|
||||
|
||||
document.getElementById('focus-on-modal-window').addEventListener("click", () => {
|
||||
ipcRenderer.send("listen-to-window-set-focus");
|
||||
});
|
||||
ipcRenderer.on("listen-to-window-focus", (sender, data) => {
|
||||
const focusModalBtn = document.getElementById('focus-on-modal-window');
|
||||
focusModalBtn.classList.add('disappear');
|
||||
focusModalBtn.classList.remove('smooth-appear');
|
||||
});
|
||||
|
||||
ipcRenderer.on("listen-to-window-focus", (sender, data) => {
|
||||
const focusModalBtn = document.getElementById('focus-on-modal-window');
|
||||
focusModalBtn.classList.add('disappear');
|
||||
focusModalBtn.classList.remove('smooth-appear');
|
||||
});
|
||||
|
||||
ipcRenderer.on("listen-to-window-blur", (sender, data) => {
|
||||
const focusModalBtn = document.getElementById('focus-on-modal-window');
|
||||
focusModalBtn.classList.add('smooth-appear');
|
||||
focusModalBtn.classList.remove('disappear');
|
||||
});
|
||||
|
||||
document.getElementById('frameless-window').addEventListener("click", () => {
|
||||
ipcRenderer.send("frameless-window");
|
||||
});
|
||||
ipcRenderer.on("listen-to-window-blur", (sender, data) => {
|
||||
const focusModalBtn = document.getElementById('focus-on-modal-window');
|
||||
focusModalBtn.classList.add('smooth-appear');
|
||||
focusModalBtn.classList.remove('disappear');
|
||||
});
|
||||
|
||||
document.getElementById('frameless-window').addEventListener("click", () => {
|
||||
ipcRenderer.send("frameless-window");
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user