using ElectronNET.API.Entities; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace ElectronNET.API { /// /// /// public sealed class WindowManager { private static WindowManager _windowManager; private static object _syncRoot = new object(); internal WindowManager() { } internal static WindowManager Instance { get { if (_windowManager == null) { lock (_syncRoot) { if (_windowManager == null) { _windowManager = new WindowManager(); } } } return _windowManager; } } private readonly SemaphoreSlim _singleCreate = new SemaphoreSlim(1, 1); /// /// Quit when all windows are closed. (Default is true) /// /// /// true if [quit window all closed]; otherwise, false. /// public bool IsQuitOnWindowAllClosed { get { return _isQuitOnWindowAllClosed; } set { BridgeConnector.Emit("quit-app-window-all-closed-event", value); _isQuitOnWindowAllClosed = value; } } private bool _isQuitOnWindowAllClosed = true; /// /// Gets the browser windows. /// /// /// The browser windows. /// public IReadOnlyCollection BrowserWindows { get { return _browserWindows.Values.ToList().AsReadOnly(); } } private ConcurrentDictionary _browserWindows = new (); /// /// Gets the browser views. /// /// /// The browser view. /// public IReadOnlyCollection BrowserViews { get { return _browserViews.Values.ToList().AsReadOnly(); } } private ConcurrentDictionary _browserViews = new (); /// /// Creates the window asynchronous. /// /// The load URL. /// public async Task CreateWindowAsync(string loadUrl = "http://localhost") { return await CreateWindowAsync(new BrowserWindowOptions(), loadUrl); } /// /// Creates the window asynchronous. /// /// The options. /// The load URL. /// public async Task CreateWindowAsync(BrowserWindowOptions options, string loadUrl = "http://localhost") { await _singleCreate.WaitAsync(); try { var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); BridgeConnector.On("BrowserWindowCreated", (id) => { BridgeConnector.Off("BrowserWindowCreated"); var browserWindow = new BrowserWindow(id); _browserWindows[id] = browserWindow; taskCompletionSource.SetResult(browserWindow); }); BridgeConnector.Off("BrowserWindowClosed"); BridgeConnector.On("BrowserWindowClosed", (browserWindowIds) => { foreach (var id in browserWindowIds) { _browserWindows.TryRemove(id, out _); } }); if (string.Equals(loadUrl, "HTTP://LOCALHOST", StringComparison.InvariantCultureIgnoreCase)) { loadUrl = $"{loadUrl}:{BridgeSettings.WebPort}"; } // Workaround Windows 10 / Electron Bug // https://github.com/electron/electron/issues/4045 if (IsWindows10()) { options.Width = options.Width + 14; options.Height = options.Height + 7; } if (options.X == -1 && options.Y == -1) { options.X = 0; options.Y = 0; BridgeConnector.Emit("createBrowserWindow", JObject.FromObject(options, _jsonSerializer), loadUrl); } else { // Workaround Windows 10 / Electron Bug // https://github.com/electron/electron/issues/4045 if (IsWindows10()) { options.X = options.X - 7; } BridgeConnector.Emit("createBrowserWindow", JObject.FromObject(options, _keepDefaultValuesSerializer), loadUrl); } return await taskCompletionSource.Task; } finally { _singleCreate.Release(); } } private bool IsWindows10() { return OperatingSystem.IsWindowsVersionAtLeast(10); } /// /// A BrowserView can be used to embed additional web content into a BrowserWindow. /// It is like a child window, except that it is positioned relative to its owning window. /// It is meant to be an alternative to the webview tag. /// /// public Task CreateBrowserViewAsync() { return CreateBrowserViewAsync(new BrowserViewConstructorOptions()); } /// /// A BrowserView can be used to embed additional web content into a BrowserWindow. /// It is like a child window, except that it is positioned relative to its owning window. /// It is meant to be an alternative to the webview tag. /// /// /// public async Task CreateBrowserViewAsync(BrowserViewConstructorOptions options) { await _singleCreate.WaitAsync(); try { var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); BridgeConnector.On("BrowserViewCreated", (id) => { BridgeConnector.Off("BrowserViewCreated"); BrowserView browserView = new BrowserView(id); _browserViews[id] = browserView; taskCompletionSource.SetResult(browserView); }); BridgeConnector.Emit("createBrowserView", JObject.FromObject(options, _keepDefaultValuesSerializer)); return await taskCompletionSource.Task; } finally { _singleCreate.Release(); } } private static JsonSerializer _jsonSerializer = new JsonSerializer() { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore }; private static JsonSerializer _keepDefaultValuesSerializer = new JsonSerializer() { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore }; } }