diff --git a/ElectronNET.API/App.cs b/ElectronNET.API/App.cs index 715207c..e4849ad 100644 --- a/ElectronNET.API/App.cs +++ b/ElectronNET.API/App.cs @@ -258,9 +258,9 @@ namespace ElectronNET.API private event Action _accessibilitySupportChanged; - private App() { } + internal App() { } - public static App Instance + internal static App Instance { get { @@ -280,25 +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) - { - BridgeConnector.Socket.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer)); - } - /// /// Try to close all windows. The before-quit event will be emitted first. If all /// windows are successfully closed, the will-quit event will be emitted and by diff --git a/ElectronNET.API/BrowserWindow.cs b/ElectronNET.API/BrowserWindow.cs new file mode 100644 index 0000000..790f0f0 --- /dev/null +++ b/ElectronNET.API/BrowserWindow.cs @@ -0,0 +1,612 @@ +using ElectronNET.API.Entities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using System.Threading.Tasks; + +namespace ElectronNET.API +{ + public class BrowserWindow + { + public int Id { get; private set; } + + internal BrowserWindow(int id) { + Id = id; + } + + /// + /// Force closing the window, the unload and beforeunload event won’t be + /// emitted for the web page, and close event will also not be emitted + /// for this window, but it guarantees the closed event will be emitted. + /// + public void Destroy() + { + BridgeConnector.Socket.Emit("browserWindow-destroy", Id); + } + + /// + /// Try to close the window. This has the same effect as a user manually + /// clicking the close button of the window. The web page may cancel the close though. + /// + public void Close() + { + BridgeConnector.Socket.Emit("browserWindow-close", Id); + } + + /// + /// Focuses on the window. + /// + public void Focus() + { + BridgeConnector.Socket.Emit("browserWindow-focus", Id); + } + + /// + /// Removes focus from the window. + /// + public void Blur() + { + BridgeConnector.Socket.Emit("browserWindow-blur", Id); + } + + /// + /// Whether the window is focused. + /// + /// + public Task IsFocusedAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isFocused-completed", (isFocused) => { + BridgeConnector.Socket.Off("browserWindow-isFocused-completed"); + + taskCompletionSource.SetResult((bool)isFocused); + }); + + BridgeConnector.Socket.Emit("browserWindow-isFocused", Id); + + return taskCompletionSource.Task; + } + + /// + /// Whether the window is destroyed. + /// + /// + public Task IsDestroyedAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isDestroyed-completed", (isDestroyed) => { + BridgeConnector.Socket.Off("browserWindow-isDestroyed-completed"); + + taskCompletionSource.SetResult((bool)isDestroyed); + }); + + BridgeConnector.Socket.Emit("browserWindow-isDestroyed", Id); + + return taskCompletionSource.Task; + } + + /// + /// Shows and gives focus to the window. + /// + public void Show() + { + BridgeConnector.Socket.Emit("browserWindow-show", Id); + } + + /// + /// Shows the window but doesn’t focus on it. + /// + public void ShowInactive() + { + BridgeConnector.Socket.Emit("browserWindow-showInactive", Id); + } + + /// + /// Hides the window. + /// + public void Hide() + { + BridgeConnector.Socket.Emit("browserWindow-hide", Id); + } + + /// + /// Whether the window is visible to the user. + /// + /// + public Task IsVisibleAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isVisible-completed", (isVisible) => { + BridgeConnector.Socket.Off("browserWindow-isVisible-completed"); + + taskCompletionSource.SetResult((bool)isVisible); + }); + + BridgeConnector.Socket.Emit("browserWindow-isVisible", Id); + + return taskCompletionSource.Task; + } + + /// + /// Whether current window is a modal window. + /// + /// + public Task IsModalAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isModal-completed", (isModal) => { + BridgeConnector.Socket.Off("browserWindow-isModal-completed"); + + taskCompletionSource.SetResult((bool)isModal); + }); + + BridgeConnector.Socket.Emit("browserWindow-isModal", Id); + + return taskCompletionSource.Task; + } + + /// + /// Maximizes the window. This will also show (but not focus) the window if it isn’t being displayed already. + /// + public void Maximize() + { + BridgeConnector.Socket.Emit("browserWindow-maximize", Id); + } + + /// + /// Unmaximizes the window. + /// + public void Unmaximize() + { + BridgeConnector.Socket.Emit("browserWindow-unmaximize", Id); + } + + /// + /// Whether the window is maximized. + /// + /// + public Task IsMaximizedAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isMaximized-completed", (isMaximized) => { + BridgeConnector.Socket.Off("browserWindow-isMaximized-completed"); + + taskCompletionSource.SetResult((bool)isMaximized); + }); + + BridgeConnector.Socket.Emit("browserWindow-isMaximized", Id); + + return taskCompletionSource.Task; + } + + /// + /// Minimizes the window. On some platforms the minimized window will be shown in the Dock. + /// + public void Minimize() + { + BridgeConnector.Socket.Emit("browserWindow-minimize", Id); + } + + /// + /// Restores the window from minimized state to its previous state. + /// + public void Restore() + { + BridgeConnector.Socket.Emit("browserWindow-restore", Id); + } + + /// + /// Whether the window is minimized. + /// + /// + public Task IsMinimizedAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isMinimized-completed", (isMinimized) => { + BridgeConnector.Socket.Off("browserWindow-isMinimized-completed"); + + taskCompletionSource.SetResult((bool)isMinimized); + }); + + BridgeConnector.Socket.Emit("browserWindow-isMinimized", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets whether the window should be in fullscreen mode. + /// + public void SetFullScreen(bool flag) + { + BridgeConnector.Socket.Emit("browserWindow-setFullScreen", Id, flag); + } + + /// + /// Whether the window is in fullscreen mode. + /// + /// + public Task IsFullScreenAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isFullScreen-completed", (isFullScreen) => { + BridgeConnector.Socket.Off("browserWindow-isFullScreen-completed"); + + taskCompletionSource.SetResult((bool)isFullScreen); + }); + + BridgeConnector.Socket.Emit("browserWindow-isFullScreen", Id); + + return taskCompletionSource.Task; + } + + /// + /// This will make a window maintain an aspect ratio. The extra size allows a developer to have space, + /// specified in pixels, not included within the aspect ratio calculations. This API already takes into + /// account the difference between a window’s size and its content size. + /// + /// Consider a normal window with an HD video player and associated controls.Perhaps there are 15 pixels + /// of controls on the left edge, 25 pixels of controls on the right edge and 50 pixels of controls below + /// the player. In order to maintain a 16:9 aspect ratio (standard aspect ratio for HD @1920x1080) within + /// the player itself we would call this function with arguments of 16/9 and[40, 50]. The second argument + /// doesn’t care where the extra width and height are within the content view–only that they exist. Just + /// sum any extra width and height areas you have within the overall content view. + /// + /// The aspect ratio to maintain for some portion of the content view. + /// The extra size not to be included while maintaining the aspect ratio. + public void SetAspectRatio(int aspectRatio, Size extraSize) + { + BridgeConnector.Socket.Emit("browserWindow-setAspectRatio", Id, aspectRatio, JObject.FromObject(extraSize, _jsonSerializer)); + } + + /// + /// Uses Quick Look to preview a file at a given path. + /// + /// The absolute path to the file to preview with QuickLook. This is important as + /// Quick Look uses the file name and file extension on the path to determine the content type of the + /// file to open. + public void PreviewFile(string path) + { + BridgeConnector.Socket.Emit("browserWindow-previewFile", Id, path); + } + + /// + /// Uses Quick Look to preview a file at a given path. + /// + /// The absolute path to the file to preview with QuickLook. This is important as + /// Quick Look uses the file name and file extension on the path to determine the content type of the + /// file to open. + /// The name of the file to display on the Quick Look modal view. This is + /// purely visual and does not affect the content type of the file. Defaults to path. + public void PreviewFile(string path, string displayname) + { + BridgeConnector.Socket.Emit("browserWindow-previewFile", Id, path, displayname); + } + + /// + /// Closes the currently open Quick Look panel. + /// + public void CloseFilePreview() + { + BridgeConnector.Socket.Emit("browserWindow-closeFilePreview", Id); + } + + /// + /// Resizes and moves the window to the supplied bounds + /// + /// + public void SetBounds(Rectangle bounds) + { + BridgeConnector.Socket.Emit("browserWindow-setBounds", Id, JObject.FromObject(bounds, _jsonSerializer)); + } + + /// + /// Resizes and moves the window to the supplied bounds + /// + /// + /// + public void SetBounds(Rectangle bounds, bool animate) + { + BridgeConnector.Socket.Emit("browserWindow-setBounds", Id, JObject.FromObject(bounds, _jsonSerializer), animate); + } + + public Task GetBoundsAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getBounds-completed", (getBounds) => { + BridgeConnector.Socket.Off("browserWindow-getBounds-completed"); + + taskCompletionSource.SetResult(((JObject)getBounds).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getBounds", Id); + + return taskCompletionSource.Task; + } + + /// + /// Resizes and moves the window’s client area (e.g. the web page) to the supplied bounds. + /// + /// + public void SetContentBounds(Rectangle bounds) + { + BridgeConnector.Socket.Emit("browserWindow-setContentBounds", Id, JObject.FromObject(bounds, _jsonSerializer)); + } + + /// + /// Resizes and moves the window’s client area (e.g. the web page) to the supplied bounds. + /// + /// + /// + public void SetContentBounds(Rectangle bounds, bool animate) + { + BridgeConnector.Socket.Emit("browserWindow-setContentBounds", Id, JObject.FromObject(bounds, _jsonSerializer), animate); + } + + public Task GetContentBoundsAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getContentBounds-completed", (getContentBounds) => { + BridgeConnector.Socket.Off("browserWindow-getContentBounds-completed"); + + taskCompletionSource.SetResult(((JObject)getContentBounds).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getContentBounds", Id); + + return taskCompletionSource.Task; + } + + /// + /// Resizes the window to width and height. + /// + /// + /// + /// + public void SetSize(int width, int height) + { + BridgeConnector.Socket.Emit("browserWindow-setSize", Id, width, height); + } + + /// + /// Resizes the window to width and height. + /// + /// + /// + /// + public void SetSize(int width, int height, bool animate) + { + BridgeConnector.Socket.Emit("browserWindow-setSize", Id, width, height, animate); + } + + /// + /// Contains the window’s width and height. + /// + /// + public Task GetSizeAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getSize-completed", (size) => { + BridgeConnector.Socket.Off("browserWindow-getSize-completed"); + + taskCompletionSource.SetResult(((JArray)size).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getSize", Id); + + return taskCompletionSource.Task; + } + + /// + /// Resizes the window’s client area (e.g. the web page) to width and height. + /// + /// + /// + /// + public void SetContentSize(int width, int height) + { + BridgeConnector.Socket.Emit("browserWindow-setContentSize", Id, width, height); + } + + /// + /// Resizes the window’s client area (e.g. the web page) to width and height. + /// + /// + /// + /// + public void SetContentSize(int width, int height, bool animate) + { + BridgeConnector.Socket.Emit("browserWindow-setContentSize", Id, width, height, animate); + } + + /// + /// Contains the window’s client area’s width and height. + /// + /// + public Task GetContentSizeAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getContentSize-completed", (size) => { + BridgeConnector.Socket.Off("browserWindow-getContentSize-completed"); + + taskCompletionSource.SetResult(((JArray)size).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getContentSize", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets the minimum size of window to width and height. + /// + /// + /// + public void SetMinimumSize(int width, int height) + { + BridgeConnector.Socket.Emit("browserWindow-setMinimumSize", Id, width, height); + } + + /// + /// Contains the window’s minimum width and height. + /// + /// + public Task GetMinimumSizeAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getMinimumSize-completed", (size) => { + BridgeConnector.Socket.Off("browserWindow-getMinimumSize-completed"); + + taskCompletionSource.SetResult(((JArray)size).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getMinimumSize", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets the maximum size of window to width and height. + /// + /// + /// + public void SetMaximumSize(int width, int height) + { + BridgeConnector.Socket.Emit("browserWindow-setMaximumSize", Id, width, height); + } + + /// + /// Contains the window’s maximum width and height. + /// + /// + public Task GetMaximumSizeAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-getMaximumSize-completed", (size) => { + BridgeConnector.Socket.Off("browserWindow-getMaximumSize-completed"); + + taskCompletionSource.SetResult(((JArray)size).ToObject()); + }); + + BridgeConnector.Socket.Emit("browserWindow-getMaximumSize", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets whether the window can be manually resized by user. + /// + /// + public void SetResizable(bool resizable) + { + BridgeConnector.Socket.Emit("browserWindow-setResizable", Id, resizable); + } + + /// + /// Whether the window can be manually resized by user. + /// + /// + public Task IsResizableAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isResizable-completed", (resizable) => { + BridgeConnector.Socket.Off("browserWindow-isResizable-completed"); + + taskCompletionSource.SetResult((bool)resizable); + }); + + BridgeConnector.Socket.Emit("browserWindow-isResizable", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets whether the window can be moved by user. On Linux does nothing. + /// + /// + public void SetMovable(bool movable) + { + BridgeConnector.Socket.Emit("browserWindow-setMovable", Id, movable); + } + + /// + /// Whether the window can be moved by user. + /// + /// On Linux always returns true. + /// + /// On Linux always returns true. + public Task IsMovableAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isMovable-completed", (movable) => { + BridgeConnector.Socket.Off("browserWindow-isMovable-completed"); + + taskCompletionSource.SetResult((bool)movable); + }); + + BridgeConnector.Socket.Emit("browserWindow-isMovable", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets whether the window can be manually minimized by user. On Linux does nothing. + /// + /// + public void SetMinimizable(bool minimizable) + { + BridgeConnector.Socket.Emit("browserWindow-setMinimizable", Id, minimizable); + } + + /// + /// Whether the window can be manually minimized by user. + /// + /// On Linux always returns true. + /// + /// On Linux always returns true. + public Task IsMinimizableAsync() + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("browserWindow-isMinimizable-completed", (minimizable) => { + BridgeConnector.Socket.Off("browserWindow-isMinimizable-completed"); + + taskCompletionSource.SetResult((bool)minimizable); + }); + + BridgeConnector.Socket.Emit("browserWindow-isMinimizable", Id); + + return taskCompletionSource.Task; + } + + /// + /// Sets whether the window can be manually maximized by user. On Linux does nothing. + /// + /// + public void SetMaximizable(bool maximizable) + { + BridgeConnector.Socket.Emit("browserWindow-setMaximizable", Id, maximizable); + } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; + } +} diff --git a/ElectronNET.API/Dialog.cs b/ElectronNET.API/Dialog.cs new file mode 100644 index 0000000..f47afac --- /dev/null +++ b/ElectronNET.API/Dialog.cs @@ -0,0 +1,89 @@ +using ElectronNET.API.Entities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using System; +using System.Threading.Tasks; + +namespace ElectronNET.API +{ + public sealed class Dialog + { + private static Dialog _dialog; + + internal Dialog() { } + + internal static Dialog Instance + { + get + { + if (_dialog == null) + { + _dialog = new Dialog(); + } + + return _dialog; + } + } + + /// + /// Shows a message box, it will block the process until the message box is closed. + /// It returns the index of the clicked button. The browserWindow argument allows + /// the dialog to attach itself to a parent window, making it modal. If a callback + /// is passed, the dialog will not block the process.The API call will be + /// asynchronous and the result will be passed via callback(response). + /// + /// + /// The API call will be asynchronous and the result will be passed via MessageBoxResult. + public async Task ShowMessageBoxAsync(MessageBoxOptions messageBoxOptions) + { + return await ShowMessageBoxAsync(null, messageBoxOptions); + } + + /// + /// Shows a message box, it will block the process until the message box is closed. + /// It returns the index of the clicked button. If a callback + /// is passed, the dialog will not block the process. + /// + /// The browserWindow argument allows the dialog to attach itself to a parent window, making it modal. + /// + /// The API call will be asynchronous and the result will be passed via MessageBoxResult. + public Task ShowMessageBoxAsync(BrowserWindow browserWindow, MessageBoxOptions messageBoxOptions) + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("showMessageBoxComplete", (args) => + { + BridgeConnector.Socket.Off("showMessageBoxComplete"); + + var result = ((JArray)args); + + taskCompletionSource.SetResult(new MessageBoxResult + { + Response = (int)result.First, + CheckboxChecked = (bool)result.Last + }); + + }); + + if (browserWindow == null) + { + BridgeConnector.Socket.Emit("showMessageBox", JObject.FromObject(messageBoxOptions, _jsonSerializer)); + } else + { + BridgeConnector.Socket.Emit("showMessageBox", + JObject.FromObject(browserWindow, _jsonSerializer), + JObject.FromObject(messageBoxOptions, _jsonSerializer)); + } + + return taskCompletionSource.Task; + } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; + } +} diff --git a/ElectronNET.API/Electron.cs b/ElectronNET.API/Electron.cs index f7b854d..6002677 100644 --- a/ElectronNET.API/Electron.cs +++ b/ElectronNET.API/Electron.cs @@ -11,5 +11,30 @@ /// Control your application's event lifecycle. /// public static App App { get { return App.Instance; } } + + /// + /// Control your windows. + /// + public static WindowManager WindowManager { get { return WindowManager.Instance; } } + + /// + /// Create native application menus and context menus. + /// + public static Menu Menu { get { return Menu.Instance; } } + + /// + /// Display native system dialogs for opening and saving files, alerting, etc. + /// + public static Dialog Dialog { get { return Dialog.Instance; } } + + /// + /// Create OS desktop notifications + /// + public static Notification Notification { get { return Notification.Instance; } } + + /// + /// Add icons and context menus to the system’s notification area. + /// + public static Tray Tray { get { return Tray.Instance; } } } } diff --git a/ElectronNET.API/Entities/BrowserWindowOptions.cs b/ElectronNET.API/Entities/BrowserWindowOptions.cs index 8a817e7..9e6ed33 100644 --- a/ElectronNET.API/Entities/BrowserWindowOptions.cs +++ b/ElectronNET.API/Entities/BrowserWindowOptions.cs @@ -2,8 +2,243 @@ { public class BrowserWindowOptions { + /// + /// Window's width in pixels. Default is 800. + /// public int Width { get; set; } + + /// + /// Window's height in pixels. Default is 600. + /// public int Height { get; set; } + + /// + /// ( if y is used) Window's left offset from screen. Default is to center the + /// window. + /// + public int X { get; set; } + + /// + /// ( if x is used) Window's top offset from screen. Default is to center the + /// window. + /// + public int Y { get; set; } + + /// + /// 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. + /// + public bool UseContentSize { get; set; } + + /// + /// Show window in the center of the screen. + /// + public bool Center { get; set; } + + /// + /// Window's minimum width. Default is 0. + /// + public int MinWidth { get; set; } + + /// + /// Window's minimum height. Default is 0. + /// + public int MinHeight { get; set; } + + /// + /// Window's maximum width. Default is no limit. + /// + public int MaxWidth { get; set; } + + /// + /// Window's maximum height. Default is no limit. + /// + public int MaxHeight { get; set; } + + /// + /// Whether window is resizable. Default is true. + /// + public bool Resizable { get; set; } + + /// + /// Whether window is movable. This is not implemented on Linux. Default is true. + /// + public bool Movable { get; set; } + + /// + /// Whether window is minimizable. This is not implemented on Linux. Default is true. + /// + public bool Minimizable { get; set; } + + /// + /// Whether window is maximizable. This is not implemented on Linux. Default is true. + /// + public bool Maximizable { get; set; } + + /// + /// Whether window is closable. This is not implemented on Linux. Default is true. + /// + public bool Closable { get; set; } + + /// + /// 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. + /// + public bool Focusable { get; set; } + + /// + /// Whether the window should always stay on top of other windows. Default is false. + /// + 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; } + + /// + /// 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; } + + /// + /// Whether to show the window in taskbar. Default is false. + /// + public bool SkipTaskbar { get; set; } + + /// + /// The kiosk mode. Default is false. + /// + public bool Kiosk { get; set; } + + /// + /// Default window title. Default is "Electron.NET". + /// + public string Title { get; set; } = "Electron.NET"; + + /// + /// 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. + /// + public string Icon { get; set; } + + /// + /// Whether window should be shown when created. Default is true. + /// public bool Show { get; set; } + + /// + /// Specify false to create a . Default is true. + /// + public bool Frame { get; set; } + + /// + /// Whether this is a modal window. This only works when the window is a child + /// window.Default is false. + /// + 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; } + + /// + /// Whether to hide cursor when typing. Default is false. + /// + public bool DisableAutoHideCursor { get; set; } + + /// + /// Auto hide the menu bar unless the Alt key is pressed. Default is false. + /// + public bool AutoHideMenuBar { get; set; } + + /// + /// Enable the window to be resized larger than screen. Default is false. + /// + public bool EnableLargerThanScreen { get; set; } + + /// + /// Window's background color as Hexadecimal value, like #66CD00 or #FFF or + /// #80FFFFFF (alpha is supported). Default is #FFF (white). + /// + public string BackgroundColor { get; set; } + + /// + /// Whether window should have a shadow. This is only implemented on macOS. Default + /// is true. + /// + 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; } + + /// + /// Makes the window . Default is false. + /// + public bool Transparent { get; set; } + + /// + /// The type of window, default is normal window. + /// + public string Type { get; set; } + + /// + /// The style of window title bar. Default is default. Possible values are: + /// 'default' | 'hidden' | 'hidden-inset' | 'hiddenInset' | 'customButtonsOnHover' + /// + public string 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; } + + /// + /// 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. + /// + public bool ThickFrame { get; set; } + + /// + /// 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. + /// + public string Vibrancy { get; set; } + + /// + /// 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. + /// + public bool ZoomToPageWidth { get; set; } + + /// + /// 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. + /// + public string TabbingIdentifier { get; set; } + + /// + /// Settings of web page's features. + /// + public WebPreferences WebPreferences { get; set; } } } diff --git a/ElectronNET.API/Entities/MenuItem.cs b/ElectronNET.API/Entities/MenuItem.cs new file mode 100644 index 0000000..667993b --- /dev/null +++ b/ElectronNET.API/Entities/MenuItem.cs @@ -0,0 +1,72 @@ +using Newtonsoft.Json; +using System; + +namespace ElectronNET.API.Entities +{ + public class MenuItem + { + /// + /// Will be called with click(menuItem, browserWindow, event) when the menu item is + /// clicked. + /// + [JsonIgnore] + public Action Click { get; set; } + + /// + /// Define the action of the menu item, when specified the click property will be + /// ignored. + /// + public string Role { get; set; } + + /// + /// Can be normal, separator, submenu, checkbox or radio. + /// + 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; } + + /// + /// If false, the menu item will be greyed out and unclickable. + /// + public bool Enabled { get; set; } + + /// + /// If false, the menu item will be entirely hidden. + /// + public bool Visible { get; set; } + + /// + /// Should only be specified for checkbox or radio type menu items. + /// + public bool Checked { get; set; } + + /// + /// 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. + /// + public MenuItem[] Submenu { get; set; } + + /// + /// Unique within a single menu. If defined then it can be used as a reference to + /// this item by the position attribute. + /// + public string Id { get; internal set; } + + /// + /// This field allows fine-grained definition of the specific location within a + /// given menu. + /// + public string Position { get; set; } + } +} diff --git a/ElectronNET.API/Entities/MessageBoxOptions.cs b/ElectronNET.API/Entities/MessageBoxOptions.cs new file mode 100644 index 0000000..960471c --- /dev/null +++ b/ElectronNET.API/Entities/MessageBoxOptions.cs @@ -0,0 +1,84 @@ +namespace ElectronNET.API.Entities +{ + public class MessageBoxOptions + { + /// + /// Can be "none", "info", "error", "question" or "warning". On Windows, "question" + /// displays the same icon as "info", unless you set an icon using the "icon" + /// option.On macOS, both "warning" and "error" display the same warning icon. + /// + public string Type { get; set; } + + /// + /// Array of texts for buttons. On Windows, an empty array will result in one button + /// labeled "OK". + /// + public string[] Buttons { get; set; } + + /// + /// Index of the button in the buttons array which will be selected by default when + /// the message box opens. + /// + public int DefaultId { get; set; } + + /// + /// Title of the message box, some platforms will not show it. + /// + public string Title { get; set; } + + /// + /// Content of the message box. + /// + public string Message { get; set; } + + /// + /// Extra information of the message. + /// + public string Detail { get; set; } + + /// + /// If provided, the message box will include a checkbox with the given label. The + /// checkbox state can be inspected only when using callback. + /// + public string CheckboxLabel { get; set; } + + /// + /// Initial checked state of the checkbox. false by default. + /// + public bool CheckboxChecked { get; set; } + + public string Icon { get; set; } + + /// + /// The index of the button to be used to cancel the dialog, via the Esc key. By + /// default this is assigned to the first button with "cancel" or "no" as the label. + /// If no such labeled buttons exist and this option is not set, 0 will be used as + /// the return value or callback response. This option is ignored on Windows. + /// + public int CancelId { get; set; } + + /// + /// On Windows Electron will try to figure out which one of the buttons are common + /// buttons(like "Cancel" or "Yes"), and show the others as command links in the + /// dialog.This can make the dialog appear in the style of modern Windows apps. If + /// you don't like this behavior, you can set noLink to true. + /// + public bool NoLink { get; set; } + + /// + /// Normalize the keyboard access keys across platforms. Default is false. Enabling + /// this assumes & is used in the button labels for the placement of the keyboard + /// shortcut access key and labels will be converted so they work correctly on each + /// platform, & characters are removed on macOS, converted to _ on Linux, and left + /// untouched on Windows.For example, a button label of Vie&w will be converted to + /// Vie_w on Linux and View on macOS and can be selected via Alt-W on Windows and + /// Linux. + /// + public bool NormalizeAccessKeys { get; set; } + + public MessageBoxOptions(string message) + { + Message = message; + } + } +} diff --git a/ElectronNET.API/Entities/MessageBoxResult.cs b/ElectronNET.API/Entities/MessageBoxResult.cs new file mode 100644 index 0000000..20a19b6 --- /dev/null +++ b/ElectronNET.API/Entities/MessageBoxResult.cs @@ -0,0 +1,9 @@ +namespace ElectronNET.API.Entities +{ + public class MessageBoxResult + { + public int Response { get; set; } + + public bool CheckboxChecked { get; set; } + } +} diff --git a/ElectronNET.API/Entities/NotificationOptions.cs b/ElectronNET.API/Entities/NotificationOptions.cs index b58e954..efa127f 100644 --- a/ElectronNET.API/Entities/NotificationOptions.cs +++ b/ElectronNET.API/Entities/NotificationOptions.cs @@ -2,7 +2,52 @@ { public class NotificationOptions { + /// + /// A title for the notification, which will be shown at the top of the notification + /// window when it is shown + /// public string Title { get; set; } + + /// + /// The body text of the notification, which will be displayed below the title or + /// subtitle + /// public string Body { get; set; } + + /// + /// A subtitle for the notification, which will be displayed below the title. + /// + public string Subtitle { get; set; } + + /// + /// Whether or not to emit an OS notification noise when showing the notification + /// + public bool Silent { get; set; } + + /// + /// An icon to use in the notification + /// + public string Icon { get; set; } + + /// + /// Whether or not to add an inline reply option to the notification. + /// + public bool HasReply { get; set; } + + /// + /// The placeholder to write in the inline reply input field. + /// + public string ReplyPlaceholder { get; set; } + + /// + /// The name of the sound file to play when the notification is shown. + /// + public string Sound { get; set; } + + public NotificationOptions(string title, string body) + { + Title = title; + Body = body; + } } } diff --git a/ElectronNET.API/Entities/Rectangle.cs b/ElectronNET.API/Entities/Rectangle.cs new file mode 100644 index 0000000..7e1a8eb --- /dev/null +++ b/ElectronNET.API/Entities/Rectangle.cs @@ -0,0 +1,10 @@ +namespace ElectronNET.API.Entities +{ + public class Rectangle + { + public int X { get; set; } + public int Y { get; set; } + public int Width { get; set; } + public int Height { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/Size.cs b/ElectronNET.API/Entities/Size.cs new file mode 100644 index 0000000..459428a --- /dev/null +++ b/ElectronNET.API/Entities/Size.cs @@ -0,0 +1,9 @@ +namespace ElectronNET.API.Entities +{ + public class Size + { + public int Width { get; set; } + + public int Height { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Entities/WebPreferences.cs b/ElectronNET.API/Entities/WebPreferences.cs new file mode 100644 index 0000000..8f4e738 --- /dev/null +++ b/ElectronNET.API/Entities/WebPreferences.cs @@ -0,0 +1,186 @@ +namespace ElectronNET.API.Entities +{ + public class WebPreferences + { + /// + /// Whether to enable DevTools. If it is set to false, can not use + /// BrowserWindow.webContents.openDevTools() to open DevTools.Default is true. + /// + public bool DevTools { get; set; } + + /// + /// Whether node integration is enabled. Default is true. + /// + public bool NodeIntegration { get; set; } + + /// + /// Whether node integration is enabled in web workers. Default is false. + /// + public bool NodeIntegrationInWorker { get; set; } + + /// + /// 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. + /// + public string Preload { get; set; } + + /// + /// 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. + /// + public bool Sandbox { get; set; } + + /// + /// 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. + /// + public string Partition { get; set; } + + /// + /// The default zoom factor of the page, 3.0 represents 300%. Default is 1.0. + /// + public int ZoomFactor { get; set; } + + /// + /// Enables JavaScript support. Default is true. + /// + public bool Javascript { get; set; } + + /// + /// 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. + /// + public bool WebSecurity { get; set; } + + /// + /// Allow an https page to run JavaScript, CSS or plugins from http URLs. Default is + /// false. + /// + public bool AllowRunningInsecureContent { get; set; } + + /// + /// Enables image support. Default is true. + /// + public bool Images { get; set; } + + /// + /// Make TextArea elements resizable. Default is true. + /// + public bool TextAreasAreResizable { get; set; } + + /// + /// Enables WebGL support. Default is true. + /// + public bool Webgl { get; set; } + + /// + /// Enables WebAudio support. Default is true. + /// + public bool Webaudio { get; set; } + + /// + /// Whether plugins should be enabled. Default is false. + /// + public bool Plugins { get; set; } + + /// + /// Enables Chromium's experimental features. Default is false. + /// + public bool ExperimentalFeatures { get; set; } + + /// + /// Enables Chromium's experimental canvas features. Default is false. + /// + public bool ExperimentalCanvasFeatures { get; set; } + + /// + /// Enables scroll bounce (rubber banding) effect on macOS. Default is false. + /// + public bool ScrollBounce { get; set; } + + /// + /// 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. + /// + public string BlinkFeatures { get; set; } + + /// + /// 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. + /// + public string DisableBlinkFeatures { get; set; } + + /// + /// Defaults to 16. + /// + public int DefaultFontSize { get; set; } + + /// + /// Defaults to 13. + /// + public int DefaultMonospaceFontSize { get; set; } + + /// + /// Defaults to 0. + /// + public int MinimumFontSize { get; set; } + + /// + /// Defaults to ISO-8859-1. + /// + public string DefaultEncoding { get; set; } + + /// + /// Whether to throttle animations and timers when the page becomes background. This + /// also affects the[Page Visibility API][#page-visibility]. Defaults to true. + /// + public bool BackgroundThrottling { get; set; } + + /// + /// Whether to enable offscreen rendering for the browser window. Defaults to false. + /// + public bool Offscreen { get; set; } + + /// + /// 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. + /// + public bool ContextIsolation { get; set; } + + /// + /// Whether to use native window.open(). Defaults to false. This option is currently experimental. + /// + public bool NativeWindowOpen { get; set; } + + /// + /// Whether to enable the . Defaults to the value of the nodeIntegration option. The + /// preload script configured for the will have node integration enabled + /// when it is executed so you should ensure remote/untrusted content is not able to + /// create a 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's initial settings. + /// + public bool WebviewTag { get; set; } + } +} \ No newline at end of file diff --git a/ElectronNET.API/Extensions/MenuItemExtensions.cs b/ElectronNET.API/Extensions/MenuItemExtensions.cs new file mode 100644 index 0000000..ba5f8b5 --- /dev/null +++ b/ElectronNET.API/Extensions/MenuItemExtensions.cs @@ -0,0 +1,53 @@ +using ElectronNET.API.Entities; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ElectronNET.API.Extensions +{ + internal static class MenuItemExtensions + { + public static MenuItem[] AddMenuItemsId(this 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) && + string.IsNullOrEmpty(menuItem.Id)) + { + menuItem.Id = Guid.NewGuid().ToString(); + } + } + + return menuItems; + } + + public static MenuItem GetMenuItem(this List 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; + } + } +} diff --git a/ElectronNET.API/IpcMain.cs b/ElectronNET.API/IpcMain.cs index 7cf2577..b0f56f9 100644 --- a/ElectronNET.API/IpcMain.cs +++ b/ElectronNET.API/IpcMain.cs @@ -1,4 +1,7 @@ -using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using System; namespace ElectronNET.API { @@ -9,9 +12,9 @@ namespace ElectronNET.API { private static IpcMain _ipcMain; - private IpcMain() { } + internal IpcMain() { } - public static IpcMain Instance + internal static IpcMain Instance { get { @@ -63,11 +66,19 @@ namespace ElectronNET.API /// no functions or prototype chain will be included. The renderer process handles it by /// listening for channel with ipcRenderer module. /// + /// BrowserWindow with channel. /// Channelname. /// Arguments data. - public void Send(string channel, params object[] data) + public void Send(BrowserWindow browserWindow, string channel, params object[] data) { - BridgeConnector.Socket.Emit("sendToIpcRenderer", channel, data); + BridgeConnector.Socket.Emit("sendToIpcRenderer", JObject.FromObject(browserWindow, _jsonSerializer), channel, data); } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; } } \ No newline at end of file diff --git a/ElectronNET.API/Menu.cs b/ElectronNET.API/Menu.cs new file mode 100644 index 0000000..cfd1af5 --- /dev/null +++ b/ElectronNET.API/Menu.cs @@ -0,0 +1,53 @@ +using ElectronNET.API.Entities; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; +using System; +using System.Linq; +using ElectronNET.API.Extensions; + +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 Items { get { return _items.AsReadOnly(); } } + private List _items = new List(); + + public void SetApplicationMenu(MenuItem[] menuItems) + { + menuItems.AddMenuItemsId(); + BridgeConnector.Socket.Emit("menu-setApplicationMenu", JArray.FromObject(menuItems, _jsonSerializer)); + _items.AddRange(menuItems); + + BridgeConnector.Socket.On("menuItemClicked", (id) => { + MenuItem menuItem = _items.GetMenuItem(id.ToString()); + menuItem?.Click(); + }); + } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; + } +} diff --git a/ElectronNET.API/Notification.cs b/ElectronNET.API/Notification.cs new file mode 100644 index 0000000..ad396d0 --- /dev/null +++ b/ElectronNET.API/Notification.cs @@ -0,0 +1,39 @@ +using ElectronNET.API.Entities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +namespace ElectronNET.API +{ + public sealed class Notification + { + private static Notification _notification; + + internal Notification() { } + + internal static Notification Instance + { + get + { + if (_notification == null) + { + _notification = new Notification(); + } + + return _notification; + } + } + + public void Show(NotificationOptions notificationOptions) + { + BridgeConnector.Socket.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer)); + } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; + } +} diff --git a/ElectronNET.API/Tray.cs b/ElectronNET.API/Tray.cs new file mode 100644 index 0000000..01599d9 --- /dev/null +++ b/ElectronNET.API/Tray.cs @@ -0,0 +1,51 @@ +using ElectronNET.API.Entities; +using ElectronNET.API.Extensions; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using System.Collections.Generic; + +namespace ElectronNET.API +{ + public sealed class Tray + { + private static Tray _tray; + + internal Tray() { } + + internal static Tray Instance + { + get + { + if (_tray == null) + { + _tray = new Tray(); + } + + return _tray; + } + } + + public IReadOnlyCollection Items { get { return _items.AsReadOnly(); } } + private List _items = new List(); + + public void Show(string image, MenuItem[] menuItems) + { + menuItems.AddMenuItemsId(); + BridgeConnector.Socket.Emit("create-tray", image, JArray.FromObject(menuItems, _jsonSerializer)); + _items.AddRange(menuItems); + + BridgeConnector.Socket.On("trayMenuItemClicked", (id) => { + MenuItem menuItem = _items.GetMenuItem(id.ToString()); + menuItem?.Click(); + }); + } + + private JsonSerializer _jsonSerializer = new JsonSerializer() + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore + }; + } +} diff --git a/ElectronNET.API/WindowManager.cs b/ElectronNET.API/WindowManager.cs new file mode 100644 index 0000000..2103041 --- /dev/null +++ b/ElectronNET.API/WindowManager.cs @@ -0,0 +1,69 @@ +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 BrowserWindows { get { return _browserWindows.AsReadOnly(); } } + private List _browserWindows = new List(); + + public async Task CreateWindowAsync(string loadUrl = "http://localhost") + { + return await CreateWindowAsync(new BrowserWindowOptions(), loadUrl); + } + + public Task CreateWindowAsync(BrowserWindowOptions options, string loadUrl = "http://localhost") + { + var taskCompletionSource = new TaskCompletionSource(); + + BridgeConnector.Socket.On("BrowserWindowCreated", (id) => + { + BridgeConnector.Socket.Off("BrowserWindowCreated"); + + 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 + }; + } +} diff --git a/ElectronNET.CLI/Commands/BuildCommand.cs b/ElectronNET.CLI/Commands/BuildCommand.cs index c240da9..66503b9 100644 --- a/ElectronNET.CLI/Commands/BuildCommand.cs +++ b/ElectronNET.CLI/Commands/BuildCommand.cs @@ -40,6 +40,12 @@ 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."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "dialog.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "menu.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "notification.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "tray.js", "api."); Console.WriteLine("Start npm install..."); ProcessHelper.CmdExecute("npm install", tempPath); diff --git a/ElectronNET.CLI/Commands/StartElectronCommand.cs b/ElectronNET.CLI/Commands/StartElectronCommand.cs index c0e4995..474e1da 100644 --- a/ElectronNET.CLI/Commands/StartElectronCommand.cs +++ b/ElectronNET.CLI/Commands/StartElectronCommand.cs @@ -59,6 +59,12 @@ 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."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "dialog.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "menu.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "notification.js", "api."); + EmbeddedFileHelper.DeployEmbeddedFile(hostApiFolder, "tray.js", "api."); Console.WriteLine("Start npm install..."); ProcessHelper.CmdExecute("npm install", tempPath); diff --git a/ElectronNET.CLI/ElectronNET.CLI.csproj b/ElectronNET.CLI/ElectronNET.CLI.csproj index 3a7efa6..994d6f5 100644 --- a/ElectronNET.CLI/ElectronNET.CLI.csproj +++ b/ElectronNET.CLI/ElectronNET.CLI.csproj @@ -34,4 +34,16 @@ + + + + + + + + + + + + diff --git a/ElectronNET.Host/api/browserWindows.js b/ElectronNET.Host/api/browserWindows.js new file mode 100644 index 0000000..9ff87d5 --- /dev/null +++ b/ElectronNET.Host/api/browserWindows.js @@ -0,0 +1,100 @@ +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +var windows = []; +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 (loadUrl) { + window.loadURL(loadUrl); + } + windows.push(window); + socket.emit('BrowserWindowCreated', window.id); + }); + socket.on('browserWindow-destroy', function (id) { + getWindowById(id).destroy(); + }); + socket.on('browserWindow-close', function (id) { + getWindowById(id).close(); + }); + socket.on('browserWindow-focus', function (id) { + getWindowById(id).focus(); + }); + socket.on('browserWindow-blur', function (id) { + getWindowById(id).blur(); + }); + socket.on('browserWindow-isFocused', function (id) { + var isFocused = getWindowById(id).isFocused(); + socket.emit('browserWindow-isFocused-completed', isFocused); + }); + socket.on('browserWindow-isDestroyed', function (id) { + var isDestroyed = getWindowById(id).isDestroyed(); + socket.emit('browserWindow-isDestroyed-completed', isDestroyed); + }); + socket.on('browserWindow-show', function (id) { + getWindowById(id).show(); + }); + socket.on('browserWindow-showInactive', function (id) { + getWindowById(id).showInactive(); + }); + socket.on('browserWindow-hide', function (id) { + getWindowById(id).hide(); + }); + socket.on('browserWindow-isVisible', function (id) { + var isVisible = getWindowById(id).isVisible(); + socket.emit('browserWindow-isVisible-completed', isVisible); + }); + socket.on('browserWindow-isModal', function (id) { + var isModal = getWindowById(id).isModal(); + socket.emit('browserWindow-isModal-completed', isModal); + }); + socket.on('browserWindow-maximize', function (id) { + getWindowById(id).maximize(); + }); + socket.on('browserWindow-unmaximize', function (id) { + getWindowById(id).unmaximize(); + }); + socket.on('browserWindow-isMaximized', function (id) { + var isMaximized = getWindowById(id).isMaximized(); + socket.emit('browserWindow-isMaximized-completed', isMaximized); + }); + socket.on('browserWindow-minimize', function (id) { + getWindowById(id).minimize(); + }); + socket.on('browserWindow-restore', function (id) { + getWindowById(id).restore(); + }); + socket.on('browserWindow-isMinimized', function (id) { + var isMinimized = getWindowById(id).isMinimized(); + socket.emit('browserWindow-isMinimized-completed', isMinimized); + }); + socket.on('browserWindow-setFullScreen', function (id, fullscreen) { + getWindowById(id).setFullScreen(fullscreen); + }); + socket.on('browserWindow-isFullScreen', function (id) { + var isFullScreen = getWindowById(id).isFullScreen(); + socket.emit('browserWindow-isFullScreen-completed', isFullScreen); + }); + 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 \ No newline at end of file diff --git a/ElectronNET.Host/api/browserWindows.js.map b/ElectronNET.Host/api/browserWindows.js.map new file mode 100644 index 0000000..b819a5b --- /dev/null +++ b/ElectronNET.Host/api/browserWindows.js.map @@ -0,0 +1 @@ +{"version":3,"file":"browserWindows.js","sourceRoot":"","sources":["browserWindows.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/ElectronNET.Host/api/browserWindows.ts b/ElectronNET.Host/api/browserWindows.ts new file mode 100644 index 0000000..0614241 --- /dev/null +++ b/ElectronNET.Host/api/browserWindows.ts @@ -0,0 +1,233 @@ +import { BrowserWindow } from "electron"; +const windows: Electron.BrowserWindow[] = [] + +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); + } + } + } + }); + + if (loadUrl) { + window.loadURL(loadUrl); + } + + windows.push(window); + socket.emit('BrowserWindowCreated', window.id); + }); + + socket.on('browserWindow-destroy', (id) => { + getWindowById(id).destroy(); + }); + + socket.on('browserWindow-close', (id) => { + getWindowById(id).close(); + }); + + socket.on('browserWindow-focus', (id) => { + getWindowById(id).focus(); + }); + + socket.on('browserWindow-blur', (id) => { + getWindowById(id).blur(); + }); + + socket.on('browserWindow-isFocused', (id) => { + const isFocused = getWindowById(id).isFocused(); + + socket.emit('browserWindow-isFocused-completed', isFocused); + }); + + socket.on('browserWindow-isDestroyed', (id) => { + const isDestroyed = getWindowById(id).isDestroyed(); + + socket.emit('browserWindow-isDestroyed-completed', isDestroyed); + }); + + socket.on('browserWindow-show', (id) => { + getWindowById(id).show(); + }); + + socket.on('browserWindow-showInactive', (id) => { + getWindowById(id).showInactive(); + }); + + socket.on('browserWindow-hide', (id) => { + getWindowById(id).hide(); + }); + + socket.on('browserWindow-isVisible', (id) => { + const isVisible = getWindowById(id).isVisible(); + + socket.emit('browserWindow-isVisible-completed', isVisible); + }); + + socket.on('browserWindow-isModal', (id) => { + const isModal = getWindowById(id).isModal(); + + socket.emit('browserWindow-isModal-completed', isModal); + }); + + socket.on('browserWindow-maximize', (id) => { + getWindowById(id).maximize(); + }); + + socket.on('browserWindow-unmaximize', (id) => { + getWindowById(id).unmaximize(); + }); + + socket.on('browserWindow-isMaximized', (id) => { + const isMaximized = getWindowById(id).isMaximized(); + + socket.emit('browserWindow-isMaximized-completed', isMaximized); + }); + + socket.on('browserWindow-minimize', (id) => { + getWindowById(id).minimize(); + }); + + socket.on('browserWindow-restore', (id) => { + getWindowById(id).restore(); + }); + + socket.on('browserWindow-isMinimized', (id) => { + const isMinimized = getWindowById(id).isMinimized(); + + socket.emit('browserWindow-isMinimized-completed', isMinimized); + }); + + socket.on('browserWindow-setFullScreen', (id, fullscreen) => { + getWindowById(id).setFullScreen(fullscreen); + }); + + socket.on('browserWindow-isFullScreen', (id) => { + const isFullScreen = getWindowById(id).isFullScreen(); + + socket.emit('browserWindow-isFullScreen-completed', isFullScreen); + }); + + socket.on('browserWindow-setAspectRatio', (id, aspectRatio, extraSize) => { + getWindowById(id).setAspectRatio(aspectRatio, extraSize); + }); + + socket.on('browserWindow-previewFile', (id, path, displayname) => { + getWindowById(id).previewFile(path, displayname); + }); + + socket.on('browserWindow-closeFilePreview', (id) => { + getWindowById(id).closeFilePreview(); + }); + + socket.on('browserWindow-setBounds', (id, bounds, animate) => { + getWindowById(id).setBounds(bounds, animate); + }); + + socket.on('browserWindow-getBounds', (id) => { + const rectangle = getWindowById(id).getBounds(); + + socket.emit('browserWindow-getBounds-completed', rectangle); + }); + + socket.on('browserWindow-setContentBounds', (id, bounds, animate) => { + getWindowById(id).setContentBounds(bounds, animate); + }); + + socket.on('browserWindow-getContentBounds', (id) => { + const rectangle = getWindowById(id).getContentBounds(); + + socket.emit('browserWindow-getContentBounds-completed', rectangle); + }); + + socket.on('browserWindow-setSize', (id, width, height, animate) => { + getWindowById(id).setSize(width, height, animate); + }); + + socket.on('browserWindow-getSize', (id) => { + const size = getWindowById(id).getSize(); + + socket.emit('browserWindow-getSize-completed', size); + }); + + socket.on('browserWindow-setContentSize', (id, width, height, animate) => { + getWindowById(id).setContentSize(width, height, animate); + }); + + socket.on('browserWindow-getContentSize', (id) => { + const size = getWindowById(id).getContentSize(); + + socket.emit('browserWindow-getContentSize-completed', size); + }); + + socket.on('browserWindow-setMinimumSize', (id, width, height) => { + getWindowById(id).setMinimumSize(width, height); + }); + + socket.on('browserWindow-getMinimumSize', (id) => { + const size = getWindowById(id).getMinimumSize(); + + socket.emit('browserWindow-getMinimumSize-completed', size); + }); + + socket.on('browserWindow-setMaximumSize', (id, width, height) => { + getWindowById(id).setMaximumSize(width, height); + }); + + socket.on('browserWindow-getMaximumSize', (id) => { + const size = getWindowById(id).getMaximumSize(); + + socket.emit('browserWindow-getMaximumSize-completed', size); + }); + + socket.on('browserWindow-setResizable', (id, resizable) => { + getWindowById(id).setResizable(resizable); + }); + + socket.on('browserWindow-isResizable', (id) => { + const resizable = getWindowById(id).isResizable(); + + socket.emit('browserWindow-isResizable-completed', resizable); + }); + + socket.on('browserWindow-setMovable', (id, movable) => { + getWindowById(id).setMovable(movable); + }); + + socket.on('browserWindow-isMovable', (id) => { + const movable = getWindowById(id).isMovable(); + + socket.emit('browserWindow-isMovable-completed', movable); + }); + + socket.on('browserWindow-setMinimizable', (id, minimizable) => { + getWindowById(id).setMinimizable(minimizable); + }); + + socket.on('browserWindow-isMinimizable', (id) => { + const minimizable = getWindowById(id).isMinimizable(); + + socket.emit('browserWindow-isMinimizable-completed', minimizable); + }); + + socket.on('browserWindow-setMaximizable', (id, maximizable) => { + getWindowById(id).setMaximizable(maximizable); + }); + + function getWindowById(id: number): Electron.BrowserWindow { + for (var index = 0; index < windows.length; index++) { + var element = windows[index]; + if (element.id == id) { + return element; + } + } + } +} \ No newline at end of file diff --git a/ElectronNET.Host/api/dialog.js b/ElectronNET.Host/api/dialog.js new file mode 100644 index 0000000..7d0a88a --- /dev/null +++ b/ElectronNET.Host/api/dialog.js @@ -0,0 +1,19 @@ +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +module.exports = function (socket) { + socket.on('showMessageBox', function (browserWindow, options) { + if ("id" in browserWindow) { + var window = electron_1.BrowserWindow.fromId(browserWindow.id); + electron_1.dialog.showMessageBox(window, options, function (response, checkboxChecked) { + socket.emit('showMessageBoxComplete', [response, checkboxChecked]); + }); + } + else { + electron_1.dialog.showMessageBox(browserWindow, function (response, checkboxChecked) { + socket.emit('showMessageBoxComplete', [response, checkboxChecked]); + }); + } + }); +}; +//# sourceMappingURL=dialog.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/dialog.js.map b/ElectronNET.Host/api/dialog.js.map new file mode 100644 index 0000000..672e716 --- /dev/null +++ b/ElectronNET.Host/api/dialog.js.map @@ -0,0 +1 @@ +{"version":3,"file":"dialog.js","sourceRoot":"","sources":["dialog.ts"],"names":[],"mappings":";;AAAA,qCAAiD;AAEjD,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,gBAAgB,EAAE,UAAC,aAAa,EAAE,OAAO;QAC/C,EAAE,CAAA,CAAC,IAAI,IAAI,aAAa,CAAC,CAAC,CAAC;YACvB,IAAI,MAAM,GAAG,wBAAa,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAEpD,iBAAM,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC7D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QACP,CAAC;QAAC,IAAI,CAAC,CAAC;YACJ,iBAAM,CAAC,cAAc,CAAC,aAAa,EAAE,UAAC,QAAQ,EAAE,eAAe;gBAC3D,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/dialog.ts b/ElectronNET.Host/api/dialog.ts new file mode 100644 index 0000000..24a03c2 --- /dev/null +++ b/ElectronNET.Host/api/dialog.ts @@ -0,0 +1,17 @@ +import { BrowserWindow, dialog } from "electron"; + +module.exports = (socket: SocketIO.Server) => { + socket.on('showMessageBox', (browserWindow, options) => { + if("id" in browserWindow) { + var window = BrowserWindow.fromId(browserWindow.id); + + dialog.showMessageBox(window, options, (response, checkboxChecked) => { + socket.emit('showMessageBoxComplete', [response, checkboxChecked]); + }); + } else { + dialog.showMessageBox(browserWindow, (response, checkboxChecked) => { + socket.emit('showMessageBoxComplete', [response, checkboxChecked]); + }); + } + }); +} \ No newline at end of file diff --git a/ElectronNET.Host/api/ipc.js b/ElectronNET.Host/api/ipc.js index 80535a6..81d465d 100644 --- a/ElectronNET.Host/api/ipc.js +++ b/ElectronNET.Host/api/ipc.js @@ -1,23 +1,26 @@ -var ipcMain = require('electron').ipcMain; -module.exports = function (socket, window) { +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +module.exports = function (socket) { socket.on('registerIpcMainChannel', function (channel) { - ipcMain.on(channel, function (event, args) { + electron_1.ipcMain.on(channel, function (event, args) { socket.emit(channel, [event.preventDefault(), args]); }); }); socket.on('registerOnceIpcMainChannel', function (channel) { - ipcMain.once(channel, function (event, args) { + electron_1.ipcMain.once(channel, function (event, args) { socket.emit(channel, [event.preventDefault(), args]); }); }); socket.on('removeAllListenersIpcMainChannel', function (channel) { - ipcMain.removeAllListeners(channel); + electron_1.ipcMain.removeAllListeners(channel); }); - socket.on('sendToIpcRenderer', function (channel) { + socket.on('sendToIpcRenderer', function (browserWindow, channel) { var data = []; - for (var _i = 1; _i < arguments.length; _i++) { - data[_i - 1] = arguments[_i]; + for (var _i = 2; _i < arguments.length; _i++) { + data[_i - 2] = arguments[_i]; } + var window = electron_1.BrowserWindow.fromId(browserWindow.id); if (window) { window.webContents.send(channel, data); } diff --git a/ElectronNET.Host/api/ipc.js.map b/ElectronNET.Host/api/ipc.js.map index 36547f4..dea2f4c 100644 --- a/ElectronNET.Host/api/ipc.js.map +++ b/ElectronNET.Host/api/ipc.js.map @@ -1 +1 @@ -{"version":3,"file":"ipc.js","sourceRoot":"","sources":["ipc.ts"],"names":[],"mappings":"AAAQ,IAAA,qCAAO,CAAyB;AAExC,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB,EAAE,MAAM;IAC7C,MAAM,CAAC,EAAE,CAAC,wBAAwB,EAAE,UAAC,OAAO;QACxC,OAAO,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,OAAO,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,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,mBAAmB,EAAE,UAAC,OAAO;QAAE,cAAO;aAAP,UAAO,EAAP,qBAAO,EAAP,IAAO;YAAP,6BAAO;;QAC5C,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"} \ No newline at end of file +{"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"} \ No newline at end of file diff --git a/ElectronNET.Host/api/ipc.ts b/ElectronNET.Host/api/ipc.ts index 7f65581..b3a7b2b 100644 --- a/ElectronNET.Host/api/ipc.ts +++ b/ElectronNET.Host/api/ipc.ts @@ -1,6 +1,6 @@ -const { ipcMain } = require('electron'); +import { ipcMain, BrowserWindow } from 'electron'; -module.exports = (socket: SocketIO.Server, window) => { +module.exports = (socket: SocketIO.Server) => { socket.on('registerIpcMainChannel', (channel) => { ipcMain.on(channel, (event, args) => { socket.emit(channel, [event.preventDefault(), args]); @@ -17,7 +17,9 @@ module.exports = (socket: SocketIO.Server, window) => { ipcMain.removeAllListeners(channel); }); - socket.on('sendToIpcRenderer', (channel, ...data) => { + socket.on('sendToIpcRenderer', (browserWindow, channel, ...data) => { + const window = BrowserWindow.fromId(browserWindow.id); + if (window) { window.webContents.send(channel, data); } diff --git a/ElectronNET.Host/api/menu.js b/ElectronNET.Host/api/menu.js new file mode 100644 index 0000000..4eb7eb3 --- /dev/null +++ b/ElectronNET.Host/api/menu.js @@ -0,0 +1,23 @@ +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +module.exports = function (socket) { + socket.on('menu-setApplicationMenu', function (menuItems) { + var menu = electron_1.Menu.buildFromTemplate(menuItems); + addMenuItemClickConnector(menu.items, function (id) { + socket.emit("menuItemClicked", id); + }); + electron_1.Menu.setApplicationMenu(menu); + }); + function addMenuItemClickConnector(menuItems, callback) { + menuItems.forEach(function (item) { + if (item.submenu && item.submenu.items.length > 0) { + addMenuItemClickConnector(item.submenu.items, callback); + } + if ("id" in item && item.id) { + item.click = function () { callback(item.id); }; + } + }); + } +}; +//# sourceMappingURL=menu.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/menu.js.map b/ElectronNET.Host/api/menu.js.map new file mode 100644 index 0000000..94adcce --- /dev/null +++ b/ElectronNET.Host/api/menu.js.map @@ -0,0 +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"} \ No newline at end of file diff --git a/ElectronNET.Host/api/menu.ts b/ElectronNET.Host/api/menu.ts new file mode 100644 index 0000000..1d96344 --- /dev/null +++ b/ElectronNET.Host/api/menu.ts @@ -0,0 +1,25 @@ +import { Menu } from "electron"; + +module.exports = (socket: SocketIO.Server) => { + socket.on('menu-setApplicationMenu', (menuItems) => { + const menu = Menu.buildFromTemplate(menuItems); + + addMenuItemClickConnector(menu.items, (id) => { + socket.emit("menuItemClicked", id); + }); + + Menu.setApplicationMenu(menu); + }); + + 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); }; + } + }); + } +} \ No newline at end of file diff --git a/ElectronNET.Host/api/notification.js b/ElectronNET.Host/api/notification.js new file mode 100644 index 0000000..8ce83a7 --- /dev/null +++ b/ElectronNET.Host/api/notification.js @@ -0,0 +1,10 @@ +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +module.exports = function (socket) { + socket.on('createNotification', function (options) { + var notification = new electron_1.Notification(options); + notification.show(); + }); +}; +//# sourceMappingURL=notification.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/notification.js.map b/ElectronNET.Host/api/notification.js.map new file mode 100644 index 0000000..b7d3ab1 --- /dev/null +++ b/ElectronNET.Host/api/notification.js.map @@ -0,0 +1 @@ +{"version":3,"file":"notification.js","sourceRoot":"","sources":["notification.ts"],"names":[],"mappings":";;AAAA,qCAAwC;AAExC,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,UAAC,OAAO;QACpC,IAAM,YAAY,GAAG,IAAI,uBAAY,CAAC,OAAO,CAAC,CAAC;QAC/C,YAAY,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"} \ No newline at end of file diff --git a/ElectronNET.Host/api/notification.ts b/ElectronNET.Host/api/notification.ts new file mode 100644 index 0000000..2264373 --- /dev/null +++ b/ElectronNET.Host/api/notification.ts @@ -0,0 +1,8 @@ +import { Notification } from "electron"; + +module.exports = (socket: SocketIO.Server) => { + socket.on('createNotification', (options) => { + const notification = new Notification(options); + notification.show(); + }); +} \ No newline at end of file diff --git a/ElectronNET.Host/api/tray.js b/ElectronNET.Host/api/tray.js new file mode 100644 index 0000000..1a9a53f --- /dev/null +++ b/ElectronNET.Host/api/tray.js @@ -0,0 +1,27 @@ +"use strict"; +exports.__esModule = true; +var electron_1 = require("electron"); +var path = require('path'); +var tray; +module.exports = function (socket) { + socket.on('create-tray', function (image, menuItems) { + var menu = electron_1.Menu.buildFromTemplate(menuItems); + addMenuItemClickConnector(menu.items, function (id) { + socket.emit("trayMenuItemClicked", id); + }); + var imagePath = path.join(__dirname.replace('api', ''), 'bin', image); + tray = new electron_1.Tray(imagePath); + tray.setContextMenu(menu); + }); + function addMenuItemClickConnector(menuItems, callback) { + menuItems.forEach(function (item) { + if (item.submenu && item.submenu.items.length > 0) { + addMenuItemClickConnector(item.submenu.items, callback); + } + if ("id" in item && item.id) { + item.click = function () { callback(item.id); }; + } + }); + } +}; +//# sourceMappingURL=tray.js.map \ No newline at end of file diff --git a/ElectronNET.Host/api/tray.js.map b/ElectronNET.Host/api/tray.js.map new file mode 100644 index 0000000..e28487a --- /dev/null +++ b/ElectronNET.Host/api/tray.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tray.js","sourceRoot":"","sources":["tray.ts"],"names":[],"mappings":";;AAAA,qCAAsC;AACtC,IAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAC7B,IAAI,IAAI,CAAC;AAET,MAAM,CAAC,OAAO,GAAG,UAAC,MAAuB;IACrC,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,UAAC,KAAK,EAAE,SAAS;QACtC,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,qBAAqB,EAAE,EAAE,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,IAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAExE,IAAI,GAAG,IAAI,eAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IAC9B,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"} \ No newline at end of file diff --git a/ElectronNET.Host/api/tray.ts b/ElectronNET.Host/api/tray.ts new file mode 100644 index 0000000..01b8150 --- /dev/null +++ b/ElectronNET.Host/api/tray.ts @@ -0,0 +1,30 @@ +import { Menu, Tray } from "electron"; +const path = require('path'); +let tray; + +module.exports = (socket: SocketIO.Server) => { + socket.on('create-tray', (image, menuItems) => { + const menu = Menu.buildFromTemplate(menuItems); + + addMenuItemClickConnector(menu.items, (id) => { + socket.emit("trayMenuItemClicked", id); + }); + + const imagePath = path.join(__dirname.replace('api', ''), 'bin', image); + + tray = new Tray(imagePath); + tray.setContextMenu(menu); + }); + + 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); }; + } + }); + } +} \ No newline at end of file diff --git a/ElectronNET.Host/main.js b/ElectronNET.Host/main.js index e22ac47..0e766de 100644 --- a/ElectronNET.Host/main.js +++ b/ElectronNET.Host/main.js @@ -1,9 +1,9 @@ -const { app, BrowserWindow, Notification } = require('electron'); +const { app } = 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, ipc, apiProcess, loadURL, appApi, menu, dialog, notification, tray; app.on('ready', () => { portfinder(8000, (error, port) => { @@ -17,25 +17,14 @@ function startSocketApiBridge(port) { io.on('connection', (socket) => { console.log('ASP.NET Core Application connected...'); + appApi = require('./api/app')(socket, app); - - socket.on('createBrowserWindow', (options) => { - window = new BrowserWindow(options); - window.loadURL(loadURL); - - window.on('closed', function () { - mainWindow = null; - apiProcess = null; - }); - - ipc = require('./api/ipc')(socket, window); - }); - - socket.on('createNotification', (options) => { - const notification = new Notification(options); - notification.show(); - }); - + browserWindows = require('./api/browserWindows')(socket); + ipc = require('./api/ipc')(socket); + menu = require('./api/menu')(socket); + dialog = require('./api/dialog')(socket); + notification = require('./api/notification')(socket); + tray = require('./api/tray')(socket); }); } @@ -68,10 +57,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(); - } -}); \ No newline at end of file +// 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(); +// } +// }); \ No newline at end of file diff --git a/ElectronNET.WebApp/Assets/electron.ico b/ElectronNET.WebApp/Assets/electron.ico new file mode 100644 index 0000000..3a10449 Binary files /dev/null and b/ElectronNET.WebApp/Assets/electron.ico differ diff --git a/ElectronNET.WebApp/Assets/electron_32x32.png b/ElectronNET.WebApp/Assets/electron_32x32.png new file mode 100644 index 0000000..125dde6 Binary files /dev/null and b/ElectronNET.WebApp/Assets/electron_32x32.png differ diff --git a/ElectronNET.WebApp/Controllers/HomeController.cs b/ElectronNET.WebApp/Controllers/HomeController.cs index 1c7bc7c..2690c75 100644 --- a/ElectronNET.WebApp/Controllers/HomeController.cs +++ b/ElectronNET.WebApp/Controllers/HomeController.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using ElectronNET.API; using ElectronNET.API.Entities; +using System.Linq; namespace ElectronNET.WebApp.Controllers { @@ -9,20 +10,25 @@ namespace ElectronNET.WebApp.Controllers public IActionResult Index() { Electron.IpcMain.On("SayHello", (args) => { - Electron.App.CreateNotification(new NotificationOptions - { - Title = "Hallo Robert", - Body = "Nachricht von ASP.NET Core App" - }); + Electron.Notification.Show(new NotificationOptions("Hallo Robert","Nachricht von ASP.NET Core App")); - Electron.IpcMain.Send("Goodbye", "Elephant!"); + Electron.IpcMain.Send(Electron.WindowManager.BrowserWindows.First(), "Goodbye", "Elephant!"); }); Electron.IpcMain.On("GetPath", async (args) => { + var currentBrowserWindow = Electron.WindowManager.BrowserWindows.First(); + string pathName = await Electron.App.GetPathAsync(PathName.pictures); - Electron.IpcMain.Send("GetPathComplete", pathName); + Electron.IpcMain.Send(currentBrowserWindow, "GetPathComplete", pathName); + + currentBrowserWindow.Minimize(); + await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { + Title = "My second Window", + AutoHideMenuBar = true + },"http://www.google.de"); }); + return View(); } diff --git a/ElectronNET.WebApp/ElectronNET.WebApp.csproj b/ElectronNET.WebApp/ElectronNET.WebApp.csproj index 33d154c..afa9da2 100644 --- a/ElectronNET.WebApp/ElectronNET.WebApp.csproj +++ b/ElectronNET.WebApp/ElectronNET.WebApp.csproj @@ -6,6 +6,7 @@ + @@ -24,4 +25,13 @@ + + + + PreserveNewest + + + PreserveNewest + + diff --git a/ElectronNET.WebApp/Startup.cs b/ElectronNET.WebApp/Startup.cs index fa2b53a..c7c3b49 100644 --- a/ElectronNET.WebApp/Startup.cs +++ b/ElectronNET.WebApp/Startup.cs @@ -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,40 @@ namespace ElectronNET.WebApp template: "{controller=Home}/{action=Index}/{id?}"); }); - Electron.App.OpenWindow(800, 600, true); + Bootstrap(); + } + + public async void Bootstrap() + { + var menuItems = new MenuItem[] { + new MenuItem { + Label = "File", + Submenu = new MenuItem[] { + new MenuItem { + Label = "Exit", + Click = () => + { + Electron.App.Exit(); + } + } + } + }, + new MenuItem + { + Label = "About", + Click = async () => { + await Electron.Dialog.ShowMessageBoxAsync(new MessageBoxOptions("(c) 2017 Gregor Biswanger & Robert Muehsig") { + Title = "About us...", + Type = "info" + }); + } + } + }; + + Electron.Menu.SetApplicationMenu(menuItems); + Electron.Tray.Show("/Assets/electron_32x32.png", menuItems); + + var browserWindow = await Electron.WindowManager.CreateWindowAsync(); } } } diff --git a/ElectronNET.WebApp/Views/Home/Index.cshtml b/ElectronNET.WebApp/Views/Home/Index.cshtml index 22197ef..75b5ee5 100644 --- a/ElectronNET.WebApp/Views/Home/Index.cshtml +++ b/ElectronNET.WebApp/Views/Home/Index.cshtml @@ -3,7 +3,6 @@ - Home

Hello from ASP.NET Core MVC!