implement WindowManager, BrowserWindow-API and Menu-API

This commit is contained in:
Gregor Biswanger
2017-10-15 06:03:48 +02:00
parent 90d0cc6189
commit 8f9a84cb0f
18 changed files with 847 additions and 34 deletions

View File

@@ -258,9 +258,9 @@ namespace ElectronNET.API
private event Action<bool> _accessibilitySupportChanged;
private App() { }
internal App() { }
public static App Instance
internal static App Instance
{
get
{
@@ -280,19 +280,6 @@ namespace ElectronNET.API
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
// TODO: Auslagern in eigenes Window-Management
public void OpenWindow(int width, int height, bool show)
{
var browserWindowOptions = new BrowserWindowOptions()
{
Height = height,
Width = width,
Show = show
};
BridgeConnector.Socket.Emit("createBrowserWindow", JObject.FromObject(browserWindowOptions, _jsonSerializer));
}
// TODO: Auslagern in eigenes Notification-API
public void CreateNotification(NotificationOptions notificationOptions)
{

View File

@@ -0,0 +1,16 @@
namespace ElectronNET.API
{
public class BrowserWindow
{
public int Id { get; private set; }
internal BrowserWindow(int id) {
Id = id;
}
public void Minimize()
{
BridgeConnector.Socket.Emit("browserWindow-minimize", Id);
}
}
}

View File

@@ -11,5 +11,15 @@
/// Control your application's event lifecycle.
/// </summary>
public static App App { get { return App.Instance; } }
/// <summary>
/// Control your windows.
/// </summary>
public static WindowManager WindowManager { get { return WindowManager.Instance; } }
/// <summary>
/// Create native application menus and context menus.
/// </summary>
public static Menu Menu { get { return Menu.Instance; } }
}
}

View File

@@ -0,0 +1,244 @@
namespace ElectronNET.API.Entities
{
public class BrowserWindowConstructorOptions
{
/// <summary>
/// Window's width in pixels. Default is 800.
/// </summary>
public int Width { get; set; }
/// <summary>
/// Window's height in pixels. Default is 600.
/// </summary>
public int Height { get; set; }
/// <summary>
/// ( if y is used) Window's left offset from screen. Default is to center the
/// window.
/// </summary>
public int X { get; set; }
/// <summary>
/// ( if x is used) Window's top offset from screen. Default is to center the
/// window.
/// </summary>
public int Y { get; set; }
/// <summary>
/// The width and height would be used as web page's size, which means the actual
/// window's size will include window frame's size and be slightly larger.Default
/// is false.
/// </summary>
public bool UseContentSize { get; set; }
/// <summary>
/// Show window in the center of the screen.
/// </summary>
public bool Center { get; set; }
/// <summary>
/// Window's minimum width. Default is 0.
/// </summary>
public int MinWidth { get; set; }
/// <summary>
/// Window's minimum height. Default is 0.
/// </summary>
public int MinHeight { get; set; }
/// <summary>
/// Window's maximum width. Default is no limit.
/// </summary>
public int MaxWidth { get; set; }
/// <summary>
/// Window's maximum height. Default is no limit.
/// </summary>
public int MaxHeight { get; set; }
/// <summary>
/// Whether window is resizable. Default is true.
/// </summary>
public bool Resizable { get; set; }
/// <summary>
/// Whether window is movable. This is not implemented on Linux. Default is true.
/// </summary>
public bool Movable { get; set; }
/// <summary>
/// Whether window is minimizable. This is not implemented on Linux. Default is true.
/// </summary>
public bool Minimizable { get; set; }
/// <summary>
/// Whether window is maximizable. This is not implemented on Linux. Default is true.
/// </summary>
public bool Maximizable { get; set; }
/// <summary>
/// Whether window is closable. This is not implemented on Linux. Default is true.
/// </summary>
public bool Closable { get; set; }
/// <summary>
/// Whether the window can be focused. Default is true. On Windows setting
/// focusable: false also implies setting skipTaskbar: true. On Linux setting
/// focusable: false makes the window stop interacting with wm, so the window will
/// always stay on top in all workspaces.
/// </summary>
public bool Focusable { get; set; }
/// <summary>
/// Whether the window should always stay on top of other windows. Default is false.
/// </summary>
public bool AlwaysOnTop { get; set; }
/// <summary>
/// Whether the window should show in fullscreen. When explicitly set to false the
/// fullscreen button will be hidden or disabled on macOS.Default is false.
/// </summary>
public bool Fullscreen { get; set; }
/// <summary>
/// Whether the window can be put into fullscreen mode. On macOS, also whether the
/// maximize/zoom button should toggle full screen mode or maximize window.Default
/// is true.
/// </summary>
public bool Fullscreenable { get; set; }
/// <summary>
/// Whether to show the window in taskbar. Default is false.
/// </summary>
public bool SkipTaskbar { get; set; }
/// <summary>
/// The kiosk mode. Default is false.
/// </summary>
public bool Kiosk { get; set; }
/// <summary>
/// Default window title. Default is "Electron.NET".
/// </summary>
public string Title { get; set; } = "Electron.NET";
/// <summary>
/// The window icon. On Windows it is recommended to use ICO icons to get best
/// visual effects, you can also leave it undefined so the executable's icon will be used.
/// </summary>
public string Icon { get; set; }
/// <summary>
/// Whether window should be shown when created. Default is true.
/// </summary>
public bool Show { get; set; }
/// <summary>
/// Specify false to create a . Default is true.
/// </summary>
public bool Frame { get; set; }
/// <summary>
/// Whether this is a modal window. This only works when the window is a child
/// window.Default is false.
/// </summary>
public bool Modal { get; set; }
/// <summary>
/// Whether the web view accepts a single mouse-down event that simultaneously
/// activates the window.Default is false.
/// </summary>
public bool AcceptFirstMouse { get; set; }
/// <summary>
/// Whether to hide cursor when typing. Default is false.
/// </summary>
public bool DisableAutoHideCursor { get; set; }
/// <summary>
/// Auto hide the menu bar unless the Alt key is pressed. Default is false.
/// </summary>
public bool AutoHideMenuBar { get; set; }
/// <summary>
/// Enable the window to be resized larger than screen. Default is false.
/// </summary>
public bool EnableLargerThanScreen { get; set; }
/// <summary>
/// Window's background color as Hexadecimal value, like #66CD00 or #FFF or
/// #80FFFFFF (alpha is supported). Default is #FFF (white).
/// </summary>
public string BackgroundColor { get; set; }
/// <summary>
/// Whether window should have a shadow. This is only implemented on macOS. Default
/// is true.
/// </summary>
public bool HasShadow { get; set; }
/// <summary>
/// Forces using dark theme for the window, only works on some GTK+3 desktop
/// environments.Default is false.
/// </summary>
public bool DarkTheme { get; set; }
/// <summary>
/// Makes the window . Default is false.
/// </summary>
public bool Transparent { get; set; }
/// <summary>
/// The type of window, default is normal window. See more about this below.
/// </summary>
public string Type { get; set; }
/// <summary>
/// The style of window title bar. Default is default. Possible values are:
/// 'default' | 'hidden' | 'hidden-inset' | 'hiddenInset' | 'customButtonsOnHover'
/// </summary>
public string TitleBarStyle { get; set; }
/// <summary>
/// Shows the title in the tile bar in full screen mode on macOS for all
/// titleBarStyle options.Default is false.
/// </summary>
public bool FullscreenWindowTitle { get; set; }
/// <summary>
/// Use WS_THICKFRAME style for frameless windows on Windows, which adds standard
/// window frame.Setting it to false will remove window shadow and window
/// animations.Default is true.
/// </summary>
public bool ThickFrame { get; set; }
/// <summary>
/// Add a type of vibrancy effect to the window, only on macOS. Can be
/// appearance-based, light, dark, titlebar, selection, menu, popover, sidebar,
/// medium-light or ultra-dark.
/// </summary>
public string Vibrancy { get; set; }
/// <summary>
/// Controls the behavior on macOS when option-clicking the green stoplight button
/// on the toolbar or by clicking the Window > Zoom menu item.If true, the window
/// will grow to the preferred width of the web page when zoomed, false will cause
/// it to zoom to the width of the screen.This will also affect the behavior when
/// calling maximize() directly.Default is false.
/// </summary>
public bool ZoomToPageWidth { get; set; }
/// <summary>
/// Tab group name, allows opening the window as a native tab on macOS 10.12+.
/// Windows with the same tabbing identifier will be grouped together.This also
/// adds a native new tab button to your window's tab bar and allows your app and
/// window to receive the new-window-for-tab event.
/// </summary>
public string TabbingIdentifier { get; set; }
/// <summary>
/// Settings of web page's features.
/// </summary>
public WebPreferences WebPreferences { get; set; }
}
}

View File

@@ -0,0 +1,72 @@
using Newtonsoft.Json;
using System;
namespace ElectronNET.API.Entities
{
public class MenuItem
{
/// <summary>
/// Will be called with click(menuItem, browserWindow, event) when the menu item is
/// clicked.
/// </summary>
[JsonIgnore]
public Action Click { get; set; }
/// <summary>
/// Define the action of the menu item, when specified the click property will be
/// ignored.
/// </summary>
public string Role { get; set; }
/// <summary>
/// Can be normal, separator, submenu, checkbox or radio.
/// </summary>
public string Type { get; set; }
public string Label { get; set; }
public string Sublabel { get; set; }
public string Accelerator { get; set; }
public string Icon { get; set; }
/// <summary>
/// If false, the menu item will be greyed out and unclickable.
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// If false, the menu item will be entirely hidden.
/// </summary>
public bool Visible { get; set; }
/// <summary>
/// Should only be specified for checkbox or radio type menu items.
/// </summary>
public bool Checked { get; set; }
/// <summary>
/// Should be specified for submenu type menu items. If submenu is specified, the
/// type: 'submenu' can be omitted.If the value is not a Menu then it will be
/// automatically converted to one using Menu.buildFromTemplate.
/// </summary>
public MenuItem[] Submenu { get; set; }
/// <summary>
/// Unique within a single menu. If defined then it can be used as a reference to
/// this item by the position attribute.
/// </summary>
public string Id { get; internal set; }
/// <summary>
/// This field allows fine-grained definition of the specific location within a
/// given menu.
/// </summary>
public string Position { get; set; }
}
}

View File

@@ -0,0 +1,186 @@
namespace ElectronNET.API.Entities
{
public class WebPreferences
{
/// <summary>
/// Whether to enable DevTools. If it is set to false, can not use
/// BrowserWindow.webContents.openDevTools() to open DevTools.Default is true.
/// </summary>
public bool DevTools { get; set; }
/// <summary>
/// Whether node integration is enabled. Default is true.
/// </summary>
public bool NodeIntegration { get; set; }
/// <summary>
/// Whether node integration is enabled in web workers. Default is false.
/// </summary>
public bool NodeIntegrationInWorker { get; set; }
/// <summary>
/// Specifies a script that will be loaded before other scripts run in the page.
/// This script will always have access to node APIs no matter whether node
/// integration is turned on or off.The value should be the absolute file path to
/// the script. When node integration is turned off, the preload script can
/// reintroduce Node global symbols back to the global scope.
/// </summary>
public string Preload { get; set; }
/// <summary>
/// If set, this will sandbox the renderer associated with the window, making it
/// compatible with the Chromium OS-level sandbox and disabling the Node.js engine.
/// This is not the same as the nodeIntegration option and the APIs available to the
/// preload script are more limited. Read more about the option.This option is
/// currently experimental and may change or be removed in future Electron releases.
/// </summary>
public bool Sandbox { get; set; }
/// <summary>
/// Sets the session used by the page according to the session's partition string.
/// If partition starts with persist:, the page will use a persistent session
/// available to all pages in the app with the same partition.If there is no
/// persist: prefix, the page will use an in-memory session. By assigning the same
/// partition, multiple pages can share the same session.Default is the default
/// session.
/// </summary>
public string Partition { get; set; }
/// <summary>
/// The default zoom factor of the page, 3.0 represents 300%. Default is 1.0.
/// </summary>
public int ZoomFactor { get; set; }
/// <summary>
/// Enables JavaScript support. Default is true.
/// </summary>
public bool Javascript { get; set; }
/// <summary>
/// When false, it will disable the same-origin policy (usually using testing
/// websites by people), and set allowRunningInsecureContent to true if this options
/// has not been set by user.Default is true.
/// </summary>
public bool WebSecurity { get; set; }
/// <summary>
/// Allow an https page to run JavaScript, CSS or plugins from http URLs. Default is
/// false.
/// </summary>
public bool AllowRunningInsecureContent { get; set; }
/// <summary>
/// Enables image support. Default is true.
/// </summary>
public bool Images { get; set; }
/// <summary>
/// Make TextArea elements resizable. Default is true.
/// </summary>
public bool TextAreasAreResizable { get; set; }
/// <summary>
/// Enables WebGL support. Default is true.
/// </summary>
public bool Webgl { get; set; }
/// <summary>
/// Enables WebAudio support. Default is true.
/// </summary>
public bool Webaudio { get; set; }
/// <summary>
/// Whether plugins should be enabled. Default is false.
/// </summary>
public bool Plugins { get; set; }
/// <summary>
/// Enables Chromium's experimental features. Default is false.
/// </summary>
public bool ExperimentalFeatures { get; set; }
/// <summary>
/// Enables Chromium's experimental canvas features. Default is false.
/// </summary>
public bool ExperimentalCanvasFeatures { get; set; }
/// <summary>
/// Enables scroll bounce (rubber banding) effect on macOS. Default is false.
/// </summary>
public bool ScrollBounce { get; set; }
/// <summary>
/// A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to
/// enable.The full list of supported feature strings can be found in the file.
/// </summary>
public string BlinkFeatures { get; set; }
/// <summary>
/// A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to
/// disable.The full list of supported feature strings can be found in the file.
/// </summary>
public string DisableBlinkFeatures { get; set; }
/// <summary>
/// Defaults to 16.
/// </summary>
public int DefaultFontSize { get; set; }
/// <summary>
/// Defaults to 13.
/// </summary>
public int DefaultMonospaceFontSize { get; set; }
/// <summary>
/// Defaults to 0.
/// </summary>
public int MinimumFontSize { get; set; }
/// <summary>
/// Defaults to ISO-8859-1.
/// </summary>
public string DefaultEncoding { get; set; }
/// <summary>
/// Whether to throttle animations and timers when the page becomes background. This
/// also affects the[Page Visibility API][#page-visibility]. Defaults to true.
/// </summary>
public bool BackgroundThrottling { get; set; }
/// <summary>
/// Whether to enable offscreen rendering for the browser window. Defaults to false.
/// </summary>
public bool Offscreen { get; set; }
/// <summary>
/// Whether to run Electron APIs and the specified preload script in a separate
/// JavaScript context.Defaults to false. The context that the preload script runs
/// in will still have full access to the document and window globals but it will
/// use its own set of JavaScript builtins (Array, Object, JSON, etc.) and will be
/// isolated from any changes made to the global environment by the loaded page.The
/// Electron API will only be available in the preload script and not the loaded
/// page. This option should be used when loading potentially untrusted remote
/// content to ensure the loaded content cannot tamper with the preload script and
/// any Electron APIs being used. This option uses the same technique used by . You
/// can access this context in the dev tools by selecting the 'Electron Isolated
/// Context' entry in the combo box at the top of the Console tab. This option is
/// currently experimental and may change or be removed in future Electron releases.
/// </summary>
public bool ContextIsolation { get; set; }
/// <summary>
/// Whether to use native window.open(). Defaults to false. This option is currently experimental.
/// </summary>
public bool NativeWindowOpen { get; set; }
/// <summary>
/// Whether to enable the . Defaults to the value of the nodeIntegration option. The
/// preload script configured for the<webview> will have node integration enabled
/// when it is executed so you should ensure remote/untrusted content is not able to
/// create a<webview> tag with a possibly malicious preload script.You can use the
/// will-attach-webview event on to strip away the preload script and to validate or
/// alter the<webview>'s initial settings.
/// </summary>
public bool WebviewTag { get; set; }
}
}

View File

@@ -9,9 +9,9 @@ namespace ElectronNET.API
{
private static IpcMain _ipcMain;
private IpcMain() { }
internal IpcMain() { }
public static IpcMain Instance
internal static IpcMain Instance
{
get
{

92
ElectronNET.API/Menu.cs Normal file
View File

@@ -0,0 +1,92 @@
using ElectronNET.API.Entities;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
using System;
using System.Linq;
namespace ElectronNET.API
{
public sealed class Menu
{
private static Menu _menu;
internal Menu() { }
internal static Menu Instance
{
get
{
if (_menu == null)
{
_menu = new Menu();
}
return _menu;
}
}
public IReadOnlyCollection<MenuItem> Items { get { return _items.AsReadOnly(); } }
private List<MenuItem> _items = new List<MenuItem>();
public void SetApplicationMenu(MenuItem[] menuItems)
{
AddMenuItemsId(menuItems);
BridgeConnector.Socket.Emit("menu-setApplicationMenu", JArray.FromObject(menuItems, _jsonSerializer));
_items.AddRange(menuItems);
BridgeConnector.Socket.On("menuItemClicked", (id) => {
MenuItem menuItem = GetMenuItem(_items, id.ToString());
menuItem?.Click();
});
}
private void AddMenuItemsId(MenuItem[] menuItems)
{
for (int index = 0; index < menuItems.Length; index++)
{
var menuItem = menuItems[index];
if(menuItem?.Submenu?.Length > 0)
{
AddMenuItemsId(menuItem.Submenu);
}
if(string.IsNullOrEmpty(menuItem.Role))
{
menuItem.Id = Guid.NewGuid().ToString();
}
}
}
private MenuItem GetMenuItem(List<MenuItem> menuItems, string id)
{
MenuItem result = new MenuItem();
foreach (var item in menuItems)
{
if(item.Id == id)
{
result = item;
}
else if(item?.Submenu?.Length > 0)
{
var menuItem = GetMenuItem(item.Submenu.ToList(), id);
if(menuItem.Id == id)
{
result = menuItem;
}
}
}
return result;
}
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
}
}

View File

@@ -0,0 +1,67 @@
using ElectronNET.API.Entities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ElectronNET.API
{
public sealed class WindowManager
{
private static WindowManager _windowManager;
internal WindowManager() { }
internal static WindowManager Instance
{
get
{
if (_windowManager == null)
{
_windowManager = new WindowManager();
}
return _windowManager;
}
}
public IReadOnlyCollection<BrowserWindow> BrowserWindows { get { return _browserWindows.AsReadOnly(); } }
private List<BrowserWindow> _browserWindows = new List<BrowserWindow>();
public async Task<BrowserWindow> CreateWindowAsync(string loadUrl = "http://localhost")
{
return await CreateWindowAsync(new BrowserWindowConstructorOptions(), loadUrl);
}
public Task<BrowserWindow> CreateWindowAsync(BrowserWindowConstructorOptions options, string loadUrl = "http://localhost")
{
var taskCompletionSource = new TaskCompletionSource<BrowserWindow>();
BridgeConnector.Socket.On("BrowserWindowCreated", (id) =>
{
string windowId = id.ToString();
BrowserWindow browserWindow = new BrowserWindow(int.Parse(windowId));
_browserWindows.Add(browserWindow);
taskCompletionSource.SetResult(browserWindow);
});
if (loadUrl.ToUpper() == "HTTP://LOCALHOST")
{
loadUrl = $"{loadUrl}:{BridgeSettings.WebPort}";
}
BridgeConnector.Socket.Emit("createBrowserWindow", JObject.FromObject(options, _jsonSerializer), loadUrl);
return taskCompletionSource.Task;
}
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
}
}

View File

@@ -40,6 +40,8 @@ namespace ElectronNET.CLI.Commands
Directory.CreateDirectory(hostApiFolder);
}
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "ipc.js", "api.");
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "app.js", "api.");
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "browserWindows.js", "api.");
Console.WriteLine("Start npm install...");
ProcessHelper.CmdExecute("npm install", tempPath);

View File

@@ -59,6 +59,8 @@ namespace ElectronNET.CLI.Commands
Directory.CreateDirectory(hostApiFolder);
}
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "ipc.js", "api.");
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "app.js", "api.");
EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "browserWindows.js", "api.");
Console.WriteLine("Start npm install...");
ProcessHelper.CmdExecute("npm install", tempPath);

View File

@@ -34,4 +34,9 @@
<EmbeddedResource Include="..\ElectronNET.Host\api\ipc.js" Link="ElectronHost\api\ipc.js" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\ElectronNET.Host\api\app.js" Link="ElectronHost\api\app.js" />
<EmbeddedResource Include="..\ElectronNET.Host\api\browserWindows.js" Link="ElectronHost\api\browserWindows.js" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,43 @@
"use strict";
exports.__esModule = true;
var electron_1 = require("electron");
var windows = [];
var ipc;
module.exports = function (socket) {
socket.on('createBrowserWindow', function (options, loadUrl) {
var window = new electron_1.BrowserWindow(options);
window.on('closed', function (sender) {
for (var index = 0; index < windows.length; index++) {
var windowItem = windows[index];
try {
windowItem.id;
}
catch (error) {
if (error.message === 'Object has been destroyed') {
windows.splice(index, 1);
}
}
}
});
if (ipc == undefined) {
ipc = require('./ipc')(socket, window);
}
if (loadUrl) {
window.loadURL(loadUrl);
}
windows.push(window);
socket.emit('BrowserWindowCreated', window.id);
});
socket.on('browserWindow-minimize', function (id) {
getWindowById(id).minimize();
});
function getWindowById(id) {
for (var index = 0; index < windows.length; index++) {
var element = windows[index];
if (element.id == id) {
return element;
}
}
}
};
//# sourceMappingURL=browserWindows.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"browserWindows.js","sourceRoot":"","sources":["browserWindows.ts"],"names":[],"mappings":";;AAAA,qCAAyC;AACzC,IAAI,OAAO,GAA6B,EAAE,CAAA;AAC1C,IAAI,GAAG,CAAC;AAER,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,qBAAqB,EAAE,UAAC,OAAO,EAAE,OAAO;QAC9C,IAAI,MAAM,GAAG,IAAI,wBAAa,CAAC,OAAO,CAAC,CAAC;QAExC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAC,MAAM;YACvB,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;gBAClD,IAAI,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;gBAChC,IAAI,CAAC;oBACD,UAAU,CAAC,EAAE,CAAC;gBAClB,CAAC;gBAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;oBACb,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,2BAA2B,CAAC,CAAC,CAAC;wBAChD,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;oBAC7B,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC;YACnB,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAED,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACV,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,UAAC,EAAE;QACnC,aAAa,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,uBAAuB,EAAU;QAC7B,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YAClD,IAAI,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7B,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBACnB,MAAM,CAAC,OAAO,CAAC;YACnB,CAAC;QACL,CAAC;IACL,CAAC;AACL,CAAC,CAAA"}

View File

@@ -0,0 +1,47 @@
import { BrowserWindow } from "electron";
let windows: Electron.BrowserWindow[] = []
let ipc;
module.exports = (socket: SocketIO.Server) => {
socket.on('createBrowserWindow', (options, loadUrl) => {
let window = new BrowserWindow(options);
window.on('closed', (sender) => {
for (var index = 0; index < windows.length; index++) {
var windowItem = windows[index];
try {
windowItem.id;
} catch (error) {
if (error.message === 'Object has been destroyed') {
windows.splice(index, 1);
}
}
}
});
// TODO: IPC Lösung für mehrere Fenster finden
if (ipc == undefined) {
ipc = require('./ipc')(socket, window);
}
if (loadUrl) {
window.loadURL(loadUrl);
}
windows.push(window);
socket.emit('BrowserWindowCreated', window.id);
});
socket.on('browserWindow-minimize', (id) => {
getWindowById(id).minimize();
});
function getWindowById(id: number): Electron.BrowserWindow {
for (var index = 0; index < windows.length; index++) {
var element = windows[index];
if (element.id == id) {
return element;
}
}
}
}

View File

@@ -1,9 +1,9 @@
const { app, BrowserWindow, Notification } = require('electron');
const { app, Notification, Menu } = require('electron');
const fs = require('fs');
const path = require('path');
const process = require('child_process').spawn;
const portfinder = require('detect-port');
let io, window, apiProcess, loadURL, ipc, appApi;
let io, browserWindows, apiProcess, loadURL, appApi;
app.on('ready', () => {
portfinder(8000, (error, port) => {
@@ -18,17 +18,16 @@ function startSocketApiBridge(port) {
io.on('connection', (socket) => {
console.log('ASP.NET Core Application connected...');
appApi = require('./api/app')(socket, app);
browserWindows = require('./api/browserWindows')(socket);
socket.on('createBrowserWindow', (options) => {
window = new BrowserWindow(options);
window.loadURL(loadURL);
socket.on('menu-setApplicationMenu', (menuItems) => {
const menu = Menu.buildFromTemplate(menuItems);
window.on('closed', function () {
mainWindow = null;
apiProcess = null;
addMenuItemClickConnector(menu.items, (id) => {
socket.emit("menuItemClicked", id);
});
ipc = require('./api/ipc')(socket, window);
Menu.setApplicationMenu(menu);
});
socket.on('createNotification', (options) => {
@@ -39,6 +38,19 @@ function startSocketApiBridge(port) {
});
}
function addMenuItemClickConnector(menuItems, callback) {
menuItems.forEach((item) => {
if(item.submenu && item.submenu.items.length > 0) {
addMenuItemClickConnector(item.submenu.items, callback);
}
if("id" in item && item.id) {
item.click = () => { callback(item.id); };
}
});
}
function startAspCoreBackend(electronPort) {
portfinder(8000, (error, electronWebPort) => {
loadURL = `http://localhost:${electronWebPort}`
@@ -68,10 +80,10 @@ app.on('window-all-closed', () => {
}
});
app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (win === null) {
createWindow();
}
});
// app.on('activate', () => {
// // On macOS it's common to re-create a window in the app when the
// // dock icon is clicked and there are no other windows open.
// if (window === null) {
// createWindow();
// }
// });

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using ElectronNET.API;
using ElectronNET.API.Entities;
using System.Linq;
namespace ElectronNET.WebApp.Controllers
{
@@ -22,7 +23,11 @@ namespace ElectronNET.WebApp.Controllers
{
string pathName = await Electron.App.GetPathAsync(PathName.pictures);
Electron.IpcMain.Send("GetPathComplete", pathName);
Electron.WindowManager.BrowserWindows.First().Minimize();
await Electron.WindowManager.CreateWindowAsync("http://www.google.de");
});
return View();
}

View File

@@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Linq;
using System.Threading;
namespace ElectronNET.WebApp
{
@@ -36,7 +38,27 @@ namespace ElectronNET.WebApp
template: "{controller=Home}/{action=Index}/{id?}");
});
Electron.App.OpenWindow(800, 600, true);
Bootstrap();
}
public async void Bootstrap()
{
Electron.Menu.SetApplicationMenu(new MenuItem[] {
new MenuItem {
Label = "Datei",
Submenu = new MenuItem[] {
new MenuItem {
Label = "Beenden",
Click = () =>
{
Electron.App.Exit();
}
}
}
}
});
var browserWindow = await Electron.WindowManager.CreateWindowAsync();
}
}
}