Compare commits

..

12 Commits

Author SHA1 Message Date
Robert Muehsig
a2514ed5bc build script updated 2023-03-27 20:04:37 +02:00
Gregor Biswanger
b453278803 Update to new Electron.NET 23.6.1 2023-03-24 14:16:14 +01:00
Gregor Biswanger
a244382383 Add workaround for web-socket communication 2023-03-24 14:15:55 +01:00
Gregor Biswanger
a82e714ef8 Update Changelog für Electron.NET 23.6.1 2023-03-24 13:27:21 +01:00
Gregor Biswanger
b339485fdc Change signature of PrintToPDFOptions 2023-03-24 13:26:15 +01:00
Gregor Biswanger
551635867d Replace deprecated scroll-touch-events with input-event 2023-03-24 01:50:13 +01:00
Gregor Biswanger
941b8cf5c2 Add vscode dev profiles 2023-03-24 01:47:11 +01:00
Gregor Biswanger
06b01f75da Update Demo App to 23.6.1 2023-03-23 20:53:58 +01:00
Gregor Biswanger
e4b1f6586e Update NPM packages 2023-03-23 20:42:42 +01:00
Gregor Biswanger
0657a274d4 Add socket.io client csharp package 2023-03-23 20:29:08 +01:00
Gregor Biswanger
e3acc79c4f Change to .NET 6 build 2023-03-15 23:26:09 +01:00
Gregor Biswanger
73c1d1cd46 Upgrade to .NET 7 2023-02-24 14:56:40 +01:00
255 changed files with 11677 additions and 12892 deletions

View File

@@ -1,84 +0,0 @@
variables:
PackageVersion: 19.0.9.$(Build.BuildId)
projectAPI: './ElectronNET.API/ElectronNET.API.csproj'
projectCLI: './ElectronNET.CLI/ElectronNET.CLI.csproj'
trigger:
- master
pool:
vmImage: windows-latest
steps:
- checkout: self
submodules: true
fetchDepth: 10
- task: NuGetToolInstaller@1
- task: UseDotNet@2
displayName: 'Use .NET Core sdk'
inputs:
packageType: sdk
version: 6.0.100
installationPath: $(Agent.ToolsDirectory)/dotnet
- task: DotNetCoreCLI@2
displayName: 'restore nuget'
inputs:
command: 'restore'
projects: '$(projectAPI)'
- task: DotNetCoreCLI@2
displayName: 'restore nuget'
inputs:
command: 'restore'
projects: '$(projectCLI)'
- task: DotNetCoreCLI@2
inputs:
command: 'build'
projects: '$(projectAPI)'
arguments: '--configuration Release --force /property:Version=$(PackageVersion)'
- task: DotNetCoreCLI@2
inputs:
command: 'build'
projects: '$(projectCLI)'
arguments: '--configuration Release --force /property:Version=$(PackageVersion)'
- task: DotNetCoreCLI@2
inputs:
command: 'pack'
packagesToPack: '$(projectAPI)'
configuration: 'Release'
versioningScheme: 'off'
buildProperties: 'Version=$(PackageVersion)'
arguments: -IncludeReferencedProjects
- task: DotNetCoreCLI@2
inputs:
command: 'pack'
packagesToPack: '$(projectCLI)'
configuration: 'Release'
versioningScheme: 'off'
buildProperties: 'Version=$(PackageVersion)'
- task: NuGetCommand@2
displayName: 'push API to nuget'
inputs:
command: 'push'
packagesToPush: '$(Build.ArtifactStagingDirectory)/h5.ElectronNET.API.$(PackageVersion).nupkg'
nuGetFeedType: 'external'
publishFeedCredentials: 'nuget-curiosity'
- task: NuGetCommand@2
displayName: 'push CLI to nuget'
inputs:
command: 'push'
packagesToPush: '$(Build.ArtifactStagingDirectory)/h5.ElectronNET.CLI.$(PackageVersion).nupkg'
nuGetFeedType: 'external'
publishFeedCredentials: 'nuget-curiosity'

37
.vscode/tasks.json vendored
View File

@@ -1,21 +1,18 @@
{
"version": "2.0.0",
"command": "dotnet",
"args": [],
"tasks": [
{
"label": "build",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"${workspaceRoot}/ElectronNET.CLI/ElectronNET.CLI.csproj"
],
"problemMatcher": "$msCompile",
"group": {
"_id": "build",
"isDefault": false
}
}
]
}
"version": "2.0.0",
"command": "dotnet",
"args": [],
"tasks": [
{
"label": "build",
"type": "shell",
"command": "dotnet",
"args": ["build", "${workspaceRoot}/ElectronNET.CLI/ElectronNET.CLI.csproj"],
"problemMatcher": "$msCompile",
"group": {
"_id": "build",
"isDefault": false
}
}
]
}

View File

@@ -1,16 +0,0 @@
{
"version": "0.1.0",
"command": "dotnet",
"isShellCommand": true,
"args": [],
"tasks": [
{
"taskName": "build",
"args": [
"${workspaceRoot}/ElectronNET.CLI/ElectronNET.CLI.csproj"
],
"isBuildCommand": true,
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -1,29 +1,22 @@
# Not released
# 18.6.1
ElectronNET.CLI:
* New Feature: Support for additional dotnet publish flags (thanks [danatcofo](https://github.com/danatcofo)) [\#655](https://github.com/ElectronNET/Electron.NET/pull/655)
* New Feature: Support Apple Silicon Natively (thanks [bman46](https://github.com/bman46)) [\#624](https://github.com/ElectronNET/Electron.NET/pull/624)
ElectronNET.API:
* New Feature: Support for .NET 6 (thanks [danatcofo](https://github.com/danatcofo)) [\#636](https://github.com/ElectronNET/Electron.NET/pull/636)
* New Feature: Switch to async socket lib (thanks [theolivenbaum](https://github.com/theolivenbaum)) [\#595](https://github.com/ElectronNET/Electron.NET/pull/595)
* New Feature: Conversion to use ImageSharp rather than System.Drawing.Common (thanks [danatcofo](https://github.com/danatcofo)) [\#658](https://github.com/ElectronNET/Electron.NET/pull/658)
* New Feature: Support DI and Mocking better + Support launching app with file for win and linux (thanks [danatcofo](https://github.com/danatcofo)) [\#656](https://github.com/ElectronNET/Electron.NET/pull/656)
* New Feature: Support launching app with file for win and linux (thanks [schaveyt](https://github.com/schaveyt)) [\#648](https://github.com/ElectronNET/Electron.NET/pull/648)
* New Feature: Add ability to set a window's parent using BrowserWindowOptions (thanks [MutatedGamer](https://github.com/MutatedGamer)) [\#673](https://github.com/ElectronNET/Electron.NET/pull/673)
* New Feature: changed the processing of loadUrl at CreateWindowAsync (thanks [yannikHoeflich](https://github.com/yannikHoeflich)) [\#631](https://github.com/ElectronNET/Electron.NET/pull/631)
* New Feature: Recent Document Support for MacOS (thanks [danatcofo](https://github.com/danatcofo)) [\#634](https://github.com/ElectronNET/Electron.NET/pull/634)
* New Feature: Support DI and Mocking better (thanks [danatcofo](https://github.com/danatcofo)) [\#633](https://github.com/ElectronNET/Electron.NET/pull/633)
* New Feature: Allow ignoring certificate errors (thanks [javierlarota](https://github.com/javierlarota)) [\#626](https://github.com/ElectronNET/Electron.NET/pull/626)
* New Feature: Log errors in the dotnet process (thanks [Meberem](https://github.com/Meberem)) [\#592](https://github.com/ElectronNET/Electron.NET/pull/592)
* Fixed bug: Error on reloading a window after a second window is closed #664 (thanks [danatcofo](https://github.com/danatcofo)) [\#668](https://github.com/ElectronNET/Electron.NET/pull/668)
# 23.6.2
# Released
# 23.6.1
ElectronNET.CLI:
* New Feature: Upgrade to .NET 6 support
ElectronNET.API:
* New Feature: Native Electron 23.2.0 support, but not all new API features included (we search contributors)
* New Feature: Upgrade to .NET 6 support
* New Feature: Changed Web-Socket .NET Library to [SocketIOClient](https://github.com/doghappy/socket.io-client-csharp)
* Breaking Changes: We removed deprecated API events/methods from ElectronNET.API [(More Details)](https://www.electronjs.org/docs/latest/breaking-changes)
# 13.5.1
ElectronNET.CLI:

808
ElectronNET.API/App.cs Executable file → Normal file

File diff suppressed because it is too large Load Diff

331
ElectronNET.API/AutoUpdater.cs Executable file → Normal file
View File

@@ -5,52 +5,39 @@ using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ElectronNET.API.Interfaces;
using ElectronNET.API;
namespace ElectronNET.API
{
/// <summary>
/// Enable apps to automatically update themselves. Based on electron-updater.
/// </summary>
public sealed class AutoUpdater : IAutoUpdater
public sealed class AutoUpdater
{
/// <summary>
/// Whether to automatically download an update when it is found. (Default is true)
/// </summary>
public Task<bool> IsAutoDownloadEnabledAsync => BridgeConnector.OnResult<bool>("autoUpdater-autoDownload-get", "autoUpdater-autoDownload-get-reply");
/// <summary>
/// Whether to automatically install a downloaded update on app quit (if `QuitAndInstall` was not called before).
///
/// Applicable only on Windows and Linux.
/// </summary>
public Task<bool> IsAutoInstallOnAppQuitEnabledAsync => BridgeConnector.OnResult<bool>("autoUpdater-autoInstallOnAppQuit-get", "autoUpdater-autoInstallOnAppQuit-get-reply");
/// <summary>
/// *GitHub provider only.* Whether to allow update to pre-release versions.
/// Defaults to "true" if application version contains prerelease components (e.g. "0.12.1-alpha.1", here "alpha" is a prerelease component), otherwise "false".
///
/// If "true", downgrade will be allowed("allowDowngrade" will be set to "true").
/// </summary>
public Task<bool> IsAllowPrereleaseEnabledAsync => BridgeConnector.OnResult<bool>("autoUpdater-allowPrerelease-get", "autoUpdater-allowPrerelease-get-reply");
/// <summary>
/// *GitHub provider only.*
/// Get all release notes (from current version to latest), not just the latest (Default is false).
/// </summary>
public Task<bool> IsFullChangeLogEnabledAsync => BridgeConnector.OnResult<bool>("autoUpdater-fullChangelog-get", "autoUpdater-fullChangelog-get-reply");
public Task<bool> IsAllowDowngradeEnabledAsync => BridgeConnector.OnResult<bool>("autoUpdater-allowDowngrade-get", "autoUpdater-allowDowngrade-get-reply");
/// <summary>
/// Whether to automatically download an update when it is found. (Default is true)
/// </summary>
public bool AutoDownload
{
get
{
return Task.Run<bool>(() =>
{
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.Socket.On("autoUpdater-autoDownload-get-reply", (result) =>
{
BridgeConnector.Socket.Off("autoUpdater-autoDownload-get-reply");
taskCompletionSource.SetResult((bool)result);
});
BridgeConnector.Socket.Emit("autoUpdater-autoDownload-get");
return taskCompletionSource.Task;
}).Result;
}
set
{
BridgeConnector.Emit("autoUpdater-autoDownload-set", value);
BridgeConnector.Socket.Emit("autoUpdater-autoDownload-set", value);
}
}
@@ -61,9 +48,26 @@ namespace ElectronNET.API
/// </summary>
public bool AutoInstallOnAppQuit
{
get
{
return Task.Run<bool>(() =>
{
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.Socket.On("autoUpdater-autoInstallOnAppQuit-get-reply", (result) =>
{
BridgeConnector.Socket.Off("autoUpdater-autoInstallOnAppQuit-get-reply");
taskCompletionSource.SetResult((bool)result);
});
BridgeConnector.Socket.Emit("autoUpdater-autoInstallOnAppQuit-get");
return taskCompletionSource.Task;
}).Result;
}
set
{
BridgeConnector.Emit("autoUpdater-autoInstallOnAppQuit-set", value);
BridgeConnector.Socket.Emit("autoUpdater-autoInstallOnAppQuit-set", value);
}
}
@@ -75,9 +79,26 @@ namespace ElectronNET.API
/// </summary>
public bool AllowPrerelease
{
get
{
return Task.Run<bool>(() =>
{
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.Socket.On("autoUpdater-allowPrerelease-get-reply", (result) =>
{
BridgeConnector.Socket.Off("autoUpdater-allowPrerelease-get-reply");
taskCompletionSource.SetResult((bool)result);
});
BridgeConnector.Socket.Emit("autoUpdater-allowPrerelease-get");
return taskCompletionSource.Task;
}).Result;
}
set
{
BridgeConnector.Emit("autoUpdater-allowPrerelease-set", value);
BridgeConnector.Socket.Emit("autoUpdater-allowPrerelease-set", value);
}
}
@@ -87,9 +108,26 @@ namespace ElectronNET.API
/// </summary>
public bool FullChangelog
{
get
{
return Task.Run<bool>(() =>
{
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.Socket.On("autoUpdater-fullChangelog-get-reply", (result) =>
{
BridgeConnector.Socket.Off("autoUpdater-fullChangelog-get-reply");
taskCompletionSource.SetResult((bool)result);
});
BridgeConnector.Socket.Emit("autoUpdater-fullChangelog-get");
return taskCompletionSource.Task;
}).Result;
}
set
{
BridgeConnector.Emit("autoUpdater-fullChangelog-set", value);
BridgeConnector.Socket.Emit("autoUpdater-fullChangelog-set", value);
}
}
@@ -100,32 +138,137 @@ namespace ElectronNET.API
/// </summary>
public bool AllowDowngrade
{
get
{
return Task.Run<bool>(() =>
{
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.Socket.On("autoUpdater-allowDowngrade-get-reply", (result) =>
{
BridgeConnector.Socket.Off("autoUpdater-allowDowngrade-get-reply");
taskCompletionSource.SetResult((bool)result);
});
BridgeConnector.Socket.Emit("autoUpdater-allowDowngrade-get");
return taskCompletionSource.Task;
}).Result;
}
set
{
BridgeConnector.Emit("autoUpdater-allowDowngrade-set", value);
BridgeConnector.Socket.Emit("autoUpdater-allowDowngrade-set", value);
}
}
/// <summary>
/// For test only.
/// </summary>
public Task<string> GetUpdateConfigPathAsync => BridgeConnector.OnResult<string>("autoUpdater-updateConfigPath-get", "autoUpdater-updateConfigPath-get-reply");
public string UpdateConfigPath
{
get
{
return Task.Run<string>(() =>
{
var taskCompletionSource = new TaskCompletionSource<string>();
BridgeConnector.Socket.On("autoUpdater-updateConfigPath-get-reply", (result) =>
{
BridgeConnector.Socket.Off("autoUpdater-updateConfigPath-get-reply");
taskCompletionSource.SetResult(result.ToString());
});
BridgeConnector.Socket.Emit("autoUpdater-updateConfigPath-get");
return taskCompletionSource.Task;
}).Result;
}
}
/// <summary>
/// The current application version
/// </summary>
public Task<SemVer> GetCurrentVersionAsync => BridgeConnector.OnResult<SemVer>("autoUpdater-updateConcurrentVersionfigPath-get", "autoUpdater-currentVersion-get-reply");
public Task<SemVer> CurrentVersionAsync
{
get
{
return Task.Run<SemVer>(() =>
{
var taskCompletionSource = new TaskCompletionSource<SemVer>();
BridgeConnector.Socket.On("autoUpdater-currentVersion-get-reply", (result) =>
{
BridgeConnector.Socket.Off("autoUpdater-currentVersion-get-reply");
SemVer version = ((JObject)result).ToObject<SemVer>();
taskCompletionSource.SetResult(version);
});
BridgeConnector.Socket.Emit("autoUpdater-currentVersion-get");
return taskCompletionSource.Task;
});
}
}
/// <summary>
/// Get the update channel. Not applicable for GitHub.
/// Doesnt return channel from the update configuration, only if was previously set.
/// </summary>
public Task<string> GetChannelAsync => BridgeConnector.OnResult<string>("autoUpdater-channel-get", "autoUpdater-channel-get-reply");
[Obsolete("Use the asynchronous version ChannelAsync instead")]
public string Channel
{
get
{
return ChannelAsync.Result;
}
}
/// <summary>
/// Get the update channel. Not applicable for GitHub.
/// Doesnt return channel from the update configuration, only if was previously set.
/// </summary>
public Task<string> ChannelAsync
{
get
{
return Task.Run<string>(() =>
{
var taskCompletionSource = new TaskCompletionSource<string>();
BridgeConnector.Socket.On("autoUpdater-channel-get-reply", (result) =>
{
BridgeConnector.Socket.Off("autoUpdater-channel-get-reply");
taskCompletionSource.SetResult(result.ToString());
});
BridgeConnector.Socket.Emit("autoUpdater-channel-get");
return taskCompletionSource.Task;
});
}
}
/// <summary>
/// The request headers.
/// </summary>
public Task<Dictionary<string, string>> GetRequestHeadersAsync => BridgeConnector.OnResult<Dictionary<string, string>>("autoUpdater-requestHeaders-get", "autoUpdater-requestHeaders-get-reply");
public Task<Dictionary<string, string>> RequestHeadersAsync
{
get
{
return Task.Run(() =>
{
var taskCompletionSource = new TaskCompletionSource<Dictionary<string, string>>();
BridgeConnector.Socket.On("autoUpdater-requestHeaders-get-reply", (headers) =>
{
BridgeConnector.Socket.Off("autoUpdater-requestHeaders-get-reply");
Dictionary<string, string> result = ((JObject)headers).ToObject<Dictionary<string, string>>();
taskCompletionSource.SetResult(result);
});
BridgeConnector.Socket.Emit("autoUpdater-requestHeaders-get");
return taskCompletionSource.Task;
});
}
}
/// <summary>
/// The request headers.
@@ -134,7 +277,7 @@ namespace ElectronNET.API
{
set
{
BridgeConnector.Emit("autoUpdater-requestHeaders-set", value);
BridgeConnector.Socket.Emit("autoUpdater-requestHeaders-set", JObject.FromObject(value, _jsonSerializer));
}
}
@@ -147,12 +290,12 @@ namespace ElectronNET.API
{
if (_error == null)
{
BridgeConnector.On<string>("autoUpdater-error" + GetHashCode(), (message) =>
BridgeConnector.Socket.On("autoUpdater-error" + GetHashCode(), (message) =>
{
_error(message.ToString());
});
BridgeConnector.Emit("register-autoUpdater-error-event", GetHashCode());
BridgeConnector.Socket.Emit("register-autoUpdater-error-event", GetHashCode());
}
_error += value;
}
@@ -161,7 +304,7 @@ namespace ElectronNET.API
_error -= value;
if (_error == null)
BridgeConnector.Off("autoUpdater-error" + GetHashCode());
BridgeConnector.Socket.Off("autoUpdater-error" + GetHashCode());
}
}
@@ -176,12 +319,12 @@ namespace ElectronNET.API
{
if (_checkingForUpdate == null)
{
BridgeConnector.On("autoUpdater-checking-for-update" + GetHashCode(), () =>
BridgeConnector.Socket.On("autoUpdater-checking-for-update" + GetHashCode(), () =>
{
_checkingForUpdate();
});
BridgeConnector.Emit("register-autoUpdater-checking-for-update-event", GetHashCode());
BridgeConnector.Socket.Emit("register-autoUpdater-checking-for-update-event", GetHashCode());
}
_checkingForUpdate += value;
}
@@ -190,7 +333,7 @@ namespace ElectronNET.API
_checkingForUpdate -= value;
if (_checkingForUpdate == null)
BridgeConnector.Off("autoUpdater-checking-for-update" + GetHashCode());
BridgeConnector.Socket.Off("autoUpdater-checking-for-update" + GetHashCode());
}
}
@@ -206,12 +349,12 @@ namespace ElectronNET.API
{
if (_updateAvailable == null)
{
BridgeConnector.On<UpdateInfo>("autoUpdater-update-available" + GetHashCode(), (updateInfo) =>
BridgeConnector.Socket.On("autoUpdater-update-available" + GetHashCode(), (updateInfo) =>
{
_updateAvailable(updateInfo);
_updateAvailable(JObject.Parse(updateInfo.ToString()).ToObject<UpdateInfo>());
});
BridgeConnector.Emit("register-autoUpdater-update-available-event", GetHashCode());
BridgeConnector.Socket.Emit("register-autoUpdater-update-available-event", GetHashCode());
}
_updateAvailable += value;
}
@@ -220,7 +363,7 @@ namespace ElectronNET.API
_updateAvailable -= value;
if (_updateAvailable == null)
BridgeConnector.Off("autoUpdater-update-available" + GetHashCode());
BridgeConnector.Socket.Off("autoUpdater-update-available" + GetHashCode());
}
}
@@ -235,12 +378,12 @@ namespace ElectronNET.API
{
if (_updateNotAvailable == null)
{
BridgeConnector.On<UpdateInfo>("autoUpdater-update-not-available" + GetHashCode(), (updateInfo) =>
BridgeConnector.Socket.On("autoUpdater-update-not-available" + GetHashCode(), (updateInfo) =>
{
_updateNotAvailable(updateInfo);
_updateNotAvailable(JObject.Parse(updateInfo.ToString()).ToObject<UpdateInfo>());
});
BridgeConnector.Emit("register-autoUpdater-update-not-available-event", GetHashCode());
BridgeConnector.Socket.Emit("register-autoUpdater-update-not-available-event", GetHashCode());
}
_updateNotAvailable += value;
}
@@ -249,7 +392,7 @@ namespace ElectronNET.API
_updateNotAvailable -= value;
if (_updateNotAvailable == null)
BridgeConnector.Off("autoUpdater-update-not-available" + GetHashCode());
BridgeConnector.Socket.Off("autoUpdater-update-not-available" + GetHashCode());
}
}
@@ -264,12 +407,12 @@ namespace ElectronNET.API
{
if (_downloadProgress == null)
{
BridgeConnector.On<ProgressInfo>("autoUpdater-download-progress" + GetHashCode(), (progressInfo) =>
BridgeConnector.Socket.On("autoUpdater-download-progress" + GetHashCode(), (progressInfo) =>
{
_downloadProgress(progressInfo);
_downloadProgress(JObject.Parse(progressInfo.ToString()).ToObject<ProgressInfo>());
});
BridgeConnector.Emit("register-autoUpdater-download-progress-event", GetHashCode());
BridgeConnector.Socket.Emit("register-autoUpdater-download-progress-event", GetHashCode());
}
_downloadProgress += value;
}
@@ -278,7 +421,7 @@ namespace ElectronNET.API
_downloadProgress -= value;
if (_downloadProgress == null)
BridgeConnector.Off("autoUpdater-download-progress" + GetHashCode());
BridgeConnector.Socket.Off("autoUpdater-download-progress" + GetHashCode());
}
}
@@ -293,12 +436,12 @@ namespace ElectronNET.API
{
if (_updateDownloaded == null)
{
BridgeConnector.On<UpdateInfo>("autoUpdater-update-downloaded" + GetHashCode(), (updateInfo) =>
BridgeConnector.Socket.On("autoUpdater-update-downloaded" + GetHashCode(), (updateInfo) =>
{
_updateDownloaded(updateInfo);
_updateDownloaded(JObject.Parse(updateInfo.ToString()).ToObject<UpdateInfo>());
});
BridgeConnector.Emit("register-autoUpdater-update-downloaded-event", GetHashCode());
BridgeConnector.Socket.Emit("register-autoUpdater-update-downloaded-event", GetHashCode());
}
_updateDownloaded += value;
}
@@ -307,14 +450,14 @@ namespace ElectronNET.API
_updateDownloaded -= value;
if (_updateDownloaded == null)
BridgeConnector.Off("autoUpdater-update-downloaded" + GetHashCode());
BridgeConnector.Socket.Off("autoUpdater-update-downloaded" + GetHashCode());
}
}
private event Action<UpdateInfo> _updateDownloaded;
private static AutoUpdater _autoUpdater;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal AutoUpdater() { }
@@ -343,33 +486,33 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<UpdateCheckResult> CheckForUpdatesAsync()
{
var taskCompletionSource = new TaskCompletionSource<UpdateCheckResult>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<UpdateCheckResult>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<UpdateCheckResult>("autoUpdaterCheckForUpdatesComplete" + guid, (updateCheckResult) =>
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesComplete" + guid, (updateCheckResult) =>
{
try
{
BridgeConnector.Off("autoUpdaterCheckForUpdatesComplete" + guid);
BridgeConnector.Off("autoUpdaterCheckForUpdatesError" + guid);
taskCompletionSource.SetResult(updateCheckResult);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesError" + guid);
taskCompletionSource.SetResult(JObject.Parse(updateCheckResult.ToString()).ToObject<UpdateCheckResult>());
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
BridgeConnector.On<string>("autoUpdaterCheckForUpdatesError" + guid, (error) =>
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesError" + guid, (error) =>
{
BridgeConnector.Off("autoUpdaterCheckForUpdatesComplete" + guid);
BridgeConnector.Off("autoUpdaterCheckForUpdatesError" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesError" + guid);
string message = "An error occurred in CheckForUpdatesAsync";
if (error != null && !string.IsNullOrEmpty(error.ToString()))
message = JsonConvert.SerializeObject(error);
taskCompletionSource.SetException(new Exception(message));
});
BridgeConnector.Emit("autoUpdaterCheckForUpdates", guid);
BridgeConnector.Socket.Emit("autoUpdaterCheckForUpdates", guid);
return taskCompletionSource.Task;
}
@@ -382,36 +525,36 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<UpdateCheckResult> CheckForUpdatesAndNotifyAsync()
{
var taskCompletionSource = new TaskCompletionSource<UpdateCheckResult>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<UpdateCheckResult>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<UpdateCheckResult>("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid, (updateCheckResult) =>
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid, (updateCheckResult) =>
{
try
{
BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
if (updateCheckResult == null)
taskCompletionSource.SetResult(null);
else
taskCompletionSource.SetResult(updateCheckResult);
taskCompletionSource.SetResult(JObject.Parse(updateCheckResult.ToString()).ToObject<UpdateCheckResult>());
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
BridgeConnector.On<string>("autoUpdaterCheckForUpdatesAndNotifyError" + guid, (error) =>
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesAndNotifyError" + guid, (error) =>
{
BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
string message = "An error occurred in autoUpdaterCheckForUpdatesAndNotify";
if (error != null)
message = JsonConvert.SerializeObject(error);
taskCompletionSource.SetException(new Exception(message));
});
BridgeConnector.Emit("autoUpdaterCheckForUpdatesAndNotify", guid);
BridgeConnector.Socket.Emit("autoUpdaterCheckForUpdatesAndNotify", guid);
return taskCompletionSource.Task;
}
@@ -427,8 +570,7 @@ namespace ElectronNET.API
/// <param name="isForceRunAfter">Run the app after finish even on silent install. Not applicable for macOS. Ignored if `isSilent` is set to `false`.</param>
public void QuitAndInstall(bool isSilent = false, bool isForceRunAfter = false)
{
BridgeConnector.EmitSync("prepare-for-update");
BridgeConnector.EmitSync("autoUpdaterQuitAndInstall", isSilent, isForceRunAfter);
BridgeConnector.Socket.Emit("autoUpdaterQuitAndInstall", isSilent, isForceRunAfter);
}
/// <summary>
@@ -437,16 +579,16 @@ namespace ElectronNET.API
/// <returns>Path to downloaded file.</returns>
public Task<string> DownloadUpdateAsync()
{
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<string>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<string>("autoUpdaterDownloadUpdateComplete" + guid, (downloadedPath) =>
BridgeConnector.Socket.On("autoUpdaterDownloadUpdateComplete" + guid, (downloadedPath) =>
{
BridgeConnector.Off("autoUpdaterDownloadUpdateComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterDownloadUpdateComplete" + guid);
taskCompletionSource.SetResult(downloadedPath.ToString());
});
BridgeConnector.Emit("autoUpdaterDownloadUpdate", guid);
BridgeConnector.Socket.Emit("autoUpdaterDownloadUpdate", guid);
return taskCompletionSource.Task;
}
@@ -457,18 +599,23 @@ namespace ElectronNET.API
/// <returns>Feed URL.</returns>
public Task<string> GetFeedURLAsync()
{
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<string>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<string>("autoUpdaterGetFeedURLComplete" + guid, (downloadedPath) =>
BridgeConnector.Socket.On("autoUpdaterGetFeedURLComplete" + guid, (downloadedPath) =>
{
BridgeConnector.Off("autoUpdaterGetFeedURLComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterGetFeedURLComplete" + guid);
taskCompletionSource.SetResult(downloadedPath.ToString());
});
BridgeConnector.Emit("autoUpdaterGetFeedURL", guid);
BridgeConnector.Socket.Emit("autoUpdaterGetFeedURL", guid);
return taskCompletionSource.Task;
}
private readonly JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
}
}

View File

@@ -1,550 +1,32 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Nito.AsyncEx;
using SocketIOClient;
using SocketIOClient.Newtonsoft.Json;
namespace ElectronNET.API
namespace ElectronNET.API
{
internal static class BridgeConnector
{
internal static class EventTasks<T>
{
//Although SocketIO already manage event handlers, we need to manage this here as well for the OnResult calls,
//because SocketIO will simply replace the existing event handler on every call to On(key, ...) , which means there is
//a race condition between On / Off calls that can lead to tasks deadlocking forever without ever triggering their On handler
private static SocketIoFacade _socket;
private static readonly object SyncRoot = new();
private static readonly Dictionary<string, TaskCompletionSource<T>> _taskCompletionSources = new();
private static readonly Dictionary<string, string> _eventKeys = new();
private static readonly object _lock = new();
/// <summary>
/// Get or add a new TaskCompletionSource<typeparamref name="T"/> for a given event key
/// </summary>
/// <param name="key"></param>
/// <param name="eventKey"></param>
/// <param name="taskCompletionSource"></param>
/// <param name="waitThisFirstAndThenTryAgain"></param>
/// <returns>Returns true if a new TaskCompletionSource<typeparamref name="T"/> was added to the dictionary</returns>
internal static bool TryGetOrAdd(string key, string eventKey, out TaskCompletionSource<T> taskCompletionSource, out Task waitThisFirstAndThenTryAgain)
{
lock (_lock)
{
if (!_taskCompletionSources.TryGetValue(key, out taskCompletionSource))
{
taskCompletionSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
_taskCompletionSources[key] = taskCompletionSource;
_eventKeys[key] = eventKey;
waitThisFirstAndThenTryAgain = null;
return true; //Was added, so we need to also register the socket events
}
if (_eventKeys.TryGetValue(key, out var existingEventKey) && existingEventKey == eventKey)
{
waitThisFirstAndThenTryAgain = null;
return false; //No need to register the socket events twice
}
waitThisFirstAndThenTryAgain = taskCompletionSource.Task; //Will need to try again after the previous existing one is done
taskCompletionSource = null;
return true; //Need to register the socket events, but must first await the previous task to complete
}
}
/// <summary>
/// Clean up the TaskCompletionSource<typeparamref name="T"/> from the dictionary if and only if it is the same as the passed argument
/// </summary>
/// <param name="key"></param>
/// <param name="eventKey"></param>
/// <param name="taskCompletionSource"></param>
internal static void DoneWith(string key, string eventKey, TaskCompletionSource<T> taskCompletionSource)
{
lock (_lock)
{
if (_taskCompletionSources.TryGetValue(key, out var existingTaskCompletionSource)
&& ReferenceEquals(existingTaskCompletionSource, taskCompletionSource))
{
_taskCompletionSources.Remove(key);
}
if (_eventKeys.TryGetValue(key, out var existingEventKey) && existingEventKey == eventKey)
{
_eventKeys.Remove(key);
}
}
}
}
private static SocketIO _socket;
private static readonly object _syncRoot = new();
private static readonly SemaphoreSlim _socketSemaphoreEmit = new(1, 1);
private static readonly SemaphoreSlim _socketSemaphoreHandlers = new(1, 1);
private static AsyncManualResetEvent _connectedSocketEvent = new AsyncManualResetEvent();
private static Dictionary<string, Action<SocketIOResponse>> _eventHandlers = new();
private static Task<SocketIO> _waitForConnection
public static SocketIoFacade Socket
{
get
{
EnsureSocketTaskIsCreated();
return GetSocket();
}
}
private static async Task<SocketIO> GetSocket()
{
await _connectedSocketEvent.WaitAsync();
return _socket;
}
public static bool IsConnected => _waitForConnection is Task task && task.IsCompletedSuccessfully;
public static void Emit(string eventString, params object[] args)
{
//We don't care about waiting for the event to be emitted, so this doesn't need to be async
Task.Run(() => EmitAsync(eventString, args));
}
private static async Task EmitAsync(string eventString, object[] args)
{
if (App.SocketDebug)
{
Log("Sending event {0}", eventString);
}
var socket = await _waitForConnection;
await _socketSemaphoreEmit.WaitAsync();
try
{
await socket.EmitAsync(eventString, args);
}
finally
{
_socketSemaphoreEmit.Release();
}
if (App.SocketDebug)
{
Log($"Sent event {eventString}");
}
}
/// <summary>
/// This method is only used on places where we need to be sure the event was sent on the socket, such as Quit, Exit, Relaunch and QuitAndInstall methods
/// </summary>
/// <param name="eventString"></param>
/// <param name="args"></param>
internal static void EmitSync(string eventString, params object[] args)
{
if (App.SocketDebug)
{
Log("Sending event {0}", eventString);
}
Task.Run(async () =>
{
var socket = await _waitForConnection;
try
if (_socket == null)
{
await _socketSemaphoreEmit.WaitAsync();
await socket.EmitAsync(eventString, args);
}
finally
{
_socketSemaphoreEmit.Release();
}
}).Wait();
if (App.SocketDebug)
{
Log("Sent event {0}", eventString);
}
}
public static void Off(string eventString)
{
EnsureSocketTaskIsCreated();
_socketSemaphoreHandlers.Wait();
try
{
if (_eventHandlers.ContainsKey(eventString))
{
_eventHandlers.Remove(eventString);
}
_socket.Off(eventString);
}
finally
{
_socketSemaphoreHandlers.Release();
}
}
public static void On(string eventString, Action fn)
{
EnsureSocketTaskIsCreated();
_socketSemaphoreHandlers.Wait();
try
{
if (_eventHandlers.ContainsKey(eventString))
{
_eventHandlers.Remove(eventString);
}
_eventHandlers.Add(eventString, _ =>
{
try
lock (SyncRoot)
{
fn();
}
catch (Exception E)
{
LogError(E, "Error running handler for event {0}", eventString);
}
});
_socket.On(eventString, _eventHandlers[eventString]);
}
finally
{
_socketSemaphoreHandlers.Release();
}
}
public static void On<T>(string eventString, Action<T> fn)
{
EnsureSocketTaskIsCreated();
_socketSemaphoreHandlers.Wait();
try
{
if (_eventHandlers.ContainsKey(eventString))
{
_eventHandlers.Remove(eventString);
}
_eventHandlers.Add(eventString, o =>
{
try
{
fn(o.GetValue<T>(0));
}
catch (Exception E)
{
LogError(E, "Error running handler for event {0}", eventString);
}
});
_socket.On(eventString, _eventHandlers[eventString]);
}
finally
{
_socketSemaphoreHandlers.Release();
}
}
private static void RehookHandlers(SocketIO newSocket)
{
_socketSemaphoreHandlers.Wait();
try
{
foreach (var kv in _eventHandlers)
{
newSocket.On(kv.Key, kv.Value);
}
}
finally
{
_socketSemaphoreHandlers.Release();
}
}
public static void Once<T>(string eventString, Action<T> fn)
{
On<T>(eventString, (o) =>
{
Off(eventString);
fn(o);
});
}
public static async Task<T> OnResult<T>(string triggerEvent, string completedEvent, params object[] args)
{
string eventKey = completedEvent;
if (args is object && args.Length > 0) // If there are arguments passed, we generate a unique event key with the arguments
// this allow us to wait for previous events first before registering new ones
{
var hash = new HashCode();
foreach (var obj in args)
{
hash.Add(obj);
}
eventKey = $"{eventKey}-{(uint)hash.ToHashCode()}";
}
if (EventTasks<T>.TryGetOrAdd(completedEvent, eventKey, out var taskCompletionSource, out var waitThisFirstAndThenTryAgain))
{
if (waitThisFirstAndThenTryAgain is object)
{
//There was a pending call with different parameters, so we need to wait that first and then call here again
try
{
await waitThisFirstAndThenTryAgain;
}
catch
{
//Ignore any exceptions here so we can set a new event below
//The exception will also be visible to the original first caller due to taskCompletionSource.Task
}
//Try again to set the event
return await OnResult<T>(triggerEvent, completedEvent, args);
}
else
{
//A new TaskCompletionSource was added, so we need to register the completed event here
On<T>(completedEvent, (result) =>
{
Off(completedEvent);
taskCompletionSource.SetResult(result);
EventTasks<T>.DoneWith(completedEvent, eventKey, taskCompletionSource);
});
await EmitAsync(triggerEvent, args);
}
}
return await taskCompletionSource.Task;
}
public static async Task<T> OnResult<T>(string triggerEvent, string completedEvent, CancellationToken cancellationToken, params object[] args)
{
string eventKey = completedEvent;
if (args is object && args.Length > 0) // If there are arguments passed, we generate a unique event key with the arguments
// this allow us to wait for previous events first before registering new ones
{
var hash = new HashCode();
foreach (var obj in args)
{
hash.Add(obj);
}
eventKey = $"{eventKey}-{(uint)hash.ToHashCode()}";
}
if (EventTasks<T>.TryGetOrAdd(completedEvent, eventKey, out var taskCompletionSource, out var waitThisFirstAndThenTryAgain))
{
if (waitThisFirstAndThenTryAgain is object)
{
//There was a pending call with different parameters, so we need to wait that first and then call here again
try
{
await Task.Run(() => waitThisFirstAndThenTryAgain, cancellationToken);
}
catch
{
//Ignore any exceptions here so we can set a new event below
//The exception will also be visible to the original first caller due to taskCompletionSource.Task
}
//Try again to set the event
return await OnResult<T>(triggerEvent, completedEvent, cancellationToken, args);
}
else
{
using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled()))
{
//A new TaskCompletionSource was added, so we need to register the completed event here
On<T>(completedEvent, (result) =>
if (_socket == null)
{
Off(completedEvent);
taskCompletionSource.SetResult(result);
EventTasks<T>.DoneWith(completedEvent, eventKey, taskCompletionSource);
});
Emit(triggerEvent, args);
}
}
}
string socketUrl = HybridSupport.IsElectronActive
? $"http://localhost:{BridgeSettings.SocketPort}"
: "http://localhost";
return await taskCompletionSource.Task;
}
internal static void Log(string formatString, params object[] args)
{
if (Logger is object)
{
Logger.LogInformation(formatString, args);
}
else
{
Console.WriteLine(formatString, args);
}
}
internal static void LogError(Exception E, string formatString, params object[] args)
{
if (Logger is object)
{
Logger.LogError(E, formatString, args);
}
else
{
Console.WriteLine(formatString, args);
Console.WriteLine(E.ToString());
}
}
private static Thread _backgroundMonitorThread;
private static void EnsureSocketTaskIsCreated()
{
if (_socket is null)
{
if (string.IsNullOrWhiteSpace(AuthKey))
{
throw new Exception("You must call Electron.ReadAuth() first thing on your main entry point.");
}
if (HybridSupport.IsElectronActive)
{
lock (_syncRoot)
{
if (_socket is null)
{
if (HybridSupport.IsElectronActive)
{
var socket = new SocketIO($"http://localhost:{BridgeSettings.SocketPort}", new SocketIOOptions()
{
EIO = 4,
Reconnection = true,
ReconnectionAttempts = int.MaxValue,
ReconnectionDelay = 500,
ReconnectionDelayMax = 2000,
RandomizationFactor = 0.5,
ConnectionTimeout = TimeSpan.FromSeconds(10),
Transport = SocketIOClient.Transport.TransportProtocol.WebSocket
});
socket.JsonSerializer = new CamelCaseNewtonsoftJsonSerializer();
_connectedSocketEvent.Reset();
socket.OnConnected += (_, __) =>
{
Task.Run(async () =>
{
await socket.EmitAsync("auth", AuthKey);
_connectedSocketEvent.Set();
Log("ElectronNET socket {1} connected on port {0}!", BridgeSettings.SocketPort, socket.Id);
});
};
socket.OnReconnectAttempt += (_, __) =>
{
_connectedSocketEvent.Reset();
Log("ElectronNET socket {1} is trying to reconnect on port {0}...", BridgeSettings.SocketPort, socket.Id);
};
socket.OnReconnectError += (_, ex) =>
{
_connectedSocketEvent.Reset();
Log("ElectronNET socket {1} failed to connect {0}", ex, socket.Id);
};
socket.OnReconnectFailed += (_, ex) =>
{
_connectedSocketEvent.Reset();
Log("ElectronNET socket {1} failed to reconnect {0}", ex, socket.Id);
};
socket.OnReconnected += (_, __) =>
{
_connectedSocketEvent.Set();
Log("ElectronNET socket {1} reconnected on port {0}...", BridgeSettings.SocketPort, socket.Id);
};
socket.OnDisconnected += (_, reason) =>
{
_connectedSocketEvent.Reset();
Log("ElectronNET socket {2} disconnected with reason {0}, trying to reconnect on port {1}!", reason, BridgeSettings.SocketPort, socket.Id);
};
socket.OnError += (_, msg) =>
{
//_connectedSocketEvent.Reset();
Log("ElectronNET socket {1} error: {0}...", msg, socket.Id);
};
_socket = socket;
Task.Run(async () =>
{
try
{
await socket.ConnectAsync();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
if (!App.TryRaiseOnSocketConnectFail())
{
Environment.Exit(0xDEAD);
}
}
});
RehookHandlers(socket);
}
else
{
throw new Exception("Missing Socket Port");
}
_socket = new SocketIoFacade(socketUrl);
_socket.Connect();
}
}
}
else
{
throw new Exception("Missing Socket Port");
}
}
}
internal static ILogger<App> Logger { private get; set; }
internal static string AuthKey { get; set; } = null;
private class CamelCaseNewtonsoftJsonSerializer : NewtonsoftJsonSerializer
{
public CamelCaseNewtonsoftJsonSerializer() : base()
{
OptionsProvider = () => new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
};
return _socket;
}
}
}

View File

@@ -20,16 +20,5 @@
/// The web port.
/// </value>
public static string WebPort { get; internal set; }
/// <summary>
/// Manually set the port values instead of using the UseElectron extension method
/// </summary>
/// <param name="socketPort"></param>
/// <param name="webPort"></param>
public static void InitializePorts(int socketPort, int webPort)
{
SocketPort = socketPort.ToString();
WebPort = webPort.ToString();
}
}
}

View File

@@ -31,15 +31,29 @@ namespace ElectronNET.API
///
/// (experimental)
/// </summary>
public Task<Rectangle> GetBoundsAsync() => BridgeConnector.OnResult<Rectangle>("browserView-getBounds", "browserView-getBounds-reply" + Id, Id);
/// <summary>
/// Set the bounds of the current view inside the window
/// </summary>
/// <param name="value"></param>
public void SetBounds(Rectangle value)
public Rectangle Bounds
{
BridgeConnector.Emit("browserView-setBounds", Id, value);
get
{
return Task.Run<Rectangle>(() =>
{
var taskCompletionSource = new TaskCompletionSource<Rectangle>();
BridgeConnector.Socket.On("browserView-getBounds-reply", (result) =>
{
BridgeConnector.Socket.Off("browserView-getBounds-reply");
taskCompletionSource.SetResult((Rectangle)result);
});
BridgeConnector.Socket.Emit("browserView-getBounds", Id);
return taskCompletionSource.Task;
}).Result;
}
set
{
BridgeConnector.Socket.Emit("browserView-setBounds", Id, JObject.FromObject(value, _jsonSerializer));
}
}
/// <summary>
@@ -60,7 +74,7 @@ namespace ElectronNET.API
/// <param name="options"></param>
public void SetAutoResize(AutoResizeOptions options)
{
BridgeConnector.Emit("browserView-setAutoResize", Id, options);
BridgeConnector.Socket.Emit("browserView-setAutoResize", Id, JObject.FromObject(options, _jsonSerializer));
}
/// <summary>
@@ -71,7 +85,13 @@ namespace ElectronNET.API
/// <param name="color">Color in #aarrggbb or #argb form. The alpha channel is optional.</param>
public void SetBackgroundColor(string color)
{
BridgeConnector.Emit("browserView-setBackgroundColor", Id, color);
BridgeConnector.Socket.Emit("browserView-setBackgroundColor", Id, color);
}
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore
};
}
}

File diff suppressed because it is too large Load Diff

144
ElectronNET.API/Clipboard.cs Executable file → Normal file
View File

@@ -2,19 +2,17 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using ElectronNET.API.Interfaces;
namespace ElectronNET.API
{
/// <summary>
/// Perform copy and paste operations on the system clipboard.
/// </summary>
public sealed class Clipboard : IClipboard
public sealed class Clipboard
{
private static Clipboard _clipboard;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal Clipboard() { }
@@ -42,7 +40,21 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns>The content in the clipboard as plain text.</returns>
public Task<string> ReadTextAsync(string type = "") => BridgeConnector.OnResult<string>("clipboard-readText", "clipboard-readText-Completed", type);
public Task<string> ReadTextAsync(string type = "")
{
var taskCompletionSource = new TaskCompletionSource<string>();
BridgeConnector.Socket.On("clipboard-readText-Completed", (text) =>
{
BridgeConnector.Socket.Off("clipboard-readText-Completed");
taskCompletionSource.SetResult(text.ToString());
});
BridgeConnector.Socket.Emit("clipboard-readText", type);
return taskCompletionSource.Task;
}
/// <summary>
/// Writes the text into the clipboard as plain text.
@@ -51,7 +63,7 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void WriteText(string text, string type = "")
{
BridgeConnector.Emit("clipboard-writeText", text, type);
BridgeConnector.Socket.Emit("clipboard-writeText", text, type);
}
/// <summary>
@@ -59,7 +71,21 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public Task<string> ReadHTMLAsync(string type = "") => BridgeConnector.OnResult<string>("clipboard-readHTML", "clipboard-readHTML-Completed", type);
public Task<string> ReadHTMLAsync(string type = "")
{
var taskCompletionSource = new TaskCompletionSource<string>();
BridgeConnector.Socket.On("clipboard-readHTML-Completed", (text) =>
{
BridgeConnector.Socket.Off("clipboard-readHTML-Completed");
taskCompletionSource.SetResult(text.ToString());
});
BridgeConnector.Socket.Emit("clipboard-readHTML", type);
return taskCompletionSource.Task;
}
/// <summary>
/// Writes markup to the clipboard.
@@ -68,7 +94,7 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void WriteHTML(string markup, string type = "")
{
BridgeConnector.Emit("clipboard-writeHTML", markup, type);
BridgeConnector.Socket.Emit("clipboard-writeHTML", markup, type);
}
/// <summary>
@@ -76,8 +102,21 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public Task<string> ReadRTFAsync(string type = "") => BridgeConnector.OnResult<string>("clipboard-readRTF", "clipboard-readRTF-Completed", type);
public Task<string> ReadRTFAsync(string type = "")
{
var taskCompletionSource = new TaskCompletionSource<string>();
BridgeConnector.Socket.On("clipboard-readRTF-Completed", (text) =>
{
BridgeConnector.Socket.Off("clipboard-readRTF-Completed");
taskCompletionSource.SetResult(text.ToString());
});
BridgeConnector.Socket.Emit("clipboard-readRTF", type);
return taskCompletionSource.Task;
}
/// <summary>
/// Writes the text into the clipboard in RTF.
@@ -86,7 +125,7 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void WriteRTF(string text, string type = "")
{
BridgeConnector.Emit("clipboard-writeHTML", text, type);
BridgeConnector.Socket.Emit("clipboard-writeHTML", text, type);
}
/// <summary>
@@ -95,9 +134,21 @@ namespace ElectronNET.API
/// be empty strings when the bookmark is unavailable.
/// </summary>
/// <returns></returns>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public Task<ReadBookmark> ReadBookmarkAsync() => BridgeConnector.OnResult<ReadBookmark>("clipboard-readBookmark", "clipboard-readBookmark-Completed");
public Task<ReadBookmark> ReadBookmarkAsync()
{
var taskCompletionSource = new TaskCompletionSource<ReadBookmark>();
BridgeConnector.Socket.On("clipboard-readBookmark-Completed", (bookmark) =>
{
BridgeConnector.Socket.Off("clipboard-readBookmark-Completed");
taskCompletionSource.SetResult(((JObject)bookmark).ToObject<ReadBookmark>());
});
BridgeConnector.Socket.Emit("clipboard-readBookmark");
return taskCompletionSource.Task;
}
/// <summary>
/// Writes the title and url into the clipboard as a bookmark.
@@ -109,11 +160,9 @@ namespace ElectronNET.API
/// <param name="title"></param>
/// <param name="url"></param>
/// <param name="type"></param>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public void WriteBookmark(string title, string url, string type = "")
{
BridgeConnector.Emit("clipboard-writeBookmark", title, url, type);
BridgeConnector.Socket.Emit("clipboard-writeBookmark", title, url, type);
}
/// <summary>
@@ -122,18 +171,30 @@ namespace ElectronNET.API
/// find pasteboard whenever the application is activated.
/// </summary>
/// <returns></returns>
[SupportedOSPlatform("macos")]
public Task<string> ReadFindTextAsync() => BridgeConnector.OnResult<string>("clipboard-readFindText", "clipboard-readFindText-Completed");
public Task<string> ReadFindTextAsync()
{
var taskCompletionSource = new TaskCompletionSource<string>();
BridgeConnector.Socket.On("clipboard-readFindText-Completed", (text) =>
{
BridgeConnector.Socket.Off("clipboard-readFindText-Completed");
taskCompletionSource.SetResult(text.ToString());
});
BridgeConnector.Socket.Emit("clipboard-readFindText");
return taskCompletionSource.Task;
}
/// <summary>
/// macOS: Writes the text into the find pasteboard as plain text. This method uses
/// synchronous IPC when called from the renderer process.
/// </summary>
/// <param name="text"></param>
[SupportedOSPlatform("macos")]
public void WriteFindText(string text)
{
BridgeConnector.Emit("clipboard-writeFindText", text);
BridgeConnector.Socket.Emit("clipboard-writeFindText", text);
}
/// <summary>
@@ -142,7 +203,7 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void Clear(string type = "")
{
BridgeConnector.Emit("clipboard-clear", type);
BridgeConnector.Socket.Emit("clipboard-clear", type);
}
/// <summary>
@@ -150,7 +211,21 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public Task<string[]> AvailableFormatsAsync(string type = "") => BridgeConnector.OnResult<string[]>("clipboard-availableFormats", "clipboard-availableFormats-Completed", type);
public Task<string[]> AvailableFormatsAsync(string type = "")
{
var taskCompletionSource = new TaskCompletionSource<string[]>();
BridgeConnector.Socket.On("clipboard-availableFormats-Completed", (formats) =>
{
BridgeConnector.Socket.Off("clipboard-availableFormats-Completed");
taskCompletionSource.SetResult(((JArray)formats).ToObject<string[]>());
});
BridgeConnector.Socket.Emit("clipboard-availableFormats", type);
return taskCompletionSource.Task;
}
/// <summary>
/// Writes data to the clipboard.
@@ -159,7 +234,7 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void Write(Data data, string type = "")
{
BridgeConnector.Emit("clipboard-write", JObject.FromObject(data, _jsonSerializer), type);
BridgeConnector.Socket.Emit("clipboard-write", JObject.FromObject(data, _jsonSerializer), type);
}
/// <summary>
@@ -167,7 +242,24 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public Task<NativeImage> ReadImageAsync(string type = "") => BridgeConnector.OnResult<NativeImage>("clipboard-readImage", "clipboard-readImage-Completed", type);
public Task<NativeImage> ReadImageAsync(string type = "")
{
var taskCompletionSource = new TaskCompletionSource<NativeImage>();
BridgeConnector.Socket.On("clipboard-readImage-Completed", (image) =>
{
BridgeConnector.Socket.Off("clipboard-readImage-Completed");
var nativeImage = ((JObject)image).ToObject<NativeImage>();
taskCompletionSource.SetResult(nativeImage);
});
BridgeConnector.Socket.Emit("clipboard-readImage", type);
return taskCompletionSource.Task;
}
/// <summary>
/// Writes an image to the clipboard.
@@ -176,10 +268,10 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void WriteImage(NativeImage image, string type = "")
{
BridgeConnector.Emit("clipboard-writeImage", JsonConvert.SerializeObject(image), type);
BridgeConnector.Socket.Emit("clipboard-writeImage", JsonConvert.SerializeObject(image), type);
}
private static readonly JsonSerializer _jsonSerializer = new()
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,

View File

@@ -8,7 +8,7 @@ namespace ElectronNET.API
/// </summary>
public sealed class CommandLine
{
private CommandLine() { }
internal CommandLine() { }
internal static CommandLine Instance
{
@@ -31,7 +31,7 @@ namespace ElectronNET.API
private static CommandLine _commandLine;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
/// <summary>
/// Append a switch (with optional value) to Chromium's command line.
@@ -43,7 +43,7 @@ namespace ElectronNET.API
/// </remarks>
public void AppendSwitch(string the_switch, string value = "")
{
BridgeConnector.Emit("appCommandLineAppendSwitch", the_switch, value);
BridgeConnector.Socket.Emit("appCommandLineAppendSwitch", the_switch, value);
}
/// <summary>
@@ -57,7 +57,7 @@ namespace ElectronNET.API
/// </remarks>
public void AppendArgument(string value)
{
BridgeConnector.Emit("appCommandLineAppendArgument", value);
BridgeConnector.Socket.Emit("appCommandLineAppendArgument", value);
}
/// <summary>
@@ -66,7 +66,24 @@ namespace ElectronNET.API
/// <param name="switchName">A command-line switch</param>
/// <param name="cancellationToken"></param>
/// <returns>Whether the command-line switch is present.</returns>
public Task<bool> HasSwitchAsync(string switchName, CancellationToken cancellationToken = default) => BridgeConnector.OnResult<bool>("appCommandLineHasSwitch", "appCommandLineHasSwitchCompleted", cancellationToken, switchName);
public async Task<bool> HasSwitchAsync(string switchName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
var taskCompletionSource = new TaskCompletionSource<bool>();
using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled()))
{
BridgeConnector.Socket.On("appCommandLineHasSwitchCompleted", (result) =>
{
BridgeConnector.Socket.Off("appCommandLineHasSwitchCompleted");
taskCompletionSource.SetResult((bool)result);
});
BridgeConnector.Socket.Emit("appCommandLineHasSwitch", switchName);
return await taskCompletionSource.Task.ConfigureAwait(false);
}
}
/// <summary>
/// The command-line switch value.
@@ -77,6 +94,23 @@ namespace ElectronNET.API
/// <remarks>
/// Note: When the switch is not present or has no value, it returns empty string.
/// </remarks>
public Task<string> GetSwitchValueAsync(string switchName, CancellationToken cancellationToken = default) => BridgeConnector.OnResult<string>("appCommandLineGetSwitchValue", "appCommandLineGetSwitchValueCompleted", cancellationToken, switchName);
public async Task<string> GetSwitchValueAsync(string switchName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
var taskCompletionSource = new TaskCompletionSource<string>();
using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled()))
{
BridgeConnector.Socket.On("appCommandLineGetSwitchValueCompleted", (result) =>
{
BridgeConnector.Socket.Off("appCommandLineGetSwitchValueCompleted");
taskCompletionSource.SetResult((string)result);
});
BridgeConnector.Socket.Emit("appCommandLineGetSwitchValue", switchName);
return await taskCompletionSource.Task.ConfigureAwait(false);
}
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ElectronNET.API.Entities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace ElectronNET.API.Converter;
/// <summary>
///
/// </summary>
public class ModifierTypeListConverter : JsonConverter<List<ModifierType>>
{
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <param name="objectType"></param>
/// <param name="existingValue"></param>
/// <param name="hasExistingValue"></param>
/// <param name="serializer"></param>
/// <returns></returns>
public override List<ModifierType> ReadJson(JsonReader reader, Type objectType, List<ModifierType> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
if (token.Type == JTokenType.Null)
{
return null;
}
return token.ToObject<List<string>>().Select(m => (ModifierType)Enum.Parse(typeof(ModifierType), m)).ToList();
}
/// <summary>
///
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
/// <param name="serializer"></param>
public override void WriteJson(JsonWriter writer, List<ModifierType> value, JsonSerializer serializer)
{
writer.WriteStartArray();
foreach (var modifier in value)
{
writer.WriteValue(modifier.ToString());
}
writer.WriteEndArray();
}
}

View File

@@ -34,12 +34,15 @@ namespace ElectronNET.API
{
if (_changed == null)
{
BridgeConnector.On<CookieRemovedResponse>("webContents-session-cookies-changed" + Id, (args) =>
BridgeConnector.Socket.On("webContents-session-cookies-changed" + Id, (args) =>
{
_changed(args.cookie, args.cause, args.removed);
Cookie cookie = ((JArray)args)[0].ToObject<Cookie>();
CookieChangedCause cause = ((JArray)args)[1].ToObject<CookieChangedCause>();
bool removed = ((JArray)args)[2].ToObject<bool>();
_changed(cookie, cause, removed);
});
BridgeConnector.Emit("register-webContents-session-cookies-changed", Id);
BridgeConnector.Socket.Emit("register-webContents-session-cookies-changed", Id);
}
_changed += value;
}
@@ -48,7 +51,7 @@ namespace ElectronNET.API
_changed -= value;
if (_changed == null)
BridgeConnector.Off("webContents-session-cookies-changed" + Id);
BridgeConnector.Socket.Off("webContents-session-cookies-changed" + Id);
}
}
@@ -62,16 +65,18 @@ namespace ElectronNET.API
/// <returns>A task which resolves an array of cookie objects.</returns>
public Task<Cookie[]> GetAsync(CookieFilter filter)
{
var taskCompletionSource = new TaskCompletionSource<Cookie[]>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<Cookie[]>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<Cookie[]>("webContents-session-cookies-get-completed" + guid, (cookies) =>
BridgeConnector.Socket.On("webContents-session-cookies-get-completed" + guid, (cookies) =>
{
BridgeConnector.Off("webContents-session-cookies-get-completed" + guid);
taskCompletionSource.SetResult(cookies);
Cookie[] result = ((JArray)cookies).ToObject<Cookie[]>();
BridgeConnector.Socket.Off("webContents-session-cookies-get-completed" + guid);
taskCompletionSource.SetResult(result);
});
BridgeConnector.Emit("webContents-session-cookies-get", Id, JObject.FromObject(filter, _jsonSerializer), guid);
BridgeConnector.Socket.Emit("webContents-session-cookies-get", Id, JObject.FromObject(filter, _jsonSerializer), guid);
return taskCompletionSource.Task;
}
@@ -83,16 +88,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task SetAsync(CookieDetails details)
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On("webContents-session-cookies-set-completed" + guid, () =>
BridgeConnector.Socket.On("webContents-session-cookies-set-completed" + guid, () =>
{
BridgeConnector.Off("webContents-session-cookies-set-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-cookies-set-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Emit("webContents-session-cookies-set", Id, JObject.FromObject(details, _jsonSerializer), guid);
BridgeConnector.Socket.Emit("webContents-session-cookies-set", Id, JObject.FromObject(details, _jsonSerializer), guid);
return taskCompletionSource.Task;
}
@@ -105,16 +110,16 @@ namespace ElectronNET.API
/// <returns>A task which resolves when the cookie has been removed</returns>
public Task RemoveAsync(string url, string name)
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On("webContents-session-cookies-remove-completed" + guid, () =>
BridgeConnector.Socket.On("webContents-session-cookies-remove-completed" + guid, () =>
{
BridgeConnector.Off("webContents-session-cookies-remove-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-cookies-remove-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Emit("webContents-session-cookies-remove", Id, url, name, guid);
BridgeConnector.Socket.Emit("webContents-session-cookies-remove", Id, url, name, guid);
return taskCompletionSource.Task;
}
@@ -125,26 +130,25 @@ namespace ElectronNET.API
/// <returns>A task which resolves when the cookie store has been flushed</returns>
public Task FlushStoreAsync()
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On("webContents-session-cookies-flushStore-completed" + guid, () =>
BridgeConnector.Socket.On("webContents-session-cookies-flushStore-completed" + guid, () =>
{
BridgeConnector.Off("webContents-session-cookies-flushStore-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-cookies-flushStore-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Emit("webContents-session-cookies-flushStore", Id, guid);
BridgeConnector.Socket.Emit("webContents-session-cookies-flushStore", Id, guid);
return taskCompletionSource.Task;
}
private static readonly JsonSerializer _jsonSerializer = new()
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
}
}

View File

@@ -1,38 +0,0 @@
using System.Threading.Tasks;
using ElectronNET.API.Entities;
using Newtonsoft.Json;
namespace ElectronNET.API
{
public sealed class DesktopCapturer
{
private static readonly object _syncRoot = new();
private static DesktopCapturer _desktopCapturer;
internal DesktopCapturer() { }
internal static DesktopCapturer Instance
{
get
{
if (_desktopCapturer == null)
{
lock (_syncRoot)
{
if (_desktopCapturer == null)
{
_desktopCapturer = new DesktopCapturer();
}
}
}
return _desktopCapturer;
}
}
public async Task<DesktopCapturerSource[]> GetSourcesAsync(SourcesOption option)
{
return await BridgeConnector.OnResult<DesktopCapturerSource[]>("desktop-capturer-get-sources", "desktop-capturer-get-sources-result", option);
}
}
}

75
ElectronNET.API/Dialog.cs Executable file → Normal file
View File

@@ -4,20 +4,17 @@ using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using System.Web;
using ElectronNET.API.Interfaces;
namespace ElectronNET.API
{
/// <summary>
/// Display native system dialogs for opening and saving files, alerting, etc.
/// </summary>
public sealed class Dialog : IDialog
public sealed class Dialog
{
private static Dialog _dialog;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal Dialog() { }
@@ -50,23 +47,26 @@ namespace ElectronNET.API
/// <returns>An array of file paths chosen by the user</returns>
public Task<string[]> ShowOpenDialogAsync(BrowserWindow browserWindow, OpenDialogOptions options)
{
var taskCompletionSource = new TaskCompletionSource<string[]>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<string[]>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<string[]>("showOpenDialogComplete" + guid, (filePaths) =>
BridgeConnector.Socket.On("showOpenDialogComplete" + guid, (filePaths) =>
{
BridgeConnector.Off("showOpenDialogComplete" + guid);
BridgeConnector.Socket.Off("showOpenDialogComplete" + guid);
var result = ((JArray)filePaths).ToObject<string[]>();
var list = new List<string>();
foreach (var item in filePaths)
foreach (var item in result)
{
list.Add(HttpUtility.UrlDecode(item));
}
taskCompletionSource.SetResult(list.ToArray());
});
BridgeConnector.Emit("showOpenDialog", browserWindow, options, guid);
BridgeConnector.Socket.Emit("showOpenDialog",
JObject.FromObject(browserWindow, _jsonSerializer),
JObject.FromObject(options, _jsonSerializer), guid);
return taskCompletionSource.Task;
}
@@ -79,17 +79,20 @@ namespace ElectronNET.API
/// <returns>Returns String, the path of the file chosen by the user, if a callback is provided it returns an empty string.</returns>
public Task<string> ShowSaveDialogAsync(BrowserWindow browserWindow, SaveDialogOptions options)
{
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<string>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<string>("showSaveDialogComplete" + guid, (filename) =>
BridgeConnector.Socket.On("showSaveDialogComplete" + guid, (filename) =>
{
BridgeConnector.Off("showSaveDialogComplete" + guid);
BridgeConnector.Socket.Off("showSaveDialogComplete" + guid);
taskCompletionSource.SetResult(filename);
taskCompletionSource.SetResult(filename.ToString());
});
BridgeConnector.Emit("showSaveDialog", browserWindow, options, guid);
BridgeConnector.Socket.Emit("showSaveDialog",
JObject.FromObject(browserWindow, _jsonSerializer),
JObject.FromObject(options, _jsonSerializer),
guid);
return taskCompletionSource.Task;
}
@@ -145,27 +148,32 @@ namespace ElectronNET.API
/// <returns>The API call will be asynchronous and the result will be passed via MessageBoxResult.</returns>
public Task<MessageBoxResult> ShowMessageBoxAsync(BrowserWindow browserWindow, MessageBoxOptions messageBoxOptions)
{
var taskCompletionSource = new TaskCompletionSource<MessageBoxResult>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<MessageBoxResult>();
var guid = Guid.NewGuid().ToString();
BridgeConnector.On<MessageBoxResponse>("showMessageBoxComplete" + guid, (args) =>
BridgeConnector.Socket.On("showMessageBoxComplete" + guid, (args) =>
{
BridgeConnector.Off("showMessageBoxComplete" + guid);
BridgeConnector.Socket.Off("showMessageBoxComplete" + guid);
var result = ((JArray)args);
taskCompletionSource.SetResult(new MessageBoxResult
{
Response = args.response,
CheckboxChecked = args.@checked
Response = (int)result.First,
CheckboxChecked = (bool)result.Last
});
});
if (browserWindow is null)
if (browserWindow == null)
{
BridgeConnector.Emit("showMessageBox", JObject.FromObject(messageBoxOptions, _jsonSerializer), guid);
BridgeConnector.Socket.Emit("showMessageBox", JObject.FromObject(messageBoxOptions, _jsonSerializer), guid);
} else
{
BridgeConnector.Emit("showMessageBox", JObject.FromObject(messageBoxOptions, _jsonSerializer), JObject.FromObject(messageBoxOptions, _jsonSerializer), guid);
BridgeConnector.Socket.Emit("showMessageBox",
JObject.FromObject(browserWindow, _jsonSerializer),
JObject.FromObject(messageBoxOptions, _jsonSerializer),
guid);
}
return taskCompletionSource.Task;
@@ -183,7 +191,7 @@ namespace ElectronNET.API
/// <param name="content">The text content to display in the error box.</param>
public void ShowErrorBox(string title, string content)
{
BridgeConnector.Emit("showErrorBox", title, content);
BridgeConnector.Socket.Emit("showErrorBox", title, content);
}
/// <summary>
@@ -193,8 +201,6 @@ namespace ElectronNET.API
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public Task ShowCertificateTrustDialogAsync(CertificateTrustDialogOptions options)
{
return ShowCertificateTrustDialogAsync(null, options);
@@ -208,25 +214,26 @@ namespace ElectronNET.API
/// <param name="browserWindow"></param>
/// <param name="options"></param>
/// <returns></returns>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public Task ShowCertificateTrustDialogAsync(BrowserWindow browserWindow, CertificateTrustDialogOptions options)
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On("showCertificateTrustDialogComplete" + guid, () =>
BridgeConnector.Socket.On("showCertificateTrustDialogComplete" + guid, () =>
{
BridgeConnector.Off("showCertificateTrustDialogComplete" + guid);
BridgeConnector.Socket.Off("showCertificateTrustDialogComplete" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Emit("showCertificateTrustDialog", browserWindow, options, guid);
BridgeConnector.Socket.Emit("showCertificateTrustDialog",
JObject.FromObject(browserWindow, _jsonSerializer),
JObject.FromObject(options, _jsonSerializer),
guid);
return taskCompletionSource.Task;
}
private static readonly JsonSerializer _jsonSerializer = new()
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,

105
ElectronNET.API/Dock.cs Executable file → Normal file
View File

@@ -1,10 +1,8 @@
using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using ElectronNET.API.Entities;
using ElectronNET.API.Extensions;
using ElectronNET.API.Interfaces;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
@@ -14,11 +12,10 @@ namespace ElectronNET.API
/// <summary>
/// Control your app in the macOS dock.
/// </summary>
[SupportedOSPlatform("macos")]
public sealed class Dock : IDock
public sealed class Dock
{
private static Dock _dock;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal Dock()
{
@@ -53,11 +50,24 @@ namespace ElectronNET.API
/// <param name="type">Can be critical or informational. The default is informational.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Return an ID representing the request.</returns>
public Task<int> BounceAsync(DockBounceType type, CancellationToken cancellationToken = default)
public async Task<int> BounceAsync(DockBounceType type, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return BridgeConnector.OnResult<int>("dock-bounce", "dock-bounce-completed", cancellationToken, type.GetDescription());
var taskCompletionSource = new TaskCompletionSource<int>();
using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled()))
{
BridgeConnector.Socket.On("dock-bounce-completed", (id) =>
{
BridgeConnector.Socket.Off("dock-bounce-completed");
taskCompletionSource.SetResult((int) id);
});
BridgeConnector.Socket.Emit("dock-bounce", type.GetDescription());
return await taskCompletionSource.Task
.ConfigureAwait(false);
}
}
/// <summary>
@@ -66,7 +76,7 @@ namespace ElectronNET.API
/// <param name="id">Id of the request.</param>
public void CancelBounce(int id)
{
BridgeConnector.Emit("dock-cancelBounce", id);
BridgeConnector.Socket.Emit("dock-cancelBounce", id);
}
/// <summary>
@@ -75,7 +85,7 @@ namespace ElectronNET.API
/// <param name="filePath"></param>
public void DownloadFinished(string filePath)
{
BridgeConnector.Emit("dock-downloadFinished", filePath);
BridgeConnector.Socket.Emit("dock-downloadFinished", filePath);
}
/// <summary>
@@ -84,7 +94,7 @@ namespace ElectronNET.API
/// <param name="text"></param>
public void SetBadge(string text)
{
BridgeConnector.Emit("dock-setBadge", text);
BridgeConnector.Socket.Emit("dock-setBadge", text);
}
/// <summary>
@@ -92,10 +102,24 @@ namespace ElectronNET.API
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The badge string of the dock.</returns>
public Task<string> GetBadgeAsync(CancellationToken cancellationToken = default)
public async Task<string> GetBadgeAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return BridgeConnector.OnResult<string>("dock-getBadge", "dock-getBadge-completed", cancellationToken);
var taskCompletionSource = new TaskCompletionSource<string>();
using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled()))
{
BridgeConnector.Socket.On("dock-getBadge-completed", (text) =>
{
BridgeConnector.Socket.Off("dock-getBadge-completed");
taskCompletionSource.SetResult((string) text);
});
BridgeConnector.Socket.Emit("dock-getBadge");
return await taskCompletionSource.Task
.ConfigureAwait(false);
}
}
/// <summary>
@@ -103,7 +127,7 @@ namespace ElectronNET.API
/// </summary>
public void Hide()
{
BridgeConnector.Emit("dock-hide");
BridgeConnector.Socket.Emit("dock-hide");
}
/// <summary>
@@ -111,7 +135,7 @@ namespace ElectronNET.API
/// </summary>
public void Show()
{
BridgeConnector.Emit("dock-show");
BridgeConnector.Socket.Emit("dock-show");
}
/// <summary>
@@ -120,10 +144,24 @@ namespace ElectronNET.API
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the dock icon is visible.</returns>
public Task<bool> IsVisibleAsync(CancellationToken cancellationToken = default)
public async Task<bool> IsVisibleAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return BridgeConnector.OnResult<bool>("dock-isVisible", "dock-isVisible-completed", cancellationToken);
var taskCompletionSource = new TaskCompletionSource<bool>();
using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled()))
{
BridgeConnector.Socket.On("dock-isVisible-completed", (isVisible) =>
{
BridgeConnector.Socket.Off("dock-isVisible-completed");
taskCompletionSource.SetResult((bool) isVisible);
});
BridgeConnector.Socket.Emit("dock-isVisible");
return await taskCompletionSource.Task
.ConfigureAwait(false);
}
}
/// <summary>
@@ -133,7 +171,7 @@ namespace ElectronNET.API
/// The menu items.
/// </value>
public IReadOnlyCollection<MenuItem> MenuItems { get { return _items.AsReadOnly(); } }
private readonly List<MenuItem> _items = new();
private List<MenuItem> _items = new List<MenuItem>();
/// <summary>
/// Sets the application's dock menu.
@@ -141,21 +179,40 @@ namespace ElectronNET.API
public void SetMenu(MenuItem[] menuItems)
{
menuItems.AddMenuItemsId();
BridgeConnector.Emit("dock-setMenu", JArray.FromObject(menuItems, _jsonSerializer));
BridgeConnector.Socket.Emit("dock-setMenu", JArray.FromObject(menuItems, _jsonSerializer));
_items.AddRange(menuItems);
BridgeConnector.Off("dockMenuItemClicked");
BridgeConnector.On<string>("dockMenuItemClicked", (id) => {
MenuItem menuItem = _items.GetMenuItem(id);
BridgeConnector.Socket.Off("dockMenuItemClicked");
BridgeConnector.Socket.On("dockMenuItemClicked", (id) => {
MenuItem menuItem = _items.GetMenuItem(id.ToString());
menuItem?.Click();
});
}
/// <summary>
/// TODO: Menu (macOS) still to be implemented
/// Gets the application's dock menu.
/// </summary>
public Task<Menu> GetMenu(CancellationToken cancellationToken = default) => BridgeConnector.OnResult<Menu>("dock-getMenu", "dock-getMenu-completed", cancellationToken);
public async Task<Menu> GetMenu(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var taskCompletionSource = new TaskCompletionSource<Menu>();
using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled()))
{
BridgeConnector.Socket.On("dock-getMenu-completed", (menu) =>
{
BridgeConnector.Socket.Off("dock-getMenu-completed");
taskCompletionSource.SetResult(((JObject)menu).ToObject<Menu>());
});
BridgeConnector.Socket.Emit("dock-getMenu");
return await taskCompletionSource.Task
.ConfigureAwait(false);
}
}
/// <summary>
/// Sets the image associated with this dock icon.
@@ -163,10 +220,10 @@ namespace ElectronNET.API
/// <param name="image"></param>
public void SetIcon(string image)
{
BridgeConnector.Emit("dock-setIcon", image);
BridgeConnector.Socket.Emit("dock-setIcon", image);
}
private static readonly JsonSerializer _jsonSerializer = new()
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore

View File

@@ -1,144 +0,0 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Net.Sockets;
using System.Net;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ElectronNET.API
{
public static partial class Electron
{
/// <summary>
/// Experimental code, use with care
/// </summary>
public static class Experimental
{
/// <summary>
/// Starts electron from C#, use during development to avoid having to fully publish / build your app on every compile cycle
/// You will need to run the CLI at least once (and once per update) to bootstrap all required files
/// </summary>
/// <param name="webPort"></param>
/// <param name="projectPath"></param>
/// <param name="extraElectronArguments"></param>
/// <param name="clearCache"></param>
/// <exception cref="DirectoryNotFoundException"></exception>
/// <exception cref="Exception"></exception>
public static async Task<int> StartElectronForDevelopment(int webPort, string projectPath = null, string[] extraElectronArguments = null, bool clearCache = false)
{
string aspCoreProjectPath;
if (!string.IsNullOrEmpty(projectPath))
{
if (Directory.Exists(projectPath))
{
aspCoreProjectPath = projectPath;
}
else
{
throw new DirectoryNotFoundException(projectPath);
}
}
else
{
aspCoreProjectPath = Directory.GetCurrentDirectory();
}
string tempPath = Path.Combine(aspCoreProjectPath, "obj", "Host");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
var mainFileJs = Path.Combine(tempPath, "main.js");
if (!File.Exists(mainFileJs))
{
throw new Exception("You need to run once the electronize-h5 start command to bootstrap the necessary files");
}
var nodeModulesDirPath = Path.Combine(tempPath, "node_modules");
bool runNpmInstall = false;
if (!Directory.Exists(nodeModulesDirPath))
{
runNpmInstall = true;
}
var packagesJson = Path.Combine(tempPath, "package.json");
var packagesPrevious = Path.Combine(tempPath, "package.json.previous");
if (!runNpmInstall)
{
if (File.Exists(packagesPrevious))
{
if (File.ReadAllText(packagesPrevious) != File.ReadAllText(packagesJson))
{
runNpmInstall = true;
}
}
else
{
runNpmInstall = true;
}
}
if (runNpmInstall)
{
throw new Exception("You need to run once the electronize-h5 start command to bootstrap the necessary files");
}
string arguments = "";
if (extraElectronArguments is object)
{
arguments = string.Join(' ', extraElectronArguments);
}
if (clearCache)
{
arguments += " --clear-cache=true";
}
BridgeConnector.AuthKey = Guid.NewGuid().ToString().Replace("-", "");
var socketPort = FreeTcpPort();
arguments += $" --development=true --devauth={BridgeConnector.AuthKey} --devport={socketPort}";
string path = Path.Combine(tempPath, "node_modules", ".bin");
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
if (isWindows)
{
ProcessHelper.Execute(@"electron.cmd ""..\..\main.js"" " + arguments, path);
}
else
{
ProcessHelper.Execute(@"./electron ""../../main.js"" " + arguments, path);
}
BridgeSettings.InitializePorts(socketPort, webPort);
await Task.Delay(500);
return socketPort;
}
/// <summary>
/// Return a free local TCP port
/// </summary>
/// <returns></returns>
public static int FreeTcpPort()
{
TcpListener l = new TcpListener(IPAddress.Loopback, 0);
l.Start();
int port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
return port;
}
}
}
}

View File

@@ -1,51 +1,10 @@
using Microsoft.Extensions.Logging;
using System.Runtime.Versioning;
using System;
using System.Collections.Generic;
namespace ElectronNET.API
namespace ElectronNET.API
{
/// <summary>
/// The Electron.NET API
/// </summary>
public static partial class Electron
public static class Electron
{
private static ILoggerFactory loggerFactory;
/// <summary>
/// Reads the auth key from the command line. This method must be called first thing.
/// </summary>
/// <exception cref="Exception"></exception>
public static void ReadAuth()
{
if (!string.IsNullOrEmpty(BridgeConnector.AuthKey))
{
throw new Exception($"Don't call ReadAuth twice or from with {nameof(Experimental)}.{nameof(Experimental.StartElectronForDevelopment)}");
}
var line = Console.ReadLine();
if(line.StartsWith("Auth="))
{
BridgeConnector.AuthKey = line.Substring("Auth=".Length);
}
else
{
throw new Exception("The call to Electron.ReadAuth must be the first thing your app entry point does");
}
}
/// <summary>
/// Sets the logger factory to be used by Electron, if any
/// </summary>
public static ILoggerFactory LoggerFactory
{
private get => loggerFactory; set
{
loggerFactory = value;
BridgeConnector.Logger = value.CreateLogger<App>();
}
}
/// <summary>
/// Communicate asynchronously from the main process to renderer processes.
/// </summary>
@@ -101,11 +60,6 @@ namespace ElectronNET.API
/// </summary>
public static Screen Screen { get { return Screen.Instance; } }
/// <summary>
/// Access information about media sources that can be used to capture audio and video from the desktop using the navigator.mediaDevices.getUserMedia API.
/// </summary>
public static DesktopCapturer DesktopCapturer { get { return DesktopCapturer.Instance; } }
/// <summary>
/// Perform copy and paste operations on the system clipboard.
/// </summary>
@@ -133,7 +87,6 @@ namespace ElectronNET.API
/// <summary>
/// Control your app in the macOS dock.
/// </summary>
[SupportedOSPlatform("macos")]
public static Dock Dock { get { return Dock.Instance; } }
}
}

31
ElectronNET.API/ElectronNET.API.csproj Executable file → Normal file
View File

@@ -1,19 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageOutputPath>..\artifacts</PackageOutputPath>
<PackageId>ElectronNET.API</PackageId>
<Authors>Gregor Biswanger, Robert Muehsig</Authors>
<Authors>Gregor Biswanger, Florian Rappl</Authors>
<Company />
<Product>Electron.NET</Product>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/ElectronNET/Electron.NET/</PackageProjectUrl>
<Description>
Building cross platform electron based desktop apps with .NET Core and ASP.NET Core.
This package contains the API to access the "native" electron API.
</Description>
<Description>Building cross platform electron based desktop apps with .NET Core and ASP.NET Core.
This package contains the API to access the "native" electron API.</Description>
<RepositoryUrl>https://github.com/ElectronNET/Electron.NET/</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
@@ -22,13 +19,10 @@
<PackageIcon>PackageIcon.png</PackageIcon>
<Version>99.0.0.0</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TargetFrameworks>net6.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<None Include="PackageIcon.png" Pack="true" PackagePath="\" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(OS)' == 'Windows_NT'">
<Exec Command="$(ProjectDir)devCleanup.cmd" IgnoreExitCode="true" />
</Target>
@@ -39,17 +33,16 @@
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="SocketIOClient" Version="3.0.6" />
<PackageReference Include="System.Collections" Version="4.3.0" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SocketIOClient" Version="3.0.8" />
<PackageReference Include="SocketIOClient.Newtonsoft.Json" Version="3.0.7" />
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
<PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.4.410601">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
</Project>

View File

@@ -18,7 +18,7 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = NativeImage.DefaultScaleFactor;
public float ScaleFactor { get; set; } = 1.0f;
/// <summary>
/// Gets or sets the buffer

View File

@@ -1,20 +1,13 @@
using System;
namespace ElectronNET.API.Entities
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
[Obsolete("Use ImageOptions instead.")]
public class BitmapOptions
{
/// <summary>
/// Gets or sets the scale factor
/// </summary>
public float ScaleFactor { get; set; } = NativeImage.DefaultScaleFactor;
/// <summary>
/// Utility conversion for obsolete class
/// </summary>
public static implicit operator ImageOptions(BitmapOptions o) => new() {ScaleFactor = o.ScaleFactor};
public float ScaleFactor { get; set; } = 1.0f;
}
}

View File

@@ -1,7 +1,6 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.ComponentModel;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
@@ -37,62 +36,62 @@ namespace ElectronNET.API.Entities
/// window's size will include window frame's size and be slightly larger. Default
/// is false.
/// </summary>
public bool? UseContentSize { get; set; }
public bool UseContentSize { get; set; }
/// <summary>
/// Show window in the center of the screen.
/// </summary>
public bool? Center { get; set; }
public bool Center { get; set; }
/// <summary>
/// Window's minimum width. Default is 0.
/// </summary>
public int? MinWidth { get; set; }
public int MinWidth { get; set; }
/// <summary>
/// Window's minimum height. Default is 0.
/// </summary>
public int? MinHeight { get; set; }
public int MinHeight { get; set; }
/// <summary>
/// Window's maximum width. Default is no limit.
/// </summary>
public int? MaxWidth { get; set; }
public int MaxWidth { get; set; }
/// <summary>
/// Window's maximum height. Default is no limit.
/// </summary>
public int? MaxHeight { get; set; }
public int MaxHeight { get; set; }
/// <summary>
/// Whether window is resizable. Default is true.
/// </summary>
[DefaultValue(true)]
public bool? Resizable { get; set; } = true;
public bool Resizable { get; set; } = true;
/// <summary>
/// Whether window is movable. This is not implemented on Linux. Default is true.
/// </summary>
[DefaultValue(true)]
public bool? Movable { get; set; } = true;
public bool Movable { get; set; } = true;
/// <summary>
/// Whether window is minimizable. This is not implemented on Linux. Default is true.
/// </summary>
[DefaultValue(true)]
public bool? Minimizable { get; set; } = true;
public bool Minimizable { get; set; } = true;
/// <summary>
/// Whether window is maximizable. This is not implemented on Linux. Default is true.
/// </summary>
[DefaultValue(true)]
public bool? Maximizable { get; set; } = true;
public bool Maximizable { get; set; } = true;
/// <summary>
/// Whether window is closable. This is not implemented on Linux. Default is true.
/// </summary>
[DefaultValue(true)]
public bool? Closable { get; set; } = true;
public bool Closable { get; set; } = true;
/// <summary>
/// Whether the window can be focused. Default is true. On Windows setting
@@ -101,35 +100,35 @@ namespace ElectronNET.API.Entities
/// always stay on top in all workspaces.
/// </summary>
[DefaultValue(true)]
public bool? Focusable { get; set; } = true;
public bool Focusable { get; set; } = true;
/// <summary>
/// Whether the window should always stay on top of other windows. Default is false.
/// </summary>
public bool? AlwaysOnTop { get; set; }
public bool AlwaysOnTop { get; set; }
/// <summary>
/// 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.
/// </summary>
public bool? Fullscreen { get; set; }
public bool Fullscreen { get; set; }
/// <summary>
/// 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.
/// </summary>
public bool? Fullscreenable { get; set; }
public bool Fullscreenable { get; set; }
/// <summary>
/// Whether to show the window in taskbar. Default is false.
/// </summary>
public bool? SkipTaskbar { get; set; }
public bool SkipTaskbar { get; set; }
/// <summary>
/// The kiosk mode. Default is false.
/// </summary>
public bool? Kiosk { get; set; }
public bool Kiosk { get; set; }
/// <summary>
/// Default window title. Default is "Electron.NET".
@@ -146,40 +145,40 @@ namespace ElectronNET.API.Entities
/// Whether window should be shown when created. Default is true.
/// </summary>
[DefaultValue(true)]
public bool? Show { get; set; } = true;
public bool Show { get; set; } = true;
/// <summary>
/// Specify false to create a . Default is true.
/// </summary>
[DefaultValue(true)]
public bool? Frame { get; set; } = true;
public bool Frame { get; set; } = true;
/// <summary>
/// Whether this is a modal window. This only works when <see cref="Parent"/> is
/// also specified. Default is false.
/// Whether this is a modal window. This only works when the window is a child
/// window.Default is false.
/// </summary>
public bool? Modal { get; set; }
public bool Modal { get; set; }
/// <summary>
/// Whether the web view accepts a single mouse-down event that simultaneously
/// activates the window. Default is false.
/// activates the window.Default is false.
/// </summary>
public bool? AcceptFirstMouse { get; set; }
public bool AcceptFirstMouse { get; set; }
/// <summary>
/// Whether to hide cursor when typing. Default is false.
/// </summary>
public bool? DisableAutoHideCursor { get; set; }
public bool DisableAutoHideCursor { get; set; }
/// <summary>
/// Auto hide the menu bar unless the Alt key is pressed. Default is false.
/// </summary>
public bool? AutoHideMenuBar { get; set; }
public bool AutoHideMenuBar { get; set; }
/// <summary>
/// Enable the window to be resized larger than screen. Default is false.
/// </summary>
public bool? EnableLargerThanScreen { get; set; }
public bool EnableLargerThanScreen { get; set; }
/// <summary>
/// Window's background color as Hexadecimal value, like #66CD00 or #FFF or
@@ -191,18 +190,18 @@ namespace ElectronNET.API.Entities
/// Whether window should have a shadow. This is only implemented on macOS. Default
/// is true.
/// </summary>
public bool? HasShadow { get; set; }
public bool HasShadow { get; set; }
/// <summary>
/// Forces using dark theme for the window, only works on some GTK+3 desktop
/// environments.Default is false.
/// </summary>
public bool? DarkTheme { get; set; }
public bool DarkTheme { get; set; }
/// <summary>
/// Makes the window . Default is false.
/// </summary>
public bool? Transparent { get; set; }
public bool Transparent { get; set; }
/// <summary>
/// The type of window, default is normal window.
@@ -214,21 +213,13 @@ namespace ElectronNET.API.Entities
/// 'default' | 'hidden' | 'hiddenInset' | 'customButtonsOnHover'
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public TitleBarStyle? TitleBarStyle { get; set; }
public TitleBarStyle TitleBarStyle { get; set; }
/// <summary>
/// Shows the title in the tile bar in full screen mode on macOS for all
/// titleBarStyle options.Default is false.
/// </summary>
public bool? FullscreenWindowTitle { get; set; }
/// <summary>
/// Activate the Window Controls Overlay on Windows, when combined with <see cref="TitleBarStyle"/> = <see cref="TitleBarStyle.hidden"/>
/// </summary>
[SupportedOSPlatform("win")]
[SupportedOSPlatform("macos")]
[DefaultValue(null)]
public TitleBarOverlayConfig TitleBarOverlay { get; set; }
public bool FullscreenWindowTitle { get; set; }
/// <summary>
/// Use WS_THICKFRAME style for frameless windows on Windows, which adds standard
@@ -236,7 +227,7 @@ namespace ElectronNET.API.Entities
/// animations. Default is true.
/// </summary>
[DefaultValue(true)]
public bool? ThickFrame { get; set; } = true;
public bool ThickFrame { get; set; } = true;
/// <summary>
/// Add a type of vibrancy effect to the window, only on macOS. Can be
@@ -244,7 +235,7 @@ namespace ElectronNET.API.Entities
/// medium-light or ultra-dark.
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public Vibrancy? Vibrancy { get; set; }
public Vibrancy Vibrancy { get; set; }
/// <summary>
/// Controls the behavior on macOS when option-clicking the green stoplight button
@@ -253,7 +244,7 @@ namespace ElectronNET.API.Entities
/// it to zoom to the width of the screen.This will also affect the behavior when
/// calling maximize() directly.Default is false.
/// </summary>
public bool? ZoomToPageWidth { get; set; }
public bool ZoomToPageWidth { get; set; }
/// <summary>
/// Tab group name, allows opening the window as a native tab on macOS 10.12+.
@@ -279,19 +270,5 @@ namespace ElectronNET.API.Entities
/// These will only be used if the Proxy field is also set.
/// </summary>
public string ProxyCredentials { get; set; }
/// <summary>
/// The window to use as the created window's parent.
/// </summary>
[DefaultValue(null)]
public BrowserWindow Parent { get; set; }
/// <summary>
/// Set a custom position for the traffic light buttons in frameless windows.
/// </summary>
[DefaultValue(null)]
[SupportedOSPlatform("macos")]
public Point TrafficLightPosition { get; set; }
}
}

View File

@@ -1,9 +1,7 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace ElectronNET.API.Entities
{
namespace ElectronNET.API.Entities {
/// <summary>
/// The cause of the change
/// </summary>

View File

@@ -1,10 +0,0 @@
namespace ElectronNET.API.Entities
{
public class CookieRemovedResponse
{
public Cookie cookie {get;set;}
public CookieChangedCause cause { get; set; }
public bool removed { get; set; }
}
}

View File

@@ -1,11 +1,8 @@
using System;
namespace ElectronNET.API.Entities
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
[Obsolete("Use CreateOptions instead")]
public class CreateFromBitmapOptions
{
/// <summary>
@@ -21,12 +18,6 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = NativeImage.DefaultScaleFactor;
/// <summary>
/// Utility conversion for obsolete class
/// </summary>
public static implicit operator CreateOptions(CreateFromBitmapOptions o) => new()
{Width = o.Width, Height = o.Height, ScaleFactor = o.ScaleFactor};
public float ScaleFactor { get; set; } = 1.0f;
}
}

View File

@@ -1,11 +1,8 @@
using System;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
[Obsolete("Use CreateOptions instead")]
public class CreateFromBufferOptions
{
/// <summary>
@@ -21,11 +18,6 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = NativeImage.DefaultScaleFactor;
/// <summary>
/// Utility conversion for obsolete class
/// </summary>
public static implicit operator CreateOptions(CreateFromBufferOptions o) => new()
{Width = o.Width, Height = o.Height, ScaleFactor = o.ScaleFactor};
public float ScaleFactor { get; set; } = 1.0f;
}
}

View File

@@ -1,24 +0,0 @@
namespace ElectronNET.API.Entities
{
/// <summary>
/// Options for creating a new <see cref="NativeImage"/>
/// </summary>
public class CreateOptions
{
/// <summary>
/// Gets or sets the width
/// </summary>
public int? Width { get; set; }
/// <summary>
/// Gets or sets the height
/// </summary>
public int? Height { get; set; }
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = NativeImage.DefaultScaleFactor;
}
}

View File

@@ -34,7 +34,5 @@
/// The title of the url at text.
/// </summary>
public string Bookmark { get; set; }
public NativeImage? Image { get; set; }
}
}
}

View File

@@ -1,15 +0,0 @@
using Newtonsoft.Json;
namespace ElectronNET.API.Entities
{
public sealed class DesktopCapturerSource
{
public string Id { get; set; }
public string Name { get; set; }
public NativeImage Thumbnail { get; set; }
[JsonProperty("display_id")]
public string DisplayId { get; set; }
public NativeImage AppIcon { get; set; }
}
}

View File

@@ -1,10 +1,5 @@
namespace ElectronNET.API.Entities
{
public class DisplayChanged
{
public Display display { get; set; }
public string[] metrics { get; set; }
}
/// <summary>
///
/// </summary>
@@ -26,12 +21,12 @@
/// <summary>
/// Can be 0, 90, 180, 270, represents screen rotation in clock-wise degrees.
/// </summary>
public float Rotation { get; set; }
public int Rotation { get; set; }
/// <summary>
/// Output device's pixel scale factor.
/// </summary>
public float ScaleFactor { get; set; }
public int ScaleFactor { get; set; }
/// <summary>
/// Gets or sets the size.

View File

@@ -1,13 +0,0 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class ImageOptions
{
/// <summary>
/// Gets or sets the scale factor
/// </summary>
public float ScaleFactor { get; set; } = NativeImage.DefaultScaleFactor;
}
}

View File

@@ -0,0 +1,81 @@
using Newtonsoft.Json.Converters;
using System.Collections.Generic;
using ElectronNET.API.Converter;
using Newtonsoft.Json;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class InputEvent
{
/// <summary>
/// Equivalent to KeyboardEvent.key.
/// </summary>
public string Key { get; set; } = "";
/// <summary>
/// Equivalent to KeyboardEvent.code.
/// </summary>
public string Code { get; set; } = "";
/// <summary>
/// Equivalent to KeyboardEvent.repeat.
/// </summary>
public bool IsAutoRepeat { get; set; } = false;
/// <summary>
/// Equivalent to KeyboardEvent.isComposing.
/// </summary>
public bool IsComposing { get; set; } = false;
/// <summary>
/// Equivalent to KeyboardEvent.shiftKey.
/// </summary>
public bool Shift { get; set; } = false;
/// <summary>
/// Equivalent to KeyboardEvent.controlKey.
/// </summary>
public bool Control { get; set; } = false;
/// <summary>
/// Equivalent to KeyboardEvent.altKey.
/// </summary>
public bool Alt { get; set; } = false;
/// <summary>
/// Equivalent to KeyboardEvent.metaKey.
/// </summary>
public bool Meta { get; set; } = false;
/// <summary>
/// Equivalent to KeyboardEvent.location.
/// </summary>
public int Location { get; set; } = 0;
/// <summary>
/// An array of modifiers of the event, can be `shift`, `control`, `ctrl`, `alt`,
/// `meta`, `command`, `cmd`, `isKeypad`, `isAutoRepeat`, `leftButtonDown`,
/// `middleButtonDown`, `rightButtonDown`, `capsLock`, `numLock`, `left`, `right`
/// </summary>
[JsonConverter(typeof(ModifierTypeListConverter))]
public List<ModifierType> Modifiers { get; set; }
/// <summary>
/// Can be `undefined`, `mouseDown`, `mouseUp`, `mouseMove`, `mouseEnter`,
/// `mouseLeave`, `contextMenu`, `mouseWheel`, `rawKeyDown`, `keyDown`, `keyUp`,
/// `gestureScrollBegin`, `gestureScrollEnd`, `gestureScrollUpdate`,
/// `gestureFlingStart`, `gestureFlingCancel`, `gesturePinchBegin`,
/// `gesturePinchEnd`, `gesturePinchUpdate`, `gestureTapDown`, `gestureShowPress`,
/// `gestureTap`, `gestureTapCancel`, `gestureShortPress`, `gestureLongPress`,
/// `gestureLongTap`, `gestureTwoFingerTap`, `gestureTapUnconfirmed`,
/// `gestureDoubleTap`, `touchStart`, `touchMove`, `touchEnd`, `touchCancel`,
/// `touchScrollStarted`, `pointerDown`, `pointerUp`, `pointerMove`,
/// `pointerRawUpdate`, `pointerCancel` or `pointerCausedUaAction`.
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public InputEventType Type { get; set; }
}
}

View File

@@ -0,0 +1,201 @@
namespace ElectronNET.API.Entities;
/// <summary>
///
/// </summary>
public enum InputEventType
{
/// <summary>
///
/// </summary>
undefined,
/// <summary>
///
/// </summary>
mouseDown,
/// <summary>
///
/// </summary>
mouseUp,
/// <summary>
///
/// </summary>
mouseMove,
/// <summary>
///
/// </summary>
mouseEnter,
/// <summary>
///
/// </summary>
mouseLeave,
/// <summary>
///
/// </summary>
contextMenu,
/// <summary>
///
/// </summary>
mouseWheel,
/// <summary>
///
/// </summary>
rawKeyDown,
/// <summary>
///
/// </summary>
keyDown,
/// <summary>
///
/// </summary>
keyUp,
/// <summary>
///
/// </summary>
gestureScrollBegin,
/// <summary>
///
/// </summary>
gestureScrollEnd,
/// <summary>
///
/// </summary>
gestureScrollUpdate,
/// <summary>
///
/// </summary>
gestureFlingStart,
/// <summary>
///
/// </summary>
gestureFlingCancel,
/// <summary>
///
/// </summary>
gesturePinchBegin,
/// <summary>
///
/// </summary>
gesturePinchEnd,
/// <summary>
///
/// </summary>
gesturePinchUpdate,
/// <summary>
///
/// </summary>
gestureTapDown,
/// <summary>
///
/// </summary>
gestureShowPress,
/// <summary>
///
/// </summary>
gestureTap,
/// <summary>
///
/// </summary>
gestureTapCancel,
/// <summary>
///
/// </summary>
gestureShortPress,
/// <summary>
///
/// </summary>
gestureLongPress,
/// <summary>
///
/// </summary>
gestureLongTap,
/// <summary>
///
/// </summary>
gestureTwoFingerTap,
/// <summary>
///
/// </summary>
gestureTapUnconfirmed,
/// <summary>
///
/// </summary>
gestureDoubleTap,
/// <summary>
///
/// </summary>
touchStart,
/// <summary>
///
/// </summary>
touchMove,
/// <summary>
///
/// </summary>
touchEnd,
/// <summary>
///
/// </summary>
touchCancel,
/// <summary>
///
/// </summary>
touchScrollStarted,
/// <summary>
///
/// </summary>
pointerDown,
/// <summary>
///
/// </summary>
pointerUp,
/// <summary>
///
/// </summary>
pointerMove,
/// <summary>
///
/// </summary>
pointerRawUpdate,
/// <summary>
///
/// </summary>
pointerCancel,
/// <summary>
///
/// </summary>
pointerCausedUaAction
}

View File

@@ -1,7 +1,8 @@
using System;
namespace ElectronNET.API.Entities
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class JumpListSettings
{
/// <summary>
@@ -15,6 +16,6 @@ namespace ElectronNET.API.Entities
/// in the Jump List. These items must not be re-added to the Jump List in the next call to <see cref="App.SetJumpList"/>, Windows will
/// not display any custom category that contains any of the removed items.
/// </summary>
public JumpListItem[] RemovedItems { get; set; } = Array.Empty<JumpListItem>();
public JumpListItem[] RemovedItems { get; set; } = new JumpListItem[0];
}
}

View File

@@ -0,0 +1,33 @@
namespace ElectronNET.API.Entities;
/// <summary>
///
/// </summary>
public class Margins
{
/// <summary>
/// Can be `default`, `none`, `printableArea`, or `custom`. If `custom` is chosen,
/// you will also need to specify `top`, `bottom`, `left`, and `right`.
/// </summary>
public string MarginType { get; set; }
/// <summary>
/// The top margin of the printed web page, in pixels.
/// </summary>
public int Top { get; set; }
/// <summary>
/// The bottom margin of the printed web page, in pixels.
/// </summary>
public int Bottom { get; set; }
/// <summary>
/// The left margin of the printed web page, in pixels.
/// </summary>
public int Left { get; set; }
/// <summary>
/// The right margin of the printed web page, in pixels.
/// </summary>
public int Right { get; set; }
}

View File

@@ -158,17 +158,6 @@
/// <summary>
/// Only macOS: The submenu is a “Services” menu
/// </summary>
services,
/// <summary>
/// Only macOS: the submenue for "Recent Documents"
/// </summary>
recentdocuments,
/// <summary>
/// Only macOS: the menu to clear the recent document list
/// </summary>
clearrecentdocuments,
services
}
}

View File

@@ -1,11 +1,5 @@
namespace ElectronNET.API.Entities
{
internal class MessageBoxResponse
{
public int response { get; set; }
public bool @checked { get; set; }
}
/// <summary>
///
/// </summary>

View File

@@ -0,0 +1,87 @@
namespace ElectronNET.API.Entities;
/// <summary>
/// Specifies the possible modifier keys for a keyboard input.
/// </summary>
public enum ModifierType
{
/// <summary>
/// The Shift key.
/// </summary>
shift,
/// <summary>
/// The Control key.
/// </summary>
control,
/// <summary>
/// The Control key (alias for control).
/// </summary>
ctrl,
/// <summary>
/// The Alt key.
/// </summary>
alt,
/// <summary>
/// The Meta key.
/// </summary>
meta,
/// <summary>
/// The Command key.
/// </summary>
command,
/// <summary>
/// The Command key (alias for command).
/// </summary>
cmd,
/// <summary>
/// Indicates whether the keypad modifier key is pressed.
/// </summary>
isKeypad,
/// <summary>
/// Indicates whether the key is an auto-repeated key.
/// </summary>
isAutoRepeat,
/// <summary>
/// Indicates whether the left mouse button is pressed.
/// </summary>
leftButtonDown,
/// <summary>
/// Indicates whether the middle mouse button is pressed.
/// </summary>
middleButtonDown,
/// <summary>
/// Indicates whether the right mouse button is pressed.
/// </summary>
rightButtonDown,
/// <summary>
/// The Caps Lock key.
/// </summary>
capsLock,
/// <summary>
/// The Num Lock key.
/// </summary>
numlock,
/// <summary>
/// The Left key.
/// </summary>
left,
/// <summary>
/// The Right key.
/// </summary>
right
}

View File

@@ -1,15 +1,12 @@
using System;
using System.Collections.Generic;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Processing;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Processing;
namespace ElectronNET.API.Entities
{
@@ -19,15 +16,10 @@ namespace ElectronNET.API.Entities
[JsonConverter(typeof(NativeImageJsonConverter))]
public class NativeImage
{
/// <summary>
///
/// </summary>
public const float DefaultScaleFactor = 1.0f;
private readonly Dictionary<float, Image> _images = new Dictionary<float, Image>();
private bool _isTemplateImage;
private static readonly Dictionary<string, float> ScaleFactorPairs = new()
private static readonly Dictionary<string, float> ScaleFactorPairs = new Dictionary<string, float>
{
{"@2x", 2.0f}, {"@3x", 3.0f}, {"@1x", 1.0f}, {"@4x", 4.0f},
{"@5x", 5.0f}, {"@1.25x", 1.25f}, {"@1.33x", 1.33f}, {"@1.4x", 1.4f},
@@ -44,7 +36,8 @@ namespace ElectronNET.API.Entities
}
private static Image BytesToImage(byte[] bytes)
{
return Image.Load(new MemoryStream(bytes));
var ms = new MemoryStream(bytes);
return Image.FromStream(ms);
}
/// <summary>
@@ -58,35 +51,30 @@ namespace ElectronNET.API.Entities
/// <summary>
///
/// </summary>
public static NativeImage CreateFromImage(Image image, CreateFromBitmapOptions options = null)
public static NativeImage CreateFromBitmap(Bitmap bitmap, CreateFromBitmapOptions options = null)
{
if (options is null)
{
options = new CreateFromBitmapOptions();
}
return new NativeImage(image, options.ScaleFactor);
return new NativeImage(bitmap, options.ScaleFactor);
}
/// <summary>
///
/// </summary>
public static NativeImage CreateFromImage(Image image, CreateOptions options = null)
=> new (image, options?.ScaleFactor ?? DefaultScaleFactor);
/// <summary>
/// Creates a NativeImage from a byte array.
/// </summary>
public static NativeImage CreateFromBuffer(byte[] buffer, CreateOptions options = null)
public static NativeImage CreateFromBuffer(byte[] buffer, CreateFromBufferOptions options = null)
{
if (options is null)
{
options = new CreateFromBufferOptions();
}
var image = Image.Load(new MemoryStream(buffer));
var ms = new MemoryStream(buffer);
var image = Image.FromStream(ms);
return new NativeImage(image, options?.ScaleFactor ?? DefaultScaleFactor);
return new NativeImage(image, options.ScaleFactor);
}
/// <summary>
@@ -122,14 +110,14 @@ namespace ElectronNET.API.Entities
throw new Exception($"Invalid scaling factor for '{path}'.");
}
images[dpi.Value] = Image.Load(path);
images[dpi.Value] = Image.FromFile(path);
}
else
{
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
var extension = Path.GetExtension(path);
// Load as 1x dpi
images[1.0f] = Image.Load(path);
images[1.0f] = Image.FromFile(path);
foreach (var scale in ScaleFactorPairs)
{
@@ -139,7 +127,7 @@ namespace ElectronNET.API.Entities
var dpi = ExtractDpiFromFilePath(fileName);
if (dpi != null)
{
images[dpi.Value] = Image.Load(fileName);
images[dpi.Value] = Image.FromFile(fileName);
}
}
}
@@ -158,9 +146,9 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Creates a NativeImage from a bitmap and scale factor
/// </summary>
public NativeImage(Image image, float scaleFactor = DefaultScaleFactor)
public NativeImage(Image bitmap, float scaleFactor = 1.0f)
{
_images.Add(scaleFactor, image);
_images.Add(scaleFactor, bitmap);
}
/// <summary>
@@ -176,7 +164,7 @@ namespace ElectronNET.API.Entities
/// </summary>
public NativeImage Crop(Rectangle rect)
{
var images = new Dictionary<float, Image>();
var images = new Dictionary<float,Image>();
foreach (var image in _images)
{
images.Add(image.Key, Crop(rect.X, rect.Y, rect.Width, rect.Height, image.Key));
@@ -208,7 +196,7 @@ namespace ElectronNET.API.Entities
if (options.Buffer.Length > 0)
{
_images[options.ScaleFactor] =
CreateFromBuffer(options.Buffer, new CreateOptions {ScaleFactor = options.ScaleFactor})
CreateFromBuffer(options.Buffer, new CreateFromBufferOptions {ScaleFactor = options.ScaleFactor})
.GetScale(options.ScaleFactor);
}
else if (!string.IsNullOrEmpty(options.DataUrl))
@@ -226,7 +214,7 @@ namespace ElectronNET.API.Entities
var image = GetScale(scaleFactor);
if (image != null)
{
return (float)image.Width / image.Height;
return image.Width / image.Height;
}
return 0f;
@@ -235,9 +223,9 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Returns a byte array that contains the image's raw bitmap pixel data.
/// </summary>
public byte[] GetBitmap(float scaleFactor)
public byte[] GetBitmap(BitmapOptions options)
{
return ToBitmap(scaleFactor).ToArray();
return ToBitmap(new ToBitmapOptions{ ScaleFactor = options.ScaleFactor });
}
/// <summary>
@@ -245,14 +233,26 @@ namespace ElectronNET.API.Entities
/// </summary>
public byte[] GetNativeHandle()
{
return ToBitmap().ToArray();
return ToBitmap(new ToBitmapOptions());
}
/// <summary>
/// Gets the size of the specified image based on scale factor
/// </summary>
public Size GetSize(float scaleFactor = 1.0f)
=> _images.TryGetValue(scaleFactor, out var image) ? image.Size() : null;
{
if (_images.ContainsKey(scaleFactor))
{
var image = _images[scaleFactor];
return new Size
{
Width = image.Width,
Height = image.Height
};
}
return null;
}
/// <summary>
/// Checks to see if the NativeImage instance is empty.
@@ -278,99 +278,148 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Outputs a bitmap based on the scale factor
/// </summary>
public MemoryStream ToBitmap(float scaleFactor = 1.0f)
public byte[] ToBitmap(ToBitmapOptions options)
{
var ms = new MemoryStream();
_images[scaleFactor].SaveAsBmp(ms);
return ms;
}
/// <summary>
/// Outputs a PNG based on the scale factor
/// </summary>
public string ToDataURL(ImageOptions options)
=> _images.TryGetValue(options.ScaleFactor, out var image)
? $"data:image/png;base64,{image.ToBase64String(PngFormat.Instance)}"
: null;
public MemoryStream ToPng(float scaleFactor = 1.0f)
{
var ms = new MemoryStream();
_images[scaleFactor].SaveAsPng(ms);
return ms;
}
/// <summary>
/// Outputs a JPEG for the default scale factor
/// </summary>
public MemoryStream ToJpeg(int quality, float scaleFactor = 1.0f)
{
var ms = new MemoryStream();
_images[scaleFactor].SaveAsJpeg(ms, new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder() { Quality = quality });
return ms;
return ImageToBytes(ImageFormat.Bmp, options.ScaleFactor);
}
/// <summary>
/// Outputs a data URL based on the scale factor
/// </summary>
public string ToDataURL(float scaleFactor = 1.0f)
public string ToDataURL(ToDataUrlOptions options)
{
if (!_images.TryGetValue(scaleFactor, out var image))
if (!_images.ContainsKey(options.ScaleFactor))
{
throw new KeyNotFoundException($"Missing scaleFactor = {scaleFactor}");
return null;
}
return image.ToBase64String(PngFormat.Instance);
var image = _images[options.ScaleFactor];
var mimeType = ImageCodecInfo.GetImageEncoders().FirstOrDefault(x => x.FormatID == image.RawFormat.Guid)?.MimeType;
if (mimeType is null)
{
mimeType = "image/png";
}
var bytes = ImageToBytes(image.RawFormat, options.ScaleFactor);
var base64 = Convert.ToBase64String(bytes);
return $"data:{mimeType};base64,{base64}";
}
/// <summary>
/// Outputs a JPEG for the default scale factor
/// </summary>
public byte[] ToJPEG(int quality)
{
return ImageToBytes(ImageFormat.Jpeg, 1.0f, quality);
}
/// <summary>
/// Outputs a PNG for the specified scale factor
/// </summary>
public byte[] ToPNG(ToPNGOptions options)
{
return ImageToBytes(ImageFormat.Png, options.ScaleFactor);
}
private byte[] ImageToBytes(ImageFormat imageFormat = null, float scaleFactor = 1.0f, int quality = 100)
{
using var ms = new MemoryStream();
if (_images.ContainsKey(scaleFactor))
{
var image = _images[scaleFactor];
var encoderCodecInfo = GetEncoder(imageFormat ?? image.RawFormat);
var encoder = Encoder.Quality;
var encoderParameters = new EncoderParameters(1)
{
Param = new[]
{
new EncoderParameter(encoder, quality)
}
};
image.Save(ms, encoderCodecInfo, encoderParameters);
return ms.ToArray();
}
return null;
}
private Image Resize(int? width, int? height, float scaleFactor = 1.0f)
{
if (!_images.TryGetValue(scaleFactor, out var image) || (width is null && height is null))
if (!_images.ContainsKey(scaleFactor) || (width is null && height is null))
{
throw new KeyNotFoundException($"Missing scaleFactor = {scaleFactor}");
}
if (width is null && height is null)
{
throw new ArgumentNullException("Missing width or height");
return null;
}
var aspect = GetAspectRatio(scaleFactor);
width ??= Convert.ToInt32(image.Width * aspect);
height ??= Convert.ToInt32(image.Height * aspect);
width = Convert.ToInt32(width * scaleFactor);
height = Convert.ToInt32(height * scaleFactor);
return image.Clone(c => c.Resize(new SixLabors.ImageSharp.Processing.ResizeOptions
var image = _images[scaleFactor];
using (var g = Graphics.FromImage(image))
{
Size = new(width.Value, height.Value),
Sampler = KnownResamplers.Triangle,
}));
g.CompositingQuality = CompositingQuality.HighQuality;
var aspect = GetAspectRatio(scaleFactor);
width ??= Convert.ToInt32(image.Width * aspect);
height ??= Convert.ToInt32(image.Height * aspect);
width = Convert.ToInt32(width * scaleFactor);
height = Convert.ToInt32(height * scaleFactor);
var bmp = new Bitmap(width.Value, height.Value);
g.DrawImage(bmp,
new System.Drawing.Rectangle(0, 0, image.Width, image.Height),
new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
GraphicsUnit.Pixel);
return bmp;
}
}
private Image Crop(int? x, int? y, int? width, int? height, float scaleFactor = 1.0f)
{
var image = _images[scaleFactor];
if (!_images.TryGetValue(scaleFactor, out image))
if (!_images.ContainsKey(scaleFactor))
{
throw new KeyNotFoundException($"Missing scaleFactor = {scaleFactor}");
return null;
}
x ??= 0;
y ??= 0;
x = Convert.ToInt32(x * scaleFactor);
y = Convert.ToInt32(y * scaleFactor);
var image = _images[scaleFactor];
using (var g = Graphics.FromImage(image))
{
g.CompositingQuality = CompositingQuality.HighQuality;
width ??= image.Width;
height ??= image.Height;
x ??= 0;
y ??= 0;
width = Convert.ToInt32(width * scaleFactor);
height = Convert.ToInt32(height * scaleFactor);
x = Convert.ToInt32(x * scaleFactor);
y = Convert.ToInt32(y * scaleFactor);
return image.Clone(c => c.Crop(new SixLabors.ImageSharp.Rectangle(x.Value, y.Value, width.Value, height.Value)));
width ??= image.Width;
height ??= image.Height;
width = Convert.ToInt32(width * scaleFactor);
height = Convert.ToInt32(height * scaleFactor);
var bmp = new Bitmap(width.Value, height.Value);
g.DrawImage(bmp, new System.Drawing.Rectangle(0, 0, image.Width, image.Height), new System.Drawing.Rectangle(x.Value, y.Value, width.Value, height.Value), GraphicsUnit.Pixel);
return bmp;
}
}
private ImageCodecInfo GetEncoder(ImageFormat format)
{
var codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
internal Dictionary<float,string> GetAllScaledImages()
@@ -380,12 +429,12 @@ namespace ElectronNET.API.Entities
{
foreach (var (scale, image) in _images)
{
dict.Add(scale, image.ToBase64String(PngFormat.Instance));
dict.Add(scale, Convert.ToBase64String(ImageToBytes(null, scale)));
}
}
catch (Exception ex)
{
BridgeConnector.LogError(ex, "Error getting scaled images");
Console.WriteLine(ex);
}
return dict;
@@ -393,9 +442,9 @@ namespace ElectronNET.API.Entities
internal Image GetScale(float scaleFactor)
{
if (_images.TryGetValue(scaleFactor, out var image))
if (_images.ContainsKey(scaleFactor))
{
return image;
return _images[scaleFactor];
}
return null;

View File

@@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Processing;
using System.Drawing;
using System.IO;
using Newtonsoft.Json;
using SixLabors.ImageSharp;
namespace ElectronNET.API.Entities
{
@@ -22,15 +19,12 @@ namespace ElectronNET.API.Entities
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var dict = serializer.Deserialize<Dictionary<string, string>>(reader);
var dict = serializer.Deserialize<Dictionary<float, string>>(reader);
var newDictionary = new Dictionary<float, Image>();
foreach (var item in dict)
{
if (float.TryParse(item.Key, out var size))
{
var bytes = Convert.FromBase64String(item.Value);
newDictionary.Add(size, Image.Load(new MemoryStream(bytes)));
}
var bytes = Convert.FromBase64String(item.Value);
newDictionary.Add(item.Key, Image.FromStream(new MemoryStream(bytes)));
}
return new NativeImage(newDictionary);
}

View File

@@ -1,11 +1,8 @@
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
[SupportedOSPlatform("macos")]
public class NotificationAction
{
/// <summary>

View File

@@ -1,6 +1,5 @@
using Newtonsoft.Json;
using System;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
@@ -18,8 +17,6 @@ namespace ElectronNET.API.Entities
/// <summary>
/// A subtitle for the notification, which will be displayed below the title.
/// </summary>
[SupportedOSPlatform("macos")]
public string SubTitle { get; set; }
/// <summary>
@@ -41,46 +38,38 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Whether or not to add an inline reply option to the notification.
/// </summary>
[SupportedOSPlatform("macos")]
public bool HasReply { get; set; }
/// <summary>
/// The timeout duration of the notification. Can be 'default' or 'never'.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("linux")]
public string TimeoutType { get; set; }
/// <summary>
/// The placeholder to write in the inline reply input field.
/// </summary>
[SupportedOSPlatform("macos")]
public string ReplyPlaceholder { get; set; }
/// <summary>
/// The name of the sound file to play when the notification is shown.
/// </summary>
[SupportedOSPlatform("macos")]
public string Sound { get; set; }
/// <summary>
/// The urgency level of the notification. Can be 'normal', 'critical', or 'low'.
/// </summary>
[SupportedOSPlatform("linux")]
public string Urgency { get; set; }
/// <summary>
/// Actions to add to the notification. Please read the available actions and
/// limitations in the NotificationAction documentation.
/// </summary>
[SupportedOSPlatform("macos")]
public NotificationAction Actions { get; set; }
/// <summary>
/// A custom title for the close button of an alert. An empty string will cause the
/// default localized text to be used.
/// </summary>
[SupportedOSPlatform("macos")]
public string CloseButtonText { get; set; }
/// <summary>
@@ -138,7 +127,6 @@ namespace ElectronNET.API.Entities
/// The string the user entered into the inline reply field
/// </summary>
[JsonIgnore]
[SupportedOSPlatform("macos")]
public Action<string> OnReply { get; set; }
/// <summary>
@@ -154,7 +142,6 @@ namespace ElectronNET.API.Entities
/// macOS only - The index of the action that was activated
/// </summary>
[JsonIgnore]
[SupportedOSPlatform("macos")]
public Action<string> OnAction { get; set; }
/// <summary>

View File

@@ -1,6 +1,5 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
@@ -40,7 +39,6 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Message to display above input boxes.
/// </summary>
[SupportedOSPlatform("macos")]
public string Message { get; set; }
/// <summary>

View File

@@ -1,6 +1,4 @@
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
namespace ElectronNET.API.Entities
{
/// <summary>
///
@@ -30,66 +28,21 @@ namespace ElectronNET.API.Entities
/// <summary>
/// The create directory
/// </summary>
[SupportedOSPlatform("macos")]
createDirectory,
/// <summary>
/// The prompt to create
/// </summary>
[SupportedOSPlatform("windows")]
promptToCreate,
/// <summary>
/// The no resolve aliases
/// </summary>
[SupportedOSPlatform("macos")]
noResolveAliases,
/// <summary>
/// Treat packages, such as .app folders, as a directory instead of a file.
/// The treat package as directory
/// </summary>
[SupportedOSPlatform("macos")]
treatPackageAsDirectory,
/// <summary>
/// Don't add the item being opened to recent documents list
/// </summary>
[SupportedOSPlatform("windows")]
dontAddToRecent
}
/// <summary>
///
/// </summary>
public enum SaveDialogProperty
{
/// <summary>
/// The show hidden files
/// </summary>
showHiddenFiles,
/// <summary>
/// The create directory
/// </summary>
[SupportedOSPlatform("macos")]
createDirectory,
/// <summary>
/// Treat packages, such as .app folders, as a directory instead of a file.
/// </summary>
[SupportedOSPlatform("macos")]
treatPackageAsDirectory,
/// <summary>
/// Sets whether the user will be presented a confirmation dialog if the user types a file name that already exists.
/// </summary>
[SupportedOSPlatform("linux")]
showOverwriteConfirmation,
/// <summary>
/// Don't add the item being opened to recent documents list
/// </summary>
[SupportedOSPlatform("windows")]
dontAddToRecent
treatPackageAsDirectory
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.ComponentModel;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
@@ -13,13 +12,11 @@ namespace ElectronNET.API.Entities
/// <see langword="true"/> to bring the opened application to the foreground. The default is <see langword="true"/>.
/// </summary>
[DefaultValue(true)]
[SupportedOSPlatform("macos")]
public bool Activate { get; set; } = true;
/// <summary>
/// The working directory.
/// </summary>
[SupportedOSPlatform("windows")]
public string WorkingDirectory { get; set; }
}
}

View File

@@ -1,35 +1,63 @@
namespace ElectronNET.API.Entities
namespace ElectronNET.API.Entities;
/// <summary>
///
/// </summary>
public class PrintToPDFOptions
{
/// <summary>
///
/// Paper orientation. `true` for landscape, `false` for portrait. Defaults to false.
/// </summary>
public class PrintToPDFOptions
{
/// <summary>
/// Specifies the type of margins to use. Uses 0 for default margin, 1 for no
/// margin, and 2 for minimum margin.
/// </summary>
public int MarginsType { get; set; }
public bool Landscape { get; set; } = false;
/// <summary>
/// Specify page size of the generated PDF. Can be A3, A4, A5, Legal, Letter,
/// Tabloid or an Object containing height and width in microns.
/// </summary>
public string PageSize { get; set; }
/// <summary>
/// Whether to display header and footer. Defaults to false.
/// </summary>
public bool DisplayHeaderFooter { get; set; } = false;
/// <summary>
/// Whether to print CSS backgrounds.
/// </summary>
public bool PrintBackground { get; set; }
/// <summary>
/// Whether to print background graphics. Defaults to false.
/// </summary>
public bool PrintBackground { get; set; } = false;
/// <summary>
/// Whether to print selection only.
/// </summary>
public bool PrintSelectionOnly { get; set; }
/// <summary>
/// Scale of the webpage rendering. Defaults to 1.
/// </summary>
public double Scale { get; set; } = 1;
/// <summary>
/// true for landscape, false for portrait.
/// </summary>
public bool Landscape { get; set; }
}
/// <summary>
/// Specify page size of the generated PDF. Can be `A0`, `A1`, `A2`, `A3`, `A4`,
/// `A5`, `A6`, `Legal`, `Letter`, `Tabloid`, `Ledger`, or an Object containing
/// `height` and `width` in inches. Defaults to `Letter`.
/// </summary>
public string PageSize { get; set; } = "Letter";
/// <summary>
/// Paper ranges to print, e.g., '1-5, 8, 11-13'. Defaults to the empty string,
/// which means print all pages.
/// </summary>
public string PageRanges { get; set; } = "";
/// <summary>
/// HTML template for the print header. Should be valid HTML markup with following
/// classes used to inject printing values into them: `date` (formatted print date),
/// `title` (document title), `url` (document location), `pageNumber` (current page
/// number) and `totalPages` (total pages in the document). For example, `<span class="title"></span>`
/// would generate span containing the title.
/// </summary>
public string HeaderTemplate { get; set; }
/// <summary>
/// HTML template for the print footer. Should use the same format as the
/// `headerTemplate`.
/// </summary>
public string FooterTemplate { get; set; }
/// <summary>
/// Whether or not to prefer page size as defined by css. Defaults to false, in
/// which case the content will be scaled to fit the paper size.
/// </summary>
public bool PreferCSSPageSize { get; set; } = false;
public Margins Margins { get; set; }
}

View File

@@ -1,10 +0,0 @@
namespace ElectronNET.API
{
/// <summary>
/// An object listing the version strings specific to Electron
/// </summary>
/// <param name="Chrome">Value representing Chrome's version string</param>
/// <param name="Electron">Value representing Electron's version string</param>
/// <returns></returns>
public record ProcessVersions(string Chrome, string Electron);
}

View File

@@ -0,0 +1,18 @@
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class ReleaseNoteInfo
{
/// <summary>
/// The version.
/// </summary>
public string Version { get; set; }
/// <summary>
/// The note.
/// </summary>
public string Note { get; set; }
}
}

View File

@@ -1,7 +1,4 @@
using ElectronNET.API.Entities;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Runtime.Versioning;
namespace ElectronNET.API
{
@@ -49,26 +46,16 @@ namespace ElectronNET.API
/// <summary>
/// Message to display above text fields.
/// </summary>
[SupportedOSPlatform("macos")]
public string Message { get; set; }
/// <summary>
/// Custom label for the text displayed in front of the filename text field.
/// </summary>
[SupportedOSPlatform("macos")]
public string NameFieldLabel { get; set; }
/// <summary>
/// Show the tags input box, defaults to true.
/// </summary>
[SupportedOSPlatform("macos")]
public bool ShowsTagField { get; set; }
/// <summary>
/// Contains which features the dialog should use. The following values are supported:
/// 'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory'
/// </summary>
[JsonProperty("properties", ItemConverterType = typeof(StringEnumConverter))]
public SaveDialogProperty[] Properties { get; set; }
}
}

View File

@@ -1,9 +0,0 @@
namespace ElectronNET.API.Entities
{
public class SecondInstanceResponse
{
public string[] args { get; set; }
public string workingDirectory { get;set;}
}
}

View File

@@ -20,17 +20,5 @@
/// The height.
/// </value>
public int Height { get; set; }
/// <summary>
/// Utility implicit conversion
/// </summary>
public static implicit operator SixLabors.ImageSharp.Size(Size s) =>
new (s.Width, s.Height);
/// <summary>
/// Utility implicit conversion
/// </summary>
public static implicit operator Size(SixLabors.ImageSharp.Size s) =>
new (){Height = s.Height, Width = s.Width};
}
}

View File

@@ -1,9 +0,0 @@
namespace ElectronNET.API.Entities
{
public sealed class SourcesOption
{
public string[] Types { get; set; }
public Size ThumbnailSize { get; set; }
public bool FetchWindowIcons { get; set; }
}
}

View File

@@ -28,11 +28,4 @@ namespace ElectronNET.API.Entities
/// </summary>
customButtonsOnHover
}
public class TitleBarOverlayConfig
{
public string color { get; set; }
public string symbolColor { get; set; }
public int height { get; set; }
}
}

View File

@@ -1,20 +1,13 @@
using System;
namespace ElectronNET.API.Entities
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
[Obsolete("Use ImageOptions instead.")]
public class ToBitmapOptions
{
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = NativeImage.DefaultScaleFactor;
/// <summary>
/// Utility conversion for obsolete class
/// </summary>
public static implicit operator ImageOptions(ToBitmapOptions o) => new () {ScaleFactor = o.ScaleFactor};
public float ScaleFactor { get; set; } = 1.0f;
}
}

View File

@@ -1,20 +1,13 @@
using System;
namespace ElectronNET.API.Entities
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
[Obsolete("Use ImageOptions instead.")]
public class ToDataUrlOptions
{
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = NativeImage.DefaultScaleFactor;
/// <summary>
/// Utility conversion for obsolete class
/// </summary>
public static implicit operator ImageOptions(ToDataUrlOptions o) => new () {ScaleFactor = o.ScaleFactor};
public float ScaleFactor { get; set; } = 1.0f;
}
}

View File

@@ -1,20 +1,13 @@
using System;
namespace ElectronNET.API.Entities
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
[Obsolete("Use ImageOptions instead.")]
public class ToPNGOptions
{
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
/// <summary>
/// Utility conversion for obsolete class
/// </summary>
public static implicit operator ImageOptions(ToPNGOptions o) => new () {ScaleFactor = o.ScaleFactor};
}
}

View File

@@ -1,6 +1,5 @@
namespace ElectronNET.API
{
/// <summary>
///
/// </summary>

View File

@@ -1,8 +0,0 @@
namespace ElectronNET.API.Entities
{
public class TrayClickEventResponse
{
public TrayClickEventArgs eventArgs { get; set; }
public Rectangle bounds { get; set; }
}
}

View File

@@ -1,6 +1,4 @@
using System;
namespace ElectronNET.API.Entities
namespace ElectronNET.API.Entities
{
/// <summary>
///
@@ -15,7 +13,7 @@ namespace ElectronNET.API.Entities
/// <summary>
///
/// </summary>
public UpdateFileInfo[] Files { get; set; } = Array.Empty<UpdateFileInfo>();
public UpdateFileInfo[] Files { get; set; } = new UpdateFileInfo[0];
/// <summary>
/// The release name.
@@ -25,7 +23,7 @@ namespace ElectronNET.API.Entities
/// <summary>
/// The release notes.
/// </summary>
public string ReleaseNotes { get; set; }
public ReleaseNoteInfo[] ReleaseNotes { get; set; } = new ReleaseNoteInfo[0];
/// <summary>
///

View File

@@ -1,5 +1,4 @@
using System.Runtime.Serialization;
using System;
using System.Runtime.Serialization;
namespace ElectronNET.API.Entities
{
@@ -12,19 +11,16 @@ namespace ElectronNET.API.Entities
/// The appearance based
/// </summary>
[EnumMember(Value = "appearance-based")]
[Obsolete("Removed in macOS Catalina (10.15).")]
appearanceBased,
/// <summary>
/// The light
/// </summary>
[Obsolete("Removed in macOS Catalina (10.15).")]
light,
/// <summary>
/// The dark
/// </summary>
[Obsolete("Removed in macOS Catalina (10.15).")]
dark,
/// <summary>
@@ -56,38 +52,12 @@ namespace ElectronNET.API.Entities
/// The medium light
/// </summary>
[EnumMember(Value = "medium-light")]
[Obsolete("Removed in macOS Catalina (10.15).")]
mediumLight,
/// <summary>
/// The ultra dark
/// </summary>
[EnumMember(Value = "ultra-dark")]
[Obsolete("Removed in macOS Catalina (10.15).")]
ultraDark,
header,
sheet,
window,
hud,
[EnumMember(Value = "fullscreen-ui")]
fullscreenUI,
tooltip,
content,
[EnumMember(Value = "under-window")]
underWindow,
[EnumMember(Value = "under-page")]
underPage
ultraDark
}
}
}

View File

@@ -171,12 +171,6 @@ namespace ElectronNET.API.Entities
/// </summary>
public bool Offscreen { get; set; }
/// <summary>
/// Whether to enable built-in spellcheck
/// </summary>
[DefaultValue(true)]
public bool Spellcheck { get; set; } = true;
/// <summary>
/// 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
@@ -190,11 +184,15 @@ namespace ElectronNET.API.Entities
/// 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.
/// Default value is false.
/// </summary>
[DefaultValue(false)]
public bool ContextIsolation { get; set; } = false;
/// <summary>
/// Whether to use native window.open(). Defaults to false. This option is currently experimental.
/// </summary>
public bool NativeWindowOpen { get; set; }
/// <summary>
/// Whether to enable the Webview. Defaults to the value of the nodeIntegration option. The
/// preload script configured for the Webview will have node integration enabled
@@ -210,9 +208,9 @@ namespace ElectronNET.API.Entities
public bool WebviewTag { get; set; } = false;
/// <summary>
/// Make the web view transparent
/// Whether to enable the remote module. Defaults to false.
/// </summary>
[DefaultValue(false)]
public bool Transparent { get; set; } = false;
public bool EnableRemoteModule { get; set; } = false;
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Globalization;
using System.Threading.Tasks;
namespace ElectronNET.API
{
@@ -9,12 +10,11 @@ namespace ElectronNET.API
internal class Events
{
private static Events _events;
private static readonly object _syncRoot = new();
private readonly TextInfo _ti = new CultureInfo("en-US", false).TextInfo;
private static readonly object SyncRoot = new();
private readonly TextInfo _textInfo = new CultureInfo("en-US", false).TextInfo;
private Events()
{
}
{}
public static Events Instance
{
@@ -22,7 +22,7 @@ namespace ElectronNET.API
{
if (_events == null)
{
lock (_syncRoot)
lock (SyncRoot)
{
if (_events == null)
{
@@ -40,8 +40,9 @@ namespace ElectronNET.API
/// </summary>
/// <param name="moduleName">The name of the module, e.g. app, dock, etc...</param>
/// <param name="eventName">The name of the event</param>
/// <param name="fn">The event handler</param>
public void On(string moduleName, string eventName, Action fn) => On(moduleName, eventName, _ => fn());
/// <param name="action">The event handler</param>
public void On(string moduleName, string eventName, Action action)
=> On(moduleName, eventName, action);
/// <summary>
@@ -49,14 +50,14 @@ namespace ElectronNET.API
/// </summary>
/// <param name="moduleName">The name of the module, e.g. app, dock, etc...</param>
/// <param name="eventName">The name of the event</param>
/// <param name="fn">The event handler</param>
public void On(string moduleName, string eventName, Action<object> fn)
/// <param name="action">The event handler</param>
public async Task On<T>(string moduleName, string eventName, Action<T> action)
{
var listener = $"{moduleName}{_ti.ToTitleCase(eventName)}Completed";
var listener = $"{moduleName}{_textInfo.ToTitleCase(eventName)}Completed";
var subscriber = $"register-{moduleName}-on-event";
BridgeConnector.On(listener, fn);
BridgeConnector.Emit(subscriber, eventName, listener);
BridgeConnector.Socket.On(listener, action);
await BridgeConnector.Socket.Emit(subscriber, eventName, listener);
}
/// <summary>
@@ -65,20 +66,23 @@ namespace ElectronNET.API
/// <param name="moduleName">The name of the module, e.g. app, dock, etc...</param>
/// <param name="eventName">The name of the event</param>
/// <param name="fn">The event handler</param>
public void Once(string moduleName, string eventName, Action fn) => Once(moduleName, eventName, _ => fn());
public void Once(string moduleName, string eventName, Action action)
=> Once(moduleName, eventName, action);
/// <summary>
/// Subscribe to an unmapped electron event.
/// </summary>
/// <param name="moduleName">The name of the module, e.g. app, dock, etc...</param>
/// <param name="eventName">The name of the event</param>
/// <param name="fn">The event handler</param>
public void Once(string moduleName, string eventName, Action<object> fn)
/// <param name="action">The event handler</param>
public async Task Once<T>(string moduleName, string eventName, Action<T> action)
{
var listener = $"{moduleName}{_ti.ToTitleCase(eventName)}Completed";
var listener = $"{moduleName}{_textInfo.ToTitleCase(eventName)}Completed";
var subscriber = $"register-{moduleName}-once-event";
BridgeConnector.Once(listener, fn);
BridgeConnector.Emit(subscriber, eventName, listener);
BridgeConnector.Socket.Once(listener, action);
await BridgeConnector.Socket.Emit(subscriber, eventName, listener);
}
}
}

View File

@@ -11,7 +11,7 @@ namespace ElectronNET.API.Extensions
Type type = enumerationValue.GetType();
if (!type.IsEnum)
{
throw new ArgumentException("EnumerationValue must be of Enum type", nameof(enumerationValue));
throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
}
//Tries to find a DescriptionAttribute for a potential friendly name

View File

@@ -28,7 +28,7 @@ namespace ElectronNET.API.Extensions
public static MenuItem GetMenuItem(this List<MenuItem> menuItems, string id)
{
MenuItem result = new();
MenuItem result = new MenuItem();
foreach (var item in menuItems)
{

View File

@@ -23,7 +23,7 @@ namespace ElectronNET.API.Extensions
public static ThumbarButton GetThumbarButton(this List<ThumbarButton> thumbarButtons, string id)
{
ThumbarButton result = new("");
ThumbarButton result = new ThumbarButton("");
foreach (var item in thumbarButtons)
{

34
ElectronNET.API/GlobalShortcut.cs Executable file → Normal file
View File

@@ -1,17 +1,16 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ElectronNET.API.Interfaces;
namespace ElectronNET.API
{
/// <summary>
/// Detect keyboard events when the application does not have keyboard focus.
/// </summary>
public sealed class GlobalShortcut : IGlobalShortcut
public sealed class GlobalShortcut
{
private static GlobalShortcut _globalShortcut;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal GlobalShortcut() { }
@@ -34,7 +33,7 @@ namespace ElectronNET.API
}
}
private readonly Dictionary<string, Action> _shortcuts = new();
private Dictionary<string, Action> _shortcuts = new Dictionary<string, Action>();
/// <summary>
/// Registers a global shortcut of accelerator.
@@ -50,16 +49,16 @@ namespace ElectronNET.API
{
_shortcuts.Add(accelerator, function);
BridgeConnector.Off("globalShortcut-pressed");
BridgeConnector.On<string>("globalShortcut-pressed", (shortcut) =>
BridgeConnector.Socket.Off("globalShortcut-pressed");
BridgeConnector.Socket.On("globalShortcut-pressed", (shortcut) =>
{
if (_shortcuts.ContainsKey(shortcut))
if (_shortcuts.ContainsKey(shortcut.ToString()))
{
_shortcuts[shortcut.ToString()]();
}
});
BridgeConnector.Emit("globalShortcut-register", accelerator);
BridgeConnector.Socket.Emit("globalShortcut-register", accelerator);
}
}
@@ -69,8 +68,21 @@ namespace ElectronNET.API
/// since they dont want applications to fight for global shortcuts.
/// </summary>
/// <returns>Whether this application has registered accelerator.</returns>
public Task<bool> IsRegisteredAsync(string accelerator) => BridgeConnector.OnResult<bool>("globalShortcut-isRegistered", "globalShortcut-isRegisteredCompleted", accelerator);
public Task<bool> IsRegisteredAsync(string accelerator)
{
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.Socket.On("globalShortcut-isRegisteredCompleted", (isRegistered) =>
{
BridgeConnector.Socket.Off("globalShortcut-isRegisteredCompleted");
taskCompletionSource.SetResult((bool)isRegistered);
});
BridgeConnector.Socket.Emit("globalShortcut-isRegistered", accelerator);
return taskCompletionSource.Task;
}
/// <summary>
/// Unregisters the global shortcut of accelerator.
@@ -78,7 +90,7 @@ namespace ElectronNET.API
public void Unregister(string accelerator)
{
_shortcuts.Remove(accelerator);
BridgeConnector.Emit("globalShortcut-unregister", accelerator);
BridgeConnector.Socket.Emit("globalShortcut-unregister", accelerator);
}
/// <summary>
@@ -87,7 +99,7 @@ namespace ElectronNET.API
public void UnregisterAll()
{
_shortcuts.Clear();
BridgeConnector.Emit("globalShortcut-unregisterAll");
BridgeConnector.Socket.Emit("globalShortcut-unregisterAll");
}
}
}

View File

@@ -0,0 +1,3 @@
// Global using directives
global using System.Web;

71
ElectronNET.API/HostHook.cs Executable file → Normal file
View File

@@ -3,7 +3,6 @@ using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Threading.Tasks;
using ElectronNET.API.Interfaces;
namespace ElectronNET.API
{
@@ -14,11 +13,11 @@ namespace ElectronNET.API
/// ElectronHostHook directory:
/// <c>electronize add HostHook</c>
/// </summary>
public sealed class HostHook : IHostHook
public sealed class HostHook
{
private static HostHook _electronHostHook;
private static readonly object _syncRoot = new();
readonly string oneCallguid = Guid.NewGuid().ToString();
private static object _syncRoot = new object();
string oneCallguid = Guid.NewGuid().ToString();
internal HostHook() { }
@@ -48,13 +47,13 @@ namespace ElectronNET.API
/// <param name="arguments">Optional parameters.</param>
public void Call(string socketEventName, params dynamic[] arguments)
{
BridgeConnector.On<string>(socketEventName + "Error" + oneCallguid, (result) =>
BridgeConnector.Socket.On(socketEventName + "Error" + oneCallguid, (result) =>
{
BridgeConnector.Off(socketEventName + "Error" + oneCallguid);
Electron.Dialog.ShowErrorBox("Host Hook Exception", result);
BridgeConnector.Socket.Off(socketEventName + "Error" + oneCallguid);
Electron.Dialog.ShowErrorBox("Host Hook Exception", result.ToString());
});
BridgeConnector.Emit(socketEventName, arguments, oneCallguid);
BridgeConnector.Socket.Emit(socketEventName, arguments, oneCallguid);
}
/// <summary>
@@ -66,26 +65,64 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<T> CallAsync<T>(string socketEventName, params dynamic[] arguments)
{
var taskCompletionSource = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<T>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<string>(socketEventName + "Error" + guid, (result) =>
BridgeConnector.Socket.On(socketEventName + "Error" + guid, (result) =>
{
BridgeConnector.Off(socketEventName + "Error" + guid);
Electron.Dialog.ShowErrorBox("Host Hook Exception", result);
BridgeConnector.Socket.Off(socketEventName + "Error" + guid);
Electron.Dialog.ShowErrorBox("Host Hook Exception", result.ToString());
taskCompletionSource.SetException(new Exception($"Host Hook Exception {result}"));
});
BridgeConnector.On<T>(socketEventName + "Complete" + guid, (result) =>
BridgeConnector.Socket.On(socketEventName + "Complete" + guid, (result) =>
{
BridgeConnector.Off(socketEventName + "Error" + guid);
BridgeConnector.Off(socketEventName + "Complete" + guid);
taskCompletionSource.SetResult(result);
BridgeConnector.Socket.Off(socketEventName + "Error" + guid);
BridgeConnector.Socket.Off(socketEventName + "Complete" + guid);
T data = default;
try
{
if (result.GetType().IsValueType || result is string)
{
data = (T)result;
}
else
{
var token = JToken.Parse(result.ToString());
if (token is JArray)
{
data = token.ToObject<T>();
}
else if (token is JObject)
{
data = token.ToObject<T>();
}
else
{
data = (T)result;
}
}
}
catch (Exception exception)
{
taskCompletionSource.SetException(exception);
//throw new InvalidCastException("Return value does not match with the generic type.", exception);
}
taskCompletionSource.SetResult(data);
});
BridgeConnector.Emit(socketEventName, arguments, guid);
BridgeConnector.Socket.Emit(socketEventName, arguments, guid);
return taskCompletionSource.Task;
}
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
}
}

View File

@@ -1,709 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Control your application's event lifecycle.
/// </summary>
public interface IApp
{
/// <summary>
/// Emitted when all windows have been closed.
/// <para/>
/// If you do not subscribe to this event and all windows are closed, the default behavior is to quit
/// the app; however, if you subscribe, you control whether the app quits or not.If the user pressed
/// Cmd + Q, or the developer called <see cref="Quit"/>, Electron will first try to close all the windows
/// and then emit the <see cref="WillQuit"/> event, and in this case the <see cref="WindowAllClosed"/> event
/// would not be emitted.
/// </summary>
event Action WindowAllClosed;
/// <summary>
/// Emitted before the application starts closing its windows.
/// <para/>
/// Note: If application quit was initiated by <see cref="AutoUpdater.QuitAndInstall"/> then <see cref="BeforeQuit"/>
/// is emitted after emitting close event on all windows and closing them.
/// <para/>
/// Note: On Windows, this event will not be emitted if the app is closed due to a shutdown/restart of the system or a user logout.
/// </summary>
event Func<QuitEventArgs, Task> BeforeQuit;
/// <summary>
/// Emitted when all windows have been closed and the application will quit.
/// <para/>
/// See the description of the <see cref="WindowAllClosed"/> event for the differences between the <see cref="WillQuit"/>
/// and <see cref="WindowAllClosed"/> events.
/// <para/>
/// Note: On Windows, this event will not be emitted if the app is closed due to a shutdown/restart of the system or a user logout.
/// </summary>
event Func<QuitEventArgs, Task> WillQuit;
/// <summary>
/// Emitted when the application is quitting.
/// <para/>
/// Note: On Windows, this event will not be emitted if the app is closed due to a shutdown/restart of the system or a user logout.
/// </summary>
event Func<Task> Quitting;
/// <summary>
/// Emitted when a <see cref="BrowserWindow"/> blurred.
/// </summary>
event Action BrowserWindowBlur;
/// <summary>
/// Emitted when a <see cref="BrowserWindow"/> gets focused.
/// </summary>
event Action BrowserWindowFocus;
/// <summary>
/// Emitted when a new <see cref="BrowserWindow"/> is created.
/// </summary>
event Action BrowserWindowCreated;
/// <summary>
/// Emitted when a new <see cref="WebContents"/> is created.
/// </summary>
event Action WebContentsCreated;
/// <summary>
/// Emitted when Chromes accessibility support changes. This event fires when assistive technologies, such as
/// screen readers, are enabled or disabled. See https://www.chromium.org/developers/design-documents/accessibility for more details.
/// </summary>
/// <returns><see langword="true"/> when Chrome's accessibility support is enabled, <see langword="false"/> otherwise.</returns>
event Action<bool> AccessibilitySupportChanged;
/// <summary>
/// Emitted when the application has finished basic startup.
/// </summary>
event Action Ready;
/// <summary>
/// Application host fully started.
/// </summary>
bool IsReady { get; }
/// <summary>
/// A <see cref="string"/> property that indicates the current application's name, which is the name in the
/// application's package.json file.
///
/// Usually the name field of package.json is a short lowercase name, according to the npm modules spec. You
/// should usually also specify a productName field, which is your application's full capitalized name, and
/// which will be preferred over name by Electron.
/// </summary>
string Name
{
set;
}
/// <summary>
/// A <see cref="string"/> property that indicates the current application's name, which is the name in the
/// application's package.json file.
///
/// Usually the name field of package.json is a short lowercase name, according to the npm modules spec. You
/// should usually also specify a productName field, which is your application's full capitalized name, and
/// which will be preferred over name by Electron.
/// </summary>
Task<string> GetNameAsync { get; }
/// <summary>
/// A <see cref="CommandLine"/> object that allows you to read and manipulate the command line arguments that Chromium uses.
/// </summary>
CommandLine CommandLine { get; }
/// <summary>
/// A <see cref="string"/> which is the user agent string Electron will use as a global fallback.
/// <para/>
/// This is the user agent that will be used when no user agent is set at the webContents or
/// session level. It is useful for ensuring that your entire app has the same user agent. Set to a
/// custom value as early as possible in your app's initialization to ensure that your overridden value
/// is used.
/// </summary>
string UserAgentFallback
{
set;
}
/// <summary>
/// A <see cref="string"/> which is the user agent string Electron will use as a global fallback.
/// <para/>
/// This is the user agent that will be used when no user agent is set at the webContents or
/// session level. It is useful for ensuring that your entire app has the same user agent. Set to a
/// custom value as early as possible in your app's initialization to ensure that your overridden value
/// is used.
/// </summary>
Task<string> GetUserAgentFallbackAsync { get; }
/// <summary>
/// Emitted when a MacOS user wants to open a file with the application. The open-file event is usually emitted
/// when the application is already open and the OS wants to reuse the application to open the file.
/// open-file is also emitted when a file is dropped onto the dock and the application is not yet running.
/// <para/>
/// On Windows, you have to parse the arguments using App.CommandLine to get the filepath.
/// </summary>
event Action<string> OpenFile;
/// <summary>
/// Emitted when a MacOS user wants to open a URL with the application. Your application's Info.plist file must
/// define the URL scheme within the CFBundleURLTypes key, and set NSPrincipalClass to AtomApplication.
/// </summary>
event Action<string> OpenUrl;
/// <summary>
/// Try to close all windows. The <see cref="BeforeQuit"/> event will be emitted first. If all windows are successfully
/// closed, the <see cref="WillQuit"/> event will be emitted and by default the application will terminate. This method
/// guarantees that all beforeunload and unload event handlers are correctly executed. It is possible
/// that a window cancels the quitting by returning <see langword="false"/> in the beforeunload event handler.
/// </summary>
void Quit();
/// <summary>
/// All windows will be closed immediately without asking user and the <see cref="BeforeQuit"/> and <see cref="WillQuit"/>
/// events will not be emitted.
/// </summary>
/// <param name="exitCode">Exits immediately with exitCode. exitCode defaults to 0.</param>
void Exit(int exitCode = 0);
/// <summary>
/// Relaunches the app when current instance exits. By default the new instance will use the same working directory
/// and command line arguments with current instance.
/// <para/>
/// Note that this method does not quit the app when executed, you have to call <see cref="Quit"/> or <see cref="Exit"/>
/// after calling <see cref="Relaunch()"/> to make the app restart.
/// <para/>
/// When <see cref="Relaunch()"/> is called for multiple times, multiple instances will be started after current instance
/// exited.
/// </summary>
void Relaunch();
/// <summary>
/// Relaunches the app when current instance exits. By default the new instance will use the same working directory
/// and command line arguments with current instance. When <see cref="RelaunchOptions.Args"/> is specified, the
/// <see cref="RelaunchOptions.Args"/> will be passed as command line arguments instead. When <see cref="RelaunchOptions.ExecPath"/>
/// is specified, the <see cref="RelaunchOptions.ExecPath"/> will be executed for relaunch instead of current app.
/// <para/>
/// Note that this method does not quit the app when executed, you have to call <see cref="Quit"/> or <see cref="Exit"/>
/// after calling <see cref="Relaunch()"/> to make the app restart.
/// <para/>
/// When <see cref="Relaunch()"/> is called for multiple times, multiple instances will be started after current instance
/// exited.
/// </summary>
/// <param name="relaunchOptions">Options for the relaunch.</param>
void Relaunch(RelaunchOptions relaunchOptions);
/// <summary>
/// On Linux, focuses on the first visible window. On macOS, makes the application the active app. On Windows, focuses
/// on the application's first window.
/// </summary>
void Focus();
/// <summary>
/// On Linux, focuses on the first visible window. On macOS, makes the application the active app. On Windows, focuses
/// on the application's first window.
/// <para/>
/// You should seek to use the <see cref="FocusOptions.Steal"/> option as sparingly as possible.
/// </summary>
void Focus(FocusOptions focusOptions);
/// <summary>
/// Hides all application windows without minimizing them.
/// </summary>
void Hide();
/// <summary>
/// Shows application windows after they were hidden. Does not automatically focus them.
/// </summary>
void Show();
/// <summary>
/// The current application directory.
/// </summary>
Task<string> GetAppPathAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Sets or creates a directory your app's logs which can then be manipulated with <see cref="GetPathAsync"/>
/// or <see cref="SetPath"/>.
/// <para/>
/// Calling <see cref="SetAppLogsPath"/> without a path parameter will result in this directory being set to
/// ~/Library/Logs/YourAppName on macOS, and inside the userData directory on Linux and Windows.
/// </summary>
/// <param name="path">A custom path for your logs. Must be absolute.</param>
void SetAppLogsPath(string path);
/// <summary>
/// The path to a special directory. If <see cref="GetPathAsync"/> is called without called
/// <see cref="SetAppLogsPath"/> being called first, a default directory will be created equivalent
/// to calling <see cref="SetAppLogsPath"/> without a path parameter.
/// </summary>
/// <param name="pathName">Special directory.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A path to a special directory or file associated with name.</returns>
Task<string> GetPathAsync(PathName pathName, CancellationToken cancellationToken = default);
/// <summary>
/// Overrides the path to a special directory or file associated with name. If the path specifies a directory
/// that does not exist, an Error is thrown. In that case, the directory should be created with fs.mkdirSync or similar.
/// <para/>
/// You can only override paths of a name defined in <see cref="GetPathAsync"/>.
/// <para/>
/// By default, web pages' cookies and caches will be stored under the <see cref="PathName.UserData"/> directory. If you
/// want to change this location, you have to override the <see cref="PathName.UserData"/> path before the <see cref="Ready"/>
/// event of the <see cref="App"/> module is emitted.
/// <param name="name">Special directory.</param>
/// <param name="path">New path to a special directory.</param>
/// </summary>
void SetPath(PathName name, string path);
/// <summary>
/// The version of the loaded application. If no version is found in the applications package.json file,
/// the version of the current bundle or executable is returned.
/// </summary>
/// <returns>The version of the loaded application.</returns>
Task<string> GetVersionAsync(CancellationToken cancellationToken = default);
/// <summary>
/// The current application locale. Possible return values are documented <see href="https://www.electronjs.org/docs/api/locales">here</see>.
/// <para/>
/// Note: When distributing your packaged app, you have to also ship the locales folder.
/// <para/>
/// Note: On Windows, you have to call it after the <see cref="Ready"/> events gets emitted.
/// </summary>
/// <returns>The current application locale.</returns>
Task<string> GetLocaleAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Adds path to the recent documents list. This list is managed by the OS. On Windows you can visit the
/// list from the task bar, and on macOS you can visit it from dock menu.
/// </summary>
/// <param name="path">Path to add.</param>
void AddRecentDocument(string path);
/// <summary>
/// Clears the recent documents list.
/// </summary>
void ClearRecentDocuments();
/// <summary>
/// Sets the current executable as the default handler for a protocol (aka URI scheme). It allows you to
/// integrate your app deeper into the operating system. Once registered, all links with your-protocol://
/// will be opened with the current executable. The whole link, including protocol, will be passed to your
/// application as a parameter.
/// <para/>
/// Note: On macOS, you can only register protocols that have been added to your app's info.plist, which
/// cannot be modified at runtime. However, you can change the file during build time via
/// <see href="https://www.electronforge.io/">Electron Forge</see>,
/// <see href="https://github.com/electron/electron-packager">Electron Packager</see>, or by editing info.plist
/// with a text editor. Please refer to
/// <see href="https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115">Apple's documentation</see>
/// for details.
/// <para/>
/// Note: In a Windows Store environment (when packaged as an appx) this API will return true for all calls but
/// the registry key it sets won't be accessible by other applications. In order to register your Windows Store
/// application as a default protocol handler you <see href="https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol">must declare the protocol in your manifest</see>.
/// <para/>
/// The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally.
/// </summary>
/// <param name="protocol">
/// The name of your protocol, without ://. For example, if you want your app to handle electron:// links,
/// call this method with electron as the parameter.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
Task<bool> SetAsDefaultProtocolClientAsync(string protocol, CancellationToken cancellationToken = default);
/// <summary>
/// Sets the current executable as the default handler for a protocol (aka URI scheme). It allows you to
/// integrate your app deeper into the operating system. Once registered, all links with your-protocol://
/// will be opened with the current executable. The whole link, including protocol, will be passed to your
/// application as a parameter.
/// <para/>
/// Note: On macOS, you can only register protocols that have been added to your app's info.plist, which
/// cannot be modified at runtime. However, you can change the file during build time via
/// <see href="https://www.electronforge.io/">Electron Forge</see>,
/// <see href="https://github.com/electron/electron-packager">Electron Packager</see>, or by editing info.plist
/// with a text editor. Please refer to
/// <see href="https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115">Apple's documentation</see>
/// for details.
/// <para/>
/// Note: In a Windows Store environment (when packaged as an appx) this API will return true for all calls but
/// the registry key it sets won't be accessible by other applications. In order to register your Windows Store
/// application as a default protocol handler you <see href="https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol">must declare the protocol in your manifest</see>.
/// <para/>
/// The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally.
/// </summary>
/// <param name="protocol">
/// The name of your protocol, without ://. For example, if you want your app to handle electron:// links,
/// call this method with electron as the parameter.</param>
/// <param name="path">The path to the Electron executable. Defaults to process.execPath</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
Task<bool> SetAsDefaultProtocolClientAsync(string protocol, string path, CancellationToken cancellationToken = default);
/// <summary>
/// Sets the current executable as the default handler for a protocol (aka URI scheme). It allows you to
/// integrate your app deeper into the operating system. Once registered, all links with your-protocol://
/// will be opened with the current executable. The whole link, including protocol, will be passed to your
/// application as a parameter.
/// <para/>
/// Note: On macOS, you can only register protocols that have been added to your app's info.plist, which
/// cannot be modified at runtime. However, you can change the file during build time via
/// <see href="https://www.electronforge.io/">Electron Forge</see>,
/// <see href="https://github.com/electron/electron-packager">Electron Packager</see>, or by editing info.plist
/// with a text editor. Please refer to
/// <see href="https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115">Apple's documentation</see>
/// for details.
/// <para/>
/// Note: In a Windows Store environment (when packaged as an appx) this API will return true for all calls but
/// the registry key it sets won't be accessible by other applications. In order to register your Windows Store
/// application as a default protocol handler you <see href="https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol">must declare the protocol in your manifest</see>.
/// <para/>
/// The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally.
/// </summary>
/// <param name="protocol">
/// The name of your protocol, without ://. For example, if you want your app to handle electron:// links,
/// call this method with electron as the parameter.</param>
/// <param name="path">The path to the Electron executable. Defaults to process.execPath</param>
/// <param name="args">Arguments passed to the executable. Defaults to an empty array.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
Task<bool> SetAsDefaultProtocolClientAsync(string protocol, string path, string[] args, CancellationToken cancellationToken = default);
/// <summary>
/// This method checks if the current executable as the default handler for a protocol (aka URI scheme).
/// If so, it will remove the app as the default handler.
/// </summary>
/// <param name="protocol">The name of your protocol, without ://.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
Task<bool> RemoveAsDefaultProtocolClientAsync(string protocol, CancellationToken cancellationToken = default);
/// <summary>
/// This method checks if the current executable as the default handler for a protocol (aka URI scheme).
/// If so, it will remove the app as the default handler.
/// </summary>
/// <param name="protocol">The name of your protocol, without ://.</param>
/// <param name="path">Defaults to process.execPath.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
Task<bool> RemoveAsDefaultProtocolClientAsync(string protocol, string path, CancellationToken cancellationToken = default);
/// <summary>
/// This method checks if the current executable as the default handler for a protocol (aka URI scheme).
/// If so, it will remove the app as the default handler.
/// </summary>
/// <param name="protocol">The name of your protocol, without ://.</param>
/// <param name="path">Defaults to process.execPath.</param>
/// <param name="args">Defaults to an empty array.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
Task<bool> RemoveAsDefaultProtocolClientAsync(string protocol, string path, string[] args, CancellationToken cancellationToken = default);
/// <summary>
/// This method checks if the current executable is the default handler for a protocol (aka URI scheme).
/// <para/>
/// Note: On macOS, you can use this method to check if the app has been registered as the default protocol
/// handler for a protocol. You can also verify this by checking ~/Library/Preferences/com.apple.LaunchServices.plist
/// on the macOS machine. Please refer to <see href="https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme">Apple's documentation</see>
/// for details.
/// <para/>
/// The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally.
/// </summary>
/// <param name="protocol">The name of your protocol, without ://.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the current executable is the default handler for a protocol (aka URI scheme).</returns>
Task<bool> IsDefaultProtocolClientAsync(string protocol, CancellationToken cancellationToken = default);
/// <summary>
/// This method checks if the current executable is the default handler for a protocol (aka URI scheme).
/// <para/>
/// Note: On macOS, you can use this method to check if the app has been registered as the default protocol
/// handler for a protocol. You can also verify this by checking ~/Library/Preferences/com.apple.LaunchServices.plist
/// on the macOS machine. Please refer to <see href="https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme">Apple's documentation</see>
/// for details.
/// <para/>
/// The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally.
/// </summary>
/// <param name="protocol">The name of your protocol, without ://.</param>
/// <param name="path">Defaults to process.execPath.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the current executable is the default handler for a protocol (aka URI scheme).</returns>
Task<bool> IsDefaultProtocolClientAsync(string protocol, string path, CancellationToken cancellationToken = default);
/// <summary>
/// This method checks if the current executable is the default handler for a protocol (aka URI scheme).
/// <para/>
/// Note: On macOS, you can use this method to check if the app has been registered as the default protocol
/// handler for a protocol. You can also verify this by checking ~/Library/Preferences/com.apple.LaunchServices.plist
/// on the macOS machine. Please refer to <see href="https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme">Apple's documentation</see>
/// for details.
/// <para/>
/// The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally.
/// </summary>
/// <param name="protocol">The name of your protocol, without ://.</param>
/// <param name="path">Defaults to process.execPath.</param>
/// <param name="args">Defaults to an empty array.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the current executable is the default handler for a protocol (aka URI scheme).</returns>
Task<bool> IsDefaultProtocolClientAsync(string protocol, string path, string[] args, CancellationToken cancellationToken = default);
/// <summary>
/// Adds tasks to the <see cref="UserTask"/> category of the JumpList on Windows.
/// <para/>
/// Note: If you'd like to customize the Jump List even more use <see cref="SetJumpList"/> instead.
/// </summary>
/// <param name="userTasks">Array of <see cref="UserTask"/> objects.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
Task<bool> SetUserTasksAsync(UserTask[] userTasks, CancellationToken cancellationToken = default);
/// <summary>
/// Jump List settings for the application.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Jump List settings.</returns>
Task<JumpListSettings> GetJumpListSettingsAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Sets or removes a custom Jump List for the application. If categories is null the previously set custom
/// Jump List (if any) will be replaced by the standard Jump List for the app (managed by Windows).
/// <para/>
/// Note: If a <see cref="JumpListCategory"/> object has neither the <see cref="JumpListCategory.Type"/> nor
/// the <see cref="JumpListCategory.Name"/> property set then its <see cref="JumpListCategory.Type"/> is assumed
/// to be <see cref="JumpListCategoryType.tasks"/>. If the <see cref="JumpListCategory.Name"/> property is set but
/// the <see cref="JumpListCategory.Type"/> property is omitted then the <see cref="JumpListCategory.Type"/> is
/// assumed to be <see cref="JumpListCategoryType.custom"/>.
/// <para/>
/// Note: Users can remove items from custom categories, and Windows will not allow a removed item to be added
/// back into a custom category until after the next successful call to <see cref="SetJumpList"/>. Any attempt
/// to re-add a removed item to a custom category earlier than that will result in the entire custom category being
/// omitted from the Jump List. The list of removed items can be obtained using <see cref="GetJumpListSettingsAsync"/>.
/// </summary>
/// <param name="categories">Array of <see cref="JumpListCategory"/> objects.</param>
void SetJumpList(JumpListCategory[] categories);
/// <summary>
/// The return value of this method indicates whether or not this instance of your application successfully obtained
/// the lock. If it failed to obtain the lock, you can assume that another instance of your application is already
/// running with the lock and exit immediately.
/// <para/>
/// I.e.This method returns <see langword="true"/> if your process is the primary instance of your application and your
/// app should continue loading. It returns <see langword="false"/> if your process should immediately quit as it has
/// sent its parameters to another instance that has already acquired the lock.
/// <para/>
/// On macOS, the system enforces single instance automatically when users try to open a second instance of your app
/// in Finder, and the open-file and open-url events will be emitted for that.However when users start your app in
/// command line, the system's single instance mechanism will be bypassed, and you have to use this method to ensure
/// single instance.
/// </summary>
/// <param name="newInstanceOpened">Lambda with an array of the second instances command line arguments.
/// The second parameter is the working directory path.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>This method returns false if your process is the primary instance of the application and your app
/// should continue loading. And returns true if your process has sent its parameters to another instance, and
/// you should immediately quit.
/// </returns>
Task<bool> RequestSingleInstanceLockAsync(Action<string[], string> newInstanceOpened, CancellationToken cancellationToken = default);
/// <summary>
/// Releases all locks that were created by makeSingleInstance. This will allow
/// multiple instances of the application to once again run side by side.
/// </summary>
void ReleaseSingleInstanceLock();
/// <summary>
/// This method returns whether or not this instance of your app is currently holding the single instance lock.
/// You can request the lock with <see cref="RequestSingleInstanceLockAsync"/> and release with
/// <see cref="ReleaseSingleInstanceLock"/>.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
Task<bool> HasSingleInstanceLockAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Creates an NSUserActivity and sets it as the current activity. The activity is
/// eligible for <see href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html">Handoff</see>
/// to another device afterward.
/// </summary>
/// <param name="type">Uniquely identifies the activity. Maps to <see href="https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUserActivity_Class/index.html#//apple_ref/occ/instp/NSUserActivity/activityType">NSUserActivity.activityType</see>.</param>
/// <param name="userInfo">App-specific state to store for use by another device.</param>
void SetUserActivity(string type, object userInfo);
/// <summary>
/// Creates an NSUserActivity and sets it as the current activity. The activity is
/// eligible for <see href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html">Handoff</see>
/// to another device afterward.
/// </summary>
/// <param name="type">
/// Uniquely identifies the activity. Maps to <see href="https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUserActivity_Class/index.html#//apple_ref/occ/instp/NSUserActivity/activityType">NSUserActivity.activityType</see>.
/// </param>
/// <param name="userInfo">App-specific state to store for use by another device.</param>
/// <param name="webpageUrl">
/// The webpage to load in a browser if no suitable app is installed on the resuming device. The scheme must be http or https.
/// </param>
void SetUserActivity(string type, object userInfo, string webpageUrl);
/// <summary>
/// The type of the currently running activity.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
Task<string> GetCurrentActivityTypeAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Invalidates the current <see href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html">Handoff</see> user activity.
/// </summary>
void InvalidateCurrentActivity();
/// <summary>
/// Marks the current <see href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html">Handoff</see> user activity as inactive without invalidating it.
/// </summary>
void ResignCurrentActivity();
/// <summary>
/// Changes the <see href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx">Application User Model ID</see> to id.
/// </summary>
/// <param name="id">Model Id.</param>
void SetAppUserModelId(string id);
/// TODO: Check new parameter which is a function [App.ImportCertificate]
/// <summary>
/// Imports the certificate in pkcs12 format into the platform certificate store.
/// callback is called with the result of import operation, a value of 0 indicates
/// success while any other value indicates failure according to chromium net_error_list.
/// </summary>
/// <param name="options"></param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Result of import. Value of 0 indicates success.</returns>
Task<int> ImportCertificateAsync(ImportCertificateOptions options, CancellationToken cancellationToken = default);
/// <summary>
/// Memory and cpu usage statistics of all the processes associated with the app.
/// </summary>
/// <returns>
/// Array of ProcessMetric objects that correspond to memory and cpu usage
/// statistics of all the processes associated with the app.
/// <param name="cancellationToken">The cancellation token.</param>
/// </returns>
Task<ProcessMetric[]> GetAppMetricsAsync(CancellationToken cancellationToken = default);
/// <summary>
/// The Graphics Feature Status from chrome://gpu/.
/// <para/>
/// Note: This information is only usable after the gpu-info-update event is emitted.
/// <param name="cancellationToken">The cancellation token.</param>
/// </summary>
Task<GPUFeatureStatus> GetGpuFeatureStatusAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Sets the counter badge for current app. Setting the count to 0 will hide the badge.
/// On macOS it shows on the dock icon. On Linux it only works for Unity launcher.
/// <para/>
/// Note: Unity launcher requires the existence of a .desktop file to work, for more
/// information please read <see href="https://www.electronjs.org/docs/tutorial/desktop-environment-integration#unity-launcher">Desktop Environment Integration</see>.
/// </summary>
/// <param name="count">Counter badge.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the call succeeded.</returns>
Task<bool> SetBadgeCountAsync(int count, CancellationToken cancellationToken = default);
/// <summary>
/// The current value displayed in the counter badge.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
Task<int> GetBadgeCountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Whether the current desktop environment is Unity launcher.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
Task<bool> IsUnityRunningAsync(CancellationToken cancellationToken = default);
/// <summary>
/// If you provided path and args options to <see cref="SetLoginItemSettings"/> then you need to pass the same
/// arguments here for <see cref="LoginItemSettings.OpenAtLogin"/> to be set correctly.
/// </summary>
Task<LoginItemSettings> GetLoginItemSettingsAsync(CancellationToken cancellationToken = default);
/// <summary>
/// If you provided path and args options to <see cref="SetLoginItemSettings"/> then you need to pass the same
/// arguments here for <see cref="LoginItemSettings.OpenAtLogin"/> to be set correctly.
/// </summary>
/// <param name="options"></param>
/// <param name="cancellationToken">The cancellation token.</param>
Task<LoginItemSettings> GetLoginItemSettingsAsync(LoginItemSettingsOptions options, CancellationToken cancellationToken = default);
/// <summary>
/// Set the app's login item settings.
/// To work with Electron's autoUpdater on Windows, which uses <see href="https://github.com/Squirrel/Squirrel.Windows">Squirrel</see>,
/// you'll want to set the launch path to Update.exe, and pass arguments that specify your application name.
/// </summary>
/// <param name="loginSettings"></param>
void SetLoginItemSettings(LoginSettings loginSettings);
/// <summary>
/// <see langword="true"/> if Chrome's accessibility support is enabled, <see langword="false"/> otherwise. This API will
/// return <see langword="true"/> if the use of assistive technologies, such as screen readers, has been detected.
/// See <see href="chromium.org/developers/design-documents/accessibility">Chromium's accessibility docs</see> for more details.
/// </summary>
/// <returns><see langword="true"/> if Chromes accessibility support is enabled, <see langword="false"/> otherwise.</returns>
Task<bool> IsAccessibilitySupportEnabledAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Manually enables Chrome's accessibility support, allowing to expose accessibility switch to users in application settings.
/// See <see href="chromium.org/developers/design-documents/accessibility">Chromium's accessibility docs</see> for more details.
/// Disabled (<see langword="false"/>) by default.
/// <para/>
/// This API must be called after the <see cref="Ready"/> event is emitted.
/// <para/>
/// Note: Rendering accessibility tree can significantly affect the performance of your app. It should not be enabled by default.
/// </summary>
/// <param name="enabled">Enable or disable <see href="https://developers.google.com/web/fundamentals/accessibility/semantics-builtin/the-accessibility-tree">accessibility tree</see> rendering.</param>
void SetAccessibilitySupportEnabled(bool enabled);
/// <summary>
/// Show the app's about panel options. These options can be overridden with
/// <see cref="SetAboutPanelOptions"/>.
/// </summary>
void ShowAboutPanel();
/// <summary>
/// Set the about panel options. This will override the values defined in the app's .plist file on macOS. See the
/// <see href="https://developer.apple.com/reference/appkit/nsapplication/1428479-orderfrontstandardaboutpanelwith?language=objc">Apple docs</see>
/// for more details. On Linux, values must be set in order to be shown; there are no defaults.
/// <para/>
/// If you do not set credits but still wish to surface them in your app, AppKit will look for a file named "Credits.html",
/// "Credits.rtf", and "Credits.rtfd", in that order, in the bundle returned by the NSBundle class method main. The first file
/// found is used, and if none is found, the info area is left blank. See Apple
/// <see href="https://developer.apple.com/documentation/appkit/nsaboutpaneloptioncredits?language=objc">documentation</see> for more information.
/// </summary>
/// <param name="options">About panel options.</param>
void SetAboutPanelOptions(AboutPanelOptions options);
/// <summary>
/// Subscribe to an unmapped event on the <see cref="App"/> module.
/// </summary>
/// <param name="eventName">The event name</param>
/// <param name="fn">The handler</param>
void On(string eventName, Action fn);
/// <summary>
/// Subscribe to an unmapped event on the <see cref="App"/> module.
/// </summary>
/// <param name="eventName">The event name</param>
/// <param name="fn">The handler</param>
void On(string eventName, Action<object> fn);
/// <summary>
/// Subscribe to an unmapped event on the <see cref="App"/> module once.
/// </summary>
/// <param name="eventName">The event name</param>
/// <param name="fn">The handler</param>
void Once(string eventName, Action fn);
/// <summary>
/// Subscribe to an unmapped event on the <see cref="App"/> module once.
/// </summary>
/// <param name="eventName">The event name</param>
/// <param name="fn">The handler</param>
void Once(string eventName, Action<object> fn);
}
}

View File

@@ -1,168 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Enable apps to automatically update themselves. Based on electron-updater.
/// </summary>
public interface IAutoUpdater
{
/// <summary>
/// Whether to automatically download an update when it is found. (Default is true)
/// </summary>
bool AutoDownload { set; }
/// <summary>
/// Whether to automatically download an update when it is found. (Default is true)
/// </summary>
Task<bool> IsAutoDownloadEnabledAsync { get; }
/// <summary>
/// Whether to automatically install a downloaded update on app quit (if `QuitAndInstall` was not called before).
///
/// Applicable only on Windows and Linux.
/// </summary>
bool AutoInstallOnAppQuit { set; }
/// <summary>
/// Whether to automatically install a downloaded update on app quit (if `QuitAndInstall` was not called before).
///
/// Applicable only on Windows and Linux.
/// </summary>
Task<bool> IsAutoInstallOnAppQuitEnabledAsync { get; }
/// <summary>
/// *GitHub provider only.* Whether to allow update to pre-release versions.
/// Defaults to "true" if application version contains prerelease components (e.g. "0.12.1-alpha.1", here "alpha" is a prerelease component), otherwise "false".
///
/// If "true", downgrade will be allowed("allowDowngrade" will be set to "true").
/// </summary>
bool AllowPrerelease { set; }
/// <summary>
/// *GitHub provider only.* Whether to allow update to pre-release versions.
/// Defaults to "true" if application version contains prerelease components (e.g. "0.12.1-alpha.1", here "alpha" is a prerelease component), otherwise "false".
///
/// If "true", downgrade will be allowed("allowDowngrade" will be set to "true").
/// </summary>
Task<bool> IsAllowPrereleaseEnabledAsync { get; }
/// <summary>
/// *GitHub provider only.*
/// Get all release notes (from current version to latest), not just the latest (Default is false).
/// </summary>
bool FullChangelog { set; }
/// <summary>
/// *GitHub provider only.*
/// Get all release notes (from current version to latest), not just the latest (Default is false).
/// </summary>
Task<bool> IsFullChangeLogEnabledAsync { get; }
/// <summary>
/// Whether to allow version downgrade (when a user from the beta channel wants to go back to the stable channel).
/// Taken in account only if channel differs (pre-release version component in terms of semantic versioning).
/// Default is false.
/// </summary>
bool AllowDowngrade { set; }
Task<bool> IsAllowDowngradeEnabledAsync { get; }
/// <summary>
/// For test only.
/// </summary>
Task<string> GetUpdateConfigPathAsync { get; }
/// <summary>
/// The current application version
/// </summary>
Task<SemVer> GetCurrentVersionAsync { get; }
/// <summary>
/// Get the update channel. Not applicable for GitHub.
/// Doesnt return channel from the update configuration, only if was previously set.
/// </summary>
Task<string> GetChannelAsync { get; }
/// <summary>
/// The request headers.
/// </summary>
Task<Dictionary<string, string>> GetRequestHeadersAsync { get; }
/// <summary>
/// The request headers.
/// </summary>
Dictionary<string, string> RequestHeaders { set; }
/// <summary>
/// Emitted when there is an error while updating.
/// </summary>
event Action<string> OnError;
/// <summary>
/// Emitted when checking if an update has started.
/// </summary>
event Action OnCheckingForUpdate;
/// <summary>
/// Emitted when there is an available update.
/// The update is downloaded automatically if AutoDownload is true.
/// </summary>
event Action<UpdateInfo> OnUpdateAvailable;
/// <summary>
/// Emitted when there is no available update.
/// </summary>
event Action<UpdateInfo> OnUpdateNotAvailable;
/// <summary>
/// Emitted on download progress.
/// </summary>
event Action<ProgressInfo> OnDownloadProgress;
/// <summary>
/// Emitted on download complete.
/// </summary>
event Action<UpdateInfo> OnUpdateDownloaded;
/// <summary>
/// Asks the server whether there is an update.
/// </summary>
/// <returns></returns>
Task<UpdateCheckResult> CheckForUpdatesAsync();
/// <summary>
/// Asks the server whether there is an update.
///
/// This will immediately download an update, then install when the app quits.
/// </summary>
/// <returns></returns>
Task<UpdateCheckResult> CheckForUpdatesAndNotifyAsync();
/// <summary>
/// Restarts the app and installs the update after it has been downloaded.
/// It should only be called after `update-downloaded` has been emitted.
///
/// Note: QuitAndInstall() will close all application windows first and only emit `before-quit` event on `app` after that.
/// This is different from the normal quit event sequence.
/// </summary>
/// <param name="isSilent">*windows-only* Runs the installer in silent mode. Defaults to `false`.</param>
/// <param name="isForceRunAfter">Run the app after finish even on silent install. Not applicable for macOS. Ignored if `isSilent` is set to `false`.</param>
void QuitAndInstall(bool isSilent = false, bool isForceRunAfter = false);
/// <summary>
/// Start downloading update manually. You can use this method if "AutoDownload" option is set to "false".
/// </summary>
/// <returns>Path to downloaded file.</returns>
Task<string> DownloadUpdateAsync();
/// <summary>
/// Feed URL.
/// </summary>
/// <returns>Feed URL.</returns>
Task<string> GetFeedURLAsync();
}
}

View File

@@ -1,122 +0,0 @@
using System.Threading.Tasks;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Perform copy and paste operations on the system clipboard.
/// </summary>
public interface IClipboard
{
/// <summary>
/// Read the content in the clipboard as plain text.
/// </summary>
/// <param name="type"></param>
/// <returns>The content in the clipboard as plain text.</returns>
Task<string> ReadTextAsync(string type = "");
/// <summary>
/// Writes the text into the clipboard as plain text.
/// </summary>
/// <param name="text"></param>
/// <param name="type"></param>
void WriteText(string text, string type = "");
/// <summary>
/// The content in the clipboard as markup.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
Task<string> ReadHTMLAsync(string type = "");
/// <summary>
/// Writes markup to the clipboard.
/// </summary>
/// <param name="markup"></param>
/// <param name="type"></param>
void WriteHTML(string markup, string type = "");
/// <summary>
/// The content in the clipboard as RTF.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
Task<string> ReadRTFAsync(string type = "");
/// <summary>
/// Writes the text into the clipboard in RTF.
/// </summary>
/// <param name="text"></param>
/// <param name="type"></param>
void WriteRTF(string text, string type = "");
/// <summary>
/// Returns an Object containing title and url keys representing
/// the bookmark in the clipboard. The title and url values will
/// be empty strings when the bookmark is unavailable.
/// </summary>
/// <returns></returns>
Task<ReadBookmark> ReadBookmarkAsync();
/// <summary>
/// Writes the title and url into the clipboard as a bookmark.
///
/// Note: Most apps on Windows dont support pasting bookmarks
/// into them so you can use clipboard.write to write both a
/// bookmark and fallback text to the clipboard.
/// </summary>
/// <param name="title"></param>
/// <param name="url"></param>
/// <param name="type"></param>
void WriteBookmark(string title, string url, string type = "");
/// <summary>
/// macOS: The text on the find pasteboard. This method uses synchronous IPC
/// when called from the renderer process. The cached value is reread from the
/// find pasteboard whenever the application is activated.
/// </summary>
/// <returns></returns>
Task<string> ReadFindTextAsync();
/// <summary>
/// macOS: Writes the text into the find pasteboard as plain text. This method uses
/// synchronous IPC when called from the renderer process.
/// </summary>
/// <param name="text"></param>
void WriteFindText(string text);
/// <summary>
/// Clears the clipboard content.
/// </summary>
/// <param name="type"></param>
void Clear(string type = "");
/// <summary>
/// An array of supported formats for the clipboard type.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
Task<string[]> AvailableFormatsAsync(string type = "");
/// <summary>
/// Writes data to the clipboard.
/// </summary>
/// <param name="data"></param>
/// <param name="type"></param>
void Write(Data data, string type = "");
/// <summary>
/// Reads an image from the clipboard.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
Task<NativeImage> ReadImageAsync(string type = "");
/// <summary>
/// Writes an image to the clipboard.
/// </summary>
/// <param name="image"></param>
/// <param name="type"></param>
void WriteImage(NativeImage image, string type = "");
}
}

View File

@@ -1,102 +0,0 @@
using System.Threading.Tasks;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Display native system dialogs for opening and saving files, alerting, etc.
/// </summary>
public interface IDialog
{
/// <summary>
/// Note: On Windows and Linux an open dialog can not be both a file selector
/// and a directory selector, so if you set properties to ['openFile', 'openDirectory']
/// on these platforms, a directory selector will be shown.
/// </summary>
/// <param name="browserWindow">The browserWindow argument allows the dialog to attach itself to a parent window, making it modal.</param>
/// <param name="options"></param>
/// <returns>An array of file paths chosen by the user</returns>
Task<string[]> ShowOpenDialogAsync(BrowserWindow browserWindow, OpenDialogOptions options);
/// <summary>
/// Dialog for save files.
/// </summary>
/// <param name="browserWindow">The browserWindow argument allows the dialog to attach itself to a parent window, making it modal.</param>
/// <param name="options"></param>
/// <returns>Returns String, the path of the file chosen by the user, if a callback is provided it returns an empty string.</returns>
Task<string> ShowSaveDialogAsync(BrowserWindow browserWindow, SaveDialogOptions options);
/// <summary>
/// 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).
/// </summary>
/// <param name="message"></param>
/// <returns>The API call will be asynchronous and the result will be passed via MessageBoxResult.</returns>
Task<MessageBoxResult> ShowMessageBoxAsync(string message);
/// <summary>
/// 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).
/// </summary>
/// <param name="messageBoxOptions"></param>
/// <returns>The API call will be asynchronous and the result will be passed via MessageBoxResult.</returns>
Task<MessageBoxResult> ShowMessageBoxAsync(MessageBoxOptions messageBoxOptions);
/// <summary>
/// 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.
/// </summary>
/// <param name="browserWindow">The browserWindow argument allows the dialog to attach itself to a parent window, making it modal.</param>
/// <param name="message"></param>
/// <returns>The API call will be asynchronous and the result will be passed via MessageBoxResult.</returns>
Task<MessageBoxResult> ShowMessageBoxAsync(BrowserWindow browserWindow, string message);
/// <summary>
/// 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.
/// </summary>
/// <param name="browserWindow">The browserWindow argument allows the dialog to attach itself to a parent window, making it modal.</param>
/// <param name="messageBoxOptions"></param>
/// <returns>The API call will be asynchronous and the result will be passed via MessageBoxResult.</returns>
Task<MessageBoxResult> ShowMessageBoxAsync(BrowserWindow browserWindow, MessageBoxOptions messageBoxOptions);
/// <summary>
/// Displays a modal dialog that shows an error message.
///
/// This API can be called safely before the ready event the app module emits,
/// it is usually used to report errors in early stage of startup.If called
/// before the app readyevent on Linux, the message will be emitted to stderr,
/// and no GUI dialog will appear.
/// </summary>
/// <param name="title">The title to display in the error box.</param>
/// <param name="content">The text content to display in the error box.</param>
void ShowErrorBox(string title, string content);
/// <summary>
/// On macOS, this displays a modal dialog that shows a message and certificate information,
/// and gives the user the option of trusting/importing the certificate. If you provide a
/// browserWindow argument the dialog will be attached to the parent window, making it modal.
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
Task ShowCertificateTrustDialogAsync(CertificateTrustDialogOptions options);
/// <summary>
/// On macOS, this displays a modal dialog that shows a message and certificate information,
/// and gives the user the option of trusting/importing the certificate. If you provide a
/// browserWindow argument the dialog will be attached to the parent window, making it modal.
/// </summary>
/// <param name="browserWindow"></param>
/// <param name="options"></param>
/// <returns></returns>
Task ShowCertificateTrustDialogAsync(BrowserWindow browserWindow, CertificateTrustDialogOptions options);
}
}

View File

@@ -1,93 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Control your app in the macOS dock.
/// </summary>
public interface IDock
{
/// <summary>
/// When <see cref="DockBounceType.Critical"/> is passed, the dock icon will bounce until either the application becomes
/// active or the request is canceled. When <see cref="DockBounceType.Informational"/> is passed, the dock icon will bounce
/// for one second. However, the request remains active until either the application becomes active or the request is canceled.
/// <para/>
/// Note: This method can only be used while the app is not focused; when the app is focused it will return -1.
/// </summary>
/// <param name="type">Can be critical or informational. The default is informational.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Return an ID representing the request.</returns>
Task<int> BounceAsync(DockBounceType type, CancellationToken cancellationToken = default);
/// <summary>
/// Cancel the bounce of id.
/// </summary>
/// <param name="id">Id of the request.</param>
void CancelBounce(int id);
/// <summary>
/// Bounces the Downloads stack if the filePath is inside the Downloads folder.
/// </summary>
/// <param name="filePath"></param>
void DownloadFinished(string filePath);
/// <summary>
/// Sets the string to be displayed in the docks badging area.
/// </summary>
/// <param name="text"></param>
void SetBadge(string text);
/// <summary>
/// Gets the string to be displayed in the docks badging area.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The badge string of the dock.</returns>
Task<string> GetBadgeAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Hides the dock icon.
/// </summary>
void Hide();
/// <summary>
/// Shows the dock icon.
/// </summary>
void Show();
/// <summary>
/// Whether the dock icon is visible. The app.dock.show() call is asynchronous
/// so this method might not return true immediately after that call.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Whether the dock icon is visible.</returns>
Task<bool> IsVisibleAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Gets the dock menu items.
/// </summary>
/// <value>
/// The menu items.
/// </value>
IReadOnlyCollection<MenuItem> MenuItems { get; }
/// <summary>
/// Sets the application's dock menu.
/// </summary>
void SetMenu(MenuItem[] menuItems);
/// <summary>
/// TODO: Menu (macOS) still to be implemented
/// Gets the application's dock menu.
/// </summary>
Task<Menu> GetMenu(CancellationToken cancellationToken = default);
/// <summary>
/// Sets the image associated with this dock icon.
/// </summary>
/// <param name="image"></param>
void SetIcon(string image);
}
}

View File

@@ -1,39 +0,0 @@
using System;
using System.Threading.Tasks;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Detect keyboard events when the application does not have keyboard focus.
/// </summary>
public interface IGlobalShortcut
{
/// <summary>
/// Registers a global shortcut of accelerator.
/// The callback is called when the registered shortcut is pressed by the user.
///
/// When the accelerator is already taken by other applications, this call will
/// silently fail.This behavior is intended by operating systems, since they dont
/// want applications to fight for global shortcuts.
/// </summary>
void Register(string accelerator, Action function);
/// <summary>
/// When the accelerator is already taken by other applications,
/// this call will still return false. This behavior is intended by operating systems,
/// since they dont want applications to fight for global shortcuts.
/// </summary>
/// <returns>Whether this application has registered accelerator.</returns>
Task<bool> IsRegisteredAsync(string accelerator);
/// <summary>
/// Unregisters the global shortcut of accelerator.
/// </summary>
void Unregister(string accelerator);
/// <summary>
/// Unregisters all of the global shortcuts.
/// </summary>
void UnregisterAll();
}
}

View File

@@ -1,30 +0,0 @@
using System.Threading.Tasks;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Allows you to execute native JavaScript/TypeScript code from the host process.
///
/// It is only possible if the Electron.NET CLI has previously added an
/// ElectronHostHook directory:
/// <c>electronize add HostHook</c>
/// </summary>
public interface IHostHook
{
/// <summary>
/// Execute native JavaScript/TypeScript code.
/// </summary>
/// <param name="socketEventName">Socket name registered on the host.</param>
/// <param name="arguments">Optional parameters.</param>
void Call(string socketEventName, params dynamic[] arguments);
/// <summary>
/// Execute native JavaScript/TypeScript code.
/// </summary>
/// <typeparam name="T">Results from the executed host code.</typeparam>
/// <param name="socketEventName">Socket name registered on the host.</param>
/// <param name="arguments">Optional parameters.</param>
/// <returns></returns>
Task<T> CallAsync<T>(string socketEventName, params dynamic[] arguments);
}
}

View File

@@ -1,65 +0,0 @@
using System;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Communicate asynchronously from the main process to renderer processes.
/// </summary>
public interface IIpcMain
{
/// <summary>
/// Listens to channel, when a new message arrives listener would be called with
/// listener(event, args...).
/// </summary>
/// <param name="channel">Channelname.</param>
/// <param name="listener">Callback Method.</param>
void On(string channel, Action<object> listener);
/// <summary>
/// Send a message to the renderer process synchronously via channel,
/// you can also send arbitrary arguments.
///
/// Note: Sending a synchronous message will block the whole renderer process,
/// unless you know what you are doing you should never use it.
/// </summary>
/// <param name="channel"></param>
/// <param name="listener"></param>
void OnSync(string channel, Func<object, object> listener);
/// <summary>
/// Adds a one time listener method for the event. This listener is invoked only
/// the next time a message is sent to channel, after which it is removed.
/// </summary>
/// <param name="channel">Channelname.</param>
/// <param name="listener">Callback Method.</param>
void Once(string channel, Action<object> listener);
/// <summary>
/// Removes listeners of the specified channel.
/// </summary>
/// <param name="channel">Channelname.</param>
void RemoveAllListeners(string channel);
/// <summary>
/// Send a message to the renderer process asynchronously via channel, you can also send
/// arbitrary arguments. Arguments will be serialized in JSON internally and hence
/// no functions or prototype chain will be included. The renderer process handles it by
/// listening for channel with ipcRenderer module.
/// </summary>
/// <param name="browserWindow">BrowserWindow with channel.</param>
/// <param name="channel">Channelname.</param>
/// <param name="data">Arguments data.</param>
void Send(BrowserWindow browserWindow, string channel, params object[] data);
/// <summary>
/// Send a message to the BrowserView renderer process asynchronously via channel, you can also send
/// arbitrary arguments. Arguments will be serialized in JSON internally and hence
/// no functions or prototype chain will be included. The renderer process handles it by
/// listening for channel with ipcRenderer module.
/// </summary>
/// <param name="browserView">BrowserView with channel.</param>
/// <param name="channel">Channelname.</param>
/// <param name="data">Arguments data.</param>
void Send(BrowserView browserView, string channel, params object[] data);
}
}

View File

@@ -1,47 +0,0 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Create native application menus and context menus.
/// </summary>
public interface IMenu
{
/// <summary>
/// Gets the menu items.
/// </summary>
/// <value>
/// The menu items.
/// </value>
IReadOnlyCollection<MenuItem> MenuItems { get; }
/// <summary>
/// Gets the context menu items.
/// </summary>
/// <value>
/// The context menu items.
/// </value>
IReadOnlyDictionary<int, ReadOnlyCollection<MenuItem>> ContextMenuItems { get; }
/// <summary>
/// Sets the application menu.
/// </summary>
/// <param name="menuItems">The menu items.</param>
void SetApplicationMenu(MenuItem[] menuItems);
/// <summary>
/// Sets the context menu.
/// </summary>
/// <param name="browserWindow">The browser window.</param>
/// <param name="menuItems">The menu items.</param>
void SetContextMenu(BrowserWindow browserWindow, MenuItem[] menuItems);
/// <summary>
/// Contexts the menu popup.
/// </summary>
/// <param name="browserWindow">The browser window.</param>
void ContextMenuPopup(BrowserWindow browserWindow);
}
}

View File

@@ -1,101 +0,0 @@
using System;
using System.Threading.Tasks;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Read and respond to changes in Chromium's native color theme.
/// </summary>
public interface INativeTheme
{
/// <summary>
/// Setting this property to <see cref="ThemeSourceMode.System"/> will remove the override and everything will be reset to the OS default. By default 'ThemeSource' is <see cref="ThemeSourceMode.System"/>.
/// <para/>
/// Settings this property to <see cref="ThemeSourceMode.Dark"/> will have the following effects:
/// <list type="bullet">
/// <item>
/// <description><see cref="ShouldUseDarkColorsAsync"/> will be <see langword="true"/> when accessed</description>
/// </item>
/// <item>
/// <description>Any UI Electron renders on Linux and Windows including context menus, devtools, etc. will use the dark UI.</description>
/// </item>
/// <item>
/// <description>Any UI the OS renders on macOS including menus, window frames, etc. will use the dark UI.</description>
/// </item>
/// <item>
/// <description>The 'prefers-color-scheme' CSS query will match 'dark' mode.</description>
/// </item>
/// <item>
/// <description>The 'updated' event will be emitted</description>
/// </item>
/// </list>
/// <para/>
/// Settings this property to <see cref="ThemeSourceMode.Light"/> will have the following effects:
/// <list type="bullet">
/// <item>
/// <description><see cref="ShouldUseDarkColorsAsync"/> will be <see langword="false"/> when accessed</description>
/// </item>
/// <item>
/// <description>Any UI Electron renders on Linux and Windows including context menus, devtools, etc. will use the light UI.</description>
/// </item>
/// <item>
/// <description>Any UI the OS renders on macOS including menus, window frames, etc. will use the light UI.</description>
/// </item>
/// <item>
/// <description>The 'prefers-color-scheme' CSS query will match 'light' mode.</description>
/// </item>
/// <item>
/// <description>The 'updated' event will be emitted</description>
/// </item>
/// </list>
/// The usage of this property should align with a classic "dark mode" state machine in your application where the user has three options.
/// <para/>
/// <list type="bullet">
/// <item>
/// <description>Follow OS: SetThemeSource(ThemeSourceMode.System);</description>
/// </item>
/// <item>
/// <description>Dark Mode: SetThemeSource(ThemeSourceMode.Dark);</description>
/// </item>
/// <item>
/// <description>Light Mode: SetThemeSource(ThemeSourceMode.Light);</description>
/// </item>
/// </list>
/// Your application should then always use <see cref="ShouldUseDarkColorsAsync"/> to determine what CSS to apply.
/// </summary>
/// <param name="themeSourceMode">The new ThemeSource.</param>
void SetThemeSource(ThemeSourceMode themeSourceMode);
/// <summary>
/// A <see cref="ThemeSourceMode"/> property that can be <see cref="ThemeSourceMode.System"/>, <see cref="ThemeSourceMode.Light"/> or <see cref="ThemeSourceMode.Dark"/>. It is used to override (<seealso cref="SetThemeSource"/>) and
/// supercede the value that Chromium has chosen to use internally.
/// </summary>
Task<ThemeSourceMode> GetThemeSourceAsync();
/// <summary>
/// A <see cref="bool"/> for if the OS / Chromium currently has a dark mode enabled or is
/// being instructed to show a dark-style UI. If you want to modify this value you
/// should use <see cref="SetThemeSource"/>.
/// </summary>
Task<bool> ShouldUseDarkColorsAsync();
/// <summary>
/// A <see cref="bool"/> for if the OS / Chromium currently has high-contrast mode enabled or is
/// being instructed to show a high-contrast UI.
/// </summary>
Task<bool> ShouldUseHighContrastColorsAsync();
/// <summary>
/// A <see cref="bool"/> for if the OS / Chromium currently has an inverted color scheme or is
/// being instructed to use an inverted color scheme.
/// </summary>
Task<bool> ShouldUseInvertedColorSchemeAsync();
/// <summary>
/// Emitted when something in the underlying NativeTheme has changed. This normally means that either the value of <see cref="ShouldUseDarkColorsAsync"/>,
/// <see cref="ShouldUseHighContrastColorsAsync"/> or <see cref="ShouldUseInvertedColorSchemeAsync"/> has changed. You will have to check them to determine which one has changed.
/// </summary>
event Action Updated;
}
}

View File

@@ -1,23 +0,0 @@
using System.Threading.Tasks;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Create OS desktop notifications
/// </summary>
public interface INotification
{
/// <summary>
/// Create OS desktop notifications
/// </summary>
/// <param name="notificationOptions"></param>
void Show(NotificationOptions notificationOptions);
/// <summary>
/// Whether or not desktop notifications are supported on the current system.
/// </summary>
/// <returns></returns>
Task<bool> IsSupportedAsync();
}
}

View File

@@ -1,48 +0,0 @@
using System;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Monitor power state changes..
/// </summary>
public interface IPowerMonitor
{
/// <summary>
/// Emitted when the system is about to lock the screen.
/// </summary>
event Action OnLockScreen;
/// <summary>
/// Emitted when the system is about to unlock the screen.
/// </summary>
event Action OnUnLockScreen;
/// <summary>
/// Emitted when the system is suspending.
/// </summary>
event Action OnSuspend;
/// <summary>
/// Emitted when system is resuming.
/// </summary>
event Action OnResume;
/// <summary>
/// Emitted when the system changes to AC power.
/// </summary>
event Action OnAC;
/// <summary>
/// Emitted when system changes to battery power.
/// </summary>
event Action OnBattery;
/// <summary>
/// Emitted when the system is about to reboot or shut down. If the event handler
/// invokes `e.preventDefault()`, Electron will attempt to delay system shutdown in
/// order for the app to exit cleanly.If `e.preventDefault()` is called, the app
/// should exit as soon as possible by calling something like `app.quit()`.
/// </summary>
event Action OnShutdown;
}
}

View File

@@ -1,66 +0,0 @@
using System;
using System.Threading.Tasks;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Retrieve information about screen size, displays, cursor position, etc.
/// </summary>
public interface IScreen
{
/// <summary>
/// Emitted when an new Display has been added.
/// </summary>
event Action<Display> OnDisplayAdded;
/// <summary>
/// Emitted when oldDisplay has been removed.
/// </summary>
event Action<Display> OnDisplayRemoved;
/// <summary>
/// Emitted when one or more metrics change in a display.
/// The changedMetrics is an array of strings that describe the changes.
/// Possible changes are bounds, workArea, scaleFactor and rotation.
/// </summary>
event Action<Display, string[]> OnDisplayMetricsChanged;
/// <summary>
/// The current absolute position of the mouse pointer.
/// </summary>
/// <returns></returns>
Task<Point> GetCursorScreenPointAsync();
/// <summary>
/// macOS: The height of the menu bar in pixels.
/// </summary>
/// <returns>The height of the menu bar in pixels.</returns>
Task<int> GetMenuBarHeightAsync();
/// <summary>
/// The primary display.
/// </summary>
/// <returns></returns>
Task<Display> GetPrimaryDisplayAsync();
/// <summary>
/// An array of displays that are currently available.
/// </summary>
/// <returns>An array of displays that are currently available.</returns>
Task<Display[]> GetAllDisplaysAsync();
/// <summary>
/// The display nearest the specified point.
/// </summary>
/// <returns>The display nearest the specified point.</returns>
Task<Display> GetDisplayNearestPointAsync(Point point);
/// <summary>
/// The display that most closely intersects the provided bounds.
/// </summary>
/// <param name="rectangle"></param>
/// <returns>The display that most closely intersects the provided bounds.</returns>
Task<Display> GetDisplayMatchingAsync(Rectangle rectangle);
}
}

View File

@@ -1,70 +0,0 @@
using System.Threading.Tasks;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Manage files and URLs using their default applications.
/// </summary>
public interface IShell
{
/// <summary>
/// Show the given file in a file manager. If possible, select the file.
/// </summary>
/// <param name="fullPath">The full path to the directory / file.</param>
Task ShowItemInFolderAsync(string fullPath);
/// <summary>
/// Open the given file in the desktop's default manner.
/// </summary>
/// <param name="path">The path to the directory / file.</param>
/// <returns>The error message corresponding to the failure if a failure occurred, otherwise <see cref="string.Empty"/>.</returns>
Task<string> OpenPathAsync(string path);
/// <summary>
/// Open the given external protocol URL in the desktops default manner.
/// (For example, mailto: URLs in the users default mail agent).
/// </summary>
/// <param name="url">Max 2081 characters on windows.</param>
/// <returns>The error message corresponding to the failure if a failure occurred, otherwise <see cref="string.Empty"/>.</returns>
Task<string> OpenExternalAsync(string url);
/// <summary>
/// Open the given external protocol URL in the desktops default manner.
/// (For example, mailto: URLs in the users default mail agent).
/// </summary>
/// <param name="url">Max 2081 characters on windows.</param>
/// <param name="options">Controls the behavior of OpenExternal.</param>
/// <returns>The error message corresponding to the failure if a failure occurred, otherwise <see cref="string.Empty"/>.</returns>
Task<string> OpenExternalAsync(string url, OpenExternalOptions options);
/// <summary>
/// Move the given file to trash and returns a <see cref="bool"/> status for the operation.
/// </summary>
/// <param name="fullPath">The full path to the directory / file.</param>
/// <returns> Whether the item was successfully moved to the trash.</returns>
Task<bool> TrashItemAsync(string fullPath);
/// <summary>
/// Play the beep sound.
/// </summary>
void Beep();
/// <summary>
/// Creates or updates a shortcut link at shortcutPath.
/// </summary>
/// <param name="shortcutPath">The path to the shortcut.</param>
/// <param name="operation">Default is <see cref="ShortcutLinkOperation.Create"/></param>
/// <param name="options">Structure of a shortcut.</param>
/// <returns>Whether the shortcut was created successfully.</returns>
Task<bool> WriteShortcutLinkAsync(string shortcutPath, ShortcutLinkOperation operation, ShortcutDetails options);
/// <summary>
/// Resolves the shortcut link at shortcutPath.
/// An exception will be thrown when any error happens.
/// </summary>
/// <param name="shortcutPath">The path tot the shortcut.</param>
/// <returns><see cref="ShortcutDetails"/> of the shortcut.</returns>
Task<ShortcutDetails> ReadShortcutLinkAsync(string shortcutPath);
}
}

View File

@@ -1,141 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Add icons and context menus to the system's notification area.
/// </summary>
public interface ITray
{
/// <summary>
/// Emitted when the tray icon is clicked.
/// </summary>
event Action<TrayClickEventArgs, Rectangle> OnClick;
/// <summary>
/// macOS, Windows: Emitted when the tray icon is right clicked.
/// </summary>
event Action<TrayClickEventArgs, Rectangle> OnRightClick;
/// <summary>
/// macOS, Windows: Emitted when the tray icon is double clicked.
/// </summary>
event Action<TrayClickEventArgs, Rectangle> OnDoubleClick;
/// <summary>
/// Windows: Emitted when the tray balloon shows.
/// </summary>
event Action OnBalloonShow;
/// <summary>
/// Windows: Emitted when the tray balloon is clicked.
/// </summary>
event Action OnBalloonClick;
/// <summary>
/// Windows: Emitted when the tray balloon is closed
/// because of timeout or user manually closes it.
/// </summary>
event Action OnBalloonClosed;
/// <summary>
/// Gets the menu items.
/// </summary>
/// <value>
/// The menu items.
/// </value>
IReadOnlyCollection<MenuItem> MenuItems { get; }
/// <summary>
/// Shows the Traybar.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="menuItem">The menu item.</param>
void Show(string image, MenuItem menuItem);
/// <summary>
/// Shows the Traybar.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="menuItems">The menu items.</param>
void Show(string image, MenuItem[] menuItems);
/// <summary>
/// Shows the Traybar (empty).
/// </summary>
/// <param name="image">The image.</param>
void Show(string image);
/// <summary>
/// Destroys the tray icon immediately.
/// </summary>
void Destroy();
/// <summary>
/// Sets the image associated with this tray icon.
/// </summary>
/// <param name="image"></param>
void SetImage(string image);
/// <summary>
/// Sets the image associated with this tray icon when pressed on macOS.
/// </summary>
/// <param name="image"></param>
void SetPressedImage(string image);
/// <summary>
/// Sets the hover text for this tray icon.
/// </summary>
/// <param name="toolTip"></param>
void SetToolTip(string toolTip);
/// <summary>
/// macOS: Sets the title displayed aside of the tray icon in the status bar.
/// </summary>
/// <param name="title"></param>
void SetTitle(string title);
/// <summary>
/// Windows: Displays a tray balloon.
/// </summary>
/// <param name="options"></param>
void DisplayBalloon(DisplayBalloonOptions options);
/// <summary>
/// Whether the tray icon is destroyed.
/// </summary>
/// <returns></returns>
Task<bool> IsDestroyedAsync();
/// <summary>
/// Subscribe to an unmapped event on the <see cref="Tray"/> module.
/// </summary>
/// <param name="eventName">The event name</param>
/// <param name="fn">The handler</param>
void On(string eventName, Action fn);
/// <summary>
/// Subscribe to an unmapped event on the <see cref="Tray"/> module.
/// </summary>
/// <param name="eventName">The event name</param>
/// <param name="fn">The handler</param>
void On(string eventName, Action<object> fn);
/// <summary>
/// Subscribe to an unmapped event on the <see cref="Tray"/> module once.
/// </summary>
/// <param name="eventName">The event name</param>
/// <param name="fn">The handler</param>
void Once(string eventName, Action fn);
/// <summary>
/// Subscribe to an unmapped event on the <see cref="Tray"/> module once.
/// </summary>
/// <param name="eventName">The event name</param>
/// <param name="fn">The handler</param>
void Once(string eventName, Action<object> fn);
}
}

View File

@@ -1,68 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using ElectronNET.API.Entities;
namespace ElectronNET.API.Interfaces
{
/// <summary>
/// Manage Browser Windows and Views
/// </summary>
public interface IWindowManager
{
/// <summary>
/// Quit when all windows are closed. (Default is true)
/// </summary>
/// <value>
/// <c>true</c> if [quit window all closed]; otherwise, <c>false</c>.
/// </value>
bool IsQuitOnWindowAllClosed { get; set; }
/// <summary>
/// Gets the browser windows.
/// </summary>
/// <value>
/// The browser windows.
/// </value>
IReadOnlyCollection<BrowserWindow> BrowserWindows { get; }
/// <summary>
/// Gets the browser views.
/// </summary>
/// <value>
/// The browser view.
/// </value>
IReadOnlyCollection<BrowserView> BrowserViews { get; }
/// <summary>
/// Creates the window asynchronous.
/// </summary>
/// <param name="loadUrl">The load URL.</param>
/// <returns></returns>
Task<BrowserWindow> CreateWindowAsync(string loadUrl = "http://localhost");
/// <summary>
/// Creates the window asynchronous.
/// </summary>
/// <param name="options">The options.</param>
/// <param name="loadUrl">The load URL.</param>
/// <returns></returns>
Task<BrowserWindow> CreateWindowAsync(BrowserWindowOptions options, string loadUrl = "http://localhost");
/// <summary>
/// A BrowserView can be used to embed additional web content into a BrowserWindow.
/// It is like a child window, except that it is positioned relative to its owning window.
/// It is meant to be an alternative to the webview tag.
/// </summary>
/// <returns></returns>
Task<BrowserView> CreateBrowserViewAsync();
/// <summary>
/// A BrowserView can be used to embed additional web content into a BrowserWindow.
/// It is like a child window, except that it is positioned relative to its owning window.
/// It is meant to be an alternative to the webview tag.
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
Task<BrowserView> CreateBrowserViewAsync(BrowserViewConstructorOptions options);
}
}

173
ElectronNET.API/IpcMain.cs Executable file → Normal file
View File

@@ -5,17 +5,17 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ElectronNET.API.Interfaces;
using System.Threading.Tasks;
namespace ElectronNET.API
{
/// <summary>
/// Communicate asynchronously from the main process to renderer processes.
/// </summary>
public sealed class IpcMain : IIpcMain
public sealed class IpcMain
{
private static IpcMain _ipcMain;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal IpcMain() { }
@@ -38,23 +38,21 @@ namespace ElectronNET.API
}
}
public static bool IsConnected => BridgeConnector.IsConnected;
/// <summary>
/// Listens to channel, when a new message arrives listener would be called with
/// listener(event, args...).
/// </summary>
/// <param name="channel">Channelname.</param>
/// <param name="listener">Callback Method.</param>
public void On(string channel, Action<object> listener)
public async Task On(string channel, Action<object> listener)
{
BridgeConnector.Emit("registerIpcMainChannel", channel);
BridgeConnector.Off(channel);
BridgeConnector.On<object[]>(channel, (args) =>
await BridgeConnector.Socket.Emit("registerIpcMainChannel", channel);
BridgeConnector.Socket.Off(channel);
BridgeConnector.Socket.On(channel, (args) =>
{
var objectArray = FormatArguments(args);
List<object> objectArray = FormatArguments(args);
if (objectArray.Count == 1)
if(objectArray.Count == 1)
{
listener(objectArray.First());
}
@@ -65,41 +63,20 @@ namespace ElectronNET.API
});
}
/// <summary>
/// Listens to channel, when a new message arrives listener would be called with
/// listener(event, args...). This listner will keep the window event sender id
/// </summary>
/// <param name="channel">Channelname.</param>
/// <param name="listener">Callback Method.</param>
public void OnWithId(string channel, Action<(int browserId, int webContentId, object arguments)> listener)
private List<object> FormatArguments(object args)
{
BridgeConnector.Emit("registerIpcMainChannelWithId", channel);
BridgeConnector.Off(channel);
BridgeConnector.On<ArgsAndIds>(channel, (data) =>
List<object> objectArray = ((JArray)args).ToObject<object[]>().ToList();
for (int index = 0; index < objectArray.Count; index++)
{
var objectArray = FormatArguments(data.args);
if (objectArray.Count == 1)
var item = objectArray[index];
if (item == null)
{
listener((data.id, data.wcId, objectArray.First()));
objectArray.Remove(item);
}
else
{
listener((data.id, data.wcId, objectArray));
}
});
}
}
private class ArgsAndIds
{
public int id { get; set; }
public int wcId { get; set; }
public object[] args { get; set; }
}
private List<object> FormatArguments(object[] objectArray)
{
return objectArray.Where(o => o is object).ToList();
return objectArray;
}
/// <summary>
@@ -113,9 +90,9 @@ namespace ElectronNET.API
/// <param name="listener"></param>
public void OnSync(string channel, Func<object, object> listener)
{
BridgeConnector.Emit("registerSyncIpcMainChannel", channel);
BridgeConnector.On<object[]>(channel, (args) => {
var objectArray = FormatArguments(args);
BridgeConnector.Socket.Emit("registerSyncIpcMainChannel", channel);
BridgeConnector.Socket.On(channel, (args) => {
List<object> objectArray = FormatArguments(args);
object parameter;
if (objectArray.Count == 1)
{
@@ -127,7 +104,7 @@ namespace ElectronNET.API
}
var result = listener(parameter);
BridgeConnector.Emit(channel + "Sync", result);
BridgeConnector.Socket.Emit(channel + "Sync", result);
});
}
@@ -139,10 +116,10 @@ namespace ElectronNET.API
/// <param name="listener">Callback Method.</param>
public void Once(string channel, Action<object> listener)
{
BridgeConnector.Emit("registerOnceIpcMainChannel", channel);
BridgeConnector.Once<object[]>(channel, (args) =>
BridgeConnector.Socket.Emit("registerOnceIpcMainChannel", channel);
BridgeConnector.Socket.On(channel, (args) =>
{
var objectArray = FormatArguments(args);
List<object> objectArray = FormatArguments(args);
if (objectArray.Count == 1)
{
@@ -161,7 +138,7 @@ namespace ElectronNET.API
/// <param name="channel">Channelname.</param>
public void RemoveAllListeners(string channel)
{
BridgeConnector.Emit("removeAllListenersIpcMainChannel", channel);
BridgeConnector.Socket.Emit("removeAllListenersIpcMainChannel", channel);
}
/// <summary>
@@ -175,45 +152,9 @@ namespace ElectronNET.API
/// <param name="data">Arguments data.</param>
public void Send(BrowserWindow browserWindow, string channel, params object[] data)
{
var objectsWithCorrectSerialization = new List<object>
{
browserWindow.Id,
channel
};
foreach (var parameterObject in data)
{
if(parameterObject.GetType().IsArray || parameterObject.GetType().IsGenericType && parameterObject is IEnumerable)
{
objectsWithCorrectSerialization.Add(JArray.FromObject(parameterObject, _jsonSerializer));
}
else if(parameterObject.GetType().IsClass && !parameterObject.GetType().IsPrimitive && !(parameterObject is string))
{
objectsWithCorrectSerialization.Add(JObject.FromObject(parameterObject, _jsonSerializer));
}
else if(parameterObject.GetType().IsPrimitive || (parameterObject is string))
{
objectsWithCorrectSerialization.Add(parameterObject);
}
}
BridgeConnector.Emit("sendToIpcRenderer", objectsWithCorrectSerialization.ToArray());
}
/// <summary>
/// Send a message to the BrowserView renderer process asynchronously via channel, you can also send
/// arbitrary arguments. Arguments will be serialized in JSON internally and hence
/// no functions or prototype chain will be included. The renderer process handles it by
/// listening for channel with ipcRenderer module.
/// </summary>
/// <param name="browserView">BrowserView with channel.</param>
/// <param name="channel">Channelname.</param>
/// <param name="data">Arguments data.</param>
public void Send(BrowserView browserView, string channel, params object[] data)
{
List<JObject> jobjects = new();
List<JArray> jarrays = new();
List<object> objects = new();
List<JObject> jobjects = new List<JObject>();
List<JArray> jarrays = new List<JArray>();
List<object> objects = new List<object>();
foreach (var parameterObject in data)
{
@@ -231,36 +172,54 @@ namespace ElectronNET.API
if(jobjects.Count > 0 || jarrays.Count > 0)
{
BridgeConnector.Emit("sendToIpcRendererBrowserView", browserView.Id, channel, jarrays.ToArray(), jobjects.ToArray(), objects.ToArray());
BridgeConnector.Socket.Emit("sendToIpcRenderer", JObject.FromObject(browserWindow, _jsonSerializer), channel, jarrays.ToArray(), jobjects.ToArray(), objects.ToArray());
}
else
{
BridgeConnector.Emit("sendToIpcRendererBrowserView", browserView.Id, channel, data);
BridgeConnector.Socket.Emit("sendToIpcRenderer", JObject.FromObject(browserWindow, _jsonSerializer), channel, data);
}
}
/// <summary>
/// Log a message to the console output pipe. This is used when running with "detachedProcess" : true on the electron.manifest.json,
/// as in that case we can't open pipes to read the console output from the child process anymore
/// Send a message to the BrowserView renderer process asynchronously via channel, you can also send
/// arbitrary arguments. Arguments will be serialized in JSON internally and hence
/// no functions or prototype chain will be included. The renderer process handles it by
/// listening for channel with ipcRenderer module.
/// </summary>
/// <param name="text">Message to log</param>
public static void ConsoleLog(string text)
/// <param name="browserView">BrowserView with channel.</param>
/// <param name="channel">Channelname.</param>
/// <param name="data">Arguments data.</param>
public void Send(BrowserView browserView, string channel, params object[] data)
{
BridgeConnector.Emit("console-stdout", text);
List<JObject> jobjects = new List<JObject>();
List<JArray> jarrays = new List<JArray>();
List<object> objects = new List<object>();
foreach (var parameterObject in data)
{
if(parameterObject.GetType().IsArray || parameterObject.GetType().IsGenericType && parameterObject is IEnumerable)
{
jarrays.Add(JArray.FromObject(parameterObject, _jsonSerializer));
} else if(parameterObject.GetType().IsClass && !parameterObject.GetType().IsPrimitive && !(parameterObject is string))
{
jobjects.Add(JObject.FromObject(parameterObject, _jsonSerializer));
} else if(parameterObject.GetType().IsPrimitive || (parameterObject is string))
{
objects.Add(parameterObject);
}
}
if(jobjects.Count > 0 || jarrays.Count > 0)
{
BridgeConnector.Socket.Emit("sendToIpcRendererBrowserView", browserView.Id, channel, jarrays.ToArray(), jobjects.ToArray(), objects.ToArray());
}
else
{
BridgeConnector.Socket.Emit("sendToIpcRendererBrowserView", browserView.Id, channel, data);
}
}
/// <summary>
/// Log a message to the console error pipe. This is used when running with "detachedProcess" : true on the electron.manifest.json,
/// as in that case we can't open pipes to read the console output from the child process anymore
/// </summary>
/// <param name="text">Message to log</param>
public static void ConsoleError(string text)
{
BridgeConnector.Emit("console-stderr", text);
}
private readonly JsonSerializer _jsonSerializer = new()
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,

View File

@@ -1,4 +1,5 @@
using System.Threading;
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
@@ -15,7 +16,7 @@ namespace ElectronNET.API
{
App.Instance.IsReady = true;
BridgeConnector.Log("ASP.NET Core host has fully started.");
Console.WriteLine("ASP.NET Core host has fully started.");
});
}

32
ElectronNET.API/Menu.cs Executable file → Normal file
View File

@@ -6,17 +6,16 @@ using System.Collections.Generic;
using ElectronNET.API.Extensions;
using System.Linq;
using System.Collections.ObjectModel;
using ElectronNET.API.Interfaces;
namespace ElectronNET.API
{
/// <summary>
/// Create native application menus and context menus.
/// </summary>
public sealed class Menu : IMenu
public sealed class Menu
{
private static Menu _menu;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal Menu() { }
@@ -46,7 +45,7 @@ namespace ElectronNET.API
/// The menu items.
/// </value>
public IReadOnlyCollection<MenuItem> MenuItems { get { return _menuItems.AsReadOnly(); } }
private readonly List<MenuItem> _menuItems = new();
private List<MenuItem> _menuItems = new List<MenuItem>();
/// <summary>
/// Sets the application menu.
@@ -59,12 +58,12 @@ namespace ElectronNET.API
menuItems.AddMenuItemsId();
menuItems.AddSubmenuTypes();
BridgeConnector.Emit("menu-setApplicationMenu", JArray.FromObject(menuItems, _jsonSerializer));
BridgeConnector.Socket.Emit("menu-setApplicationMenu", JArray.FromObject(menuItems, _jsonSerializer));
_menuItems.AddRange(menuItems);
BridgeConnector.Off("menuItemClicked");
BridgeConnector.On<string>("menuItemClicked", (id) => {
MenuItem menuItem = _menuItems.GetMenuItem(id);
BridgeConnector.Socket.Off("menuItemClicked");
BridgeConnector.Socket.On("menuItemClicked", (id) => {
MenuItem menuItem = _menuItems.GetMenuItem(id.ToString());
menuItem.Click?.Invoke();
});
}
@@ -76,7 +75,7 @@ namespace ElectronNET.API
/// The context menu items.
/// </value>
public IReadOnlyDictionary<int, ReadOnlyCollection<MenuItem>> ContextMenuItems { get; internal set; }
private readonly Dictionary<int, List<MenuItem>> _contextMenuItems = new();
private Dictionary<int, List<MenuItem>> _contextMenuItems = new Dictionary<int, List<MenuItem>>();
/// <summary>
/// Sets the context menu.
@@ -88,7 +87,7 @@ namespace ElectronNET.API
menuItems.AddMenuItemsId();
menuItems.AddSubmenuTypes();
BridgeConnector.Emit("menu-setContextMenu", browserWindow.Id, JArray.FromObject(menuItems, _jsonSerializer));
BridgeConnector.Socket.Emit("menu-setContextMenu", browserWindow.Id, JArray.FromObject(menuItems, _jsonSerializer));
if (!_contextMenuItems.ContainsKey(browserWindow.Id))
{
@@ -97,10 +96,13 @@ namespace ElectronNET.API
ContextMenuItems = new ReadOnlyDictionary<int, ReadOnlyCollection<MenuItem>>(x);
}
BridgeConnector.Off("contextMenuItemClicked");
BridgeConnector.On<MenuResponse>("contextMenuItemClicked", (results) =>
BridgeConnector.Socket.Off("contextMenuItemClicked");
BridgeConnector.Socket.On("contextMenuItemClicked", (results) =>
{
MenuItem menuItem = _contextMenuItems[results.windowId].GetMenuItem(results.id);
var id = ((JArray)results).First.ToString();
var browserWindowId = (int)((JArray)results).Last;
MenuItem menuItem = _contextMenuItems[browserWindowId].GetMenuItem(id);
menuItem.Click?.Invoke();
});
}
@@ -111,10 +113,10 @@ namespace ElectronNET.API
/// <param name="browserWindow">The browser window.</param>
public void ContextMenuPopup(BrowserWindow browserWindow)
{
BridgeConnector.Emit("menu-contextMenuPopup", browserWindow.Id);
BridgeConnector.Socket.Emit("menu-contextMenuPopup", browserWindow.Id);
}
private readonly JsonSerializer _jsonSerializer = new()
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore

View File

@@ -1,8 +0,0 @@
namespace ElectronNET.API
{
internal class MenuResponse
{
public string id { get; set; }
public int windowId { get; set; }
}
}

81
ElectronNET.API/NativeTheme.cs Executable file → Normal file
View File

@@ -1,19 +1,17 @@
using System;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using ElectronNET.API.Entities;
using ElectronNET.API.Extensions;
using ElectronNET.API.Interfaces;
namespace ElectronNET.API
{
/// <summary>
/// Read and respond to changes in Chromium's native color theme.
/// </summary>
public sealed class NativeTheme : INativeTheme
public sealed class NativeTheme
{
private static NativeTheme _nativeTheme;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal NativeTheme() { }
@@ -96,37 +94,88 @@ namespace ElectronNET.API
{
var themeSource = themeSourceMode.GetDescription();
BridgeConnector.Emit("nativeTheme-themeSource", themeSource);
BridgeConnector.Socket.Emit("nativeTheme-themeSource", themeSource);
}
/// <summary>
/// A <see cref="ThemeSourceMode"/> property that can be <see cref="ThemeSourceMode.System"/>, <see cref="ThemeSourceMode.Light"/> or <see cref="ThemeSourceMode.Dark"/>. It is used to override (<seealso cref="SetThemeSource"/>) and
/// supercede the value that Chromium has chosen to use internally.
/// </summary>
public async Task<ThemeSourceMode> GetThemeSourceAsync() => Enum.Parse<ThemeSourceMode>(await BridgeConnector.OnResult<string>("nativeTheme-themeSource-get", "nativeTheme-themeSource-getCompleted"), true);
public Task<ThemeSourceMode> GetThemeSourceAsync()
{
var taskCompletionSource = new TaskCompletionSource<ThemeSourceMode>();
BridgeConnector.Socket.On("nativeTheme-themeSource-getCompleted", (themeSource) =>
{
BridgeConnector.Socket.Off("nativeTheme-themeSource-getCompleted");
var themeSourceValue = (ThemeSourceMode)Enum.Parse(typeof(ThemeSourceMode), (string)themeSource, true);
taskCompletionSource.SetResult(themeSourceValue);
});
BridgeConnector.Socket.Emit("nativeTheme-themeSource-get");
return taskCompletionSource.Task;
}
/// <summary>
/// A <see cref="bool"/> for if the OS / Chromium currently has a dark mode enabled or is
/// being instructed to show a dark-style UI. If you want to modify this value you
/// should use <see cref="SetThemeSource"/>.
/// </summary>
public Task<bool> ShouldUseDarkColorsAsync() => BridgeConnector.OnResult<bool>("nativeTheme-shouldUseDarkColors", "nativeTheme-shouldUseDarkColors-completed");
public Task<bool> ShouldUseDarkColorsAsync()
{
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.Socket.On("nativeTheme-shouldUseDarkColors-completed", (shouldUseDarkColors) => {
BridgeConnector.Socket.Off("nativeTheme-shouldUseDarkColors-completed");
taskCompletionSource.SetResult((bool)shouldUseDarkColors);
});
BridgeConnector.Socket.Emit("nativeTheme-shouldUseDarkColors");
return taskCompletionSource.Task;
}
/// <summary>
/// A <see cref="bool"/> for if the OS / Chromium currently has high-contrast mode enabled or is
/// being instructed to show a high-contrast UI.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public Task<bool> ShouldUseHighContrastColorsAsync() => BridgeConnector.OnResult<bool>("nativeTheme-shouldUseHighContrastColors", "nativeTheme-shouldUseHighContrastColors-completed");
public Task<bool> ShouldUseHighContrastColorsAsync()
{
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.Socket.On("nativeTheme-shouldUseHighContrastColors-completed", (shouldUseHighContrastColors) => {
BridgeConnector.Socket.Off("nativeTheme-shouldUseHighContrastColors-completed");
taskCompletionSource.SetResult((bool)shouldUseHighContrastColors);
});
BridgeConnector.Socket.Emit("nativeTheme-shouldUseHighContrastColors");
return taskCompletionSource.Task;
}
/// <summary>
/// A <see cref="bool"/> for if the OS / Chromium currently has an inverted color scheme or is
/// being instructed to use an inverted color scheme.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public Task<bool> ShouldUseInvertedColorSchemeAsync() => BridgeConnector.OnResult<bool>("nativeTheme-shouldUseInvertedColorScheme", "nativeTheme-shouldUseInvertedColorScheme-completed");
public Task<bool> ShouldUseInvertedColorSchemeAsync()
{
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.Socket.On("nativeTheme-shouldUseInvertedColorScheme-completed", (shouldUseInvertedColorScheme) => {
BridgeConnector.Socket.Off("nativeTheme-shouldUseInvertedColorScheme-completed");
taskCompletionSource.SetResult((bool)shouldUseInvertedColorScheme);
});
BridgeConnector.Socket.Emit("nativeTheme-shouldUseInvertedColorScheme");
return taskCompletionSource.Task;
}
/// <summary>
/// Emitted when something in the underlying NativeTheme has changed. This normally means that either the value of <see cref="ShouldUseDarkColorsAsync"/>,
@@ -138,12 +187,12 @@ namespace ElectronNET.API
{
if (_updated == null)
{
BridgeConnector.On("nativeTheme-updated" + GetHashCode(), () =>
BridgeConnector.Socket.On("nativeTheme-updated" + GetHashCode(), () =>
{
_updated();
});
BridgeConnector.Emit("register-nativeTheme-updated-event", GetHashCode());
BridgeConnector.Socket.Emit("register-nativeTheme-updated-event", GetHashCode());
}
_updated += value;
}
@@ -153,7 +202,7 @@ namespace ElectronNET.API
if (_updated == null)
{
BridgeConnector.Off("nativeTheme-updated" + GetHashCode());
BridgeConnector.Socket.Off("nativeTheme-updated" + GetHashCode());
}
}
}

51
ElectronNET.API/Notification.cs Executable file → Normal file
View File

@@ -6,17 +6,16 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ElectronNET.API.Interfaces;
namespace ElectronNET.API
{
/// <summary>
/// Create OS desktop notifications
/// </summary>
public sealed class Notification : INotification
public sealed class Notification
{
private static Notification _notification;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal Notification() { }
@@ -39,7 +38,7 @@ namespace ElectronNET.API
}
}
private static readonly List<NotificationOptions> _notificationOptions = new();
private static List<NotificationOptions> _notificationOptions = new List<NotificationOptions>();
/// <summary>
/// Create OS desktop notifications
@@ -49,7 +48,7 @@ namespace ElectronNET.API
{
GenerateIDsForDefinedActions(notificationOptions);
BridgeConnector.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer));
BridgeConnector.Socket.Emit("createNotification", JObject.FromObject(notificationOptions, _jsonSerializer));
}
private static void GenerateIDsForDefinedActions(NotificationOptions notificationOptions)
@@ -61,9 +60,9 @@ namespace ElectronNET.API
notificationOptions.ShowID = Guid.NewGuid().ToString();
isActionDefined = true;
BridgeConnector.Off("NotificationEventShow");
BridgeConnector.On<string>("NotificationEventShow", (id) => {
_notificationOptions.Single(x => x.ShowID == id).OnShow();
BridgeConnector.Socket.Off("NotificationEventShow");
BridgeConnector.Socket.On("NotificationEventShow", (id) => {
_notificationOptions.Single(x => x.ShowID == id.ToString()).OnShow();
});
}
@@ -72,9 +71,9 @@ namespace ElectronNET.API
notificationOptions.ClickID = Guid.NewGuid().ToString();
isActionDefined = true;
BridgeConnector.Off("NotificationEventClick");
BridgeConnector.On<string>("NotificationEventClick", (id) => {
_notificationOptions.Single(x => x.ClickID == id).OnClick();
BridgeConnector.Socket.Off("NotificationEventClick");
BridgeConnector.Socket.On("NotificationEventClick", (id) => {
_notificationOptions.Single(x => x.ClickID == id.ToString()).OnClick();
});
}
@@ -83,8 +82,8 @@ namespace ElectronNET.API
notificationOptions.CloseID = Guid.NewGuid().ToString();
isActionDefined = true;
BridgeConnector.Off("NotificationEventClose");
BridgeConnector.On<string>("NotificationEventClose", (id) => {
BridgeConnector.Socket.Off("NotificationEventClose");
BridgeConnector.Socket.On("NotificationEventClose", (id) => {
_notificationOptions.Single(x => x.CloseID == id.ToString()).OnClose();
});
}
@@ -94,9 +93,10 @@ namespace ElectronNET.API
notificationOptions.ReplyID = Guid.NewGuid().ToString();
isActionDefined = true;
BridgeConnector.Off("NotificationEventReply");
BridgeConnector.On<string[]>("NotificationEventReply", (args) => {
_notificationOptions.Single(x => x.ReplyID == args[0].ToString()).OnReply(args[1].ToString());
BridgeConnector.Socket.Off("NotificationEventReply");
BridgeConnector.Socket.On("NotificationEventReply", (args) => {
var arguments = ((JArray)args).ToObject<string[]>();
_notificationOptions.Single(x => x.ReplyID == arguments[0].ToString()).OnReply(arguments[1].ToString());
});
}
@@ -105,9 +105,10 @@ namespace ElectronNET.API
notificationOptions.ActionID = Guid.NewGuid().ToString();
isActionDefined = true;
BridgeConnector.Off("NotificationEventAction");
BridgeConnector.On<string[]>("NotificationEventAction", (args) => {
_notificationOptions.Single(x => x.ReplyID == args[0].ToString()).OnAction(args[1].ToString());
BridgeConnector.Socket.Off("NotificationEventAction");
BridgeConnector.Socket.On("NotificationEventAction", (args) => {
var arguments = ((JArray)args).ToObject<string[]>();
_notificationOptions.Single(x => x.ReplyID == arguments[0].ToString()).OnAction(arguments[1].ToString());
});
}
@@ -123,20 +124,20 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<bool> IsSupportedAsync()
{
var taskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.On<bool>("notificationIsSupportedComplete", (isSupported) =>
BridgeConnector.Socket.On("notificationIsSupportedComplete", (isSupported) =>
{
BridgeConnector.Off("notificationIsSupportedComplete");
taskCompletionSource.SetResult(isSupported);
BridgeConnector.Socket.Off("notificationIsSupportedComplete");
taskCompletionSource.SetResult((bool)isSupported);
});
BridgeConnector.Emit("notificationIsSupported");
BridgeConnector.Socket.Emit("notificationIsSupported");
return taskCompletionSource.Task;
}
private static readonly JsonSerializer _jsonSerializer = new()
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,

63
ElectronNET.API/PowerMonitor.cs Executable file → Normal file
View File

@@ -1,32 +1,28 @@
using System;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using ElectronNET.API.Interfaces;
namespace ElectronNET.API
{
/// <summary>
/// Monitor power state changes..
/// </summary>
public sealed class PowerMonitor : IPowerMonitor
public sealed class PowerMonitor
{
/// <summary>
/// Emitted when the system is about to lock the screen.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public event Action OnLockScreen
{
add
{
if (_lockScreen == null)
{
BridgeConnector.On("pm-lock-screen" , () =>
BridgeConnector.Socket.On("pm-lock-screen" , () =>
{
_lockScreen();
});
BridgeConnector.Emit("register-pm-lock-screen");
BridgeConnector.Socket.Emit("register-pm-lock-screen");
}
_lockScreen += value;
}
@@ -35,7 +31,7 @@ namespace ElectronNET.API
_lockScreen -= value;
if (_lockScreen == null)
BridgeConnector.Off("pm-lock-screen");
BridgeConnector.Socket.Off("pm-lock-screen");
}
}
@@ -44,20 +40,18 @@ namespace ElectronNET.API
/// <summary>
/// Emitted when the system is about to unlock the screen.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public event Action OnUnLockScreen
{
add
{
if (_unlockScreen == null)
{
BridgeConnector.On("pm-unlock-screen", () =>
BridgeConnector.Socket.On("pm-unlock-screen", () =>
{
_unlockScreen();
});
BridgeConnector.Emit("register-pm-unlock-screen");
BridgeConnector.Socket.Emit("register-pm-unlock-screen");
}
_unlockScreen += value;
}
@@ -66,7 +60,7 @@ namespace ElectronNET.API
_unlockScreen -= value;
if (_unlockScreen == null)
BridgeConnector.Off("pm-unlock-screen");
BridgeConnector.Socket.Off("pm-unlock-screen");
}
}
@@ -75,20 +69,18 @@ namespace ElectronNET.API
/// <summary>
/// Emitted when the system is suspending.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public event Action OnSuspend
{
add
{
if (_suspend == null)
{
BridgeConnector.On("pm-suspend", () =>
BridgeConnector.Socket.On("pm-suspend", () =>
{
_suspend();
});
BridgeConnector.Emit("register-pm-suspend");
BridgeConnector.Socket.Emit("register-pm-suspend");
}
_suspend += value;
}
@@ -97,7 +89,7 @@ namespace ElectronNET.API
_suspend -= value;
if (_suspend == null)
BridgeConnector.Off("pm-suspend");
BridgeConnector.Socket.Off("pm-suspend");
}
}
@@ -106,20 +98,18 @@ namespace ElectronNET.API
/// <summary>
/// Emitted when system is resuming.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public event Action OnResume
{
add
{
if (_resume == null)
{
BridgeConnector.On("pm-resume", () =>
BridgeConnector.Socket.On("pm-resume", () =>
{
_resume();
});
BridgeConnector.Emit("register-pm-resume");
BridgeConnector.Socket.Emit("register-pm-resume");
}
_resume += value;
}
@@ -128,7 +118,7 @@ namespace ElectronNET.API
_resume -= value;
if (_resume == null)
BridgeConnector.Off("pm-resume");
BridgeConnector.Socket.Off("pm-resume");
}
}
@@ -137,20 +127,18 @@ namespace ElectronNET.API
/// <summary>
/// Emitted when the system changes to AC power.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public event Action OnAC
{
add
{
if (_onAC == null)
{
BridgeConnector.On("pm-on-ac", () =>
BridgeConnector.Socket.On("pm-on-ac", () =>
{
_onAC();
});
BridgeConnector.Emit("register-pm-on-ac");
BridgeConnector.Socket.Emit("register-pm-on-ac");
}
_onAC += value;
}
@@ -159,7 +147,7 @@ namespace ElectronNET.API
_onAC -= value;
if (_onAC == null)
BridgeConnector.Off("pm-on-ac");
BridgeConnector.Socket.Off("pm-on-ac");
}
}
@@ -168,20 +156,18 @@ namespace ElectronNET.API
/// <summary>
/// Emitted when system changes to battery power.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public event Action OnBattery
{
add
{
if (_onBattery == null)
{
BridgeConnector.On("pm-on-battery", () =>
BridgeConnector.Socket.On("pm-on-battery", () =>
{
_onBattery();
});
BridgeConnector.Emit("register-pm-on-battery");
BridgeConnector.Socket.Emit("register-pm-on-battery");
}
_onBattery += value;
}
@@ -190,7 +176,7 @@ namespace ElectronNET.API
_onBattery -= value;
if (_onBattery == null)
BridgeConnector.Off("pm-on-battery");
BridgeConnector.Socket.Off("pm-on-battery");
}
}
@@ -203,21 +189,18 @@ namespace ElectronNET.API
/// order for the app to exit cleanly.If `e.preventDefault()` is called, the app
/// should exit as soon as possible by calling something like `app.quit()`.
/// </summary>
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
public event Action OnShutdown
{
add
{
if (_shutdown == null)
{
BridgeConnector.On("pm-shutdown", () =>
BridgeConnector.Socket.On("pm-shutdown", () =>
{
_shutdown();
});
BridgeConnector.Emit("register-pm-shutdown");
BridgeConnector.Socket.Emit("register-pm-shutdown");
}
_shutdown += value;
}
@@ -226,14 +209,14 @@ namespace ElectronNET.API
_shutdown -= value;
if (_shutdown == null)
BridgeConnector.Off("pm-on-shutdown");
BridgeConnector.Socket.Off("pm-on-shutdown");
}
}
private event Action _shutdown;
private static PowerMonitor _powerMonitor;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal PowerMonitor() { }

View File

@@ -1,37 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ElectronNET.API
{
internal class ProcessHelper
{
public static void Execute(string command, string workingDirectoryPath)
{
using (Process cmd = new Process())
{
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
if (isWindows)
{
cmd.StartInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
}
else
{
// works for OSX and Linux (at least on Ubuntu)
var escapedArgs = command.Replace("\"", "\\\"");
cmd.StartInfo = new ProcessStartInfo("bash", $"-c \"{escapedArgs}\"");
}
cmd.StartInfo.RedirectStandardInput = false;
cmd.StartInfo.RedirectStandardOutput = false;
cmd.StartInfo.RedirectStandardError = false;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.WorkingDirectory = workingDirectoryPath;
cmd.Start();
}
}
}
}

134
ElectronNET.API/Screen.cs Executable file → Normal file
View File

@@ -4,14 +4,13 @@ using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Threading.Tasks;
using ElectronNET.API.Interfaces;
namespace ElectronNET.API
{
/// <summary>
/// Retrieve information about screen size, displays, cursor position, etc.
/// </summary>
public sealed class Screen : IScreen
public sealed class Screen
{
/// <summary>
/// Emitted when an new Display has been added.
@@ -22,12 +21,12 @@ namespace ElectronNET.API
{
if (_onDisplayAdded == null)
{
BridgeConnector.On<Display>("screen-display-added-event" + GetHashCode(), (display) =>
BridgeConnector.Socket.On("screen-display-added-event" + GetHashCode(), (display) =>
{
_onDisplayAdded(display);
_onDisplayAdded(((JObject)display).ToObject<Display>());
});
BridgeConnector.Emit("register-screen-display-added", GetHashCode());
BridgeConnector.Socket.Emit("register-screen-display-added", GetHashCode());
}
_onDisplayAdded += value;
}
@@ -36,7 +35,7 @@ namespace ElectronNET.API
_onDisplayAdded -= value;
if (_onDisplayAdded == null)
BridgeConnector.Off("screen-display-added-event" + GetHashCode());
BridgeConnector.Socket.Off("screen-display-added-event" + GetHashCode());
}
}
@@ -51,12 +50,12 @@ namespace ElectronNET.API
{
if (_onDisplayRemoved == null)
{
BridgeConnector.On<Display>("screen-display-removed-event" + GetHashCode(), (display) =>
BridgeConnector.Socket.On("screen-display-removed-event" + GetHashCode(), (display) =>
{
_onDisplayRemoved(display);
_onDisplayRemoved(((JObject)display).ToObject<Display>());
});
BridgeConnector.Emit("register-screen-display-removed", GetHashCode());
BridgeConnector.Socket.Emit("register-screen-display-removed", GetHashCode());
}
_onDisplayRemoved += value;
}
@@ -65,7 +64,7 @@ namespace ElectronNET.API
_onDisplayRemoved -= value;
if (_onDisplayRemoved == null)
BridgeConnector.Off("screen-display-removed-event" + GetHashCode());
BridgeConnector.Socket.Off("screen-display-removed-event" + GetHashCode());
}
}
@@ -82,12 +81,15 @@ namespace ElectronNET.API
{
if (_onDisplayMetricsChanged == null)
{
BridgeConnector.On<DisplayChanged>("screen-display-metrics-changed-event" + GetHashCode(), (args) =>
BridgeConnector.Socket.On("screen-display-metrics-changed-event" + GetHashCode(), (args) =>
{
_onDisplayMetricsChanged(args.display, args.metrics);
var display = ((JArray)args).First.ToObject<Display>();
var metrics = ((JArray)args).Last.ToObject<string[]>();
_onDisplayMetricsChanged(display, metrics);
});
BridgeConnector.Emit("register-screen-display-metrics-changed", GetHashCode());
BridgeConnector.Socket.Emit("register-screen-display-metrics-changed", GetHashCode());
}
_onDisplayMetricsChanged += value;
}
@@ -96,14 +98,14 @@ namespace ElectronNET.API
_onDisplayMetricsChanged -= value;
if (_onDisplayMetricsChanged == null)
BridgeConnector.Off("screen-display-metrics-changed-event" + GetHashCode());
BridgeConnector.Socket.Off("screen-display-metrics-changed-event" + GetHashCode());
}
}
private event Action<Display, string[]> _onDisplayMetricsChanged;
private static Screen _screen;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal Screen() { }
@@ -130,38 +132,128 @@ namespace ElectronNET.API
/// The current absolute position of the mouse pointer.
/// </summary>
/// <returns></returns>
public Task<Point> GetCursorScreenPointAsync() => BridgeConnector.OnResult<Point>("screen-getCursorScreenPoint", "screen-getCursorScreenPointCompleted");
public Task<Point> GetCursorScreenPointAsync()
{
var taskCompletionSource = new TaskCompletionSource<Point>();
BridgeConnector.Socket.On("screen-getCursorScreenPointCompleted", (point) =>
{
BridgeConnector.Socket.Off("screen-getCursorScreenPointCompleted");
taskCompletionSource.SetResult(((JObject)point).ToObject<Point>());
});
BridgeConnector.Socket.Emit("screen-getCursorScreenPoint");
return taskCompletionSource.Task;
}
/// <summary>
/// macOS: The height of the menu bar in pixels.
/// </summary>
/// <returns>The height of the menu bar in pixels.</returns>
public Task<int> GetMenuBarHeightAsync() => BridgeConnector.OnResult<int>("screen-getMenuBarHeight", "screen-getMenuBarHeightCompleted");
public Task<int> GetMenuBarHeightAsync()
{
var taskCompletionSource = new TaskCompletionSource<int>();
BridgeConnector.Socket.On("screen-getMenuBarHeightCompleted", (height) =>
{
BridgeConnector.Socket.Off("screen-getMenuBarHeightCompleted");
taskCompletionSource.SetResult(int.Parse(height.ToString()));
});
BridgeConnector.Socket.Emit("screen-getMenuBarHeight");
return taskCompletionSource.Task;
}
/// <summary>
/// The primary display.
/// </summary>
/// <returns></returns>
public Task<Display> GetPrimaryDisplayAsync() => BridgeConnector.OnResult<Display>("screen-getPrimaryDisplay", "screen-getPrimaryDisplayCompleted");
public Task<Display> GetPrimaryDisplayAsync()
{
var taskCompletionSource = new TaskCompletionSource<Display>();
BridgeConnector.Socket.On("screen-getPrimaryDisplayCompleted", (display) =>
{
BridgeConnector.Socket.Off("screen-getPrimaryDisplayCompleted");
taskCompletionSource.SetResult(((JObject)display).ToObject<Display>());
});
BridgeConnector.Socket.Emit("screen-getPrimaryDisplay");
return taskCompletionSource.Task;
}
/// <summary>
/// An array of displays that are currently available.
/// </summary>
/// <returns>An array of displays that are currently available.</returns>
public Task<Display[]> GetAllDisplaysAsync() => BridgeConnector.OnResult<Display[]>("screen-getAllDisplays", "screen-getAllDisplaysCompleted");
public Task<Display[]> GetAllDisplaysAsync()
{
var taskCompletionSource = new TaskCompletionSource<Display[]>();
BridgeConnector.Socket.On("screen-getAllDisplaysCompleted", (displays) =>
{
BridgeConnector.Socket.Off("screen-getAllDisplaysCompleted");
taskCompletionSource.SetResult(((JArray)displays).ToObject<Display[]>());
});
BridgeConnector.Socket.Emit("screen-getAllDisplays");
return taskCompletionSource.Task;
}
/// <summary>
/// The display nearest the specified point.
/// </summary>
/// <returns>The display nearest the specified point.</returns>
public Task<Display> GetDisplayNearestPointAsync(Point point) => BridgeConnector.OnResult<Display>("screen-getDisplayNearestPoint", "screen-getDisplayNearestPointCompleted", point);
public Task<Display> GetDisplayNearestPointAsync(Point point)
{
var taskCompletionSource = new TaskCompletionSource<Display>();
BridgeConnector.Socket.On("screen-getDisplayNearestPointCompleted", (display) =>
{
BridgeConnector.Socket.Off("screen-getDisplayNearestPointCompleted");
taskCompletionSource.SetResult(((JObject)display).ToObject<Display>());
});
BridgeConnector.Socket.Emit("screen-getDisplayNearestPoint", JObject.FromObject(point, _jsonSerializer));
return taskCompletionSource.Task;
}
/// <summary>
/// The display that most closely intersects the provided bounds.
/// </summary>
/// <param name="rectangle"></param>
/// <returns>The display that most closely intersects the provided bounds.</returns>
public Task<Display> GetDisplayMatchingAsync(Rectangle rectangle) => BridgeConnector.OnResult<Display>("screen-getDisplayMatching", "screen-getDisplayMatchingCompleted", rectangle);
public Task<Display> GetDisplayMatchingAsync(Rectangle rectangle)
{
var taskCompletionSource = new TaskCompletionSource<Display>();
BridgeConnector.Socket.On("screen-getDisplayMatching", (display) =>
{
BridgeConnector.Socket.Off("screen-getDisplayMatching");
taskCompletionSource.SetResult(((JObject)display).ToObject<Display>());
});
BridgeConnector.Socket.Emit("screen-getDisplayMatching", JObject.FromObject(rectangle, _jsonSerializer));
return taskCompletionSource.Task;
}
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
}
}

23
ElectronNET.API/ServiceCollectionExtensions.cs Executable file → Normal file
View File

@@ -1,5 +1,4 @@
using ElectronNET.API.Interfaces;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
namespace ElectronNET.API
{
@@ -14,7 +13,6 @@ namespace ElectronNET.API
public static IServiceCollection AddElectron(this IServiceCollection services)
=> services
// adding in this manner to ensure late binding.
// this set for backwards compatibility
.AddSingleton(provider => IpcMain.Instance)
.AddSingleton(provider => App.Instance)
.AddSingleton(provider => AutoUpdater.Instance)
@@ -30,23 +28,6 @@ namespace ElectronNET.API
.AddSingleton(provider => HostHook.Instance)
.AddSingleton(provider => PowerMonitor.Instance)
.AddSingleton(provider => NativeTheme.Instance)
.AddSingleton(provider => Dock.Instance)
// this set for proper dependency injection
.AddSingleton<IIpcMain>(_ => IpcMain.Instance)
.AddSingleton<IApp>(_ => App.Instance)
.AddSingleton<IAutoUpdater>(_ => AutoUpdater.Instance)
.AddSingleton<IWindowManager>(_ => WindowManager.Instance)
.AddSingleton<IMenu>(_ => Menu.Instance)
.AddSingleton<IDialog>(_ => Dialog.Instance)
.AddSingleton<INotification>(_ => Notification.Instance)
.AddSingleton<ITray>(_ => Tray.Instance)
.AddSingleton<IGlobalShortcut>(_ => GlobalShortcut.Instance)
.AddSingleton<IShell>(_ => Shell.Instance)
.AddSingleton<IScreen>(_ => Screen.Instance)
.AddSingleton<IClipboard>(_ => Clipboard.Instance)
.AddSingleton<IHostHook>(_ => HostHook.Instance)
.AddSingleton<IPowerMonitor>(_ => PowerMonitor.Instance)
.AddSingleton<INativeTheme>(_ => NativeTheme.Instance)
.AddSingleton<IDock>(_ => Dock.Instance);
.AddSingleton(provider => Dock.Instance);
}
}

View File

@@ -37,7 +37,7 @@ namespace ElectronNET.API
/// <param name="domains">A comma-separated list of servers for which integrated authentication is enabled.</param>
public void AllowNTLMCredentialsForDomains(string domains)
{
BridgeConnector.Emit("webContents-session-allowNTLMCredentialsForDomains", Id, domains);
BridgeConnector.Socket.Emit("webContents-session-allowNTLMCredentialsForDomains", Id, domains);
}
/// <summary>
@@ -47,16 +47,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task ClearAuthCacheAsync(RemovePassword options)
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On("webContents-session-clearAuthCache-completed" + guid, () =>
BridgeConnector.Socket.On("webContents-session-clearAuthCache-completed" + guid, () =>
{
BridgeConnector.Off("webContents-session-clearAuthCache-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-clearAuthCache-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Emit("webContents-session-clearAuthCache", Id, options, guid);
BridgeConnector.Socket.Emit("webContents-session-clearAuthCache", Id, JObject.FromObject(options, _jsonSerializer), guid);
return taskCompletionSource.Task;
}
@@ -66,16 +66,16 @@ namespace ElectronNET.API
/// </summary>
public Task ClearAuthCacheAsync()
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On("webContents-session-clearAuthCache-completed" + guid, () =>
BridgeConnector.Socket.On("webContents-session-clearAuthCache-completed" + guid, () =>
{
BridgeConnector.Off("webContents-session-clearAuthCache-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-clearAuthCache-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Emit("webContents-session-clearAuthCache", Id, guid);
BridgeConnector.Socket.Emit("webContents-session-clearAuthCache", Id, guid);
return taskCompletionSource.Task;
}
@@ -86,16 +86,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task ClearCacheAsync()
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On("webContents-session-clearCache-completed" + guid, () =>
BridgeConnector.Socket.On("webContents-session-clearCache-completed" + guid, () =>
{
BridgeConnector.Off("webContents-session-clearCache-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-clearCache-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Emit("webContents-session-clearCache", Id, guid);
BridgeConnector.Socket.Emit("webContents-session-clearCache", Id, guid);
return taskCompletionSource.Task;
}
@@ -106,16 +106,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task ClearHostResolverCacheAsync()
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On("webContents-session-clearHostResolverCache-completed" + guid, () =>
BridgeConnector.Socket.On("webContents-session-clearHostResolverCache-completed" + guid, () =>
{
BridgeConnector.Off("webContents-session-clearHostResolverCache-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-clearHostResolverCache-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Emit("webContents-session-clearHostResolverCache", Id, guid);
BridgeConnector.Socket.Emit("webContents-session-clearHostResolverCache", Id, guid);
return taskCompletionSource.Task;
}
@@ -126,16 +126,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task ClearStorageDataAsync()
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On("webContents-session-clearStorageData-completed" + guid, () =>
BridgeConnector.Socket.On("webContents-session-clearStorageData-completed" + guid, () =>
{
BridgeConnector.Off("webContents-session-clearStorageData-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-clearStorageData-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Emit("webContents-session-clearStorageData", Id, guid);
BridgeConnector.Socket.Emit("webContents-session-clearStorageData", Id, guid);
return taskCompletionSource.Task;
}
@@ -147,16 +147,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task ClearStorageDataAsync(ClearStorageDataOptions options)
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On("webContents-session-clearStorageData-options-completed" + guid, () =>
BridgeConnector.Socket.On("webContents-session-clearStorageData-options-completed" + guid, () =>
{
BridgeConnector.Off("webContents-session-clearStorageData-options-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-clearStorageData-options-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Emit("webContents-session-clearStorageData-options", Id, options, guid);
BridgeConnector.Socket.Emit("webContents-session-clearStorageData-options", Id, JObject.FromObject(options, _jsonSerializer), guid);
return taskCompletionSource.Task;
}
@@ -171,7 +171,7 @@ namespace ElectronNET.API
/// <param name="options"></param>
public void CreateInterruptedDownload(CreateInterruptedDownloadOptions options)
{
BridgeConnector.Emit("webContents-session-createInterruptedDownload", Id, options);
BridgeConnector.Socket.Emit("webContents-session-createInterruptedDownload", Id, JObject.FromObject(options, _jsonSerializer));
}
/// <summary>
@@ -180,7 +180,7 @@ namespace ElectronNET.API
/// </summary>
public void DisableNetworkEmulation()
{
BridgeConnector.Emit("webContents-session-disableNetworkEmulation", Id);
BridgeConnector.Socket.Emit("webContents-session-disableNetworkEmulation", Id);
}
/// <summary>
@@ -189,7 +189,7 @@ namespace ElectronNET.API
/// <param name="options"></param>
public void EnableNetworkEmulation(EnableNetworkEmulationOptions options)
{
BridgeConnector.Emit("webContents-session-enableNetworkEmulation", Id, options);
BridgeConnector.Socket.Emit("webContents-session-enableNetworkEmulation", Id, JObject.FromObject(options, _jsonSerializer));
}
/// <summary>
@@ -197,7 +197,7 @@ namespace ElectronNET.API
/// </summary>
public void FlushStorageData()
{
BridgeConnector.Emit("webContents-session-flushStorageData", Id);
BridgeConnector.Socket.Emit("webContents-session-flushStorageData", Id);
}
/// <summary>
@@ -207,16 +207,18 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<int[]> GetBlobDataAsync(string identifier)
{
var taskCompletionSource = new TaskCompletionSource<int[]>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<int[]>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<int[]>("webContents-session-getBlobData-completed" + guid, (buffer) =>
BridgeConnector.Socket.On("webContents-session-getBlobData-completed" + guid, (buffer) =>
{
BridgeConnector.Off("webContents-session-getBlobData-completed" + guid);
taskCompletionSource.SetResult(buffer);
var result = ((JArray)buffer).ToObject<int[]>();
BridgeConnector.Socket.Off("webContents-session-getBlobData-completed" + guid);
taskCompletionSource.SetResult(result);
});
BridgeConnector.Emit("webContents-session-getBlobData", Id, identifier, guid);
BridgeConnector.Socket.Emit("webContents-session-getBlobData", Id, identifier, guid);
return taskCompletionSource.Task;
}
@@ -227,16 +229,16 @@ namespace ElectronNET.API
/// <returns>Callback is invoked with the session's current cache size.</returns>
public Task<int> GetCacheSizeAsync()
{
var taskCompletionSource = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<int>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<int>("webContents-session-getCacheSize-completed" + guid, (size) =>
BridgeConnector.Socket.On("webContents-session-getCacheSize-completed" + guid, (size) =>
{
BridgeConnector.Off("webContents-session-getCacheSize-completed" + guid);
taskCompletionSource.SetResult(size);
BridgeConnector.Socket.Off("webContents-session-getCacheSize-completed" + guid);
taskCompletionSource.SetResult((int)size);
});
BridgeConnector.Emit("webContents-session-getCacheSize", Id, guid);
BridgeConnector.Socket.Emit("webContents-session-getCacheSize", Id, guid);
return taskCompletionSource.Task;
}
@@ -247,16 +249,17 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<string[]> GetPreloadsAsync()
{
var taskCompletionSource = new TaskCompletionSource<string[]>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<string[]>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<string[]>("webContents-session-getPreloads-completed" + guid, (preloads) =>
BridgeConnector.Socket.On("webContents-session-getPreloads-completed" + guid, (preloads) =>
{
BridgeConnector.Off("webContents-session-getPreloads-completed" + guid);
taskCompletionSource.SetResult(preloads);
var result = ((JArray)preloads).ToObject<string[]>();
BridgeConnector.Socket.Off("webContents-session-getPreloads-completed" + guid);
taskCompletionSource.SetResult(result);
});
BridgeConnector.Emit("webContents-session-getPreloads", Id, guid);
BridgeConnector.Socket.Emit("webContents-session-getPreloads", Id, guid);
return taskCompletionSource.Task;
}
@@ -267,16 +270,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<string> GetUserAgent()
{
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<string>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<string>("webContents-session-getUserAgent-completed" + guid, (userAgent) =>
BridgeConnector.Socket.On("webContents-session-getUserAgent-completed" + guid, (userAgent) =>
{
BridgeConnector.Off("webContents-session-getUserAgent-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-getUserAgent-completed" + guid);
taskCompletionSource.SetResult(userAgent.ToString());
});
BridgeConnector.Emit("webContents-session-getUserAgent", Id, guid);
BridgeConnector.Socket.Emit("webContents-session-getUserAgent", Id, guid);
return taskCompletionSource.Task;
}
@@ -289,16 +292,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<string> ResolveProxyAsync(string url)
{
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<string>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On<string>("webContents-session-resolveProxy-completed" + guid, (proxy) =>
BridgeConnector.Socket.On("webContents-session-resolveProxy-completed" + guid, (proxy) =>
{
BridgeConnector.Off("webContents-session-resolveProxy-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-resolveProxy-completed" + guid);
taskCompletionSource.SetResult(proxy.ToString());
});
BridgeConnector.Emit("webContents-session-resolveProxy", Id, url, guid);
BridgeConnector.Socket.Emit("webContents-session-resolveProxy", Id, url, guid);
return taskCompletionSource.Task;
}
@@ -310,7 +313,7 @@ namespace ElectronNET.API
/// <param name="path"></param>
public void SetDownloadPath(string path)
{
BridgeConnector.Emit("webContents-session-setDownloadPath", Id, path);
BridgeConnector.Socket.Emit("webContents-session-setDownloadPath", Id, path);
}
/// <summary>
@@ -320,7 +323,7 @@ namespace ElectronNET.API
/// <param name="preloads"></param>
public void SetPreloads(string[] preloads)
{
BridgeConnector.Emit("webContents-session-setPreloads", Id, preloads);
BridgeConnector.Socket.Emit("webContents-session-setPreloads", Id, preloads);
}
/// <summary>
@@ -331,16 +334,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task SetProxyAsync(ProxyConfig config)
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
string guid = Guid.NewGuid().ToString();
BridgeConnector.On("webContents-session-setProxy-completed" + guid, () =>
BridgeConnector.Socket.On("webContents-session-setProxy-completed" + guid, () =>
{
BridgeConnector.Off("webContents-session-setProxy-completed" + guid);
BridgeConnector.Socket.Off("webContents-session-setProxy-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Emit("webContents-session-setProxy", Id, config, guid);
BridgeConnector.Socket.Emit("webContents-session-setProxy", Id, JObject.FromObject(config, _jsonSerializer), guid);
return taskCompletionSource.Task;
}
@@ -353,7 +356,7 @@ namespace ElectronNET.API
/// <param name="userAgent"></param>
public void SetUserAgent(string userAgent)
{
BridgeConnector.Emit("webContents-session-setUserAgent", Id, userAgent);
BridgeConnector.Socket.Emit("webContents-session-setUserAgent", Id, userAgent);
}
/// <summary>
@@ -369,7 +372,7 @@ namespace ElectronNET.API
/// example "en-US,fr,de,ko,zh-CN,ja".</param>
public void SetUserAgent(string userAgent, string acceptLanguages)
{
BridgeConnector.Emit("webContents-session-setUserAgent", Id, userAgent, acceptLanguages);
BridgeConnector.Socket.Emit("webContents-session-setUserAgent", Id, userAgent, acceptLanguages);
}
/// <summary>
@@ -379,15 +382,17 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<ChromeExtensionInfo[]> GetAllExtensionsAsync()
{
var taskCompletionSource = new TaskCompletionSource<ChromeExtensionInfo[]>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<ChromeExtensionInfo[]>();
BridgeConnector.On<ChromeExtensionInfo[]>("webContents-session-getAllExtensions-completed", (extensionslist) =>
BridgeConnector.Socket.On("webContents-session-getAllExtensions-completed", (extensionslist) =>
{
BridgeConnector.Off("webContents-session-getAllExtensions-completed");
taskCompletionSource.SetResult(extensionslist);
BridgeConnector.Socket.Off("webContents-session-getAllExtensions-completed");
var chromeExtensionInfos = ((JArray)extensionslist).ToObject<ChromeExtensionInfo[]>();
taskCompletionSource.SetResult(chromeExtensionInfos);
});
BridgeConnector.Emit("webContents-session-getAllExtensions", Id);
BridgeConnector.Socket.Emit("webContents-session-getAllExtensions", Id);
return taskCompletionSource.Task;
}
@@ -399,7 +404,7 @@ namespace ElectronNET.API
/// <param name="name">Name of the Chrome extension to remove</param>
public void RemoveExtension(string name)
{
BridgeConnector.Emit("webContents-session-removeExtension", Id, name);
BridgeConnector.Socket.Emit("webContents-session-removeExtension", Id, name);
}
/// <summary>
@@ -431,18 +436,25 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<Extension> LoadExtensionAsync(string path, bool allowFileAccess = false)
{
var taskCompletionSource = new TaskCompletionSource<Extension>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<Extension>();
BridgeConnector.On<Extension>("webContents-session-loadExtension-completed", (extension) =>
BridgeConnector.Socket.On("webContents-session-loadExtension-completed", (extension) =>
{
BridgeConnector.Off("webContents-session-loadExtension-completed");
BridgeConnector.Socket.Off("webContents-session-loadExtension-completed");
taskCompletionSource.SetResult(extension);
taskCompletionSource.SetResult(((JObject)extension).ToObject<Extension>());
});
BridgeConnector.Emit("webContents-session-loadExtension", Id, path, allowFileAccess);
BridgeConnector.Socket.Emit("webContents-session-loadExtension", Id, path, allowFileAccess);
return taskCompletionSource.Task;
}
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
}
}

90
ElectronNET.API/Shell.cs Executable file → Normal file
View File

@@ -4,18 +4,16 @@ using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Threading.Tasks;
using ElectronNET.API.Extensions;
using ElectronNET.API.Interfaces;
using System.Runtime.Versioning;
namespace ElectronNET.API
{
/// <summary>
/// Manage files and URLs using their default applications.
/// </summary>
public sealed class Shell : IShell
public sealed class Shell
{
private static Shell _shell;
private static readonly object _syncRoot = new();
private static object _syncRoot = new object();
internal Shell() { }
@@ -44,14 +42,14 @@ namespace ElectronNET.API
/// <param name="fullPath">The full path to the directory / file.</param>
public Task ShowItemInFolderAsync(string fullPath)
{
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<object>();
BridgeConnector.On("shell-showItemInFolderCompleted", () =>
BridgeConnector.Socket.On("shell-showItemInFolderCompleted", () =>
{
BridgeConnector.Off("shell-showItemInFolderCompleted");
BridgeConnector.Socket.Off("shell-showItemInFolderCompleted");
});
BridgeConnector.Emit("shell-showItemInFolder", fullPath);
BridgeConnector.Socket.Emit("shell-showItemInFolder", fullPath);
return taskCompletionSource.Task;
}
@@ -63,16 +61,16 @@ namespace ElectronNET.API
/// <returns>The error message corresponding to the failure if a failure occurred, otherwise <see cref="string.Empty"/>.</returns>
public Task<string> OpenPathAsync(string path)
{
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<string>();
BridgeConnector.On<string>("shell-openPathCompleted", (errorMessage) =>
BridgeConnector.Socket.On("shell-openPathCompleted", (errorMessage) =>
{
BridgeConnector.Off("shell-openPathCompleted");
BridgeConnector.Socket.Off("shell-openPathCompleted");
taskCompletionSource.SetResult(errorMessage);
taskCompletionSource.SetResult((string) errorMessage);
});
BridgeConnector.Emit("shell-openPath", path);
BridgeConnector.Socket.Emit("shell-openPath", path);
return taskCompletionSource.Task;
}
@@ -95,25 +93,24 @@ namespace ElectronNET.API
/// <param name="url">Max 2081 characters on windows.</param>
/// <param name="options">Controls the behavior of OpenExternal.</param>
/// <returns>The error message corresponding to the failure if a failure occurred, otherwise <see cref="string.Empty"/>.</returns>
public Task<string> OpenExternalAsync(string url, OpenExternalOptions options)
{
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
var taskCompletionSource = new TaskCompletionSource<string>();
BridgeConnector.On<string>("shell-openExternalCompleted", (error) =>
BridgeConnector.Socket.On("shell-openExternalCompleted", (error) =>
{
BridgeConnector.Off("shell-openExternalCompleted");
BridgeConnector.Socket.Off("shell-openExternalCompleted");
taskCompletionSource.SetResult(error);
taskCompletionSource.SetResult((string) error);
});
if (options == null)
{
BridgeConnector.Emit("shell-openExternal", url);
BridgeConnector.Socket.Emit("shell-openExternal", url);
}
else
{
BridgeConnector.Emit("shell-openExternal", url, options);
BridgeConnector.Socket.Emit("shell-openExternal", url, JObject.FromObject(options, _jsonSerializer));
}
return taskCompletionSource.Task;
@@ -126,7 +123,18 @@ namespace ElectronNET.API
/// <returns> Whether the item was successfully moved to the trash.</returns>
public Task<bool> TrashItemAsync(string fullPath)
{
return BridgeConnector.OnResult<bool>("shell-trashItem", "shell-trashItem-completed", fullPath);
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.Socket.On("shell-trashItem-completed", (success) =>
{
BridgeConnector.Socket.Off("shell-trashItem-completed");
taskCompletionSource.SetResult((bool) success);
});
BridgeConnector.Socket.Emit("shell-trashItem", fullPath);
return taskCompletionSource.Task;
}
/// <summary>
@@ -134,7 +142,7 @@ namespace ElectronNET.API
/// </summary>
public void Beep()
{
BridgeConnector.Emit("shell-beep");
BridgeConnector.Socket.Emit("shell-beep");
}
/// <summary>
@@ -144,10 +152,20 @@ namespace ElectronNET.API
/// <param name="operation">Default is <see cref="ShortcutLinkOperation.Create"/></param>
/// <param name="options">Structure of a shortcut.</param>
/// <returns>Whether the shortcut was created successfully.</returns>
[SupportedOSPlatform("windows")]
public Task<bool> WriteShortcutLinkAsync(string shortcutPath, ShortcutLinkOperation operation, ShortcutDetails options)
{
return BridgeConnector.OnResult<bool>("shell-writeShortcutLink", "shell-writeShortcutLinkCompleted", shortcutPath, operation.GetDescription(), options);
var taskCompletionSource = new TaskCompletionSource<bool>();
BridgeConnector.Socket.On("shell-writeShortcutLinkCompleted", (success) =>
{
BridgeConnector.Socket.Off("shell-writeShortcutLinkCompleted");
taskCompletionSource.SetResult((bool) success);
});
BridgeConnector.Socket.Emit("shell-writeShortcutLink", shortcutPath, operation.GetDescription(), JObject.FromObject(options, _jsonSerializer));
return taskCompletionSource.Task;
}
/// <summary>
@@ -156,10 +174,30 @@ namespace ElectronNET.API
/// </summary>
/// <param name="shortcutPath">The path tot the shortcut.</param>
/// <returns><see cref="ShortcutDetails"/> of the shortcut.</returns>
[SupportedOSPlatform("windows")]
public Task<ShortcutDetails> ReadShortcutLinkAsync(string shortcutPath)
{
return BridgeConnector.OnResult<ShortcutDetails>("shell-readShortcutLink", "shell-readShortcutLinkCompleted", shortcutPath);
var taskCompletionSource = new TaskCompletionSource<ShortcutDetails>();
BridgeConnector.Socket.On("shell-readShortcutLinkCompleted", (shortcutDetails) =>
{
BridgeConnector.Socket.Off("shell-readShortcutLinkCompleted");
var shortcutObject = shortcutDetails as JObject;
var details = shortcutObject?.ToObject<ShortcutDetails>();
taskCompletionSource.SetResult(details);
});
BridgeConnector.Socket.Emit("shell-readShortcutLink", shortcutPath);
return taskCompletionSource.Task;
}
private readonly JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
}
}

View File

@@ -1,64 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
namespace SocketIOClient.Newtonsoft.Json
{
class ByteArrayConverter : JsonConverter
{
public ByteArrayConverter()
{
Bytes = new List<byte[]>();
}
internal List<byte[]> Bytes { get; }
public override bool CanConvert(Type objectType)
{
return objectType == typeof(byte[]);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, global::Newtonsoft.Json.JsonSerializer serializer)
{
byte[] bytes = null;
if (reader.TokenType == JsonToken.StartObject)
{
reader.Read();
if (reader.TokenType == JsonToken.PropertyName && reader.Value?.ToString() == "_placeholder")
{
reader.Read();
if (reader.TokenType == JsonToken.Boolean && (bool)reader.Value)
{
reader.Read();
if (reader.TokenType == JsonToken.PropertyName && reader.Value?.ToString() == "num")
{
reader.Read();
if (reader.Value != null)
{
if (int.TryParse(reader.Value.ToString(), out int num))
{
bytes = Bytes[num];
reader.Read();
}
}
}
}
}
}
return bytes;
}
public override void WriteJson(JsonWriter writer, object value, global::Newtonsoft.Json.JsonSerializer serializer)
{
var source = value as byte[];
Bytes.Add(source.ToArray());
writer.WriteStartObject();
writer.WritePropertyName("_placeholder");
writer.WriteValue(true);
writer.WritePropertyName("num");
writer.WriteValue(Bytes.Count - 1);
writer.WriteEndObject();
}
}
}

View File

@@ -1,11 +0,0 @@
namespace SocketIOClient
{
public class DisconnectReason
{
public static string IOServerDisconnect = "io server disconnect";
public static string IOClientDisconnect = "io client disconnect";
public static string PingTimeout = "ping timeout";
public static string TransportClose = "transport close";
public static string TransportError = "transport error";
}
}

View File

@@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
namespace SocketIOClient
{
public delegate void OnAnyHandler(string eventName, SocketIOResponse response);
public delegate void OnOpenedHandler(string sid, int pingInterval, int pingTimeout);
//public delegate void OnDisconnectedHandler(string sid, int pingInterval, int pingTimeout);
public delegate void OnAck(int packetId, List<JsonElement> array);
public delegate void OnBinaryAck(int packetId, int totalCount, List<JsonElement> array);
public delegate void OnBinaryReceived(int packetId, int totalCount, string eventName, List<JsonElement> array);
public delegate void OnDisconnected();
public delegate void OnError(string error);
public delegate void OnEventReceived(int packetId, string eventName, List<JsonElement> array);
public delegate void OnOpened(string sid, int pingInterval, int pingTimeout);
public delegate void OnPing();
public delegate void OnPong();
}

Some files were not shown because too many files have changed in this diff Show More