using ElectronNET.API.Entities; using ElectronNET.API.Serialization; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text.Json; using System.Threading.Tasks; namespace ElectronNET.API { /// /// /// public sealed class WindowManager { private static WindowManager _windowManager; private static readonly object SyncRoot = new(); internal WindowManager() { } internal static WindowManager Instance { get { if (_windowManager == null) { lock (SyncRoot) { if (_windowManager == null) { _windowManager = new WindowManager(); } } } return _windowManager; } } /// /// Quit when all windows are closed. (Default is true) /// /// /// true if [quit window all closed]; otherwise, false. /// public bool IsQuitOnWindowAllClosed { get => _isQuitOnWindowAllClosed; set { BridgeConnector.Socket.Emit("quit-app-window-all-closed", value); _isQuitOnWindowAllClosed = value; } } private bool _isQuitOnWindowAllClosed = true; /// /// Gets the browser windows. /// /// /// The browser windows. /// public IReadOnlyCollection BrowserWindows => _browserWindows.AsReadOnly(); private readonly List _browserWindows = new(); /// /// Gets the browser views. /// /// /// The browser view. /// public IReadOnlyCollection BrowserViews => _browserViews.AsReadOnly(); private readonly List _browserViews = new(); /// /// Creates the window asynchronous. /// /// The load URL. /// public async Task CreateWindowAsync(string loadUrl = "http://localhost") { return await this.CreateWindowAsync(new BrowserWindowOptions(), loadUrl).ConfigureAwait(false); } /// /// Creates the window asynchronous. /// /// The options. /// The load URL. /// public async Task CreateWindowAsync(BrowserWindowOptions options, string loadUrl = "http://localhost") { var tcs = new TaskCompletionSource(); BridgeConnector.Socket.Once("BrowserWindowCreated", (id) => { var browserWindow = new BrowserWindow(id); _browserWindows.Add(browserWindow); tcs.SetResult(browserWindow); }); BridgeConnector.Socket.Once("BrowserWindowClosed", (ids) => { for (int index = 0; index < _browserWindows.Count; index++) { if (!ids.Contains(_browserWindows[index].Id)) { _browserWindows.RemoveAt(index); } } }); if (loadUrl.Equals("http://localhost", StringComparison.OrdinalIgnoreCase) && ElectronNetRuntime.AspNetWebPort.HasValue) { loadUrl = $"{loadUrl}:{ElectronNetRuntime.AspNetWebPort}"; } // Workaround Windows 10 / Electron Bug // https://github.com/electron/electron/issues/4045 if (IsWindows10()) { options.Width += 14; options.Height += 7; } if (!options.X.HasValue && !options.Y.HasValue) { await BridgeConnector.Socket.Emit("createBrowserWindow", options, loadUrl).ConfigureAwait(false); } else { // Workaround Windows 10 / Electron Bug // https://github.com/electron/electron/issues/4045 if (IsWindows10()) { options.X -= 7; } await BridgeConnector.Socket.Emit("createBrowserWindow", options, loadUrl).ConfigureAwait(false); } return await tcs.Task.ConfigureAwait(false); } private bool IsWindows10() { return RuntimeInformation.OSDescription.Contains("Windows 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) { var tcs = new TaskCompletionSource(); BridgeConnector.Socket.Once("BrowserViewCreated", (id) => { BrowserView browserView = new(id); _browserViews.Add(browserView); tcs.SetResult(browserView); }); await BridgeConnector.Socket.Emit("createBrowserView", options).ConfigureAwait(false); return await tcs.Task.ConfigureAwait(false); } } }