Compare commits

...

350 Commits

Author SHA1 Message Date
Gregor Biswanger
e9bf393165 Merge branch 'theolivenbaum-master' 2022-07-28 11:52:12 +02:00
Gregor Biswanger
d3aa9783d3 Merge of theolivenbaum PR 2022-07-28 11:51:18 +02:00
theolivenbaum
a5bc910926 Release 19.0.9 2022-07-22 10:45:24 +02:00
theolivenbaum
89033c61d4 Return socket port 2022-07-14 17:07:19 +02:00
theolivenbaum
90b4a287d9 Add experimental mode to start electron from C# (for development purposes) 2022-07-14 16:56:50 +02:00
theolivenbaum
e2615a8dc1 Import SocketIO code 2022-07-14 16:53:54 +02:00
theolivenbaum
73330185de Release 19.0.8 2022-07-12 13:43:12 +02:00
theolivenbaum
e1c5b7e3c2 Update main.js 2022-06-28 15:40:58 +02:00
theolivenbaum
a19e095f6e Fix wrong variable name 2022-06-27 16:04:32 +02:00
theolivenbaum
b7ed3d22f1 Update main.js 2022-06-27 14:53:21 +02:00
theolivenbaum
c8f944af4a Release 19.0.6 2022-06-27 14:35:16 +02:00
theolivenbaum
2f73bb8b34 Release 19.0.5 2022-06-21 10:48:40 +02:00
theolivenbaum
1f22be68d2 Drop net5.0 2022-06-09 10:23:29 +02:00
theolivenbaum
576140474e merge #624 2022-06-09 10:22:54 +02:00
theolivenbaum
68c9e80417 merge #673 2022-06-09 10:12:15 +02:00
theolivenbaum
4906babd78 Change to use ImageSharp 2022-06-08 21:28:38 +02:00
theolivenbaum
e0000f7aba Change to use ImageSharp 2022-06-08 21:28:30 +02:00
theolivenbaum
ff05ef20c8 Release 19.0.4 2022-06-08 20:52:35 +02:00
theolivenbaum
79ce1c42e7 Release 19.0.3 2022-06-08 20:20:57 +02:00
Gregor Biswanger
4e95bb3331 Static parameter for build commands method 2022-06-07 23:03:06 +02:00
Gregor Biswanger
58bfa336a8 Change to .NET 6 only 2022-06-07 22:59:26 +02:00
Gregor Biswanger
afa69280f5 Fixed merge conflicts 2022-06-07 22:55:00 +02:00
Gregor Biswanger
8621d8cea3 Merge branch 'master' into dev/electron-18-update 2022-06-07 22:48:13 +02:00
Gregor Biswanger
9106391f60 Merge pull request #698 from stackify/bad-master
#697 fixing conflicts in ElectronNET.CLI commands
2022-06-07 22:41:53 +02:00
Gregor Biswanger
490ef6231b .NET 6 support only 2022-06-07 22:40:44 +02:00
theolivenbaum
89833f42fc Release 19.0.2 2022-06-02 11:11:26 +02:00
theolivenbaum
252ab5050b Release 18.3.2 2022-06-02 11:10:38 +02:00
Daniel Gidman
2ff29e553d #697 fixing conflicts in ElectronNET.CLI commands 2022-05-31 13:49:49 -05:00
theolivenbaum
a1a5f6f9a6 Handle case where pipe breaks showing error message 2022-05-31 13:38:54 +02:00
theolivenbaum
3ea7e96324 Release 19.0.1 2022-05-27 10:10:28 +02:00
theolivenbaum
a00a8a0251 Release 18.3.0 2022-05-24 10:07:32 +02:00
theolivenbaum
e245d0d2f6 release also for 17.4.1 2022-04-23 17:02:54 +02:00
theolivenbaum
48a0d68313 Increase max message size to 100mb 2022-04-23 17:01:34 +02:00
theolivenbaum
4d5d6722a7 Check if version exists 2022-04-21 09:38:41 +02:00
theolivenbaum
d910eec8df Add releaser helper to automate updating versions 2022-04-21 09:36:22 +02:00
theolivenbaum
4f9b46c616 Electron Release 18.1.0 2022-04-21 09:16:05 +02:00
theolivenbaum
4308f2c6d6 Electron Release 17.4.1 2022-04-21 09:15:00 +02:00
theolivenbaum
87468ccd5d Update main.js 2022-04-20 13:05:25 +02:00
theolivenbaum
d863ff95f4 update to 18.0.4 2022-04-20 12:47:18 +02:00
theolivenbaum
3a24b69063 let user define own env variables to start 2022-04-20 12:36:51 +02:00
rafael-aero
d6c98759c5 handle also get nearest point 2022-04-13 20:53:33 +02:00
rafael-aero
6675571aaf handle fail on getdisplaymatching 2022-04-13 20:51:34 +02:00
rafael-aero
3f8766ff9e Build 17.4.0 2022-04-12 11:31:35 +02:00
rafael-aero
9b881b1b71 Roll back to electron 18.0.2
Roll back to electron 18.0.2
2022-04-12 10:41:28 +02:00
rafael-aero
339a319f3e fix race condition on electron socket initialization 2022-04-11 13:59:42 +02:00
rafael-aero
d929102e18 update to Electron 18.0.3 2022-04-10 22:37:43 +02:00
Gregor Biswanger
26595bf290 Refactor eslint hints 2022-04-06 22:34:56 +02:00
Gregor Biswanger
5ca65f1939 Update TypeScript 4.3.5 -> 4.6.3, replace typings to native socket.io 2022-04-06 22:33:54 +02:00
Gregor Biswanger
b4b355eeac Replace tslint with eslint 2022-04-06 22:33:06 +02:00
Gregor Biswanger
47774d2871 Update socket.io 2.4.0 -> 4.4.1, electron 13.1.5 -> 18.0.2, electron-updater 4.3.9 -> 4.6.5, replace tsint with eslint 2022-04-06 22:32:38 +02:00
Gregor Biswanger
43cbdc15b8 Merge branch 'yannikHoeflich-master' 2022-04-06 21:58:38 +02:00
Gregor Biswanger
540be39547 Merge fix: changed the processing of loadUrl at CreateWindowAsync #631 2022-04-06 21:58:20 +02:00
Gregor Biswanger
a7a8685341 Merge branch 'stackify-bug/644' 2022-04-06 19:20:13 +02:00
Gregor Biswanger
0575413768 Transpile new JavaScript host files 2022-04-06 19:19:46 +02:00
Gregor Biswanger
e8394277e7 Merge fix: Fix for #664, open for comment #644 2022-04-06 19:17:43 +02:00
Gregor Biswanger
5fe50b45bb Merge branch 'stackify-CrossPlatformNativeImage' 2022-04-06 19:03:35 +02:00
Gregor Biswanger
70ca42aeaf Merge fix: Conversion to use ImageSharp rather than System.Drawing.Common #658 2022-04-06 19:03:22 +02:00
Gregor Biswanger
f54ce1eb02 Merge branch 'stackify-feature/DI-MOC-Process' 2022-04-06 18:58:15 +02:00
Gregor Biswanger
aa9c2caf47 Merge fix: Support DI and Mocking better + Support launching app with file for win and linux #656 2022-04-06 18:58:02 +02:00
Gregor Biswanger
ba8e941cf1 Merge branch 'stackify-feature/dotnet-args' 2022-04-06 18:53:19 +02:00
Gregor Biswanger
f0003ae953 Merge fix: Support for additional dotnet publish flags #655 2022-04-06 18:52:58 +02:00
Gregor Biswanger
1f5d70be83 Merge branch 'schaveyt-feature/647-launching-app-w-file-win-linux' 2022-04-06 18:46:18 +02:00
Gregor Biswanger
1a1bd61e3e Merge fix: Support launching app with file for win and linux #648 2022-04-06 18:45:51 +02:00
Gregor Biswanger
709780fd04 Merge pull request #673 from MutatedGamer/parentOption
Add ability to set a window's parent using BrowserWindowOptions
2022-04-06 18:26:08 +02:00
Gregor Biswanger
9a17d22343 Merge branch 'stackify-NET6' 2022-04-06 18:10:31 +02:00
Gregor Biswanger
7222c9e6c5 Fix conflicts: Support for .NET 6 2022-04-06 18:10:18 +02:00
Gregor Biswanger
29b4f577a6 Merge pull request #634 from stackify/feature/RecentDocuments-Mac
Recent Document Support for MacOS
2022-04-06 16:35:00 +02:00
Gregor Biswanger
9698c80442 Merge pull request #633 from stackify/feature/DI-MOCK
Support DI and Mocking better.
2022-04-06 16:33:46 +02:00
Gregor Biswanger
4afa535c31 Merge pull request #626 from javierlarota/feature/ignore-cert-errors
Allow ignoring certificate errors
2022-04-06 16:30:00 +02:00
Gregor Biswanger
0de9699a52 Merge pull request #624 from bman46/master
Support Apple Silicon Natively
2022-04-06 16:28:14 +02:00
Gregor Biswanger
6f5fb16091 Merge pull request #595 from theolivenbaum/switch-to-new-socket-lib
Switch to async socket lib
2022-04-06 16:24:02 +02:00
Gregor Biswanger
891870abde Merge branch 'master' into switch-to-new-socket-lib 2022-04-06 16:23:47 +02:00
Gregor Biswanger
ab162d4829 Merge pull request #592 from Meberem/feature/log-dotnet-errors
Log errors in the dotnet process
2022-04-06 16:10:24 +02:00
rafael-aero
ec12dbd6ad Update electron to 18.0.2 2022-04-05 22:41:41 +02:00
rafael-aero
f05934acb8 Forgot to roll back to 18.0.1 2022-04-01 20:59:06 +02:00
rafael-aero
0632112a3a improve build command to try to read version from xml file 2022-04-01 19:03:09 +02:00
rafael-aero
793f714b9e Publish Electron 17.3.1 2022-04-01 18:37:16 +02:00
rafael-aero
e3fbce26d0 Remove default electron version parameter from CLI 2022-04-01 18:27:26 +02:00
rafael-aero
c45ea53a02 Update to Electron 18.0.1 2022-04-01 17:41:21 +02:00
rafael-aero
de56395946 Update to Electron 18.0.0 2022-03-29 10:13:20 +02:00
rafael-aero
b0262b36d0 Update electron to 17.3.0 2022-03-29 09:44:20 +02:00
rafael-aero
0ffecd5bc7 save projects 2022-03-25 12:12:35 +01:00
rafael-aero
b7196ff10d Merge branch 'master' of https://github.com/theolivenbaum/Electron.NET 2022-03-25 12:11:10 +01:00
rafael-aero
93f1875f8b remove again SocketIOClient as package was updated 2022-03-25 12:11:08 +01:00
theolivenbaum
6ca5563dc1 Update build-nuget.yaml for Azure Pipelines 2022-03-25 12:03:54 +01:00
rafael-aero
e643d0db08 Update 2022-03-25 10:30:51 +01:00
rafael-aero
9c89711d59 Import SocketIOClient code until next version is released 2022-03-25 10:23:56 +01:00
rafael-aero
606b2a6481 Update main.js 2022-03-24 19:32:15 +01:00
rafael-aero
ef13ec9cc4 update electron to 17.2.0 2022-03-24 18:45:28 +01:00
rafael-aero
b58b4579f9 Add dotenv and electron-notarize dependencies 2022-03-24 11:46:17 +01:00
rafael-aero
13edd4c3ca fix multi thread usage missing lock 2022-03-23 12:18:52 +01:00
rafael-aero
967ccf385a Add support for random port assignment 2022-03-22 18:51:32 +01:00
MutatedGamer
4a44f3e150 Set parent in createBrowserWindow 2022-03-21 20:34:38 -07:00
MutatedGamer
d6e362c83c Add Parent property to BrowserWindowOptions 2022-03-21 20:34:25 -07:00
rafael-aero
df15d249de Update to 17.1.2 2022-03-10 08:38:32 +01:00
rafael-aero
a5bb3ad36b Update ElectronNET.API.csproj 2022-03-07 23:36:18 +01:00
rafael-aero
8f26ceed3d update socket.io 2022-03-07 23:13:16 +01:00
rafael-aero
7f282c1346 Update to 17.1.1 2022-03-07 22:06:48 +01:00
rafael-aero
8067f8e1ff Update to 17.1.0, remove soon to be deprecated nativeWindowOpen optiojn 2022-03-03 08:21:35 +01:00
Daniel Gidman
468c8ba2fc #644 Check for null before taking actions. Also localize references to window on window creation 2022-02-23 14:58:44 -06:00
Daniel Gidman
ab694e22fc Change IsReady logic to also wait for connection to be open. This is to prevent actions occurring before the app can act upon them. 2022-02-23 14:57:05 -06:00
Robert Muehsig
79a6ef322c Merge pull request #609 from AndreasJagiella-EH/master
Set PublishSingleFile default back to false for NET5 compatibility
2022-02-21 23:01:46 +01:00
Robert Muehsig
0ef5edfdf6 Merge pull request #613 from aguang-xyz/fix/npe-when-target-not-specified
Fix IndexOutOfRangeException in build command parsing logic
2022-02-21 22:55:01 +01:00
Robert Muehsig
7db609c699 Merge pull request #607 from gpgpublickey/build_adds_help_publish_parameters
Adds help regarding to modify publish parameters
2022-02-21 22:49:48 +01:00
Robert Muehsig
127408afdb Merge pull request #598 from tub5/tub5-readme-fix
Update README.md
2022-02-21 22:47:53 +01:00
rafael-aero
40eae5af3d update to 17.0.1 2022-02-15 14:15:32 +01:00
rafael-aero
26ba398428 Add new WCO height option 2022-02-10 23:00:33 +01:00
rafael-aero
56fe57db46 Update to 17.0.0 2022-02-10 09:55:56 +01:00
rafael-aero
c8552041fb Add debug messages for update methods 2022-02-10 00:00:51 +01:00
rafael-aero
13a3753214 emit prepare-for-update 2022-02-09 22:18:04 +01:00
rafael-aero
93ee1871ee update electron to 16.0.8 2022-01-30 20:43:39 +01:00
Daniel Gidman
86644e7366 #637 Conversion to use ImageSharp rather than System.Drawing.Common for cross platform compatibility.
Breaking Changes:

* `System.Drawing.Common` is dropped and `SixLabors.ImageSharp` is introduced.
* uses of `NativeImage.CreateFromBitmap(bitmap, options);` is no longer supported, will cause failed builds.

Unexpected Behaviors:

* uses ToDataUrl will always create png data urls, unexpected output may happen for those that manipulate this output expecting a different data url format.

Obsoletions:

* `CreateFromBitmapOptions` & `CreateFromBufferOptions` have been consolidated into `CreateOptions`. Implicit conversions added to ease transition.
* `ToBitmapOptions`, `ToDataUrlOptions`, `ToPngOptions` & `BitmapOptions` have been consolidated into `ImageOptions`. Implicit conversions added to ease transition.
2022-01-26 11:07:48 -06:00
Daniel Gidman
2331ef43b4 Support electronize as a dotnet 6 target tool 2022-01-25 14:08:40 -06:00
Daniel Gidman
b8845bb402 Include Process into DI Mocking and interfaces 2022-01-25 10:45:49 -06:00
Daniel Gidman
1767bcd5ad Merge commit '64e058b0b59a4fbbd6c0ec9de2ee1850a66684c4' into feature/DI-MOC-Process
# Conflicts:
#	ElectronNET.API/ServiceCollectionExtensions.cs
2022-01-25 10:41:42 -06:00
Daniel Gidman
5338749e4d Fixed extraction of arguments 2022-01-21 20:03:31 -06:00
Daniel Gidman
4790c4ebb2 Support for additional dotnet publish flags. 2022-01-21 16:51:30 -06:00
rafael-aero
d646b84c74 update 16.0.7 2022-01-13 11:49:41 +01:00
Todd Schavey
64e058b0b5 #647 make ProcessVersions Entity a record to ensure readonly access to props
This is to address a PR #648 review comment to ensure that only the external
users are not able to modify the instance values.
2022-01-04 12:11:52 -05:00
Todd Schavey
24a2005776 #647 update XML documentation to ElectronNET.API Process members 2022-01-03 00:21:48 -05:00
Todd Schavey
592148116b #647 correct to ElectronNET.API Process member for versions field 2022-01-02 23:11:01 -05:00
Todd Schavey
ba82b9a600 #647 add to ElectronNET.API Process member interfaces for various fields 2022-01-02 22:46:53 -05:00
Todd Schavey
562cccbfae #647 add to ElectronNET.API Process member interfaces for argv and type 2022-01-02 18:28:39 -05:00
Todd Schavey
1406fc1d79 #647 add initial Process class to ElectronNET.API 2022-01-02 16:46:14 -05:00
Todd Schavey
8e1e184d1e #647 process argv for open-file for win and linux 2021-12-28 21:40:53 -05:00
rafael-aero
9e37d4d857 fix serializtion 2021-12-26 11:00:51 +01:00
rafael-aero
060fb19a6d Update NativeImageJsonConverter.cs 2021-12-26 09:46:55 +01:00
rafael-aero
291faa48de Missing including new file 2021-12-26 09:27:45 +01:00
rafael-aero
4358178524 Add support for DesktopCapturer 2021-12-26 09:03:48 +01:00
Daniel Gidman
4b314940d7 Update ElectronNET.sln
removing the full path reference as suggested.

Co-authored-by: alborozd <alborozd@gmail.com>
2021-12-23 10:54:44 -06:00
rafael-aero
ed841558b8 always exit if .net process exited 2021-12-23 16:54:06 +01:00
rafael-aero
36751db618 fix missing pipe option for detached process 2021-12-23 10:09:08 +01:00
rafael-aero
f229d540a8 Add simple socket auth via stdin exchange 2021-12-23 09:24:29 +01:00
rafael-aero
546c617cd0 Merge branch 'master' of https://github.com/theolivenbaum/Electron.NET 2021-12-17 13:29:49 +01:00
rafael-aero
88e8051004 update electron and electron updater 2021-12-17 13:29:47 +01:00
theolivenbaum
21427f0b44 Merge pull request #5 from theolivenbaum/clipboard-image-support-macos
add support for images in clipboards on macos
2021-12-15 18:31:49 +01:00
Pius Friesch
159c1f46c1 add support for images in clipboards on macos 2021-12-15 17:58:38 +01:00
rafael-aero
8cb235527d Change again socket reconnect logic 2021-12-15 10:44:59 +01:00
theolivenbaum
4a3a5c6aa4 Merge pull request #4 from theolivenbaum/clipboard
Clipboard
2021-12-13 18:13:47 +01:00
Pius Friesch
82bf94b2a2 fix writing images to clipboard when writing multiple formats 2021-12-13 17:40:57 +01:00
Pius Friesch
17a7886d12 formatting 2021-12-13 17:33:00 +01:00
Pius Friesch
6be3019cb6 fix typescript, based on changes to js 2021-12-13 17:31:48 +01:00
rafael-aero
3460217021 refactor socket reconnect 2021-12-13 16:22:08 +01:00
rafael-aero
427592de46 Add background thread to reconnect socket 2021-12-13 14:43:04 +01:00
rafael-aero
8f820d033a Merge branch 'master' of https://github.com/theolivenbaum/Electron.NET 2021-12-13 14:10:40 +01:00
rafael-aero
4118756711 change socket connect loop 2021-12-13 14:10:38 +01:00
Daniel Gidman
ffdf034f5f appveyor dedicated build 2021-12-09 14:01:55 -06:00
Daniel Gidman
946fbd7a72 Update appveyor image 2021-12-09 13:51:13 -06:00
Daniel Gidman
6b0205467b Support for .NET 6
Also included is the ability to target a specific project in the `electronize build/start` command using /dotnet-project
2021-12-09 13:11:24 -06:00
theolivenbaum
a28df5339d Merge pull request #3 from pfriesch/patch-1
Add Image to clipboard data
2021-12-09 17:50:54 +01:00
rafael-aero
f5a141a1c6 Update ElectronNET.API.csproj 2021-12-09 17:50:43 +01:00
Pius Friesch
b5e2b660f7 Add Image to clipboard data 2021-12-09 17:46:30 +01:00
javierlarota
431e401451 Feedback from code review. Improving logging of the domain names that will be ignored. 2021-12-09 04:22:35 +00:00
Daniel Gidman
02faeae603 Add support for Mac recent documents and clear recent documents commands on the Application Menu 2021-12-08 13:32:00 -06:00
Yannik Höflich
0d9483e325 Updated TryParseLoadUrl with UriBuilder for better readability 2021-12-08 17:30:44 +01:00
Daniel Gidman
1b14bb0fe5 Convert main static references off of Electron into interface implementation and expose the underlying Socket for low level interaction. 2021-12-07 11:52:43 -06:00
Brendan McShane
b08a0755e6 Revert automatic change by VS 2021-12-07 12:23:26 -05:00
Brendan McShane
7e89e27c26 Better way to check version
Checks to see if the dotnet 6 or greater is running.
2021-12-06 20:28:57 -05:00
Brendan McShane
e4deba2489 Add dotnet 6 check
Ensure that dotnet 6 is installed when compiling for MacOS ARM.
2021-12-06 20:17:46 -05:00
Brendan McShane
1d9e540fc2 Change back to dotnet 5 2021-12-06 20:17:08 -05:00
Yannik Höflich
c7d2dabbc3 changed the usage of loadUrl at CreateWindowAsync: adds protocol, host and port. You can also insert the port with {port} 2021-12-05 16:31:32 +01:00
theolivenbaum
2d940dbeee update to electron 16.0.4 2021-12-03 09:16:44 +01:00
javierlarota
d9d655cae8 Allow ignoring certificate errors when using an untrusted self-signed certificate for https communication with the AspCore backend.
We can ignore all cert errors or only cert errors from specific domain names configured in electron.manifest.json
2021-11-26 18:03:54 +00:00
theolivenbaum
1b7f722f31 Update Electron to 16.0.2, update electron-updater to 4.6.1 2021-11-25 08:06:49 +01:00
rafael-aero
b1c5c12004 fix null usage on ipc.js 2021-11-22 17:33:50 +01:00
rafael-aero
af0410998e update screenshot code 2021-11-22 11:38:16 +01:00
Brendan McShane
e0e8572cc1 Update README.md
Add notes about osx-arm64.
2021-11-20 21:35:02 -05:00
Brendan McShane
4ba8d9bf46 Add fix for Apple Silicon to build command 2021-11-20 18:25:46 -05:00
Brendan McShane
07d6fed712 Fix for build command 2021-11-20 18:24:41 -05:00
Brendan McShane
5dbe62bcba Add osx-arm64 support to electronize command
Updates the GetTargetPlatformInformationResult() function to add 'osx-arm64' target. 
Updates the 'default' case to automatically detect M1 mac.
2021-11-20 17:59:53 -05:00
Brendan McShane
438c8e1f14 Switch from dotnet 5.0 to dotnet 6.0
The 'osx-arm64' RID is only supported on .net versions greater than 6.0.
https://docs.microsoft.com/en-us/dotnet/core/rid-catalog
2021-11-20 17:56:33 -05:00
rafael-aero
ffedbcc5df Add missing GetFileIcon method 2021-11-20 16:37:38 +01:00
rafael-aero
9aad72431c Update electron to 16.0.1 2021-11-18 20:08:31 +01:00
rafael-aero
4405eba2d5 Add support for dark mode splashscreen 2021-11-17 14:19:07 +01:00
rafael-aero
067300bfec update nuget packages 2021-11-16 14:02:17 +01:00
theolivenbaum
b2f5a0eb34 Update to Electron 16 2021-11-16 06:45:59 +01:00
theolivenbaum
cbc150db06 Update to Electron 15.3.2 2021-11-16 06:43:15 +01:00
theolivenbaum
cbac8541be Add transparent option to webpreferences 2021-11-13 11:13:35 +01:00
rafael-aero
2f9029e267 add launch settings to test build 2021-11-10 11:07:46 +01:00
rafael-aero
390135af8a Revert "move .NET bin output to under Helpers folder to be compliant with macOS bundle formt "
This reverts commit be1182d302.
2021-11-10 11:06:40 +01:00
theolivenbaum
be1182d302 move .NET bin output to under Helpers folder to be compliant with macOS bundle formt 2021-11-10 07:57:22 +01:00
theolivenbaum
54c86d9e2f Fix auto-updater on mac not working 2021-11-10 07:46:25 +01:00
rafael-aero
2cc6823604 Update electron to 15.3.1 2021-11-09 18:16:45 +01:00
rafael-aero
326efd353c Update sdk to 6.0.100 2021-11-09 18:16:32 +01:00
rafael-aero
bd96765db5 Merge branch 'master' of https://github.com/theolivenbaum/Electron.NET 2021-11-05 11:01:33 +01:00
rafael-aero
606bb019f7 fix race condition causing unobserved exception 2021-11-05 11:01:32 +01:00
theolivenbaum
a12a7e4287 Update main.js 2021-11-03 07:51:59 +01:00
rafael-aero
5bf9aa811d Update main.js 2021-11-02 18:20:59 +01:00
rafael-aero
5477bd7f36 Add setImmediate to see if this fixes the macOS update bug 2021-11-02 15:39:57 +01:00
rafael-aero
134a5b1365 Merge branch 'master' of https://github.com/theolivenbaum/Electron.NET 2021-10-29 10:54:45 +02:00
rafael-aero
23791fcc3e fix path detection 2021-10-29 10:54:42 +02:00
theolivenbaum
751c48c804 Update Vibrancy.cs 2021-10-27 22:06:52 +02:00
theolivenbaum
316cf0108e Update Vibrancy.cs 2021-10-27 21:57:24 +02:00
rafael-aero
4caa28dc87 Merge branch 'master' of https://github.com/theolivenbaum/Electron.NET 2021-10-27 14:10:09 +02:00
rafael-aero
ad46b1a7a2 remove listeners on quitAndInstall to try fix macOS update issue 2021-10-27 14:10:07 +02:00
theolivenbaum
2297669e6d Update main.js 2021-10-27 09:11:33 +02:00
theolivenbaum
ae92973814 Update main.js 2021-10-26 22:58:54 +02:00
theolivenbaum
5ebfa2b0bd Update main.js 2021-10-26 21:34:35 +02:00
rafael-aero
1494c2f436 Update main.js 2021-10-26 18:15:37 +02:00
rafael-aero
84f027cd8d remove listeners on the call to before-quit-for-update 2021-10-26 16:27:44 +02:00
rafael-aero
f213ca4f8c move code for fixPath here 2021-10-26 15:58:55 +02:00
rafael-aero
22fa3f2780 Revert "Add fixPath to fix linux process spawn"
This reverts commit 1e50f203d8.
2021-10-26 15:41:02 +02:00
rafael-aero
b5cc631daf Revert "Update main.js"
This reverts commit b5ff42a148.
2021-10-26 15:41:00 +02:00
rafael-aero
b5ff42a148 Update main.js 2021-10-26 15:35:06 +02:00
rafael-aero
1e50f203d8 Add fixPath to fix linux process spawn
As suggested in https://github.com/electron/electron-packager/issues/603
2021-10-25 13:35:02 +02:00
rafael-aero
852b140525 Update to 15.3.0 2021-10-22 11:09:17 +02:00
rafael-aero
7e5903f5bc update to electron 15.1.2 2021-10-12 13:11:29 +02:00
rafael-aero
62003ae5fc Update BuildCommand.cs 2021-10-11 19:36:41 +02:00
Grey Wang
5cd152c1ed Fix build command logic when target is not specified 2021-10-09 20:41:12 +13:00
rafael-aero
228a5cef31 set missing return value
https://github.com/electron/electron/issues/31321
2021-10-07 18:13:32 +02:00
rafael-aero
c28355094e Update build-nuget.yaml 2021-10-07 17:54:28 +02:00
rafael-aero
329df38765 Update to Electron 15.1.1 2021-10-07 17:52:15 +02:00
AndreasJagiella-EH
5274fec200 Set PublishSingleFile default back to false for NET5 compatibility 2021-10-04 14:54:02 +02:00
Cristian Giagante
9892a90038 Adds help regarding to modify publish parameters 2021-09-26 19:54:25 -03:00
theolivenbaum
1d1e60bc63 Missed updating the nuget package version 2021-09-22 09:06:41 +02:00
theolivenbaum
21b6b14df5 Update Node to v16.9.6 2021-09-22 08:56:58 +02:00
theolivenbaum
2445b59c4d Update Electron to v15.0.0 2021-09-22 08:55:04 +02:00
rafael-aero
9a8584795b update to net6.0 2021-09-17 18:16:31 +02:00
rafael-aero
e6ea9883fd code cleanup 2021-09-17 16:05:40 +02:00
rafael-aero
e1edc764d6 update electron to 14.0.1 2021-09-16 19:39:16 +02:00
rafael-aero
51013d5825 code cleanup 2021-09-15 11:14:45 +02:00
rafael-aero
664d5f7236 Add method to get window by id 2021-09-15 11:02:54 +02:00
rafael-aero
75d7924251 Merge branch 'master' of https://github.com/theolivenbaum/Electron.NET 2021-09-15 10:58:45 +02:00
rafael-aero
c2904f3f68 Change second instance activation to raise event instead of trying to blindly open any window 2021-09-15 10:58:43 +02:00
theolivenbaum
adef39f3ec Change flags to nullable 2021-09-10 10:53:49 +02:00
theolivenbaum
836eebf256 Update browserWindows.js 2021-09-10 10:13:23 +02:00
theolivenbaum
124d24a19c Update browserWindows.ts 2021-09-10 10:12:56 +02:00
theolivenbaum
67ea8c768e Update BrowserWindow.cs 2021-09-10 10:11:41 +02:00
theolivenbaum
8604b50224 Add method to destroy all windows manually 2021-09-08 08:57:05 +02:00
theolivenbaum
5559fc61b1 force-destroy all windows on before-quit-for-update 2021-09-08 08:47:16 +02:00
theolivenbaum
9be80abfcf Add method to destroy splashscreen 2021-09-07 19:19:49 +02:00
theolivenbaum
6a4a7eff1c Workaround for electron freeze bug 2021-09-07 17:48:12 +02:00
theolivenbaum
a637174b93 typo 2021-09-07 15:00:35 +02:00
theolivenbaum
dc2662e52e Add way to set flags for Electron before the app starts 2021-09-07 14:36:57 +02:00
theolivenbaum
801616cd53 Let users set the color of the titlebar instead of just enabling it 2021-09-06 15:54:32 +02:00
theolivenbaum
9693c82792 Add TitleBarOverlay option 2021-09-06 15:18:59 +02:00
theolivenbaum
a3fb411d8a always keep default values when creating window 2021-09-06 12:39:32 +02:00
theolivenbaum
172f6ded3f Merge branch 'master' of https://github.com/theolivenbaum/Electron.NET 2021-09-06 11:45:01 +02:00
theolivenbaum
bbbc9e6f61 make splashscreen draggable 2021-09-06 11:44:57 +02:00
rafael-aero
236c31abe0 Merge branch 'master' of https://github.com/theolivenbaum/Electron.NET 2021-09-03 15:28:34 +02:00
rafael-aero
374d92f3b1 don't throw in case the tray was destroyed between calls 2021-09-03 15:28:32 +02:00
theolivenbaum
ea40249a9c refactor Socket to use a task completion source instead of a getter with locks 2021-09-02 20:37:22 +02:00
theolivenbaum
7f5db58edd fix deadlock case on emit before any Socket use 2021-09-02 20:06:01 +02:00
theolivenbaum
d18700f64d missing static flag 2021-09-02 18:29:02 +02:00
theolivenbaum
47f4516ae2 Add flag to check if the socket is connected 2021-09-02 18:20:11 +02:00
theolivenbaum
bb8965fa91 remove semaphore in favor of unique events per window /view create request 2021-09-02 17:24:07 +02:00
theolivenbaum
c229d49765 remove debug message 2021-09-02 16:45:20 +02:00
theolivenbaum
4fae2aeef2 fix ipc data serialization 2021-09-02 16:45:03 +02:00
theolivenbaum
5f4b1628e5 fix display serialization, serialize only window id when sending ipc message 2021-09-02 16:34:20 +02:00
theolivenbaum
25af4a7314 Update to Electron 14 2021-09-02 16:18:56 +02:00
rafael-aero
14ee45b88f Another attempt 2021-09-01 22:37:58 +02:00
rafael-aero
badc9f26b6 Revert "fix closed behaviour"
This reverts commit 4e7cebe519.
2021-09-01 22:25:50 +02:00
rafael-aero
4e7cebe519 fix closed behaviour 2021-09-01 22:10:44 +02:00
rafael-aero
ec74c7989a Update ipc.js 2021-09-01 21:31:30 +02:00
rafael-aero
a2579b221f fix wrong Id being passed - needed browser id, not webcontent id 2021-09-01 21:30:47 +02:00
rafael-aero
1e7ff5b6d6 fix getting id 2021-09-01 20:57:41 +02:00
rafael-aero
eac14427df Add event handler with sender id 2021-09-01 20:42:49 +02:00
rafael-aero
794248a83c fix creation of windows not being thread safe 2021-09-01 20:02:17 +02:00
rafael-aero
9a938dc680 use concurrent dictionary instead of list 2021-09-01 18:59:15 +02:00
rafael-aero
2d68ca5270 Fix LogError call 2021-09-01 18:29:20 +02:00
rafael-aero
548609f69b add handler for errors on socket.on calls 2021-09-01 18:28:44 +02:00
rafael-aero
7b5bac2083 Fix property assignment 2021-09-01 14:08:30 +02:00
rafael-aero
5ba10136e2 Add excludedFromShownWindowsMenu and TrafficLightPosition 2021-09-01 14:07:45 +02:00
tub5
aba77b1361 Update README.md
Fixed versions on the README
2021-08-31 11:56:26 +01:00
rafael-aero
bd45e23768 remove recreate behaviour from activate event on mac, and emit event to handler instead 2021-08-30 13:22:07 +02:00
rafael-aero
35b18a9501 ignore process closing if going for an update 2021-08-26 17:04:16 +02:00
rafael-aero
89d41a2750 restore serialization settings where it makes sense 2021-08-26 16:08:11 +02:00
rafael-aero
efdaf0e341 remove defaultvaluehandling setting as this breaks some cases
(like X:0 on browser view set bounds)
2021-08-26 15:59:52 +02:00
rafael-aero
0728577c40 missing using 2021-08-26 14:23:32 +02:00
rafael-aero
224bc1ef65 Add supported OS platform annotations 2021-08-26 14:22:54 +02:00
rafael-aero
7ad6803671 improve reconnect logic by waiting for reconnect before emit 2021-08-26 10:31:06 +02:00
rafael-aero
81a8367dac improve reconnect logic 2021-08-26 10:19:24 +02:00
rafael-aero
1161f59836 Update BridgeConnector.cs 2021-08-26 10:08:42 +02:00
rafael-aero
e7c56274e6 Update BridgeConnector.cs 2021-08-26 09:55:24 +02:00
rafael-aero
44d979fef4 Use ILogger instead of Console.WriteLine 2021-08-26 09:55:14 +02:00
theolivenbaum
2f0229235b fix for socket not being thread-safe 2021-08-26 08:40:36 +02:00
rafael-aero
18d425b042 Merge branch 'master' into switch-to-new-socket-lib 2021-08-25 17:08:53 +02:00
rafael-aero
5e82ae4246 add socket events to emit console messages 2021-08-25 13:38:30 +02:00
rafael-aero
48d5497045 Revert "Update main.js"
This reverts commit be41cae3bd.
2021-08-25 13:33:50 +02:00
rafael-aero
be41cae3bd Update main.js 2021-08-25 13:24:44 +02:00
rafael-aero
64b91fc235 Update main.js 2021-08-25 13:04:23 +02:00
rafael-aero
efec886f73 Update main.js 2021-08-25 11:33:32 +02:00
rafael-aero
8b03a6b006 remove stdio handling for detached case 2021-08-25 11:27:46 +02:00
rafael-aero
144a0a0ddc change argument name, detach process on creation 2021-08-25 11:14:21 +02:00
rafael-aero
8bbe6a96af Fix serialization of array of objects 2021-08-25 10:34:36 +02:00
rafael-aero
e39e34234d Update ProcessHelper.cs 2021-08-25 09:54:00 +02:00
rafael-aero
67c592a060 kill child process when electronize cli is killed 2021-08-25 09:38:37 +02:00
rafael-aero
bad59463a9 pass electron process id to child 2021-08-25 09:17:50 +02:00
rafael-aero
94efaf835e Merge branch 'master' of https://github.com/theolivenbaum/Electron.NET 2021-08-25 09:10:29 +02:00
rafael-aero
ac77643fc4 add variable to control kill behaviour 2021-08-25 09:10:27 +02:00
theolivenbaum
0393b0bd45 Merge branch 'master' into switch-to-new-socket-lib 2021-08-24 05:31:57 +02:00
theolivenbaum
953f1e2641 fix event names for when there are more than one window, view or webcontent Id 2021-08-24 05:31:46 +02:00
rafael-aero
fb99dbd5b5 add null return to avoid having to check for undefined 2021-08-23 18:28:00 +02:00
rafael-aero
fdbf7187b9 Update browserWindows.js 2021-08-23 15:30:47 +02:00
rafael-aero
9a680a41ac improve how we handle when window has been destroyed already 2021-08-23 14:59:41 +02:00
rafael-aero
f9693ce624 handle destroyed windows 2021-08-23 14:29:41 +02:00
rafael-aero
0090a7c239 fix wrong completed event name 2021-08-23 13:24:02 +02:00
rafael-aero
81431599ec Update ElectronNET.API.csproj 2021-08-23 13:01:38 +02:00
rafael-aero
31094f9d2b Remove unecessary JsonSerializer settings, fix default serializer 2021-08-23 11:57:42 +02:00
rafael-aero
bf48bc8227 replace remaining cases with OnResult, fix deserialization of json objects 2021-08-23 11:22:37 +02:00
rafael-aero
87d97efcfa Merge branch 'master' into switch-to-new-socket-lib 2021-08-21 09:17:43 +02:00
theolivenbaum
da9d9658f5 Update main.js 2021-08-21 09:17:20 +02:00
theolivenbaum
444c09fb4d Update main.js 2021-08-21 08:26:43 +02:00
rafael-aero
ca9bee7bf0 Merge branch 'master' into switch-to-new-socket-lib 2021-08-20 15:16:16 +02:00
rafael-aero
0aab352f86 remove yield call 2021-08-20 15:07:39 +02:00
rafael-aero
a8229f2fa9 add internal method to emit socket events synchronously, and use it for all exit, quit and relaunch methods 2021-08-20 15:06:58 +02:00
rafael-aero
9daaebcef6 Add timeout property for splashscreen 2021-08-20 14:56:47 +02:00
rafael-aero
67b59d95d4 fix quotes 2021-08-20 14:52:33 +02:00
rafael-aero
8f71480b3e Merge branch 'master' into switch-to-new-socket-lib 2021-08-20 14:49:56 +02:00
rafael-aero
133dcd6b65 Fix exit method 2021-08-20 14:49:49 +02:00
rafael-aero
d9a7411904 Merge branch 'master' into switch-to-new-socket-lib 2021-08-20 14:36:09 +02:00
rafael-aero
b42eba8a70 Handle non-zero exit codes from ASP.NET process 2021-08-20 14:36:02 +02:00
rafael-aero
e8e3649925 Merge branch 'master' into switch-to-new-socket-lib 2021-08-19 14:50:46 +02:00
rafael-aero
b6338ac7de log error messages from asp-net process 2021-08-19 14:50:30 +02:00
rafael-aero
b708b4b622 reset default values 2021-08-18 10:48:55 +02:00
rafael-aero
336c3b9400 cleanup for merge 2021-08-18 10:42:24 +02:00
rafael-aero
bd08938c49 Delete build-nuget.yaml 2021-08-18 10:32:41 +02:00
rafael-aero
cbe0637e84 Simplify repeated code with OnResult method, fix race condition with multiple calls to On/Off for the same event 2021-08-18 10:24:12 +02:00
rafael-aero
8063f49e4b run all continuations asyncronously 2021-08-17 16:28:07 +02:00
rafael-aero
d6b29f7350 avoid calling npm install if nothing changed 2021-08-09 17:43:54 +02:00
rafael-aero
237638a5b3 add command for simple build 2021-08-09 17:32:24 +02:00
Matt
b910807ced Log errors in the dotnet process 2021-08-04 17:33:09 +02:00
rafael-aero
7fe8f6e49e fix wrong object initializer 2021-07-21 17:04:29 +02:00
rafael-aero
c4ff481131 add debug option 2021-07-21 10:42:04 +02:00
rafael-aero
9107b161b3 Add prototype from-build-output command 2021-07-13 11:25:00 +02:00
rafael-aero
ec2261f1c5 Remove wrong ReleaseNoteInfo model 2021-07-13 09:08:31 +02:00
theolivenbaum
b03bc7c9eb Clean-up 2021-07-12 21:33:35 +02:00
theolivenbaum
e6214491cf Merge pull request #2 from theolivenbaum/remove-deprecated-socket-lib
Remove deprecated socket lib
2021-07-12 21:28:07 +02:00
theolivenbaum
95d614796e works 2021-07-12 21:27:40 +02:00
rafael-aero
126d39f4a5 wip 2021-07-12 19:50:39 +02:00
rafael-aero
8880e040f7 remove remaining .Result calls 2021-07-12 18:16:07 +02:00
rafael-aero
f200013165 add custom property handle for electronize start command 2021-07-12 17:05:26 +02:00
rafael-aero
82755e399d Add comments 2021-07-12 15:54:23 +02:00
rafael-aero
f07f2e9506 Update build-nuget.yaml 2021-07-12 15:53:02 +02:00
rafael-aero
83fd5a5d4b Add manual initialization methods 2021-07-12 15:52:30 +02:00
rafael-aero
6dee08379c Merge remote-tracking branch 'upstream/master' 2021-07-12 09:24:32 +02:00
rafael-aero
8f5a7856d5 use new Electron 12 default values 2021-06-28 16:30:24 +02:00
rafael-aero
f78573401a fix strange issue with capitalization of assembly name 2021-06-28 16:08:00 +02:00
rafael-aero
d8ba9d9372 change assembly name 2021-06-28 15:48:15 +02:00
rafael-aero
09cb7a640f Update EmbeddedFileHelper.cs 2021-06-28 15:43:55 +02:00
rafael-aero
2f3d2c00b6 print all resource names 2021-06-28 15:43:40 +02:00
rafael-aero
3b7b592ac8 fix name on resource path 2021-06-28 15:21:41 +02:00
rafael-aero
bcbe76211c Add option to set electron version 2021-06-28 15:04:26 +02:00
rafael-aero
505a5b2204 fix NPM install 2021-06-28 15:01:10 +02:00
rafael-aero
468a4fa35b fix package name 2021-06-28 14:35:59 +02:00
rafael-aero
94b5faafcd add publish to nuget 2021-06-28 14:31:33 +02:00
rafael-aero
f08711e41b Fix variables 2021-06-28 14:26:27 +02:00
theolivenbaum
1a6d50d77a Set up CI with Azure Pipelines
[skip ci]
2021-06-28 14:19:47 +02:00
rafael-aero
d188baaaff Merge remote-tracking branch 'MiniguyBrendan/master' 2021-06-28 14:11:08 +02:00
rafael-aero
916eecf117 Merge branch 'bug/578' 2021-06-28 14:08:45 +02:00
243 changed files with 10276 additions and 5316 deletions

84
.devops/build-nuget.yaml Normal file
View File

@@ -0,0 +1,84 @@
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'

15
.vscode/tasks.json vendored
View File

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

16
.vscode/tasks.json.old vendored Normal file
View File

@@ -0,0 +1,16 @@
{
"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,5 +1,29 @@
# 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)
# Released
# 13.5.1
ElectronNET.CLI:
@@ -25,9 +49,6 @@ ElectronNET.API:
* Fixed bug: Fix splash screen interaction causing crashes, ghost dragging, and resizable behavior #540 (thanks [MiniguyBrendan](https://github.com/MiniguyBrendan)) [\#540](https://github.com/ElectronNET/Electron.NET/pull/540)
* Fixed bug: Vibrancy serialization fix (thanks [tantumalice](https://github.com/tantumalice)) [\#573](https://github.com/ElectronNET/Electron.NET/pull/573)
# Released
# 11.5.1
ElectronNET.CLI:

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

File diff suppressed because it is too large Load Diff

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

@@ -5,39 +5,52 @@ 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
public sealed class AutoUpdater : IAutoUpdater
{
/// <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.Socket.Emit("autoUpdater-autoDownload-set", value);
BridgeConnector.Emit("autoUpdater-autoDownload-set", value);
}
}
@@ -48,26 +61,9 @@ 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.Socket.Emit("autoUpdater-autoInstallOnAppQuit-set", value);
BridgeConnector.Emit("autoUpdater-autoInstallOnAppQuit-set", value);
}
}
@@ -79,26 +75,9 @@ 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.Socket.Emit("autoUpdater-allowPrerelease-set", value);
BridgeConnector.Emit("autoUpdater-allowPrerelease-set", value);
}
}
@@ -108,26 +87,9 @@ 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.Socket.Emit("autoUpdater-fullChangelog-set", value);
BridgeConnector.Emit("autoUpdater-fullChangelog-set", value);
}
}
@@ -138,137 +100,32 @@ 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.Socket.Emit("autoUpdater-allowDowngrade-set", value);
BridgeConnector.Emit("autoUpdater-allowDowngrade-set", value);
}
}
/// <summary>
/// For test only.
/// </summary>
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;
}
}
public Task<string> GetUpdateConfigPathAsync => BridgeConnector.OnResult<string>("autoUpdater-updateConfigPath-get", "autoUpdater-updateConfigPath-get-reply");
/// <summary>
/// The current application version
/// </summary>
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;
});
}
}
public Task<SemVer> GetCurrentVersionAsync => BridgeConnector.OnResult<SemVer>("autoUpdater-updateConcurrentVersionfigPath-get", "autoUpdater-currentVersion-get-reply");
/// <summary>
/// Get the update channel. Not applicable for GitHub.
/// Doesnt return channel from the update configuration, only if was previously set.
/// </summary>
[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;
});
}
}
public Task<string> GetChannelAsync => BridgeConnector.OnResult<string>("autoUpdater-channel-get", "autoUpdater-channel-get-reply");
/// <summary>
/// The request headers.
/// </summary>
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;
});
}
}
public Task<Dictionary<string, string>> GetRequestHeadersAsync => BridgeConnector.OnResult<Dictionary<string, string>>("autoUpdater-requestHeaders-get", "autoUpdater-requestHeaders-get-reply");
/// <summary>
/// The request headers.
@@ -277,7 +134,7 @@ namespace ElectronNET.API
{
set
{
BridgeConnector.Socket.Emit("autoUpdater-requestHeaders-set", JObject.FromObject(value, _jsonSerializer));
BridgeConnector.Emit("autoUpdater-requestHeaders-set", value);
}
}
@@ -290,12 +147,12 @@ namespace ElectronNET.API
{
if (_error == null)
{
BridgeConnector.Socket.On("autoUpdater-error" + GetHashCode(), (message) =>
BridgeConnector.On<string>("autoUpdater-error" + GetHashCode(), (message) =>
{
_error(message.ToString());
});
BridgeConnector.Socket.Emit("register-autoUpdater-error-event", GetHashCode());
BridgeConnector.Emit("register-autoUpdater-error-event", GetHashCode());
}
_error += value;
}
@@ -304,7 +161,7 @@ namespace ElectronNET.API
_error -= value;
if (_error == null)
BridgeConnector.Socket.Off("autoUpdater-error" + GetHashCode());
BridgeConnector.Off("autoUpdater-error" + GetHashCode());
}
}
@@ -319,12 +176,12 @@ namespace ElectronNET.API
{
if (_checkingForUpdate == null)
{
BridgeConnector.Socket.On("autoUpdater-checking-for-update" + GetHashCode(), () =>
BridgeConnector.On("autoUpdater-checking-for-update" + GetHashCode(), () =>
{
_checkingForUpdate();
});
BridgeConnector.Socket.Emit("register-autoUpdater-checking-for-update-event", GetHashCode());
BridgeConnector.Emit("register-autoUpdater-checking-for-update-event", GetHashCode());
}
_checkingForUpdate += value;
}
@@ -333,7 +190,7 @@ namespace ElectronNET.API
_checkingForUpdate -= value;
if (_checkingForUpdate == null)
BridgeConnector.Socket.Off("autoUpdater-checking-for-update" + GetHashCode());
BridgeConnector.Off("autoUpdater-checking-for-update" + GetHashCode());
}
}
@@ -349,12 +206,12 @@ namespace ElectronNET.API
{
if (_updateAvailable == null)
{
BridgeConnector.Socket.On("autoUpdater-update-available" + GetHashCode(), (updateInfo) =>
BridgeConnector.On<UpdateInfo>("autoUpdater-update-available" + GetHashCode(), (updateInfo) =>
{
_updateAvailable(JObject.Parse(updateInfo.ToString()).ToObject<UpdateInfo>());
_updateAvailable(updateInfo);
});
BridgeConnector.Socket.Emit("register-autoUpdater-update-available-event", GetHashCode());
BridgeConnector.Emit("register-autoUpdater-update-available-event", GetHashCode());
}
_updateAvailable += value;
}
@@ -363,7 +220,7 @@ namespace ElectronNET.API
_updateAvailable -= value;
if (_updateAvailable == null)
BridgeConnector.Socket.Off("autoUpdater-update-available" + GetHashCode());
BridgeConnector.Off("autoUpdater-update-available" + GetHashCode());
}
}
@@ -378,12 +235,12 @@ namespace ElectronNET.API
{
if (_updateNotAvailable == null)
{
BridgeConnector.Socket.On("autoUpdater-update-not-available" + GetHashCode(), (updateInfo) =>
BridgeConnector.On<UpdateInfo>("autoUpdater-update-not-available" + GetHashCode(), (updateInfo) =>
{
_updateNotAvailable(JObject.Parse(updateInfo.ToString()).ToObject<UpdateInfo>());
_updateNotAvailable(updateInfo);
});
BridgeConnector.Socket.Emit("register-autoUpdater-update-not-available-event", GetHashCode());
BridgeConnector.Emit("register-autoUpdater-update-not-available-event", GetHashCode());
}
_updateNotAvailable += value;
}
@@ -392,7 +249,7 @@ namespace ElectronNET.API
_updateNotAvailable -= value;
if (_updateNotAvailable == null)
BridgeConnector.Socket.Off("autoUpdater-update-not-available" + GetHashCode());
BridgeConnector.Off("autoUpdater-update-not-available" + GetHashCode());
}
}
@@ -407,12 +264,12 @@ namespace ElectronNET.API
{
if (_downloadProgress == null)
{
BridgeConnector.Socket.On("autoUpdater-download-progress" + GetHashCode(), (progressInfo) =>
BridgeConnector.On<ProgressInfo>("autoUpdater-download-progress" + GetHashCode(), (progressInfo) =>
{
_downloadProgress(JObject.Parse(progressInfo.ToString()).ToObject<ProgressInfo>());
_downloadProgress(progressInfo);
});
BridgeConnector.Socket.Emit("register-autoUpdater-download-progress-event", GetHashCode());
BridgeConnector.Emit("register-autoUpdater-download-progress-event", GetHashCode());
}
_downloadProgress += value;
}
@@ -421,7 +278,7 @@ namespace ElectronNET.API
_downloadProgress -= value;
if (_downloadProgress == null)
BridgeConnector.Socket.Off("autoUpdater-download-progress" + GetHashCode());
BridgeConnector.Off("autoUpdater-download-progress" + GetHashCode());
}
}
@@ -436,12 +293,12 @@ namespace ElectronNET.API
{
if (_updateDownloaded == null)
{
BridgeConnector.Socket.On("autoUpdater-update-downloaded" + GetHashCode(), (updateInfo) =>
BridgeConnector.On<UpdateInfo>("autoUpdater-update-downloaded" + GetHashCode(), (updateInfo) =>
{
_updateDownloaded(JObject.Parse(updateInfo.ToString()).ToObject<UpdateInfo>());
_updateDownloaded(updateInfo);
});
BridgeConnector.Socket.Emit("register-autoUpdater-update-downloaded-event", GetHashCode());
BridgeConnector.Emit("register-autoUpdater-update-downloaded-event", GetHashCode());
}
_updateDownloaded += value;
}
@@ -450,14 +307,14 @@ namespace ElectronNET.API
_updateDownloaded -= value;
if (_updateDownloaded == null)
BridgeConnector.Socket.Off("autoUpdater-update-downloaded" + GetHashCode());
BridgeConnector.Off("autoUpdater-update-downloaded" + GetHashCode());
}
}
private event Action<UpdateInfo> _updateDownloaded;
private static AutoUpdater _autoUpdater;
private static object _syncRoot = new object();
private static readonly object _syncRoot = new();
internal AutoUpdater() { }
@@ -486,33 +343,33 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<UpdateCheckResult> CheckForUpdatesAsync()
{
var taskCompletionSource = new TaskCompletionSource<UpdateCheckResult>();
var taskCompletionSource = new TaskCompletionSource<UpdateCheckResult>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesComplete" + guid, (updateCheckResult) =>
BridgeConnector.On<UpdateCheckResult>("autoUpdaterCheckForUpdatesComplete" + guid, (updateCheckResult) =>
{
try
{
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesError" + guid);
taskCompletionSource.SetResult(JObject.Parse(updateCheckResult.ToString()).ToObject<UpdateCheckResult>());
BridgeConnector.Off("autoUpdaterCheckForUpdatesComplete" + guid);
BridgeConnector.Off("autoUpdaterCheckForUpdatesError" + guid);
taskCompletionSource.SetResult(updateCheckResult);
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesError" + guid, (error) =>
BridgeConnector.On<string>("autoUpdaterCheckForUpdatesError" + guid, (error) =>
{
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesError" + guid);
BridgeConnector.Off("autoUpdaterCheckForUpdatesComplete" + guid);
BridgeConnector.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.Socket.Emit("autoUpdaterCheckForUpdates", guid);
BridgeConnector.Emit("autoUpdaterCheckForUpdates", guid);
return taskCompletionSource.Task;
}
@@ -525,36 +382,36 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<UpdateCheckResult> CheckForUpdatesAndNotifyAsync()
{
var taskCompletionSource = new TaskCompletionSource<UpdateCheckResult>();
var taskCompletionSource = new TaskCompletionSource<UpdateCheckResult>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid, (updateCheckResult) =>
BridgeConnector.On<UpdateCheckResult>("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid, (updateCheckResult) =>
{
try
{
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
if (updateCheckResult == null)
taskCompletionSource.SetResult(null);
else
taskCompletionSource.SetResult(JObject.Parse(updateCheckResult.ToString()).ToObject<UpdateCheckResult>());
taskCompletionSource.SetResult(updateCheckResult);
}
catch (Exception ex)
{
taskCompletionSource.SetException(ex);
}
});
BridgeConnector.Socket.On("autoUpdaterCheckForUpdatesAndNotifyError" + guid, (error) =>
BridgeConnector.On<string>("autoUpdaterCheckForUpdatesAndNotifyError" + guid, (error) =>
{
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
BridgeConnector.Socket.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyComplete" + guid);
BridgeConnector.Off("autoUpdaterCheckForUpdatesAndNotifyError" + guid);
string message = "An error occurred in autoUpdaterCheckForUpdatesAndNotify";
if (error != null)
message = JsonConvert.SerializeObject(error);
taskCompletionSource.SetException(new Exception(message));
});
BridgeConnector.Socket.Emit("autoUpdaterCheckForUpdatesAndNotify", guid);
BridgeConnector.Emit("autoUpdaterCheckForUpdatesAndNotify", guid);
return taskCompletionSource.Task;
}
@@ -570,7 +427,8 @@ 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.Socket.Emit("autoUpdaterQuitAndInstall", isSilent, isForceRunAfter);
BridgeConnector.EmitSync("prepare-for-update");
BridgeConnector.EmitSync("autoUpdaterQuitAndInstall", isSilent, isForceRunAfter);
}
/// <summary>
@@ -579,16 +437,16 @@ namespace ElectronNET.API
/// <returns>Path to downloaded file.</returns>
public Task<string> DownloadUpdateAsync()
{
var taskCompletionSource = new TaskCompletionSource<string>();
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("autoUpdaterDownloadUpdateComplete" + guid, (downloadedPath) =>
BridgeConnector.On<string>("autoUpdaterDownloadUpdateComplete" + guid, (downloadedPath) =>
{
BridgeConnector.Socket.Off("autoUpdaterDownloadUpdateComplete" + guid);
BridgeConnector.Off("autoUpdaterDownloadUpdateComplete" + guid);
taskCompletionSource.SetResult(downloadedPath.ToString());
});
BridgeConnector.Socket.Emit("autoUpdaterDownloadUpdate", guid);
BridgeConnector.Emit("autoUpdaterDownloadUpdate", guid);
return taskCompletionSource.Task;
}
@@ -599,23 +457,18 @@ namespace ElectronNET.API
/// <returns>Feed URL.</returns>
public Task<string> GetFeedURLAsync()
{
var taskCompletionSource = new TaskCompletionSource<string>();
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("autoUpdaterGetFeedURLComplete" + guid, (downloadedPath) =>
BridgeConnector.On<string>("autoUpdaterGetFeedURLComplete" + guid, (downloadedPath) =>
{
BridgeConnector.Socket.Off("autoUpdaterGetFeedURLComplete" + guid);
BridgeConnector.Off("autoUpdaterGetFeedURLComplete" + guid);
taskCompletionSource.SetResult(downloadedPath.ToString());
});
BridgeConnector.Socket.Emit("autoUpdaterGetFeedURL", guid);
BridgeConnector.Emit("autoUpdaterGetFeedURL", guid);
return taskCompletionSource.Task;
}
private readonly JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
}
}

View File

@@ -1,44 +1,551 @@
using Quobject.SocketIoClientDotNet.Client;
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
{
internal static class BridgeConnector
{
private static Socket _socket;
private static object _syncRoot = new object();
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
public static Socket Socket
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
{
get
{
if(_socket == null && HybridSupport.IsElectronActive)
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
{
lock (_syncRoot)
{
if (_socket == null && HybridSupport.IsElectronActive)
{
_socket = IO.Socket("http://localhost:" + BridgeSettings.SocketPort);
_socket.On(Socket.EVENT_CONNECT, () =>
{
Console.WriteLine("BridgeConnector connected!");
});
}
}
await _socketSemaphoreEmit.WaitAsync();
await socket.EmitAsync(eventString, args);
}
else if(_socket == null && !HybridSupport.IsElectronActive)
finally
{
lock (_syncRoot)
{
if (_socket == null && !HybridSupport.IsElectronActive)
{
_socket = IO.Socket(new Uri("http://localhost"), new IO.Options { AutoConnect = false });
}
}
_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);
}
return _socket;
_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
{
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) =>
{
Off(completedEvent);
taskCompletionSource.SetResult(result);
EventTasks<T>.DoneWith(completedEvent, eventKey, taskCompletionSource);
});
Emit(triggerEvent, args);
}
}
}
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");
}
}
}
}
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,
};
}
}
}
}
}

View File

@@ -20,5 +20,16 @@
/// 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,29 +31,15 @@ namespace ElectronNET.API
///
/// (experimental)
/// </summary>
public Rectangle Bounds
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)
{
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));
}
BridgeConnector.Emit("browserView-setBounds", Id, value);
}
/// <summary>
@@ -74,7 +60,7 @@ namespace ElectronNET.API
/// <param name="options"></param>
public void SetAutoResize(AutoResizeOptions options)
{
BridgeConnector.Socket.Emit("browserView-setAutoResize", Id, JObject.FromObject(options, _jsonSerializer));
BridgeConnector.Emit("browserView-setAutoResize", Id, options);
}
/// <summary>
@@ -85,13 +71,7 @@ namespace ElectronNET.API
/// <param name="color">Color in #aarrggbb or #argb form. The alpha channel is optional.</param>
public void SetBackgroundColor(string color)
{
BridgeConnector.Socket.Emit("browserView-setBackgroundColor", Id, color);
BridgeConnector.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 Normal file → Executable file
View File

@@ -2,17 +2,19 @@
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
public sealed class Clipboard : IClipboard
{
private static Clipboard _clipboard;
private static object _syncRoot = new object();
private static readonly object _syncRoot = new();
internal Clipboard() { }
@@ -40,21 +42,7 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns>The content in the clipboard as plain text.</returns>
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;
}
public Task<string> ReadTextAsync(string type = "") => BridgeConnector.OnResult<string>("clipboard-readText", "clipboard-readText-Completed", type);
/// <summary>
/// Writes the text into the clipboard as plain text.
@@ -63,7 +51,7 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void WriteText(string text, string type = "")
{
BridgeConnector.Socket.Emit("clipboard-writeText", text, type);
BridgeConnector.Emit("clipboard-writeText", text, type);
}
/// <summary>
@@ -71,21 +59,7 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
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;
}
public Task<string> ReadHTMLAsync(string type = "") => BridgeConnector.OnResult<string>("clipboard-readHTML", "clipboard-readHTML-Completed", type);
/// <summary>
/// Writes markup to the clipboard.
@@ -94,7 +68,7 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void WriteHTML(string markup, string type = "")
{
BridgeConnector.Socket.Emit("clipboard-writeHTML", markup, type);
BridgeConnector.Emit("clipboard-writeHTML", markup, type);
}
/// <summary>
@@ -102,21 +76,8 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public Task<string> ReadRTFAsync(string type = "")
{
var taskCompletionSource = new TaskCompletionSource<string>();
public Task<string> ReadRTFAsync(string type = "") => BridgeConnector.OnResult<string>("clipboard-readRTF", "clipboard-readRTF-Completed", type);
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.
@@ -125,7 +86,7 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void WriteRTF(string text, string type = "")
{
BridgeConnector.Socket.Emit("clipboard-writeHTML", text, type);
BridgeConnector.Emit("clipboard-writeHTML", text, type);
}
/// <summary>
@@ -134,21 +95,9 @@ namespace ElectronNET.API
/// be empty strings when the bookmark is unavailable.
/// </summary>
/// <returns></returns>
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;
}
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public Task<ReadBookmark> ReadBookmarkAsync() => BridgeConnector.OnResult<ReadBookmark>("clipboard-readBookmark", "clipboard-readBookmark-Completed");
/// <summary>
/// Writes the title and url into the clipboard as a bookmark.
@@ -160,9 +109,11 @@ 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.Socket.Emit("clipboard-writeBookmark", title, url, type);
BridgeConnector.Emit("clipboard-writeBookmark", title, url, type);
}
/// <summary>
@@ -171,30 +122,18 @@ namespace ElectronNET.API
/// find pasteboard whenever the application is activated.
/// </summary>
/// <returns></returns>
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;
}
[SupportedOSPlatform("macos")]
public Task<string> ReadFindTextAsync() => BridgeConnector.OnResult<string>("clipboard-readFindText", "clipboard-readFindText-Completed");
/// <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.Socket.Emit("clipboard-writeFindText", text);
BridgeConnector.Emit("clipboard-writeFindText", text);
}
/// <summary>
@@ -203,7 +142,7 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void Clear(string type = "")
{
BridgeConnector.Socket.Emit("clipboard-clear", type);
BridgeConnector.Emit("clipboard-clear", type);
}
/// <summary>
@@ -211,21 +150,7 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
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;
}
public Task<string[]> AvailableFormatsAsync(string type = "") => BridgeConnector.OnResult<string[]>("clipboard-availableFormats", "clipboard-availableFormats-Completed", type);
/// <summary>
/// Writes data to the clipboard.
@@ -234,7 +159,7 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void Write(Data data, string type = "")
{
BridgeConnector.Socket.Emit("clipboard-write", JObject.FromObject(data, _jsonSerializer), type);
BridgeConnector.Emit("clipboard-write", JObject.FromObject(data, _jsonSerializer), type);
}
/// <summary>
@@ -242,24 +167,7 @@ namespace ElectronNET.API
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
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;
}
public Task<NativeImage> ReadImageAsync(string type = "") => BridgeConnector.OnResult<NativeImage>("clipboard-readImage", "clipboard-readImage-Completed", type);
/// <summary>
/// Writes an image to the clipboard.
@@ -268,10 +176,10 @@ namespace ElectronNET.API
/// <param name="type"></param>
public void WriteImage(NativeImage image, string type = "")
{
BridgeConnector.Socket.Emit("clipboard-writeImage", JsonConvert.SerializeObject(image), type);
BridgeConnector.Emit("clipboard-writeImage", JsonConvert.SerializeObject(image), type);
}
private JsonSerializer _jsonSerializer = new JsonSerializer()
private static readonly JsonSerializer _jsonSerializer = new()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,

View File

@@ -8,7 +8,7 @@ namespace ElectronNET.API
/// </summary>
public sealed class CommandLine
{
internal CommandLine() { }
private CommandLine() { }
internal static CommandLine Instance
{
@@ -31,7 +31,7 @@ namespace ElectronNET.API
private static CommandLine _commandLine;
private static object _syncRoot = new object();
private static readonly object _syncRoot = new();
/// <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.Socket.Emit("appCommandLineAppendSwitch", the_switch, value);
BridgeConnector.Emit("appCommandLineAppendSwitch", the_switch, value);
}
/// <summary>
@@ -57,7 +57,7 @@ namespace ElectronNET.API
/// </remarks>
public void AppendArgument(string value)
{
BridgeConnector.Socket.Emit("appCommandLineAppendArgument", value);
BridgeConnector.Emit("appCommandLineAppendArgument", value);
}
/// <summary>
@@ -66,24 +66,7 @@ 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 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);
}
}
public Task<bool> HasSwitchAsync(string switchName, CancellationToken cancellationToken = default) => BridgeConnector.OnResult<bool>("appCommandLineHasSwitch", "appCommandLineHasSwitchCompleted", cancellationToken, switchName);
/// <summary>
/// The command-line switch value.
@@ -94,23 +77,6 @@ namespace ElectronNET.API
/// <remarks>
/// Note: When the switch is not present or has no value, it returns empty string.
/// </remarks>
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);
}
}
public Task<string> GetSwitchValueAsync(string switchName, CancellationToken cancellationToken = default) => BridgeConnector.OnResult<string>("appCommandLineGetSwitchValue", "appCommandLineGetSwitchValueCompleted", cancellationToken, switchName);
}
}

View File

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

View File

@@ -0,0 +1,38 @@
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);
}
}
}

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

@@ -4,18 +4,20 @@ 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
public sealed class Dialog : IDialog
{
private static Dialog _dialog;
private static object _syncRoot = new object();
private static readonly object _syncRoot = new();
internal Dialog() { }
@@ -48,26 +50,23 @@ 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[]>();
var taskCompletionSource = new TaskCompletionSource<string[]>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("showOpenDialogComplete" + guid, (filePaths) =>
BridgeConnector.On<string[]>("showOpenDialogComplete" + guid, (filePaths) =>
{
BridgeConnector.Socket.Off("showOpenDialogComplete" + guid);
BridgeConnector.Off("showOpenDialogComplete" + guid);
var result = ((JArray)filePaths).ToObject<string[]>();
var list = new List<string>();
foreach (var item in result)
foreach (var item in filePaths)
{
list.Add(HttpUtility.UrlDecode(item));
}
taskCompletionSource.SetResult(list.ToArray());
});
BridgeConnector.Socket.Emit("showOpenDialog",
JObject.FromObject(browserWindow, _jsonSerializer),
JObject.FromObject(options, _jsonSerializer), guid);
BridgeConnector.Emit("showOpenDialog", browserWindow, options, guid);
return taskCompletionSource.Task;
}
@@ -80,20 +79,17 @@ 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>();
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("showSaveDialogComplete" + guid, (filename) =>
BridgeConnector.On<string>("showSaveDialogComplete" + guid, (filename) =>
{
BridgeConnector.Socket.Off("showSaveDialogComplete" + guid);
BridgeConnector.Off("showSaveDialogComplete" + guid);
taskCompletionSource.SetResult(filename.ToString());
taskCompletionSource.SetResult(filename);
});
BridgeConnector.Socket.Emit("showSaveDialog",
JObject.FromObject(browserWindow, _jsonSerializer),
JObject.FromObject(options, _jsonSerializer),
guid);
BridgeConnector.Emit("showSaveDialog", browserWindow, options, guid);
return taskCompletionSource.Task;
}
@@ -149,32 +145,27 @@ 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>();
var taskCompletionSource = new TaskCompletionSource<MessageBoxResult>(TaskCreationOptions.RunContinuationsAsynchronously);
var guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("showMessageBoxComplete" + guid, (args) =>
BridgeConnector.On<MessageBoxResponse>("showMessageBoxComplete" + guid, (args) =>
{
BridgeConnector.Socket.Off("showMessageBoxComplete" + guid);
var result = ((JArray)args);
BridgeConnector.Off("showMessageBoxComplete" + guid);
taskCompletionSource.SetResult(new MessageBoxResult
{
Response = (int)result.First,
CheckboxChecked = (bool)result.Last
Response = args.response,
CheckboxChecked = args.@checked
});
});
if (browserWindow == null)
if (browserWindow is null)
{
BridgeConnector.Socket.Emit("showMessageBox", JObject.FromObject(messageBoxOptions, _jsonSerializer), guid);
BridgeConnector.Emit("showMessageBox", JObject.FromObject(messageBoxOptions, _jsonSerializer), guid);
} else
{
BridgeConnector.Socket.Emit("showMessageBox",
JObject.FromObject(browserWindow, _jsonSerializer),
JObject.FromObject(messageBoxOptions, _jsonSerializer),
guid);
BridgeConnector.Emit("showMessageBox", JObject.FromObject(messageBoxOptions, _jsonSerializer), JObject.FromObject(messageBoxOptions, _jsonSerializer), guid);
}
return taskCompletionSource.Task;
@@ -192,7 +183,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.Socket.Emit("showErrorBox", title, content);
BridgeConnector.Emit("showErrorBox", title, content);
}
/// <summary>
@@ -202,6 +193,8 @@ namespace ElectronNET.API
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public Task ShowCertificateTrustDialogAsync(CertificateTrustDialogOptions options)
{
return ShowCertificateTrustDialogAsync(null, options);
@@ -215,26 +208,25 @@ 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>();
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("showCertificateTrustDialogComplete" + guid, () =>
BridgeConnector.On("showCertificateTrustDialogComplete" + guid, () =>
{
BridgeConnector.Socket.Off("showCertificateTrustDialogComplete" + guid);
BridgeConnector.Off("showCertificateTrustDialogComplete" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Socket.Emit("showCertificateTrustDialog",
JObject.FromObject(browserWindow, _jsonSerializer),
JObject.FromObject(options, _jsonSerializer),
guid);
BridgeConnector.Emit("showCertificateTrustDialog", browserWindow, options, guid);
return taskCompletionSource.Task;
}
private JsonSerializer _jsonSerializer = new JsonSerializer()
private static readonly JsonSerializer _jsonSerializer = new()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,

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

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

View File

@@ -0,0 +1,144 @@
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,10 +1,51 @@
namespace ElectronNET.API
using Microsoft.Extensions.Logging;
using System.Runtime.Versioning;
using System;
using System.Collections.Generic;
namespace ElectronNET.API
{
/// <summary>
/// The Electron.NET API
/// </summary>
public static class Electron
public static partial 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>
@@ -60,6 +101,11 @@
/// </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>
@@ -87,6 +133,7 @@
/// <summary>
/// Control your app in the macOS dock.
/// </summary>
[SupportedOSPlatform("macos")]
public static Dock Dock { get { return Dock.Instance; } }
}
}

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

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageOutputPath>..\artifacts</PackageOutputPath>
<PackageId>ElectronNET.API</PackageId>
@@ -10,8 +10,10 @@
<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>
@@ -20,6 +22,7 @@ This package contains the API to access the "native" electron API.</Description>
<PackageIcon>PackageIcon.png</PackageIcon>
<Version>99.0.0.0</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TargetFrameworks>net6.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
@@ -33,15 +36,20 @@ This package contains the API to access the "native" electron API.</Description>
<Exec Command="$(ProjectDir)devCleanup.sh" IgnoreExitCode="true" />
</Target>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0">
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="SocketIoClientDotNet" Version="1.0.5" />
<PackageReference Include="System.Drawing.Common" Version="5.0.0" />
<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" />
</ItemGroup>
</Project>

View File

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

View File

@@ -1,13 +1,20 @@
namespace ElectronNET.API.Entities
using System;
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; } = 1.0f;
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};
}
}

View File

@@ -1,6 +1,7 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.ComponentModel;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
@@ -36,62 +37,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
@@ -100,35 +101,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".
@@ -145,40 +146,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 the window is a child
/// window.Default is false.
/// Whether this is a modal window. This only works when <see cref="Parent"/> is
/// also specified. 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
@@ -190,18 +191,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.
@@ -213,13 +214,21 @@ 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; }
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; }
/// <summary>
/// Use WS_THICKFRAME style for frameless windows on Windows, which adds standard
@@ -227,7 +236,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
@@ -235,7 +244,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
@@ -244,7 +253,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+.
@@ -270,5 +279,19 @@ 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,7 +1,9 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace ElectronNET.API.Entities {
namespace ElectronNET.API.Entities
{
/// <summary>
/// The cause of the change
/// </summary>

View File

@@ -0,0 +1,10 @@
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,8 +1,11 @@
namespace ElectronNET.API.Entities
using System;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
[Obsolete("Use CreateOptions instead")]
public class CreateFromBitmapOptions
{
/// <summary>
@@ -18,6 +21,12 @@
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
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};
}
}

View File

@@ -1,8 +1,11 @@
using System;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
[Obsolete("Use CreateOptions instead")]
public class CreateFromBufferOptions
{
/// <summary>
@@ -18,6 +21,11 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Gets or sets the scalefactor
/// </summary>
public float ScaleFactor { get; set; } = 1.0f;
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};
}
}

View File

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

View File

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

View File

@@ -0,0 +1,13 @@
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

@@ -1,8 +1,7 @@
namespace ElectronNET.API.Entities
using System;
namespace ElectronNET.API.Entities
{
/// <summary>
///
/// </summary>
public class JumpListSettings
{
/// <summary>
@@ -16,6 +15,6 @@
/// 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; } = new JumpListItem[0];
public JumpListItem[] RemovedItems { get; set; } = Array.Empty<JumpListItem>();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
using Newtonsoft.Json;
using System;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
@@ -17,6 +18,8 @@ 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>
@@ -38,38 +41,46 @@ 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>
@@ -127,6 +138,7 @@ 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>
@@ -142,6 +154,7 @@ 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,5 +1,6 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
@@ -39,6 +40,7 @@ namespace ElectronNET.API.Entities
/// <summary>
/// Message to display above input boxes.
/// </summary>
[SupportedOSPlatform("macos")]
public string Message { get; set; }
/// <summary>

View File

@@ -1,4 +1,6 @@
namespace ElectronNET.API.Entities
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
/// <summary>
///
@@ -28,21 +30,66 @@
/// <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>
/// The treat package as directory
/// Treat packages, such as .app folders, as a directory instead of a file.
/// </summary>
treatPackageAsDirectory
[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
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using System.Runtime.Versioning;
namespace ElectronNET.API.Entities
{
@@ -12,11 +13,13 @@ 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

@@ -0,0 +1,10 @@
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

@@ -1,18 +0,0 @@
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,4 +1,7 @@
using ElectronNET.API.Entities;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Runtime.Versioning;
namespace ElectronNET.API
{
@@ -46,16 +49,26 @@ 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

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

View File

@@ -20,5 +20,17 @@
/// 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

@@ -0,0 +1,9 @@
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,4 +28,11 @@ 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,13 +1,20 @@
namespace ElectronNET.API.Entities
using System;
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; } = 1.0f;
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};
}
}

View File

@@ -1,13 +1,20 @@
namespace ElectronNET.API.Entities
using System;
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; } = 1.0f;
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};
}
}

View File

@@ -1,13 +1,20 @@
namespace ElectronNET.API.Entities
using System;
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,5 +1,6 @@
namespace ElectronNET.API
{
/// <summary>
///
/// </summary>

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
using System.Runtime.Serialization;
using System.Runtime.Serialization;
using System;
namespace ElectronNET.API.Entities
{
@@ -11,16 +12,19 @@ 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>
@@ -52,12 +56,38 @@ 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")]
ultraDark
[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
}
}
}

View File

@@ -171,6 +171,12 @@ 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
@@ -184,15 +190,11 @@ 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
@@ -208,9 +210,9 @@ namespace ElectronNET.API.Entities
public bool WebviewTag { get; set; } = false;
/// <summary>
/// Whether to enable the remote module. Defaults to false.
/// Make the web view transparent
/// </summary>
[DefaultValue(false)]
public bool EnableRemoteModule { get; set; } = false;
public bool Transparent { get; set; } = false;
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Globalization;
using Quobject.EngineIoClientDotNet.ComponentEmitter;
namespace ElectronNET.API
{
@@ -10,8 +9,8 @@ namespace ElectronNET.API
internal class Events
{
private static Events _events;
private static object _syncRoot = new object();
private TextInfo _ti = new CultureInfo("en-US", false).TextInfo;
private static readonly object _syncRoot = new();
private readonly TextInfo _ti = new CultureInfo("en-US", false).TextInfo;
private Events()
{
@@ -42,8 +41,8 @@ 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 On(string moduleName, string eventName, Action fn)
=> On(moduleName, eventName, new ListenerImpl(fn));
public void On(string moduleName, string eventName, Action fn) => On(moduleName, eventName, _ => fn());
/// <summary>
/// Subscribe to an unmapped electron event.
@@ -52,21 +51,12 @@ namespace ElectronNET.API
/// <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)
=> On(moduleName, eventName, new ListenerImpl(fn));
/// <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>
private void On(string moduleName, string eventName, IListener fn)
{
var listener = $"{moduleName}{_ti.ToTitleCase(eventName)}Completed";
var subscriber = $"register-{moduleName}-on-event";
BridgeConnector.Socket.On(listener, fn);
BridgeConnector.Socket.Emit(subscriber, eventName, listener);
BridgeConnector.On(listener, fn);
BridgeConnector.Emit(subscriber, eventName, listener);
}
/// <summary>
@@ -75,8 +65,7 @@ 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, new ListenerImpl(fn));
public void Once(string moduleName, string eventName, Action fn) => Once(moduleName, eventName, _ => fn());
/// <summary>
/// Subscribe to an unmapped electron event.
@@ -85,21 +74,11 @@ namespace ElectronNET.API
/// <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)
=> Once(moduleName, eventName, new ListenerImpl(fn));
/// <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>
private void Once(string moduleName, string eventName, IListener fn)
{
var listener = $"{moduleName}{_ti.ToTitleCase(eventName)}Completed";
var subscriber = $"register-{moduleName}-once-event";
BridgeConnector.Socket.Once(listener, fn);
BridgeConnector.Socket.Emit(subscriber, eventName, listener);
BridgeConnector.Once(listener, fn);
BridgeConnector.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", "enumerationValue");
throw new ArgumentException("EnumerationValue must be of Enum type", nameof(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();
MenuItem result = new();
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("");
ThumbarButton result = new("");
foreach (var item in thumbarButtons)
{

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

@@ -1,16 +1,17 @@
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
public sealed class GlobalShortcut : IGlobalShortcut
{
private static GlobalShortcut _globalShortcut;
private static object _syncRoot = new object();
private static readonly object _syncRoot = new();
internal GlobalShortcut() { }
@@ -33,7 +34,7 @@ namespace ElectronNET.API
}
}
private Dictionary<string, Action> _shortcuts = new Dictionary<string, Action>();
private readonly Dictionary<string, Action> _shortcuts = new();
/// <summary>
/// Registers a global shortcut of accelerator.
@@ -49,16 +50,16 @@ namespace ElectronNET.API
{
_shortcuts.Add(accelerator, function);
BridgeConnector.Socket.Off("globalShortcut-pressed");
BridgeConnector.Socket.On("globalShortcut-pressed", (shortcut) =>
BridgeConnector.Off("globalShortcut-pressed");
BridgeConnector.On<string>("globalShortcut-pressed", (shortcut) =>
{
if (_shortcuts.ContainsKey(shortcut.ToString()))
if (_shortcuts.ContainsKey(shortcut))
{
_shortcuts[shortcut.ToString()]();
}
});
BridgeConnector.Socket.Emit("globalShortcut-register", accelerator);
BridgeConnector.Emit("globalShortcut-register", accelerator);
}
}
@@ -68,21 +69,8 @@ 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)
{
var taskCompletionSource = new TaskCompletionSource<bool>();
public Task<bool> IsRegisteredAsync(string accelerator) => BridgeConnector.OnResult<bool>("globalShortcut-isRegistered", "globalShortcut-isRegisteredCompleted", accelerator);
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.
@@ -90,7 +78,7 @@ namespace ElectronNET.API
public void Unregister(string accelerator)
{
_shortcuts.Remove(accelerator);
BridgeConnector.Socket.Emit("globalShortcut-unregister", accelerator);
BridgeConnector.Emit("globalShortcut-unregister", accelerator);
}
/// <summary>
@@ -99,7 +87,7 @@ namespace ElectronNET.API
public void UnregisterAll()
{
_shortcuts.Clear();
BridgeConnector.Socket.Emit("globalShortcut-unregisterAll");
BridgeConnector.Emit("globalShortcut-unregisterAll");
}
}
}

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

@@ -3,6 +3,7 @@ using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Threading.Tasks;
using ElectronNET.API.Interfaces;
namespace ElectronNET.API
{
@@ -13,11 +14,11 @@ namespace ElectronNET.API
/// ElectronHostHook directory:
/// <c>electronize add HostHook</c>
/// </summary>
public sealed class HostHook
public sealed class HostHook : IHostHook
{
private static HostHook _electronHostHook;
private static object _syncRoot = new object();
string oneCallguid = Guid.NewGuid().ToString();
private static readonly object _syncRoot = new();
readonly string oneCallguid = Guid.NewGuid().ToString();
internal HostHook() { }
@@ -47,13 +48,13 @@ namespace ElectronNET.API
/// <param name="arguments">Optional parameters.</param>
public void Call(string socketEventName, params dynamic[] arguments)
{
BridgeConnector.Socket.On(socketEventName + "Error" + oneCallguid, (result) =>
BridgeConnector.On<string>(socketEventName + "Error" + oneCallguid, (result) =>
{
BridgeConnector.Socket.Off(socketEventName + "Error" + oneCallguid);
Electron.Dialog.ShowErrorBox("Host Hook Exception", result.ToString());
BridgeConnector.Off(socketEventName + "Error" + oneCallguid);
Electron.Dialog.ShowErrorBox("Host Hook Exception", result);
});
BridgeConnector.Socket.Emit(socketEventName, arguments, oneCallguid);
BridgeConnector.Emit(socketEventName, arguments, oneCallguid);
}
/// <summary>
@@ -65,64 +66,26 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<T> CallAsync<T>(string socketEventName, params dynamic[] arguments)
{
var taskCompletionSource = new TaskCompletionSource<T>();
var taskCompletionSource = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On(socketEventName + "Error" + guid, (result) =>
BridgeConnector.On<string>(socketEventName + "Error" + guid, (result) =>
{
BridgeConnector.Socket.Off(socketEventName + "Error" + guid);
Electron.Dialog.ShowErrorBox("Host Hook Exception", result.ToString());
BridgeConnector.Off(socketEventName + "Error" + guid);
Electron.Dialog.ShowErrorBox("Host Hook Exception", result);
taskCompletionSource.SetException(new Exception($"Host Hook Exception {result}"));
});
BridgeConnector.Socket.On(socketEventName + "Complete" + guid, (result) =>
BridgeConnector.On<T>(socketEventName + "Complete" + guid, (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.Off(socketEventName + "Error" + guid);
BridgeConnector.Off(socketEventName + "Complete" + guid);
taskCompletionSource.SetResult(result);
});
BridgeConnector.Socket.Emit(socketEventName, arguments, guid);
BridgeConnector.Emit(socketEventName, arguments, guid);
return taskCompletionSource.Task;
}
private JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
}
}

View File

@@ -0,0 +1,709 @@
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

@@ -0,0 +1,168 @@
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

@@ -0,0 +1,122 @@
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

@@ -0,0 +1,102 @@
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

@@ -0,0 +1,93 @@
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

@@ -0,0 +1,39 @@
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

@@ -0,0 +1,30 @@
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

@@ -0,0 +1,65 @@
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

@@ -0,0 +1,47 @@
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

@@ -0,0 +1,101 @@
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

@@ -0,0 +1,23 @@
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

@@ -0,0 +1,48 @@
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

@@ -0,0 +1,66 @@
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

@@ -0,0 +1,70 @@
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

@@ -0,0 +1,141 @@
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

@@ -0,0 +1,68 @@
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);
}
}

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

@@ -5,16 +5,17 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ElectronNET.API.Interfaces;
namespace ElectronNET.API
{
/// <summary>
/// Communicate asynchronously from the main process to renderer processes.
/// </summary>
public sealed class IpcMain
public sealed class IpcMain : IIpcMain
{
private static IpcMain _ipcMain;
private static object _syncRoot = new object();
private static readonly object _syncRoot = new();
internal IpcMain() { }
@@ -37,6 +38,8 @@ 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...).
@@ -45,13 +48,13 @@ namespace ElectronNET.API
/// <param name="listener">Callback Method.</param>
public void On(string channel, Action<object> listener)
{
BridgeConnector.Socket.Emit("registerIpcMainChannel", channel);
BridgeConnector.Socket.Off(channel);
BridgeConnector.Socket.On(channel, (args) =>
BridgeConnector.Emit("registerIpcMainChannel", channel);
BridgeConnector.Off(channel);
BridgeConnector.On<object[]>(channel, (args) =>
{
List<object> objectArray = FormatArguments(args);
var objectArray = FormatArguments(args);
if(objectArray.Count == 1)
if (objectArray.Count == 1)
{
listener(objectArray.First());
}
@@ -62,20 +65,41 @@ namespace ElectronNET.API
});
}
private List<object> FormatArguments(object args)
/// <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)
{
List<object> objectArray = ((JArray)args).ToObject<object[]>().ToList();
for (int index = 0; index < objectArray.Count; index++)
BridgeConnector.Emit("registerIpcMainChannelWithId", channel);
BridgeConnector.Off(channel);
BridgeConnector.On<ArgsAndIds>(channel, (data) =>
{
var item = objectArray[index];
if (item == null)
{
objectArray.Remove(item);
}
}
var objectArray = FormatArguments(data.args);
return objectArray;
if (objectArray.Count == 1)
{
listener((data.id, data.wcId, objectArray.First()));
}
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();
}
/// <summary>
@@ -89,9 +113,9 @@ namespace ElectronNET.API
/// <param name="listener"></param>
public void OnSync(string channel, Func<object, object> listener)
{
BridgeConnector.Socket.Emit("registerSyncIpcMainChannel", channel);
BridgeConnector.Socket.On(channel, (args) => {
List<object> objectArray = FormatArguments(args);
BridgeConnector.Emit("registerSyncIpcMainChannel", channel);
BridgeConnector.On<object[]>(channel, (args) => {
var objectArray = FormatArguments(args);
object parameter;
if (objectArray.Count == 1)
{
@@ -103,7 +127,7 @@ namespace ElectronNET.API
}
var result = listener(parameter);
BridgeConnector.Socket.Emit(channel + "Sync", result);
BridgeConnector.Emit(channel + "Sync", result);
});
}
@@ -115,10 +139,10 @@ namespace ElectronNET.API
/// <param name="listener">Callback Method.</param>
public void Once(string channel, Action<object> listener)
{
BridgeConnector.Socket.Emit("registerOnceIpcMainChannel", channel);
BridgeConnector.Socket.On(channel, (args) =>
BridgeConnector.Emit("registerOnceIpcMainChannel", channel);
BridgeConnector.Once<object[]>(channel, (args) =>
{
List<object> objectArray = FormatArguments(args);
var objectArray = FormatArguments(args);
if (objectArray.Count == 1)
{
@@ -137,7 +161,7 @@ namespace ElectronNET.API
/// <param name="channel">Channelname.</param>
public void RemoveAllListeners(string channel)
{
BridgeConnector.Socket.Emit("removeAllListenersIpcMainChannel", channel);
BridgeConnector.Emit("removeAllListenersIpcMainChannel", channel);
}
/// <summary>
@@ -151,32 +175,29 @@ namespace ElectronNET.API
/// <param name="data">Arguments data.</param>
public void Send(BrowserWindow browserWindow, string channel, params object[] data)
{
List<JObject> jobjects = new List<JObject>();
List<JArray> jarrays = new List<JArray>();
List<object> objects = new List<object>();
var objectsWithCorrectSerialization = new List<object>
{
browserWindow.Id,
channel
};
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))
objectsWithCorrectSerialization.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))
objectsWithCorrectSerialization.Add(JObject.FromObject(parameterObject, _jsonSerializer));
}
else if(parameterObject.GetType().IsPrimitive || (parameterObject is string))
{
objects.Add(parameterObject);
objectsWithCorrectSerialization.Add(parameterObject);
}
}
if(jobjects.Count > 0 || jarrays.Count > 0)
{
BridgeConnector.Socket.Emit("sendToIpcRenderer", JObject.FromObject(browserWindow, _jsonSerializer), channel, jarrays.ToArray(), jobjects.ToArray(), objects.ToArray());
}
else
{
BridgeConnector.Socket.Emit("sendToIpcRenderer", JObject.FromObject(browserWindow, _jsonSerializer), channel, data);
}
BridgeConnector.Emit("sendToIpcRenderer", objectsWithCorrectSerialization.ToArray());
}
/// <summary>
@@ -190,9 +211,9 @@ namespace ElectronNET.API
/// <param name="data">Arguments data.</param>
public void Send(BrowserView browserView, string channel, params object[] data)
{
List<JObject> jobjects = new List<JObject>();
List<JArray> jarrays = new List<JArray>();
List<object> objects = new List<object>();
List<JObject> jobjects = new();
List<JArray> jarrays = new();
List<object> objects = new();
foreach (var parameterObject in data)
{
@@ -210,15 +231,36 @@ namespace ElectronNET.API
if(jobjects.Count > 0 || jarrays.Count > 0)
{
BridgeConnector.Socket.Emit("sendToIpcRendererBrowserView", browserView.Id, channel, jarrays.ToArray(), jobjects.ToArray(), objects.ToArray());
BridgeConnector.Emit("sendToIpcRendererBrowserView", browserView.Id, channel, jarrays.ToArray(), jobjects.ToArray(), objects.ToArray());
}
else
{
BridgeConnector.Socket.Emit("sendToIpcRendererBrowserView", browserView.Id, channel, data);
BridgeConnector.Emit("sendToIpcRendererBrowserView", browserView.Id, channel, data);
}
}
private JsonSerializer _jsonSerializer = new JsonSerializer()
/// <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
/// </summary>
/// <param name="text">Message to log</param>
public static void ConsoleLog(string text)
{
BridgeConnector.Emit("console-stdout", text);
}
/// <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()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,

View File

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

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

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

View File

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

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

@@ -1,17 +1,19 @@
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
public sealed class NativeTheme : INativeTheme
{
private static NativeTheme _nativeTheme;
private static object _syncRoot = new object();
private static readonly object _syncRoot = new();
internal NativeTheme() { }
@@ -94,88 +96,37 @@ namespace ElectronNET.API
{
var themeSource = themeSourceMode.GetDescription();
BridgeConnector.Socket.Emit("nativeTheme-themeSource", themeSource);
BridgeConnector.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 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;
}
public async Task<ThemeSourceMode> GetThemeSourceAsync() => Enum.Parse<ThemeSourceMode>(await BridgeConnector.OnResult<string>("nativeTheme-themeSource-get", "nativeTheme-themeSource-getCompleted"), true);
/// <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()
{
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;
}
public Task<bool> ShouldUseDarkColorsAsync() => BridgeConnector.OnResult<bool>("nativeTheme-shouldUseDarkColors", "nativeTheme-shouldUseDarkColors-completed");
/// <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>
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;
}
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public Task<bool> ShouldUseHighContrastColorsAsync() => BridgeConnector.OnResult<bool>("nativeTheme-shouldUseHighContrastColors", "nativeTheme-shouldUseHighContrastColors-completed");
/// <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>
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;
}
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public Task<bool> ShouldUseInvertedColorSchemeAsync() => BridgeConnector.OnResult<bool>("nativeTheme-shouldUseInvertedColorScheme", "nativeTheme-shouldUseInvertedColorScheme-completed");
/// <summary>
/// Emitted when something in the underlying NativeTheme has changed. This normally means that either the value of <see cref="ShouldUseDarkColorsAsync"/>,
@@ -187,12 +138,12 @@ namespace ElectronNET.API
{
if (_updated == null)
{
BridgeConnector.Socket.On("nativeTheme-updated" + GetHashCode(), () =>
BridgeConnector.On("nativeTheme-updated" + GetHashCode(), () =>
{
_updated();
});
BridgeConnector.Socket.Emit("register-nativeTheme-updated-event", GetHashCode());
BridgeConnector.Emit("register-nativeTheme-updated-event", GetHashCode());
}
_updated += value;
}
@@ -202,7 +153,7 @@ namespace ElectronNET.API
if (_updated == null)
{
BridgeConnector.Socket.Off("nativeTheme-updated" + GetHashCode());
BridgeConnector.Off("nativeTheme-updated" + GetHashCode());
}
}
}

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

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

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

@@ -1,28 +1,32 @@
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
public sealed class PowerMonitor : IPowerMonitor
{
/// <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.Socket.On("pm-lock-screen" , () =>
BridgeConnector.On("pm-lock-screen" , () =>
{
_lockScreen();
});
BridgeConnector.Socket.Emit("register-pm-lock-screen");
BridgeConnector.Emit("register-pm-lock-screen");
}
_lockScreen += value;
}
@@ -31,7 +35,7 @@ namespace ElectronNET.API
_lockScreen -= value;
if (_lockScreen == null)
BridgeConnector.Socket.Off("pm-lock-screen");
BridgeConnector.Off("pm-lock-screen");
}
}
@@ -40,18 +44,20 @@ 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.Socket.On("pm-unlock-screen", () =>
BridgeConnector.On("pm-unlock-screen", () =>
{
_unlockScreen();
});
BridgeConnector.Socket.Emit("register-pm-unlock-screen");
BridgeConnector.Emit("register-pm-unlock-screen");
}
_unlockScreen += value;
}
@@ -60,7 +66,7 @@ namespace ElectronNET.API
_unlockScreen -= value;
if (_unlockScreen == null)
BridgeConnector.Socket.Off("pm-unlock-screen");
BridgeConnector.Off("pm-unlock-screen");
}
}
@@ -69,18 +75,20 @@ namespace ElectronNET.API
/// <summary>
/// Emitted when the system is suspending.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public event Action OnSuspend
{
add
{
if (_suspend == null)
{
BridgeConnector.Socket.On("pm-suspend", () =>
BridgeConnector.On("pm-suspend", () =>
{
_suspend();
});
BridgeConnector.Socket.Emit("register-pm-suspend");
BridgeConnector.Emit("register-pm-suspend");
}
_suspend += value;
}
@@ -89,7 +97,7 @@ namespace ElectronNET.API
_suspend -= value;
if (_suspend == null)
BridgeConnector.Socket.Off("pm-suspend");
BridgeConnector.Off("pm-suspend");
}
}
@@ -98,18 +106,20 @@ namespace ElectronNET.API
/// <summary>
/// Emitted when system is resuming.
/// </summary>
[SupportedOSPlatform("windows")]
[SupportedOSPlatform("macos")]
public event Action OnResume
{
add
{
if (_resume == null)
{
BridgeConnector.Socket.On("pm-resume", () =>
BridgeConnector.On("pm-resume", () =>
{
_resume();
});
BridgeConnector.Socket.Emit("register-pm-resume");
BridgeConnector.Emit("register-pm-resume");
}
_resume += value;
}
@@ -118,7 +128,7 @@ namespace ElectronNET.API
_resume -= value;
if (_resume == null)
BridgeConnector.Socket.Off("pm-resume");
BridgeConnector.Off("pm-resume");
}
}
@@ -127,18 +137,20 @@ 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.Socket.On("pm-on-ac", () =>
BridgeConnector.On("pm-on-ac", () =>
{
_onAC();
});
BridgeConnector.Socket.Emit("register-pm-on-ac");
BridgeConnector.Emit("register-pm-on-ac");
}
_onAC += value;
}
@@ -147,7 +159,7 @@ namespace ElectronNET.API
_onAC -= value;
if (_onAC == null)
BridgeConnector.Socket.Off("pm-on-ac");
BridgeConnector.Off("pm-on-ac");
}
}
@@ -156,18 +168,20 @@ 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.Socket.On("pm-on-battery", () =>
BridgeConnector.On("pm-on-battery", () =>
{
_onBattery();
});
BridgeConnector.Socket.Emit("register-pm-on-battery");
BridgeConnector.Emit("register-pm-on-battery");
}
_onBattery += value;
}
@@ -176,7 +190,7 @@ namespace ElectronNET.API
_onBattery -= value;
if (_onBattery == null)
BridgeConnector.Socket.Off("pm-on-battery");
BridgeConnector.Off("pm-on-battery");
}
}
@@ -189,18 +203,21 @@ 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.Socket.On("pm-shutdown", () =>
BridgeConnector.On("pm-shutdown", () =>
{
_shutdown();
});
BridgeConnector.Socket.Emit("register-pm-shutdown");
BridgeConnector.Emit("register-pm-shutdown");
}
_shutdown += value;
}
@@ -209,14 +226,14 @@ namespace ElectronNET.API
_shutdown -= value;
if (_shutdown == null)
BridgeConnector.Socket.Off("pm-on-shutdown");
BridgeConnector.Off("pm-on-shutdown");
}
}
private event Action _shutdown;
private static PowerMonitor _powerMonitor;
private static object _syncRoot = new object();
private static readonly object _syncRoot = new();
internal PowerMonitor() { }

View File

@@ -0,0 +1,37 @@
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 Normal file → Executable file
View File

@@ -4,13 +4,14 @@ 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
public sealed class Screen : IScreen
{
/// <summary>
/// Emitted when an new Display has been added.
@@ -21,12 +22,12 @@ namespace ElectronNET.API
{
if (_onDisplayAdded == null)
{
BridgeConnector.Socket.On("screen-display-added-event" + GetHashCode(), (display) =>
BridgeConnector.On<Display>("screen-display-added-event" + GetHashCode(), (display) =>
{
_onDisplayAdded(((JObject)display).ToObject<Display>());
_onDisplayAdded(display);
});
BridgeConnector.Socket.Emit("register-screen-display-added", GetHashCode());
BridgeConnector.Emit("register-screen-display-added", GetHashCode());
}
_onDisplayAdded += value;
}
@@ -35,7 +36,7 @@ namespace ElectronNET.API
_onDisplayAdded -= value;
if (_onDisplayAdded == null)
BridgeConnector.Socket.Off("screen-display-added-event" + GetHashCode());
BridgeConnector.Off("screen-display-added-event" + GetHashCode());
}
}
@@ -50,12 +51,12 @@ namespace ElectronNET.API
{
if (_onDisplayRemoved == null)
{
BridgeConnector.Socket.On("screen-display-removed-event" + GetHashCode(), (display) =>
BridgeConnector.On<Display>("screen-display-removed-event" + GetHashCode(), (display) =>
{
_onDisplayRemoved(((JObject)display).ToObject<Display>());
_onDisplayRemoved(display);
});
BridgeConnector.Socket.Emit("register-screen-display-removed", GetHashCode());
BridgeConnector.Emit("register-screen-display-removed", GetHashCode());
}
_onDisplayRemoved += value;
}
@@ -64,7 +65,7 @@ namespace ElectronNET.API
_onDisplayRemoved -= value;
if (_onDisplayRemoved == null)
BridgeConnector.Socket.Off("screen-display-removed-event" + GetHashCode());
BridgeConnector.Off("screen-display-removed-event" + GetHashCode());
}
}
@@ -81,15 +82,12 @@ namespace ElectronNET.API
{
if (_onDisplayMetricsChanged == null)
{
BridgeConnector.Socket.On("screen-display-metrics-changed-event" + GetHashCode(), (args) =>
BridgeConnector.On<DisplayChanged>("screen-display-metrics-changed-event" + GetHashCode(), (args) =>
{
var display = ((JArray)args).First.ToObject<Display>();
var metrics = ((JArray)args).Last.ToObject<string[]>();
_onDisplayMetricsChanged(display, metrics);
_onDisplayMetricsChanged(args.display, args.metrics);
});
BridgeConnector.Socket.Emit("register-screen-display-metrics-changed", GetHashCode());
BridgeConnector.Emit("register-screen-display-metrics-changed", GetHashCode());
}
_onDisplayMetricsChanged += value;
}
@@ -98,14 +96,14 @@ namespace ElectronNET.API
_onDisplayMetricsChanged -= value;
if (_onDisplayMetricsChanged == null)
BridgeConnector.Socket.Off("screen-display-metrics-changed-event" + GetHashCode());
BridgeConnector.Off("screen-display-metrics-changed-event" + GetHashCode());
}
}
private event Action<Display, string[]> _onDisplayMetricsChanged;
private static Screen _screen;
private static object _syncRoot = new object();
private static readonly object _syncRoot = new();
internal Screen() { }
@@ -132,128 +130,38 @@ namespace ElectronNET.API
/// The current absolute position of the mouse pointer.
/// </summary>
/// <returns></returns>
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;
}
public Task<Point> GetCursorScreenPointAsync() => BridgeConnector.OnResult<Point>("screen-getCursorScreenPoint", "screen-getCursorScreenPointCompleted");
/// <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()
{
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;
}
public Task<int> GetMenuBarHeightAsync() => BridgeConnector.OnResult<int>("screen-getMenuBarHeight", "screen-getMenuBarHeightCompleted");
/// <summary>
/// The primary display.
/// </summary>
/// <returns></returns>
public Task<Display> GetPrimaryDisplayAsync()
{
var taskCompletionSource = new TaskCompletionSource<Display>();
public Task<Display> GetPrimaryDisplayAsync() => BridgeConnector.OnResult<Display>("screen-getPrimaryDisplay", "screen-getPrimaryDisplayCompleted");
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()
{
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;
}
public Task<Display[]> GetAllDisplaysAsync() => BridgeConnector.OnResult<Display[]>("screen-getAllDisplays", "screen-getAllDisplaysCompleted");
/// <summary>
/// The display nearest the specified point.
/// </summary>
/// <returns>The display nearest the specified point.</returns>
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;
}
public Task<Display> GetDisplayNearestPointAsync(Point point) => BridgeConnector.OnResult<Display>("screen-getDisplayNearestPoint", "screen-getDisplayNearestPointCompleted", 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>
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
};
public Task<Display> GetDisplayMatchingAsync(Rectangle rectangle) => BridgeConnector.OnResult<Display>("screen-getDisplayMatching", "screen-getDisplayMatchingCompleted", rectangle);
}
}

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

@@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using ElectronNET.API.Interfaces;
using Microsoft.Extensions.DependencyInjection;
namespace ElectronNET.API
{
@@ -13,6 +14,7 @@ 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)
@@ -28,6 +30,23 @@ namespace ElectronNET.API
.AddSingleton(provider => HostHook.Instance)
.AddSingleton(provider => PowerMonitor.Instance)
.AddSingleton(provider => NativeTheme.Instance)
.AddSingleton(provider => Dock.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);
}
}

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.Socket.Emit("webContents-session-allowNTLMCredentialsForDomains", Id, domains);
BridgeConnector.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>();
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-clearAuthCache-completed" + guid, () =>
BridgeConnector.On("webContents-session-clearAuthCache-completed" + guid, () =>
{
BridgeConnector.Socket.Off("webContents-session-clearAuthCache-completed" + guid);
BridgeConnector.Off("webContents-session-clearAuthCache-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Socket.Emit("webContents-session-clearAuthCache", Id, JObject.FromObject(options, _jsonSerializer), guid);
BridgeConnector.Emit("webContents-session-clearAuthCache", Id, options, guid);
return taskCompletionSource.Task;
}
@@ -66,16 +66,16 @@ namespace ElectronNET.API
/// </summary>
public Task ClearAuthCacheAsync()
{
var taskCompletionSource = new TaskCompletionSource<object>();
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-clearAuthCache-completed" + guid, () =>
BridgeConnector.On("webContents-session-clearAuthCache-completed" + guid, () =>
{
BridgeConnector.Socket.Off("webContents-session-clearAuthCache-completed" + guid);
BridgeConnector.Off("webContents-session-clearAuthCache-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Socket.Emit("webContents-session-clearAuthCache", Id, guid);
BridgeConnector.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>();
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-clearCache-completed" + guid, () =>
BridgeConnector.On("webContents-session-clearCache-completed" + guid, () =>
{
BridgeConnector.Socket.Off("webContents-session-clearCache-completed" + guid);
BridgeConnector.Off("webContents-session-clearCache-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Socket.Emit("webContents-session-clearCache", Id, guid);
BridgeConnector.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>();
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-clearHostResolverCache-completed" + guid, () =>
BridgeConnector.On("webContents-session-clearHostResolverCache-completed" + guid, () =>
{
BridgeConnector.Socket.Off("webContents-session-clearHostResolverCache-completed" + guid);
BridgeConnector.Off("webContents-session-clearHostResolverCache-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Socket.Emit("webContents-session-clearHostResolverCache", Id, guid);
BridgeConnector.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>();
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-clearStorageData-completed" + guid, () =>
BridgeConnector.On("webContents-session-clearStorageData-completed" + guid, () =>
{
BridgeConnector.Socket.Off("webContents-session-clearStorageData-completed" + guid);
BridgeConnector.Off("webContents-session-clearStorageData-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Socket.Emit("webContents-session-clearStorageData", Id, guid);
BridgeConnector.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>();
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-clearStorageData-options-completed" + guid, () =>
BridgeConnector.On("webContents-session-clearStorageData-options-completed" + guid, () =>
{
BridgeConnector.Socket.Off("webContents-session-clearStorageData-options-completed" + guid);
BridgeConnector.Off("webContents-session-clearStorageData-options-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Socket.Emit("webContents-session-clearStorageData-options", Id, JObject.FromObject(options, _jsonSerializer), guid);
BridgeConnector.Emit("webContents-session-clearStorageData-options", Id, options, guid);
return taskCompletionSource.Task;
}
@@ -171,7 +171,7 @@ namespace ElectronNET.API
/// <param name="options"></param>
public void CreateInterruptedDownload(CreateInterruptedDownloadOptions options)
{
BridgeConnector.Socket.Emit("webContents-session-createInterruptedDownload", Id, JObject.FromObject(options, _jsonSerializer));
BridgeConnector.Emit("webContents-session-createInterruptedDownload", Id, options);
}
/// <summary>
@@ -180,7 +180,7 @@ namespace ElectronNET.API
/// </summary>
public void DisableNetworkEmulation()
{
BridgeConnector.Socket.Emit("webContents-session-disableNetworkEmulation", Id);
BridgeConnector.Emit("webContents-session-disableNetworkEmulation", Id);
}
/// <summary>
@@ -189,7 +189,7 @@ namespace ElectronNET.API
/// <param name="options"></param>
public void EnableNetworkEmulation(EnableNetworkEmulationOptions options)
{
BridgeConnector.Socket.Emit("webContents-session-enableNetworkEmulation", Id, JObject.FromObject(options, _jsonSerializer));
BridgeConnector.Emit("webContents-session-enableNetworkEmulation", Id, options);
}
/// <summary>
@@ -197,7 +197,7 @@ namespace ElectronNET.API
/// </summary>
public void FlushStorageData()
{
BridgeConnector.Socket.Emit("webContents-session-flushStorageData", Id);
BridgeConnector.Emit("webContents-session-flushStorageData", Id);
}
/// <summary>
@@ -207,18 +207,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<int[]> GetBlobDataAsync(string identifier)
{
var taskCompletionSource = new TaskCompletionSource<int[]>();
var taskCompletionSource = new TaskCompletionSource<int[]>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-getBlobData-completed" + guid, (buffer) =>
BridgeConnector.On<int[]>("webContents-session-getBlobData-completed" + guid, (buffer) =>
{
var result = ((JArray)buffer).ToObject<int[]>();
BridgeConnector.Socket.Off("webContents-session-getBlobData-completed" + guid);
taskCompletionSource.SetResult(result);
BridgeConnector.Off("webContents-session-getBlobData-completed" + guid);
taskCompletionSource.SetResult(buffer);
});
BridgeConnector.Socket.Emit("webContents-session-getBlobData", Id, identifier, guid);
BridgeConnector.Emit("webContents-session-getBlobData", Id, identifier, guid);
return taskCompletionSource.Task;
}
@@ -229,16 +227,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>();
var taskCompletionSource = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-getCacheSize-completed" + guid, (size) =>
BridgeConnector.On<int>("webContents-session-getCacheSize-completed" + guid, (size) =>
{
BridgeConnector.Socket.Off("webContents-session-getCacheSize-completed" + guid);
taskCompletionSource.SetResult((int)size);
BridgeConnector.Off("webContents-session-getCacheSize-completed" + guid);
taskCompletionSource.SetResult(size);
});
BridgeConnector.Socket.Emit("webContents-session-getCacheSize", Id, guid);
BridgeConnector.Emit("webContents-session-getCacheSize", Id, guid);
return taskCompletionSource.Task;
}
@@ -249,17 +247,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<string[]> GetPreloadsAsync()
{
var taskCompletionSource = new TaskCompletionSource<string[]>();
var taskCompletionSource = new TaskCompletionSource<string[]>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-getPreloads-completed" + guid, (preloads) =>
BridgeConnector.On<string[]>("webContents-session-getPreloads-completed" + guid, (preloads) =>
{
var result = ((JArray)preloads).ToObject<string[]>();
BridgeConnector.Socket.Off("webContents-session-getPreloads-completed" + guid);
taskCompletionSource.SetResult(result);
BridgeConnector.Off("webContents-session-getPreloads-completed" + guid);
taskCompletionSource.SetResult(preloads);
});
BridgeConnector.Socket.Emit("webContents-session-getPreloads", Id, guid);
BridgeConnector.Emit("webContents-session-getPreloads", Id, guid);
return taskCompletionSource.Task;
}
@@ -270,16 +267,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<string> GetUserAgent()
{
var taskCompletionSource = new TaskCompletionSource<string>();
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-getUserAgent-completed" + guid, (userAgent) =>
BridgeConnector.On<string>("webContents-session-getUserAgent-completed" + guid, (userAgent) =>
{
BridgeConnector.Socket.Off("webContents-session-getUserAgent-completed" + guid);
BridgeConnector.Off("webContents-session-getUserAgent-completed" + guid);
taskCompletionSource.SetResult(userAgent.ToString());
});
BridgeConnector.Socket.Emit("webContents-session-getUserAgent", Id, guid);
BridgeConnector.Emit("webContents-session-getUserAgent", Id, guid);
return taskCompletionSource.Task;
}
@@ -292,16 +289,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<string> ResolveProxyAsync(string url)
{
var taskCompletionSource = new TaskCompletionSource<string>();
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-resolveProxy-completed" + guid, (proxy) =>
BridgeConnector.On<string>("webContents-session-resolveProxy-completed" + guid, (proxy) =>
{
BridgeConnector.Socket.Off("webContents-session-resolveProxy-completed" + guid);
BridgeConnector.Off("webContents-session-resolveProxy-completed" + guid);
taskCompletionSource.SetResult(proxy.ToString());
});
BridgeConnector.Socket.Emit("webContents-session-resolveProxy", Id, url, guid);
BridgeConnector.Emit("webContents-session-resolveProxy", Id, url, guid);
return taskCompletionSource.Task;
}
@@ -313,7 +310,7 @@ namespace ElectronNET.API
/// <param name="path"></param>
public void SetDownloadPath(string path)
{
BridgeConnector.Socket.Emit("webContents-session-setDownloadPath", Id, path);
BridgeConnector.Emit("webContents-session-setDownloadPath", Id, path);
}
/// <summary>
@@ -323,7 +320,7 @@ namespace ElectronNET.API
/// <param name="preloads"></param>
public void SetPreloads(string[] preloads)
{
BridgeConnector.Socket.Emit("webContents-session-setPreloads", Id, preloads);
BridgeConnector.Emit("webContents-session-setPreloads", Id, preloads);
}
/// <summary>
@@ -334,16 +331,16 @@ namespace ElectronNET.API
/// <returns></returns>
public Task SetProxyAsync(ProxyConfig config)
{
var taskCompletionSource = new TaskCompletionSource<object>();
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
string guid = Guid.NewGuid().ToString();
BridgeConnector.Socket.On("webContents-session-setProxy-completed" + guid, () =>
BridgeConnector.On("webContents-session-setProxy-completed" + guid, () =>
{
BridgeConnector.Socket.Off("webContents-session-setProxy-completed" + guid);
BridgeConnector.Off("webContents-session-setProxy-completed" + guid);
taskCompletionSource.SetResult(null);
});
BridgeConnector.Socket.Emit("webContents-session-setProxy", Id, JObject.FromObject(config, _jsonSerializer), guid);
BridgeConnector.Emit("webContents-session-setProxy", Id, config, guid);
return taskCompletionSource.Task;
}
@@ -356,7 +353,7 @@ namespace ElectronNET.API
/// <param name="userAgent"></param>
public void SetUserAgent(string userAgent)
{
BridgeConnector.Socket.Emit("webContents-session-setUserAgent", Id, userAgent);
BridgeConnector.Emit("webContents-session-setUserAgent", Id, userAgent);
}
/// <summary>
@@ -372,7 +369,7 @@ namespace ElectronNET.API
/// example "en-US,fr,de,ko,zh-CN,ja".</param>
public void SetUserAgent(string userAgent, string acceptLanguages)
{
BridgeConnector.Socket.Emit("webContents-session-setUserAgent", Id, userAgent, acceptLanguages);
BridgeConnector.Emit("webContents-session-setUserAgent", Id, userAgent, acceptLanguages);
}
/// <summary>
@@ -382,17 +379,15 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<ChromeExtensionInfo[]> GetAllExtensionsAsync()
{
var taskCompletionSource = new TaskCompletionSource<ChromeExtensionInfo[]>();
var taskCompletionSource = new TaskCompletionSource<ChromeExtensionInfo[]>(TaskCreationOptions.RunContinuationsAsynchronously);
BridgeConnector.Socket.On("webContents-session-getAllExtensions-completed", (extensionslist) =>
BridgeConnector.On<ChromeExtensionInfo[]>("webContents-session-getAllExtensions-completed", (extensionslist) =>
{
BridgeConnector.Socket.Off("webContents-session-getAllExtensions-completed");
var chromeExtensionInfos = ((JArray)extensionslist).ToObject<ChromeExtensionInfo[]>();
taskCompletionSource.SetResult(chromeExtensionInfos);
BridgeConnector.Off("webContents-session-getAllExtensions-completed");
taskCompletionSource.SetResult(extensionslist);
});
BridgeConnector.Socket.Emit("webContents-session-getAllExtensions", Id);
BridgeConnector.Emit("webContents-session-getAllExtensions", Id);
return taskCompletionSource.Task;
}
@@ -404,7 +399,7 @@ namespace ElectronNET.API
/// <param name="name">Name of the Chrome extension to remove</param>
public void RemoveExtension(string name)
{
BridgeConnector.Socket.Emit("webContents-session-removeExtension", Id, name);
BridgeConnector.Emit("webContents-session-removeExtension", Id, name);
}
/// <summary>
@@ -436,25 +431,18 @@ namespace ElectronNET.API
/// <returns></returns>
public Task<Extension> LoadExtensionAsync(string path, bool allowFileAccess = false)
{
var taskCompletionSource = new TaskCompletionSource<Extension>();
var taskCompletionSource = new TaskCompletionSource<Extension>(TaskCreationOptions.RunContinuationsAsynchronously);
BridgeConnector.Socket.On("webContents-session-loadExtension-completed", (extension) =>
BridgeConnector.On<Extension>("webContents-session-loadExtension-completed", (extension) =>
{
BridgeConnector.Socket.Off("webContents-session-loadExtension-completed");
BridgeConnector.Off("webContents-session-loadExtension-completed");
taskCompletionSource.SetResult(((JObject)extension).ToObject<Extension>());
taskCompletionSource.SetResult(extension);
});
BridgeConnector.Socket.Emit("webContents-session-loadExtension", Id, path, allowFileAccess);
BridgeConnector.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 Normal file → Executable file
View File

@@ -4,16 +4,18 @@ 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
public sealed class Shell : IShell
{
private static Shell _shell;
private static object _syncRoot = new object();
private static readonly object _syncRoot = new();
internal Shell() { }
@@ -42,14 +44,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>();
var taskCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
BridgeConnector.Socket.On("shell-showItemInFolderCompleted", () =>
BridgeConnector.On("shell-showItemInFolderCompleted", () =>
{
BridgeConnector.Socket.Off("shell-showItemInFolderCompleted");
BridgeConnector.Off("shell-showItemInFolderCompleted");
});
BridgeConnector.Socket.Emit("shell-showItemInFolder", fullPath);
BridgeConnector.Emit("shell-showItemInFolder", fullPath);
return taskCompletionSource.Task;
}
@@ -61,16 +63,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>();
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
BridgeConnector.Socket.On("shell-openPathCompleted", (errorMessage) =>
BridgeConnector.On<string>("shell-openPathCompleted", (errorMessage) =>
{
BridgeConnector.Socket.Off("shell-openPathCompleted");
BridgeConnector.Off("shell-openPathCompleted");
taskCompletionSource.SetResult((string) errorMessage);
taskCompletionSource.SetResult(errorMessage);
});
BridgeConnector.Socket.Emit("shell-openPath", path);
BridgeConnector.Emit("shell-openPath", path);
return taskCompletionSource.Task;
}
@@ -93,24 +95,25 @@ 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>();
var taskCompletionSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
BridgeConnector.Socket.On("shell-openExternalCompleted", (error) =>
BridgeConnector.On<string>("shell-openExternalCompleted", (error) =>
{
BridgeConnector.Socket.Off("shell-openExternalCompleted");
BridgeConnector.Off("shell-openExternalCompleted");
taskCompletionSource.SetResult((string) error);
taskCompletionSource.SetResult(error);
});
if (options == null)
{
BridgeConnector.Socket.Emit("shell-openExternal", url);
BridgeConnector.Emit("shell-openExternal", url);
}
else
{
BridgeConnector.Socket.Emit("shell-openExternal", url, JObject.FromObject(options, _jsonSerializer));
BridgeConnector.Emit("shell-openExternal", url, options);
}
return taskCompletionSource.Task;
@@ -123,18 +126,7 @@ namespace ElectronNET.API
/// <returns> Whether the item was successfully moved to the trash.</returns>
public Task<bool> TrashItemAsync(string 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;
return BridgeConnector.OnResult<bool>("shell-trashItem", "shell-trashItem-completed", fullPath);
}
/// <summary>
@@ -142,7 +134,7 @@ namespace ElectronNET.API
/// </summary>
public void Beep()
{
BridgeConnector.Socket.Emit("shell-beep");
BridgeConnector.Emit("shell-beep");
}
/// <summary>
@@ -152,20 +144,10 @@ 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)
{
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;
return BridgeConnector.OnResult<bool>("shell-writeShortcutLink", "shell-writeShortcutLinkCompleted", shortcutPath, operation.GetDescription(), options);
}
/// <summary>
@@ -174,30 +156,10 @@ 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)
{
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;
return BridgeConnector.OnResult<ShortcutDetails>("shell-readShortcutLink", "shell-readShortcutLinkCompleted", shortcutPath);
}
private readonly JsonSerializer _jsonSerializer = new JsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
}
}

View File

@@ -0,0 +1,64 @@
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

@@ -0,0 +1,11 @@
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

@@ -0,0 +1,19 @@
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();
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Threading;
namespace SocketIOClient.Extensions
{
internal static class CancellationTokenSourceExtensions
{
public static void TryDispose(this CancellationTokenSource cts)
{
cts?.Dispose();
}
public static void TryCancel(this CancellationTokenSource cts)
{
cts?.Cancel();
}
}
}

View File

@@ -0,0 +1,12 @@
using System;
namespace SocketIOClient.Extensions
{
internal static class DisposableExtensions
{
public static void TryDispose(this IDisposable disposable)
{
disposable?.Dispose();
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
namespace SocketIOClient.Extensions
{
internal static class EventHandlerExtensions
{
public static void TryInvoke<T>(this EventHandler<T> handler, object sender, T args)
{
handler?.Invoke(sender, args);
}
public static void TryInvoke(this EventHandler handler, object sender, EventArgs args)
{
handler?.Invoke(sender, args);
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
namespace SocketIOClient.Extensions
{
internal static class SocketIOEventExtensions
{
public static void TryInvoke(this OnAnyHandler handler, string eventName, SocketIOResponse response)
{
try
{
handler(eventName, response);
}
catch
{
// The exception is thrown by the user code, so it can be swallowed
}
}
public static void TryInvoke(this Action<SocketIOResponse> handler, SocketIOResponse response)
{
try
{
handler(response);
}
catch
{
// The exception is thrown by the user code, so it can be swallowed
}
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SocketIOClient.JsonSerializer
{
class ByteArrayConverter : JsonConverter<byte[]>
{
public ByteArrayConverter()
{
Bytes = new List<byte[]>();
}
public List<byte[]> Bytes { get; }
public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
byte[] bytes = null;
if (reader.TokenType == JsonTokenType.StartObject)
{
reader.Read();
if (reader.TokenType == JsonTokenType.PropertyName && reader.GetString() == "_placeholder")
{
reader.Read();
if (reader.TokenType == JsonTokenType.True && reader.GetBoolean())
{
reader.Read();
if (reader.TokenType == JsonTokenType.PropertyName && reader.GetString() == "num")
{
reader.Read();
int num = reader.GetInt32();
bytes = Bytes[num];
reader.Read();
}
}
}
}
return bytes;
}
public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options)
{
Bytes.Add(value);
writer.WriteStartObject();
writer.WritePropertyName("_placeholder");
writer.WriteBooleanValue(true);
writer.WritePropertyName("num");
writer.WriteNumberValue(Bytes.Count - 1);
writer.WriteEndObject();
}
}
}

View File

@@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace SocketIOClient.JsonSerializer
{
public interface IJsonSerializer
{
JsonSerializeResult Serialize(object[] data);
T Deserialize<T>(string json);
T Deserialize<T>(string json, IList<byte[]> incomingBytes);
}
}

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace SocketIOClient.JsonSerializer
{
public class JsonSerializeResult
{
public string Json { get; set; }
public IList<byte[]> Bytes { get; set; }
}
}

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