using ElectronNET.API.Entities; using ElectronNET.API.Extensions; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Versioning; using System.Text.Json; using System.Threading.Tasks; using ElectronNET.API.Serialization; // ReSharper disable InconsistentNaming namespace ElectronNET.API { /// /// Add icons and context menus to the system's notification area. /// public sealed class Tray : ApiBase { protected override SocketTaskEventNameTypes SocketTaskEventNameType => SocketTaskEventNameTypes.DashesLowerFirst; protected override SocketEventNameTypes SocketEventNameType => SocketEventNameTypes.DashedLower; /// /// Emitted when the tray icon is clicked. /// public event Action OnClick { add { if (_click == null) { BridgeConnector.Socket.On("tray-click" + GetHashCode(), (result) => { var array = result.EnumerateArray().ToArray(); var trayClickEventArgs = array[0].Deserialize(ElectronJsonContext.Default.TrayClickEventArgs); var bounds = array[1].Deserialize(ElectronJsonContext.Default.Rectangle); _click(trayClickEventArgs, bounds); }); BridgeConnector.Socket.Emit("register-tray-click", GetHashCode()); } _click += value; } remove { _click -= value; if (_click == null) { BridgeConnector.Socket.Off("tray-click" + GetHashCode()); } } } private event Action _click; /// /// macOS, Windows: Emitted when the tray icon is right clicked. /// [SupportedOSPlatform("macOS")] [SupportedOSPlatform("Windows")] public event Action OnRightClick { add { if (_rightClick == null) { BridgeConnector.Socket.On("tray-right-click" + GetHashCode(), (result) => { var array = result.EnumerateArray().ToArray(); var trayClickEventArgs = array[0].Deserialize(ElectronJsonContext.Default.TrayClickEventArgs); var bounds = array[1].Deserialize(ElectronJsonContext.Default.Rectangle); _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" + GetHashCode()); } } } private event Action _rightClick; /// /// macOS, Windows: Emitted when the tray icon is double clicked. /// [SupportedOSPlatform("macOS")] [SupportedOSPlatform("Windows")] public event Action OnDoubleClick { add { if (_doubleClick == null) { BridgeConnector.Socket.On("tray-double-click" + GetHashCode(), (result) => { var array = result.EnumerateArray().ToArray(); var trayClickEventArgs = array[0].Deserialize(ElectronJsonContext.Default.TrayClickEventArgs); var bounds = array[1].Deserialize(ElectronJsonContext.Default.Rectangle); _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" + GetHashCode()); } } } private event Action _doubleClick; /// /// Windows: Emitted when the tray balloon shows. /// [SupportedOSPlatform("Windows")] public event Action OnBalloonShow { add => AddEvent(value, GetHashCode()); remove => RemoveEvent(value, GetHashCode()); } /// /// Windows: Emitted when the tray balloon is clicked. /// [SupportedOSPlatform("Windows")] public event Action OnBalloonClick { add => AddEvent(value, GetHashCode()); remove => RemoveEvent(value, GetHashCode()); } /// /// Windows: Emitted when the tray balloon is closed /// because of timeout or user manually closes it. /// [SupportedOSPlatform("Windows")] public event Action OnBalloonClosed { add => AddEvent(value, GetHashCode()); remove => RemoveEvent(value, GetHashCode()); } // 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 this.Show(image, new MenuItem[] { menuItem }).ConfigureAwait(false); } /// /// 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, menuItems).ConfigureAwait(false); _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).ConfigureAwait(false); } /// /// Destroys the tray icon immediately. /// public async Task Destroy() { await BridgeConnector.Socket.Emit("tray-destroy").ConfigureAwait(false); _items.Clear(); } /// /// Sets the image associated with this tray icon. /// /// public async Task SetImage(string image) { await BridgeConnector.Socket.Emit("tray-setImage", image).ConfigureAwait(false); } /// /// Sets the image associated with this tray icon when pressed on macOS. /// /// [SupportedOSPlatform("macOS")] public async Task SetPressedImage(string image) { await BridgeConnector.Socket.Emit("tray-setPressedImage", image).ConfigureAwait(false); } /// /// Sets the hover text for this tray icon. /// /// public async Task SetToolTip(string toolTip) { await BridgeConnector.Socket.Emit("tray-setToolTip", toolTip).ConfigureAwait(false); } /// /// macOS: Sets the title displayed aside of the tray icon in the status bar. /// /// [SupportedOSPlatform("macOS")] public async Task SetTitle(string title) { await BridgeConnector.Socket.Emit("tray-setTitle", title).ConfigureAwait(false); } /// /// Windows: Displays a tray balloon. /// /// [SupportedOSPlatform("Windows")] public async Task DisplayBalloon(DisplayBalloonOptions options) { await BridgeConnector.Socket.Emit("tray-displayBalloon", options).ConfigureAwait(false); } /// /// Whether the tray icon is destroyed. /// /// public async Task IsDestroyedAsync() { var tcs = new TaskCompletionSource(); BridgeConnector.Socket.Once("tray-isDestroyedCompleted", tcs.SetResult); await BridgeConnector.Socket.Emit("tray-isDestroyed").ConfigureAwait(false); return await tcs.Task.ConfigureAwait(false); } 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).ConfigureAwait(false); /// /// 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).ConfigureAwait(false); } }