using ElectronNET.API.Entities; using ElectronNET.API.Extensions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace ElectronNET.API { /// /// Add icons and context menus to the system's notification area. /// public sealed class Tray { /// /// Emitted when the tray icon is clicked. /// public event Action OnClick { add { if (_click == null) { BridgeConnector.Socket.On("tray-click-event" + GetHashCode(), (result) => { var args = ((JArray)result).ToObject(); var trayClickEventArgs = ((JObject)args[0]).ToObject(); var bounds = ((JObject)args[1]).ToObject(); _click(trayClickEventArgs, bounds); }); BridgeConnector.Socket.Emit("register-tray-click", GetHashCode()); } _click += value; } remove { _click -= value; if (_click == null) BridgeConnector.Socket.Off("tray-click-event" + GetHashCode()); } } private event Action _click; /// /// macOS, Windows: Emitted when the tray icon is right clicked. /// public event Action OnRightClick { add { if (_rightClick == null) { BridgeConnector.Socket.On("tray-right-click-event" + GetHashCode(), (result) => { var args = ((JArray)result).ToObject(); var trayClickEventArgs = ((JObject)args[0]).ToObject(); var bounds = ((JObject)args[1]).ToObject(); _rightClick(trayClickEventArgs, bounds); }); BridgeConnector.Socket.Emit("register-tray-right-click", GetHashCode()); } _rightClick += value; } remove { _rightClick -= value; if (_rightClick == null) BridgeConnector.Socket.Off("tray-right-click-event" + GetHashCode()); } } private event Action _rightClick; /// /// macOS, Windows: Emitted when the tray icon is double clicked. /// public event Action OnDoubleClick { add { if (_doubleClick == null) { BridgeConnector.Socket.On("tray-double-click-event" + GetHashCode(), (result) => { var args = ((JArray)result).ToObject(); var trayClickEventArgs = ((JObject)args[0]).ToObject(); var bounds = ((JObject)args[1]).ToObject(); _doubleClick(trayClickEventArgs, bounds); }); BridgeConnector.Socket.Emit("register-tray-double-click", GetHashCode()); } _doubleClick += value; } remove { _doubleClick -= value; if (_doubleClick == null) BridgeConnector.Socket.Off("tray-double-click-event" + GetHashCode()); } } private event Action _doubleClick; /// /// Windows: Emitted when the tray balloon shows. /// public event Action OnBalloonShow { add { if (_balloonShow == null) { BridgeConnector.Socket.On("tray-balloon-show-event" + GetHashCode(), () => { _balloonShow(); }); BridgeConnector.Socket.Emit("register-tray-balloon-show", GetHashCode()); } _balloonShow += value; } remove { _balloonShow -= value; if (_balloonShow == null) BridgeConnector.Socket.Off("tray-balloon-show-event" + GetHashCode()); } } private event Action _balloonShow; /// /// Windows: Emitted when the tray balloon is clicked. /// public event Action OnBalloonClick { add { if (_balloonClick == null) { BridgeConnector.Socket.On("tray-balloon-click-event" + GetHashCode(), () => { _balloonClick(); }); BridgeConnector.Socket.Emit("register-tray-balloon-click", GetHashCode()); } _balloonClick += value; } remove { _balloonClick -= value; if (_balloonClick == null) BridgeConnector.Socket.Off("tray-balloon-click-event" + GetHashCode()); } } private event Action _balloonClick; /// /// Windows: Emitted when the tray balloon is closed /// because of timeout or user manually closes it. /// public event Action OnBalloonClosed { add { if (_balloonClosed == null) { BridgeConnector.Socket.On("tray-balloon-closed-event" + GetHashCode(), () => { _balloonClosed(); }); BridgeConnector.Socket.Emit("register-tray-balloon-closed", GetHashCode()); } _balloonClosed += value; } remove { _balloonClosed -= value; if (_balloonClosed == null) BridgeConnector.Socket.Off("tray-balloon-closed-event" + GetHashCode()); } } private event Action _balloonClosed; // TODO: Implement macOS Events private static Tray _tray; private static readonly object _syncRoot = new(); internal Tray() { } internal static Tray Instance { get { if (_tray == null) { lock (_syncRoot) { if (_tray == null) { _tray = new Tray(); } } } return _tray; } } /// /// Gets the menu items. /// /// /// The menu items. /// public IReadOnlyCollection MenuItems => _items.AsReadOnly(); private readonly List _items = new(); /// /// Shows the Traybar. /// /// The image. /// The menu item. public async Task Show(string image, MenuItem menuItem) { await Show(image, new MenuItem[] { menuItem }); } /// /// Shows the Traybar. /// /// The image. /// The menu items. public async Task Show(string image, MenuItem[] menuItems) { menuItems.AddMenuItemsId(); await BridgeConnector.Socket.Emit("create-tray", image, JArray.FromObject(menuItems, _jsonSerializer)); _items.Clear(); _items.AddRange(menuItems); BridgeConnector.Socket.Off("trayMenuItemClicked"); BridgeConnector.Socket.On("trayMenuItemClicked", (id) => { MenuItem menuItem = _items.GetMenuItem(id); menuItem?.Click(); }); } /// /// Shows the Traybar (empty). /// /// The image. public async Task Show(string image) { await BridgeConnector.Socket.Emit("create-tray", image); } /// /// Destroys the tray icon immediately. /// public async Task Destroy() { await BridgeConnector.Socket.Emit("tray-destroy"); _items.Clear(); } /// /// Sets the image associated with this tray icon. /// /// public async Task SetImage(string image) { await BridgeConnector.Socket.Emit("tray-setImage", image); } /// /// Sets the image associated with this tray icon when pressed on macOS. /// /// public async Task SetPressedImage(string image) { await BridgeConnector.Socket.Emit("tray-setPressedImage", image); } /// /// Sets the hover text for this tray icon. /// /// public async Task SetToolTip(string toolTip) { await BridgeConnector.Socket.Emit("tray-setToolTip", toolTip); } /// /// macOS: Sets the title displayed aside of the tray icon in the status bar. /// /// public async Task SetTitle(string title) { await BridgeConnector.Socket.Emit("tray-setTitle", title); } /// /// Windows: Displays a tray balloon. /// /// public async Task DisplayBalloon(DisplayBalloonOptions options) { await BridgeConnector.Socket.Emit("tray-displayBalloon", JObject.FromObject(options, _jsonSerializer)); } /// /// Whether the tray icon is destroyed. /// /// public async Task IsDestroyedAsync() { var taskCompletionSource = new TaskCompletionSource(); BridgeConnector.Socket.On("tray-isDestroyedCompleted", (isDestroyed) => { BridgeConnector.Socket.Off("tray-isDestroyedCompleted"); taskCompletionSource.SetResult(isDestroyed); }); await BridgeConnector.Socket.Emit("tray-isDestroyed"); return await taskCompletionSource.Task; } private readonly JsonSerializer _jsonSerializer = new() { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore }; private const string ModuleName = "tray"; /// /// Subscribe to an unmapped event on the module. /// /// The event name /// The handler public void On(string eventName, Action action) => Events.Instance.On(ModuleName, eventName, action); /// /// Subscribe to an unmapped event on the module. /// /// The event name /// The handler public async Task On(string eventName, Action action) => await Events.Instance.On(ModuleName, eventName, action); /// /// Subscribe to an unmapped event on the module once. /// /// The event name /// The handler public void Once(string eventName, Action action) => Events.Instance.Once(ModuleName, eventName, action); /// /// Subscribe to an unmapped event on the module once. /// /// The event name /// The handler public async Task Once(string eventName, Action action) => await Events.Instance.Once(ModuleName, eventName, action); } }