diff --git a/ElectronNET.API/App.cs b/ElectronNET.API/App.cs index eff18e4..5376045 100644 --- a/ElectronNET.API/App.cs +++ b/ElectronNET.API/App.cs @@ -570,9 +570,9 @@ namespace ElectronNET.API public Task GetNameAsync() => BridgeConnector.OnResult("appGetName", "appGetNameCompleted"); - internal App() + private App() { - CommandLine = new CommandLine(); + CommandLine = CommandLine.Instance; } internal static App Instance @@ -1369,6 +1369,13 @@ namespace ElectronNET.API /// The handler public void Once(string eventName, Action fn) => Events.Instance.Once(ModuleName, eventName, fn); + + /// + /// If you're using a splashscreen in the electron.manifest.json, the window will ony be fully destroyed once you call this method once. + /// You should only do this after creating another window, to avoid a bug where the Electron renderer process frezees till any window interaction. + /// + public void DestroySplashScreen() => BridgeConnector.Emit("splashscreen-destroy"); + private readonly JsonSerializer _jsonSerializer = new JsonSerializer() { ContractResolver = new CamelCasePropertyNamesContractResolver() diff --git a/ElectronNET.API/BrowserWindow.cs b/ElectronNET.API/BrowserWindow.cs index e2fc622..fb5c1e4 100644 --- a/ElectronNET.API/BrowserWindow.cs +++ b/ElectronNET.API/BrowserWindow.cs @@ -1075,6 +1075,14 @@ namespace ElectronNET.API { BridgeConnector.Emit("browserWindowSetFullScreen", Id, flag); } + + /// + /// Sets whether the background color of the window + /// + public void SetBackgroundColor(string color) + { + BridgeConnector.Emit("browserWindowSetBackgroundColor", Id, color); + } /// /// Whether the window is in fullscreen mode. diff --git a/ElectronNET.API/CommandLine.cs b/ElectronNET.API/CommandLine.cs index 26a116c..40c48e4 100644 --- a/ElectronNET.API/CommandLine.cs +++ b/ElectronNET.API/CommandLine.cs @@ -8,7 +8,7 @@ namespace ElectronNET.API /// public sealed class CommandLine { - internal CommandLine() { } + private CommandLine() { } internal static CommandLine Instance { diff --git a/ElectronNET.API/Entities/BrowserWindowOptions.cs b/ElectronNET.API/Entities/BrowserWindowOptions.cs index 2199fe4..c7263ae 100644 --- a/ElectronNET.API/Entities/BrowserWindowOptions.cs +++ b/ElectronNET.API/Entities/BrowserWindowOptions.cs @@ -37,62 +37,62 @@ namespace ElectronNET.API.Entities /// window's size will include window frame's size and be slightly larger. Default /// is false. /// - public bool UseContentSize { get; set; } + public bool? UseContentSize { get; set; } /// /// Show window in the center of the screen. /// - public bool Center { get; set; } + public bool? Center { get; set; } /// /// Window's minimum width. Default is 0. /// - public int MinWidth { get; set; } + public int? MinWidth { get; set; } /// /// Window's minimum height. Default is 0. /// - public int MinHeight { get; set; } + public int? MinHeight { get; set; } /// /// Window's maximum width. Default is no limit. /// - public int MaxWidth { get; set; } + public int? MaxWidth { get; set; } /// /// Window's maximum height. Default is no limit. /// - public int MaxHeight { get; set; } + public int? MaxHeight { get; set; } /// /// Whether window is resizable. Default is true. /// [DefaultValue(true)] - public bool Resizable { get; set; } = true; + public bool? Resizable { get; set; } = true; /// /// Whether window is movable. This is not implemented on Linux. Default is true. /// [DefaultValue(true)] - public bool Movable { get; set; } = true; + public bool? Movable { get; set; } = true; /// /// Whether window is minimizable. This is not implemented on Linux. Default is true. /// [DefaultValue(true)] - public bool Minimizable { get; set; } = true; + public bool? Minimizable { get; set; } = true; /// /// Whether window is maximizable. This is not implemented on Linux. Default is true. /// [DefaultValue(true)] - public bool Maximizable { get; set; } = true; + public bool? Maximizable { get; set; } = true; /// /// Whether window is closable. This is not implemented on Linux. Default is true. /// [DefaultValue(true)] - public bool Closable { get; set; } = true; + public bool? Closable { get; set; } = true; /// /// Whether the window can be focused. Default is true. On Windows setting @@ -101,35 +101,35 @@ namespace ElectronNET.API.Entities /// always stay on top in all workspaces. /// [DefaultValue(true)] - public bool Focusable { get; set; } = true; + public bool? Focusable { get; set; } = true; /// /// Whether the window should always stay on top of other windows. Default is false. /// - public bool AlwaysOnTop { get; set; } + public bool? AlwaysOnTop { get; set; } /// /// 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. /// - public bool Fullscreen { get; set; } + public bool? Fullscreen { get; set; } /// /// 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. /// - public bool Fullscreenable { get; set; } + public bool? Fullscreenable { get; set; } /// /// Whether to show the window in taskbar. Default is false. /// - public bool SkipTaskbar { get; set; } + public bool? SkipTaskbar { get; set; } /// /// The kiosk mode. Default is false. /// - public bool Kiosk { get; set; } + public bool? Kiosk { get; set; } /// /// Default window title. Default is "Electron.NET". @@ -146,40 +146,40 @@ namespace ElectronNET.API.Entities /// Whether window should be shown when created. Default is true. /// [DefaultValue(true)] - public bool Show { get; set; } = true; + public bool? Show { get; set; } = true; /// /// Specify false to create a . Default is true. /// [DefaultValue(true)] - public bool Frame { get; set; } = true; + public bool? Frame { get; set; } = true; /// /// Whether this is a modal window. This only works when the window is a child /// window.Default is false. /// - public bool Modal { get; set; } + public bool? Modal { get; set; } /// /// Whether the web view accepts a single mouse-down event that simultaneously /// activates the window.Default is false. /// - public bool AcceptFirstMouse { get; set; } + public bool? AcceptFirstMouse { get; set; } /// /// Whether to hide cursor when typing. Default is false. /// - public bool DisableAutoHideCursor { get; set; } + public bool? DisableAutoHideCursor { get; set; } /// /// Auto hide the menu bar unless the Alt key is pressed. Default is false. /// - public bool AutoHideMenuBar { get; set; } + public bool? AutoHideMenuBar { get; set; } /// /// Enable the window to be resized larger than screen. Default is false. /// - public bool EnableLargerThanScreen { get; set; } + public bool? EnableLargerThanScreen { get; set; } /// /// Window's background color as Hexadecimal value, like #66CD00 or #FFF or @@ -191,18 +191,18 @@ namespace ElectronNET.API.Entities /// Whether window should have a shadow. This is only implemented on macOS. Default /// is true. /// - public bool HasShadow { get; set; } + public bool? HasShadow { get; set; } /// /// Forces using dark theme for the window, only works on some GTK+3 desktop /// environments.Default is false. /// - public bool DarkTheme { get; set; } + public bool? DarkTheme { get; set; } /// /// Makes the window . Default is false. /// - public bool Transparent { get; set; } + public bool? Transparent { get; set; } /// /// The type of window, default is normal window. @@ -214,13 +214,20 @@ namespace ElectronNET.API.Entities /// 'default' | 'hidden' | 'hiddenInset' | 'customButtonsOnHover' /// [JsonConverter(typeof(StringEnumConverter))] - public TitleBarStyle TitleBarStyle { get; set; } + public TitleBarStyle? TitleBarStyle { get; set; } /// /// Shows the title in the tile bar in full screen mode on macOS for all /// titleBarStyle options.Default is false. /// - public bool FullscreenWindowTitle { get; set; } + public bool? FullscreenWindowTitle { get; set; } + + /// + /// Activate the Window Controls Overlay on Windows, when combined with = + /// + [SupportedOSPlatform("win")] + [DefaultValue(null)] + public TitleBarOverlayConfig TitleBarOverlay { get; set; } /// /// Use WS_THICKFRAME style for frameless windows on Windows, which adds standard @@ -228,7 +235,7 @@ namespace ElectronNET.API.Entities /// animations. Default is true. /// [DefaultValue(true)] - public bool ThickFrame { get; set; } = true; + public bool? ThickFrame { get; set; } = true; /// /// Add a type of vibrancy effect to the window, only on macOS. Can be @@ -236,7 +243,7 @@ namespace ElectronNET.API.Entities /// medium-light or ultra-dark. /// [JsonConverter(typeof(StringEnumConverter))] - public Vibrancy Vibrancy { get; set; } + public Vibrancy? Vibrancy { get; set; } /// /// Controls the behavior on macOS when option-clicking the green stoplight button @@ -245,7 +252,7 @@ namespace ElectronNET.API.Entities /// it to zoom to the width of the screen.This will also affect the behavior when /// calling maximize() directly.Default is false. /// - public bool ZoomToPageWidth { get; set; } + public bool? ZoomToPageWidth { get; set; } /// /// Tab group name, allows opening the window as a native tab on macOS 10.12+. diff --git a/ElectronNET.API/Entities/TitleBarStyle.cs b/ElectronNET.API/Entities/TitleBarStyle.cs index 8fb108e..5ee48bc 100644 --- a/ElectronNET.API/Entities/TitleBarStyle.cs +++ b/ElectronNET.API/Entities/TitleBarStyle.cs @@ -28,4 +28,10 @@ namespace ElectronNET.API.Entities /// customButtonsOnHover } + + public class TitleBarOverlayConfig + { + public string Color { get; set; } + public string SymbolColor { get; set; } + } } \ No newline at end of file diff --git a/ElectronNET.API/Entities/WebPreferences.cs b/ElectronNET.API/Entities/WebPreferences.cs index f236247..ea4a1f1 100644 --- a/ElectronNET.API/Entities/WebPreferences.cs +++ b/ElectronNET.API/Entities/WebPreferences.cs @@ -171,6 +171,12 @@ namespace ElectronNET.API.Entities /// public bool Offscreen { get; set; } + /// + /// Whether to enable built-in spellcheck + /// + [DefaultValue(true)] + public bool Spellcheck { get; set; } = true; + /// /// 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 diff --git a/ElectronNET.API/WindowManager.cs b/ElectronNET.API/WindowManager.cs index 60f4a1c..dbb0da2 100644 --- a/ElectronNET.API/WindowManager.cs +++ b/ElectronNET.API/WindowManager.cs @@ -126,10 +126,10 @@ namespace ElectronNET.API if (options.X == -1 && options.Y == -1) { - options.X = 0; + options.X = 0; //This is manually removed by the browserWindows.js code before creating the window options.Y = 0; - BridgeConnector.Emit("createBrowserWindow", guid, JObject.FromObject(options, _jsonSerializer), loadUrl); + BridgeConnector.Emit("createBrowserWindow", guid, JObject.FromObject(options, _keepDefaultValuesSerializer), loadUrl); } else { @@ -218,6 +218,19 @@ namespace ElectronNET.API return await taskCompletionSource.Task; } + + /// + /// Destroy all windows. + /// + /// Number of windows destroyed + public async Task DestroyAllWindows() + { + var destroyed = await BridgeConnector.OnResult("browserWindowDestroyAll", "browserWindowDestroyAll-completed"); + _browserViews.Clear(); + _browserWindows.Clear(); + return destroyed; + } + private static JsonSerializer _jsonSerializer = new JsonSerializer() { ContractResolver = new CamelCasePropertyNamesContractResolver(), diff --git a/ElectronNET.Host/api/browserWindows.js b/ElectronNET.Host/api/browserWindows.js index 458c4d9..bb4d629 100644 --- a/ElectronNET.Host/api/browserWindows.js +++ b/ElectronNET.Host/api/browserWindows.js @@ -182,6 +182,12 @@ module.exports = (socket, app) => { else if (!options.webPreferences) { options = { ...options, webPreferences: { nodeIntegration: true, contextIsolation: false } }; } + + if (options.x && options.y && options.x == 0 && options.y == 0) { + delete options.x; + delete options.y; + } + // we dont want to recreate the window when watch is ready. if (app.commandLine.hasSwitch('watch') && app['mainWindowURL'] === loadUrl) { window = app['mainWindow']; @@ -256,6 +262,25 @@ module.exports = (socket, app) => { socket.on('browserWindowDestroy', (id) => { getWindowById(id)?.destroy(); }); + + socket.on('browserWindowDestroyAll', () => { + const windows = electron_1.BrowserWindow.getAllWindows(); + let count = 0; + if (windows.length) { + windows.forEach(w => { + try { + w.hide(); + w.destroy(); + count++; + } + catch { + //ignore, probably already destroyed + } + }); + } + electronSocket.emit('browserWindowDestroyAll-completed', count); + }); + socket.on('browserWindowClose', (id) => { getWindowById(id)?.close(); }); @@ -318,6 +343,9 @@ module.exports = (socket, app) => { socket.on('browserWindowSetFullScreen', (id, fullscreen) => { getWindowById(id)?.setFullScreen(fullscreen); }); + socket.on('browserWindowSetBackgroundColor', (id, color) => { + getWindowById(id)?.setBackgroundColor(color); + }); socket.on('browserWindowIsFullScreen', (id) => { const isFullScreen = getWindowById(id)?.isFullScreen() ?? null; electronSocket.emit('browserWindow-isFullScreen-completed' + id, isFullScreen); @@ -628,4 +656,4 @@ module.exports = (socket, app) => { return null; } }; -//# sourceMappingURL=browserWindows.js.map \ No newline at end of file +//# sourceMappingURL=browserWindows.js.map diff --git a/ElectronNET.Host/api/browserWindows.ts b/ElectronNET.Host/api/browserWindows.ts index c1923a1..7eda1ef 100644 --- a/ElectronNET.Host/api/browserWindows.ts +++ b/ElectronNET.Host/api/browserWindows.ts @@ -288,6 +288,24 @@ export = (socket: Socket, app: Electron.App) => { getWindowById(id)?.destroy(); }); + socket.on('browserWindowDestroyAll', () => { + const windows = BrowserWindow.getAllWindows(); + let count = 0; + if (windows.length) { + windows.forEach(w => { + try { + w.hide(); + w.destroy(); + count++; + } + catch { + //ignore, probably already destroyed + } + }); + } + electronSocket.emit('browserWindowDestroyAll-completed', count); + }); + socket.on('browserWindowClose', (id) => { getWindowById(id)?.close(); }); @@ -371,7 +389,11 @@ export = (socket: Socket, app: Electron.App) => { socket.on('browserWindowSetFullScreen', (id, fullscreen) => { getWindowById(id)?.setFullScreen(fullscreen); }); - + + socket.on('browserWindowSetBackgroundColor', (id, color) => { + getWindowById(id)?.setBackgroundColor(color); + }); + socket.on('browserWindowIsFullScreen', (id) => { const isFullScreen = getWindowById(id)?.isFullScreen() ?? null; diff --git a/ElectronNET.Host/main.js b/ElectronNET.Host/main.js index ae255b3..93bbbfb 100644 --- a/ElectronNET.Host/main.js +++ b/ElectronNET.Host/main.js @@ -51,9 +51,23 @@ app.on('will-finish-launching', () => { app.on('before-quit-for-update', () => { ignoreApiProcessClosed = true; + + const windows = BrowserWindow.getAllWindows(); + if (windows.length) { + windows.forEach(w => { + try { + w.hide(); + w.destroy(); + } + catch { + //ignore, probably already destroyed + } + }); + } }); const manifestJsonFile = require(manifestJsonFilePath); + if (manifestJsonFile.singleInstance || manifestJsonFile.aspCoreBackendPort) { const mainInstance = app.requestSingleInstanceLock(); app.on('second-instance', (events, args = []) => { @@ -88,6 +102,14 @@ if (manifestJsonFile.singleInstance || manifestJsonFile.aspCoreBackendPort) { } } +//Some flags need to be set before app is ready +if (manifestJsonFile.hasOwnProperty('cliFlags') && manifestJsonFile.cliFlags.length > 0) { + manifestJsonFile.cliFlags.forEach(flag => { + app.commandLine.appendSwitch(flag); + }); +} + + app.on('ready', () => { // Fix ERR_UNKNOWN_URL_SCHEME using file protocol @@ -170,12 +192,13 @@ function startSplashScreen() { } } - - splashScreen.setIgnoreMouseEvents(true); + //Removed as we want to be able to drag the splash screen: splashScreen.setIgnoreMouseEvents(true); app.once('browser-window-created', () => { - splashScreen.destroy(); - splashScreen = null; + if (splashScreen) { + splashScreen.hide(); + } + //We cannot destroy the window here as this triggers an electron freeze bug (https://github.com/electron/electron/issues/29050) }); const loadSplashscreenUrl = path.join(__dirname, 'splashscreen', 'index.html') + '?imgPath=' + imageFile; @@ -269,6 +292,13 @@ function startSocketApiBridge(port) { } }); + socket.on('splashscreen-destroy', () => { + if(splashScreen) { + splashScreen.destroy(); + splashScreen = null; + } + }); + socket.on('register-app-open-url-event', (id) => { global['electronsocket'] = socket; diff --git a/ElectronNET.Host/splashscreen/index.html b/ElectronNET.Host/splashscreen/index.html index ddd85e2..bafd72a 100644 --- a/ElectronNET.Host/splashscreen/index.html +++ b/ElectronNET.Host/splashscreen/index.html @@ -10,9 +10,10 @@ splashscreen