mirror of
https://github.com/taigrr/wails.git
synced 2026-04-03 05:38:56 -07:00
Compare commits
107 Commits
v2.0.0-bet
...
v2-mac-doc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a020b67f67 | ||
|
|
fa958e7a07 | ||
|
|
1a3e81a3f8 | ||
|
|
0eb7a8a771 | ||
|
|
2fa004808f | ||
|
|
cc5fd30256 | ||
|
|
c90bfc310a | ||
|
|
62d1d621aa | ||
|
|
6e8cbb8e8f | ||
|
|
32fa543164 | ||
|
|
04f93ac54e | ||
|
|
3c87d13b21 | ||
|
|
0949eab72e | ||
|
|
aab67b416f | ||
|
|
83a575e43f | ||
|
|
333949ee53 | ||
|
|
1d1238cea3 | ||
|
|
bd7b436631 | ||
|
|
c136df48b9 | ||
|
|
a090a689cf | ||
|
|
5ef2448a0c | ||
|
|
06ab4c88ad | ||
|
|
48efdea11a | ||
|
|
43cc55cb0a | ||
|
|
71f2436562 | ||
|
|
4653c77a81 | ||
|
|
72b05c6b44 | ||
|
|
b5f68e24d6 | ||
|
|
3948c8ca61 | ||
|
|
cf3a868e3a | ||
|
|
43c29abb23 | ||
|
|
7ef445f526 | ||
|
|
f6c2d4ae6b | ||
|
|
8f9fae6ad9 | ||
|
|
b45f264e2a | ||
|
|
986f8f48c7 | ||
|
|
bbc2e86286 | ||
|
|
2dc126bf19 | ||
|
|
86cbcdc089 | ||
|
|
1dd957f461 | ||
|
|
4be4946756 | ||
|
|
65979cbc75 | ||
|
|
6a7118ff6d | ||
|
|
a88b3553ba | ||
|
|
fd5348d26d | ||
|
|
569569f1fc | ||
|
|
489b9b358b | ||
|
|
71cfdfc7c8 | ||
|
|
6ebf4ed428 | ||
|
|
5be0739c5d | ||
|
|
6721e59277 | ||
|
|
77775d85ab | ||
|
|
6de0865c3e | ||
|
|
f9e559f069 | ||
|
|
9a4c603001 | ||
|
|
98a95e99a5 | ||
|
|
d19c982eed | ||
|
|
a963836e75 | ||
|
|
00e9eb4b0b | ||
|
|
262b6281e1 | ||
|
|
f66c70f0be | ||
|
|
717d373668 | ||
|
|
d29fa94aa4 | ||
|
|
01dd0cd0b2 | ||
|
|
126cc78d1a | ||
|
|
5703d465fc | ||
|
|
0c2963cf53 | ||
|
|
b61fd16936 | ||
|
|
3a8ba96cb3 | ||
|
|
3b1d74cf84 | ||
|
|
2e0a6f95a0 | ||
|
|
8470bfb26b | ||
|
|
bea0c1446a | ||
|
|
35ebbdfa12 | ||
|
|
bb25b3f42f | ||
|
|
4a11f9bb20 | ||
|
|
c1a20d0509 | ||
|
|
32c3721b1b | ||
|
|
913cc56adf | ||
|
|
38f37e817b | ||
|
|
4e68f92083 | ||
|
|
3edbda313e | ||
|
|
04cde94c96 | ||
|
|
1faa962cf5 | ||
|
|
94a74520be | ||
|
|
27dd40fd29 | ||
|
|
616ecabb41 | ||
|
|
15cd325034 | ||
|
|
450eb2e7ae | ||
|
|
84622b829c | ||
|
|
a1323ce5e9 | ||
|
|
49629f6dc6 | ||
|
|
231848cb9e | ||
|
|
a51d8bb47d | ||
|
|
e0e4c0ae11 | ||
|
|
d47b3734af | ||
|
|
26d248a4b6 | ||
|
|
6413a6fb4d | ||
|
|
5e36f4fc7f | ||
|
|
b47c278c95 | ||
|
|
a94a720a68 | ||
|
|
3caa0f1438 | ||
|
|
b8ef90cb41 | ||
|
|
9efc648e3d | ||
|
|
baa96f47d8 | ||
|
|
184ce763c1 | ||
|
|
229ee95f91 |
173
README.md
173
README.md
@@ -1,5 +1,5 @@
|
||||
<p align="center" style="text-align: center">
|
||||
<img src="logo_cropped.png" width="40%"><br/>
|
||||
<img src="logo.png" width="55%"><br/>
|
||||
</p>
|
||||
<p align="center">
|
||||
Build desktop applications using Go & Web Technologies.<br/><br/>
|
||||
@@ -18,42 +18,57 @@
|
||||
|
||||
## Internationalization
|
||||
|
||||
English | [简体中文](README.zh-Hans.md)
|
||||
[English](README.md) | [简体中文](README.zh-Hans.md)
|
||||
|
||||
<span id="nav-2"></span>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<details>
|
||||
<summary>Click me to Open/Close the directory listing</summary>
|
||||
|
||||
- [1. Internationalization](#nav-1)
|
||||
- [2. Table of Contents](#nav-2)
|
||||
- [3. Introduction](#nav-3)
|
||||
- [3.1 Official Website](#nav-3-1)
|
||||
- [4. Features](#nav-4)
|
||||
- [5. Sponsors](#nav-5)
|
||||
- [6. Installation](#nav-6)
|
||||
- [6.1 MacOS](#nav-6-1)
|
||||
- [6.2 Linux](#nav-6-2)
|
||||
- [6.2.1 Debian/Ubuntu](#nav-6-2-1)
|
||||
- [6.2.2 Arch Linux / ArchLabs / Ctlos Linux](#nav-6-2-2)
|
||||
- [6.2.3 Centos](#nav-6-2-3)
|
||||
- [6.2.4 Fedora](#nav-6-2-4)
|
||||
- [6.2.5 VoidLinux & VoidLinux-musl](#nav-6-2-5)
|
||||
- [6.2.6 Gentoo](#nav-6-2-6)
|
||||
- [6.3 Windows](#nav-6-3)
|
||||
- [7. Usage](#nav-7)
|
||||
- [7.1 Next Steps](#nav-7-1)
|
||||
- [8. FAQ](#nav-8)
|
||||
- [9. Contributors](#nav-9)
|
||||
- [10. Special Mentions](#nav-10)
|
||||
- [12. Special Thanks](#nav-11)
|
||||
|
||||
</details>
|
||||
|
||||
<span id="nav-3"></span>
|
||||
|
||||
## Introductions
|
||||
|
||||
The traditional method of providing web interfaces to Go programs is via a built-in web server. Wails offers a different
|
||||
approach: it provides the ability to wrap both Go code and a web frontend into a single binary. Tools are provided to
|
||||
make this easy for you by handling project creation, compilation and bundling. All you have to do is get creative!
|
||||
|
||||
<span id="nav-3-1"></span>
|
||||
|
||||
### Official Website
|
||||
|
||||
The official docs can be found at [https://wails.app](https://wails.app).
|
||||
|
||||
Click [here](https://wails.io) if you are interested in trying out v2 Beta for Windows.
|
||||
|
||||
<span id="nav-2"></span>
|
||||
|
||||
## Contents
|
||||
|
||||
- [1. Internationalization](#nav-1)
|
||||
- [2. Contents](#nav-2)
|
||||
- [3. Features](#nav-3)
|
||||
- [4. Sponsors](#nav-4)
|
||||
- [5. Installation](#nav-5)
|
||||
- [5.1 MacOS](#nav-5-1)
|
||||
- [5.2 Linux](#nav-5-2)
|
||||
- [5.2.1 Debian/Ubuntu](#nav-5-2-1)
|
||||
- [5.2.2 Arch Linux / ArchLabs / Ctlos Linux](#nav-5-2-2)
|
||||
- [5.2.3 Centos](#nav-5-2-3)
|
||||
- [5.2.4 Fedora](#nav-5-2-4)
|
||||
- [5.2.5 VoidLinux & VoidLinux-musl](#nav-5-2-5)
|
||||
- [5.2.6 Gentoo](#nav-5-2-6)
|
||||
- [5.3 Windows](#nav-5-3)
|
||||
- [6. Installation](#nav-6)
|
||||
- [7. Next Steps](#nav-7)
|
||||
- [8. FAQ](#nav-8)
|
||||
- [9. Contributors](#nav-9)
|
||||
- [10. Special Mentions](#nav-10)
|
||||
- [11. Special Thanks](#nav-11)
|
||||
|
||||
<span id="nav-3"></span>
|
||||
<span id="nav-4"></span>
|
||||
|
||||
## Features
|
||||
|
||||
@@ -67,7 +82,7 @@ Click [here](https://wails.io) if you are interested in trying out v2 Beta for W
|
||||
- Powerful cli tool
|
||||
- Multiplatform
|
||||
|
||||
<span id="nav-4"></span>
|
||||
<span id="nav-5"></span>
|
||||
|
||||
## Sponsors
|
||||
|
||||
@@ -79,48 +94,49 @@ This project is supported by these kind people / companies:
|
||||
<a href="https://github.com/snider" style="width:100px;">
|
||||
<img src="https://github.com/snider.png?size=100" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/codydbentley" style="width:100px">
|
||||
<img src="https://github.com/codydbentley.png?size=100" width="100"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://github.com/matryer" style="width:100px">
|
||||
<img src="https://github.com/matryer.png" width="100"/>
|
||||
</a>
|
||||
<a href="https://www.jetbrains.com?from=Wails" style="width:100px">
|
||||
<img src="jetbrains-grayscale.png" width="100"/>
|
||||
<img src="/img/jetbrains-grayscale.png" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/tc-hib" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/tc-hib.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/tc-hib" style="width:55px">
|
||||
<img src="https://github.com/tc-hib.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/picatz" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/picatz.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/picatz" style="width:50px">
|
||||
<img src="https://github.com/picatz.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/tylertravisty" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/tylertravisty.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/tylertravisty" style="width:50px">
|
||||
<img src="https://github.com/tylertravisty.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/akhudek" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/akhudek.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/akhudek" style="width:50px">
|
||||
<img src="https://github.com/akhudek.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/trea" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/trea.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/trea" style="width:50px">
|
||||
<img src="https://github.com/trea.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/LanguageAgnostic" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/LanguageAgnostic.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/LanguageAgnostic" style="width:55px">
|
||||
<img src="https://github.com/LanguageAgnostic.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/fcjr" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/fcjr.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/fcjr" style="width:55px">
|
||||
<img src="https://github.com/fcjr.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/nickarellano" style="width:60px;border-radius: 50%">
|
||||
<img src="https://github.com/nickarellano.png?size=60" width="60" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/nickarellano" style="width:60px">
|
||||
<img src="https://github.com/nickarellano.png?size=60" width="60"/>
|
||||
</a>
|
||||
<a href="https://github.com/bglw" style="width:65px;border-radius: 50%">
|
||||
<img src="https://github.com/bglw.png?size=65" width="65" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/bglw" style="width:65px">
|
||||
<img src="https://github.com/bglw.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/jugglingjsons" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/jugglingjsons.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/jugglingjsons" style="width:50px">
|
||||
<img src="https://github.com/jugglingjsons.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/marcus-crane" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/marcus-crane.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
</a>
|
||||
<a href="https://github.com/codydbentley" style="width:65px">
|
||||
<img src="https://github.com/codydbentley.png?size=65" width="65"/>
|
||||
<a href="https://github.com/marcus-crane" style="width:65px">
|
||||
<img src="https://github.com/marcus-crane.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/bbergshaven" style="width:45px">
|
||||
<img src="https://github.com/bbergshaven.png?size=45" width="45"/>
|
||||
@@ -128,9 +144,20 @@ This project is supported by these kind people / companies:
|
||||
<a href="https://github.com/Gilgames000" style="width:45px">
|
||||
<img src="https://github.com/Gilgames000.png?size=45" width="45"/>
|
||||
</a>
|
||||
<a href="https://github.com/ilgityildirim" style="width:50px">
|
||||
<img src="https://github.com/ilgityildirim.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/ondoki" style="width:65px">
|
||||
<img src="https://github.com/ondoki.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/questrail" style="width:50px">
|
||||
<img src="https://github.com/questrail.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/DonTomato" style="width:45px">
|
||||
<img src="https://github.com/DonTomato.png?size=45" width="45"/>
|
||||
</a>
|
||||
|
||||
|
||||
<span id="nav-5"></span>
|
||||
<span id="nav-6"></span>
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -140,7 +167,7 @@ an installation of Go. The basic requirements are:
|
||||
- Go 1.16
|
||||
- npm
|
||||
|
||||
<span id="nav-5-1"></span>
|
||||
<span id="nav-6-1"></span>
|
||||
|
||||
### MacOS
|
||||
|
||||
@@ -148,11 +175,11 @@ Make sure you have the xcode command line tools installed. This can be done by r
|
||||
|
||||
`xcode-select --install`
|
||||
|
||||
<span id="nav-5-2"></span>
|
||||
<span id="nav-6-2"></span>
|
||||
|
||||
### Linux
|
||||
|
||||
<span id="nav-5-2-1"></span>
|
||||
<span id="nav-6-2-1"></span>
|
||||
|
||||
#### Debian/Ubuntu
|
||||
|
||||
@@ -164,7 +191,7 @@ _Ubuntu: 16.04, 18.04, 19.04_
|
||||
|
||||
_Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_, Pop!\_OS
|
||||
|
||||
<span id="nav-5-2-2"></span>
|
||||
<span id="nav-6-2-2"></span>
|
||||
|
||||
#### Arch Linux / ArchLabs / Ctlos Linux
|
||||
|
||||
@@ -172,7 +199,7 @@ _Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, K
|
||||
|
||||
_Also succesfully test on: Manjaro & ArcoLinux_
|
||||
|
||||
<span id="nav-5-2-3"></span>
|
||||
<span id="nav-6-2-3"></span>
|
||||
|
||||
#### Centos
|
||||
|
||||
@@ -180,7 +207,7 @@ _Also succesfully test on: Manjaro & ArcoLinux_
|
||||
|
||||
_CentOS 6, 7_
|
||||
|
||||
<span id="nav-5-2-4"></span>
|
||||
<span id="nav-6-2-4"></span>
|
||||
|
||||
#### Fedora
|
||||
|
||||
@@ -188,19 +215,19 @@ _CentOS 6, 7_
|
||||
|
||||
_Fedora 29, 30_
|
||||
|
||||
<span id="nav-5-2-5"></span>
|
||||
<span id="nav-6-2-5"></span>
|
||||
|
||||
#### VoidLinux & VoidLinux-musl
|
||||
|
||||
`xbps-install gtk+3-devel webkit2gtk-devel`
|
||||
|
||||
<span id="nav-5-2-6"></span>
|
||||
<span id="nav-6-2-6"></span>
|
||||
|
||||
#### Gentoo
|
||||
|
||||
`sudo emerge gtk+:3 webkit-gtk`
|
||||
|
||||
<span id="nav-5-3"></span>
|
||||
<span id="nav-6-3"></span>
|
||||
|
||||
### Windows
|
||||
|
||||
@@ -208,21 +235,21 @@ Windows requires gcc and related tooling. The recommended download is
|
||||
from [http://tdm-gcc.tdragon.net/download](http://tdm-gcc.tdragon.net/download). Once this is installed, you are good to
|
||||
go.
|
||||
|
||||
<span id="nav-6"></span>
|
||||
<span id="nav-7"></span>
|
||||
|
||||
## Installation
|
||||
## Usage
|
||||
|
||||
**Ensure Go modules are enabled: GO111MODULE=on and go/bin is in your PATH variable.**
|
||||
|
||||
Installation is as simple as running the following command:
|
||||
|
||||
<pre style='color:white'>
|
||||
```
|
||||
go get -u github.com/wailsapp/wails/cmd/wails
|
||||
</pre>
|
||||
```
|
||||
|
||||
<span id="nav-7"></span>
|
||||
<span id="nav-7-1"></span>
|
||||
|
||||
## Next Steps
|
||||
### Next Steps
|
||||
|
||||
It is recommended at this stage to read the comprehensive documentation at [https://wails.app](https://wails.app).
|
||||
|
||||
@@ -333,7 +360,7 @@ This project was mainly coded to the following albums:
|
||||
|
||||
<p align="center" style="text-align: center">
|
||||
<a href="https://pace.dev"><img src="pace.jpeg"/></a><br/>
|
||||
A <i>huge<i/> thanks to <a href="https://pace.dev">Pace</a> for sponsoring the project and helping the efforts to get Wails ported to Apple Silicon!<br/><br/>
|
||||
A <i>huge</i> thanks to <a href="https://pace.dev">Pace</a> for sponsoring the project and helping the efforts to get Wails ported to Apple Silicon!<br/><br/>
|
||||
If you are looking for a Project Management tool that's powerful but quick and easy to use, check them out!<br/><br/>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p align="center" style="text-align: center">
|
||||
<img src="logo_cropped.png" width="40%"><br/>
|
||||
<img src="logo.png" width="55%"><br/>
|
||||
</p>
|
||||
<p align="center">
|
||||
使用 Go 和 Web 技术构建桌面应用程序。<br/><br/>
|
||||
@@ -18,44 +18,62 @@
|
||||
|
||||
## 国际化
|
||||
|
||||
[English](README.md) | 简体中文
|
||||
|
||||
向 Go 程序提供 Web 接口的传统方法是通过内置 Web 服务器。Wails 提供了一种不同的方法:它提供了将 Go 代码和 Web
|
||||
前端都包装成单个二进制文件的能力。通过提供工具,可以很轻松的完成项目的创建、编译和打包。你所要做的就是发挥创意!
|
||||
|
||||
官方文档可以在 [https://wails.app](https://wails.app) 中找到。
|
||||
|
||||
国内镜像站点 [https://wails.top](https://wails.top)。
|
||||
[English](README.md) | [简体中文](README.zh-Hans.md)
|
||||
|
||||
<span id="nav-2"></span>
|
||||
|
||||
## 内容目录
|
||||
|
||||
<details>
|
||||
<summary>点我 打开/关闭 目录列表</summary>
|
||||
|
||||
- [1. 国际化](#nav-1)
|
||||
- [2. 内容目录](#nav-2)
|
||||
- [3. 特征](#nav-3)
|
||||
- [4. 赞助商](#nav-4)
|
||||
- [5. 安装](#nav-5)
|
||||
- [5.1 MacOS](#nav-5-1)
|
||||
- [5.2 Linux](#nav-5-2)
|
||||
- [5.2.1 Debian/Ubuntu](#nav-5-2-1)
|
||||
- [5.2.2 Arch Linux / ArchLabs / Ctlos Linux](#nav-5-2-2)
|
||||
- [5.2.3 Centos](#nav-5-2-3)
|
||||
- [5.2.4 Fedora](#nav-5-2-4)
|
||||
- [5.2.5 VoidLinux & VoidLinux-musl](#nav-5-2-5)
|
||||
- [5.2.6 Gentoo](#nav-5-2-6)
|
||||
- [5.3 Windows](#nav-5-3)
|
||||
- [3. 项目介绍](#nav-3)
|
||||
- [3.1 官方网站](#nav-3-1)
|
||||
- [4. 功能](#nav-4)
|
||||
- [5. 赞助商](#nav-5)
|
||||
- [6. 安装](#nav-6)
|
||||
- [7. 下一步](#nav-7)
|
||||
- [6.1 MacOS](#nav-6-1)
|
||||
- [6.2 Linux](#nav-6-2)
|
||||
- [6.2.1 Debian/Ubuntu](#nav-6-2-1)
|
||||
- [6.2.2 Arch Linux / ArchLabs / Ctlos Linux](#nav-6-2-2)
|
||||
- [6.2.3 Centos](#nav-6-2-3)
|
||||
- [6.2.4 Fedora](#nav-6-2-4)
|
||||
- [6.2.5 VoidLinux & VoidLinux-musl](#nav-6-2-5)
|
||||
- [6.2.6 Gentoo](#nav-6-2-6)
|
||||
- [6.3 Windows](#nav-6-3)
|
||||
- [7. 使用方法](#nav-7)
|
||||
- [7.1 下一步](#nav-7-1)
|
||||
- [8. 常见问题](#nav-8)
|
||||
- [9. 贡献者](#nav-9)
|
||||
- [10. 特别提及](#nav-10)
|
||||
- [11. 许可协议](#nav-11)
|
||||
- [12. 特别感谢](#nav-12)
|
||||
- [12. 特别感谢](#nav-11)
|
||||
|
||||
</details>
|
||||
|
||||
<span id="nav-3"></span>
|
||||
|
||||
## 特征
|
||||
## 项目介绍
|
||||
|
||||
为 Go 程序提供 Web 界面的传统方法是通过内置 Web 服务器。Wails 提供了一种不同的方法:它提供了将 Go 代码和 Web
|
||||
前端一起打包成单个二进制文件的能力。通过提供的工具,可以很轻松的完成项目的创建、编译和打包。你所要做的就是发挥想象力!
|
||||
|
||||
<span id="nav-3-1"></span>
|
||||
|
||||
### 官方网站
|
||||
|
||||
官方文档可以在 [https://wails.app](https://wails.app) 中找到。
|
||||
|
||||
如果您对适用于 Windows 的 v2 测试版感兴趣,可以点击[此处](https://wails.io)查看。
|
||||
|
||||
镜像网站:
|
||||
|
||||
- [中国大陆镜像站点 - https://wails.top](https://wails.top)
|
||||
|
||||
<span id="nav-4"></span>
|
||||
|
||||
## 功能
|
||||
|
||||
- 后端使用标准 Go
|
||||
- 使用任意前端技术构建 UI 界面
|
||||
@@ -67,56 +85,82 @@
|
||||
- 强大的命令行工具
|
||||
- 跨多个平台
|
||||
|
||||
<span id="nav-4"></span>
|
||||
<span id="nav-5"></span>
|
||||
|
||||
## 赞助商
|
||||
|
||||
这个项目由以下这些人或者公司支持:
|
||||
|
||||
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
|
||||
<img src="sponsors/bronze%20sponsor.png" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/snider" style="width:100px;">
|
||||
<img src="https://github.com/snider.png?size=100" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/codydbentley" style="width:100px">
|
||||
<img src="https://github.com/codydbentley.png?size=100" width="100"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://github.com/matryer" style="width:100px">
|
||||
<img src="https://github.com/matryer.png" width="100"/>
|
||||
</a>
|
||||
<a href="https://www.jetbrains.com?from=Wails" style="width:100px">
|
||||
<img src="jetbrains-grayscale.png" width="100"/>
|
||||
<img src="/img/jetbrains-grayscale.png" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/tc-hib" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/tc-hib.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/tc-hib" style="width:55px">
|
||||
<img src="https://github.com/tc-hib.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/picatz" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/picatz.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/picatz" style="width:50px">
|
||||
<img src="https://github.com/picatz.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/tylertravisty" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/tylertravisty.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/tylertravisty" style="width:50px">
|
||||
<img src="https://github.com/tylertravisty.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/akhudek" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/akhudek.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/akhudek" style="width:50px">
|
||||
<img src="https://github.com/akhudek.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/trea" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/trea.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/trea" style="width:50px">
|
||||
<img src="https://github.com/trea.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/LanguageAgnostic" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/LanguageAgnostic.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/LanguageAgnostic" style="width:55px">
|
||||
<img src="https://github.com/LanguageAgnostic.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/snider" style="width:60px;border-radius: 50%">
|
||||
<img src="https://github.com/snider.png?size=60" width="60" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/fcjr" style="width:55px">
|
||||
<img src="https://github.com/fcjr.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/fcjr" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/fcjr.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/nickarellano" style="width:60px">
|
||||
<img src="https://github.com/nickarellano.png?size=60" width="60"/>
|
||||
</a>
|
||||
<a href="https://github.com/nickarellano" style="width:60px;border-radius: 50%">
|
||||
<img src="https://github.com/nickarellano.png?size=60" width="60" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/bglw" style="width:65px">
|
||||
<img src="https://github.com/bglw.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/bglw" style="width:65px;border-radius: 50%">
|
||||
<img src="https://github.com/bglw.png?size=65" width="65" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/jugglingjsons" style="width:50px">
|
||||
<img src="https://github.com/jugglingjsons.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/jugglingjsons" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/jugglingjsons.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/marcus-crane" style="width:65px">
|
||||
<img src="https://github.com/marcus-crane.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/marcus-crane" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/marcus-crane.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/bbergshaven" style="width:45px">
|
||||
<img src="https://github.com/bbergshaven.png?size=45" width="45"/>
|
||||
</a>
|
||||
<a href="https://github.com/Gilgames000" style="width:45px">
|
||||
<img src="https://github.com/Gilgames000.png?size=45" width="45"/>
|
||||
</a>
|
||||
<a href="https://github.com/ilgityildirim" style="width:50px">
|
||||
<img src="https://github.com/ilgityildirim.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/ondoki" style="width:65px">
|
||||
<img src="https://github.com/ondoki.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/questrail" style="width:50px">
|
||||
<img src="https://github.com/questrail.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/DonTomato" style="width:45px">
|
||||
<img src="https://github.com/DonTomato.png?size=45" width="45"/>
|
||||
</a>
|
||||
|
||||
<span id="nav-5"></span>
|
||||
<span id="nav-6"></span>
|
||||
|
||||
## 安装
|
||||
|
||||
@@ -125,7 +169,7 @@ Wails 使用 cgo 与原生渲染引擎结合,因此需要依赖一些平台的
|
||||
- Go 1.16
|
||||
- npm
|
||||
|
||||
<span id="nav-5-1"></span>
|
||||
<span id="nav-6-1"></span>
|
||||
|
||||
### MacOS
|
||||
|
||||
@@ -133,11 +177,11 @@ Wails 使用 cgo 与原生渲染引擎结合,因此需要依赖一些平台的
|
||||
|
||||
`xcode-select --install`
|
||||
|
||||
<span id="nav-5-2"></span>
|
||||
<span id="nav-6-2"></span>
|
||||
|
||||
### Linux
|
||||
|
||||
<span id="nav-5-2-1"></span>
|
||||
<span id="nav-6-2-1"></span>
|
||||
|
||||
#### Debian/Ubuntu
|
||||
|
||||
@@ -149,7 +193,7 @@ _Ubuntu: 16.04, 18.04, 19.04_
|
||||
|
||||
_也成功测试了: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_, Pop!\_OS
|
||||
|
||||
<span id="nav-5-2-2"></span>
|
||||
<span id="nav-6-2-2"></span>
|
||||
|
||||
#### Arch Linux / ArchLabs / Ctlos Linux
|
||||
|
||||
@@ -157,7 +201,7 @@ _也成功测试了: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neo
|
||||
|
||||
_也成功测试了: Manjaro & ArcoLinux_
|
||||
|
||||
<span id="nav-5-2-3"></span>
|
||||
<span id="nav-6-2-3"></span>
|
||||
|
||||
#### Centos
|
||||
|
||||
@@ -165,7 +209,7 @@ _也成功测试了: Manjaro & ArcoLinux_
|
||||
|
||||
_CentOS 6, 7_
|
||||
|
||||
<span id="nav-5-2-4"></span>
|
||||
<span id="nav-6-2-4"></span>
|
||||
|
||||
#### Fedora
|
||||
|
||||
@@ -173,39 +217,39 @@ _CentOS 6, 7_
|
||||
|
||||
_Fedora 29, 30_
|
||||
|
||||
<span id="nav-5-2-5"></span>
|
||||
<span id="nav-6-2-5"></span>
|
||||
|
||||
#### VoidLinux & VoidLinux-musl
|
||||
|
||||
`xbps-install gtk+3-devel webkit2gtk-devel`
|
||||
|
||||
<span id="nav-5-2-6"></span>
|
||||
<span id="nav-6-2-6"></span>
|
||||
|
||||
#### Gentoo
|
||||
|
||||
`sudo emerge gtk+:3 webkit-gtk`
|
||||
|
||||
<span id="nav-5-3"></span>
|
||||
<span id="nav-6-3"></span>
|
||||
|
||||
### Windows
|
||||
|
||||
Windows 需要 GCC 和相关工具。 建议从 [http://tdm-gcc.tdragon.net/download](http://tdm-gcc.tdragon.net/download) 下载, 安装完成,您就可以开始了。
|
||||
|
||||
<span id="nav-6"></span>
|
||||
<span id="nav-7"></span>
|
||||
|
||||
## 安装
|
||||
## 使用方法
|
||||
|
||||
**确保 Go modules 是开启的:GO111MODULE=on 并且 go/bin 在您的 PATH 变量中。**
|
||||
|
||||
安装很简单,运行以下命令:
|
||||
|
||||
<pre style='color:white'>
|
||||
```
|
||||
go get -u github.com/wailsapp/wails/cmd/wails
|
||||
</pre>
|
||||
```
|
||||
|
||||
<span id="nav-7"></span>
|
||||
<span id="nav-7-1"></span>
|
||||
|
||||
## 下一步
|
||||
### 下一步
|
||||
|
||||
建议在此时阅读 [https://wails.app](https://wails.app) 上面的文档.
|
||||
|
||||
@@ -217,14 +261,14 @@ go get -u github.com/wailsapp/wails/cmd/wails
|
||||
|
||||
取决于您的要求。它旨在使 Go 程序员可以轻松制作轻量级桌面应用程序或在其现有应用程序中添加前端。尽管 Wails 当前不提供对诸如菜单之类的原生元素的钩子,但将来可能会改变。
|
||||
|
||||
- 这个项目针对的是谁?
|
||||
- 这个项目针对的是哪些人?
|
||||
|
||||
希望将 HTML / JS / CSS 前端与其应用程序捆绑在一起的程序员,而不是借助创建服务并打开浏览器进行查看的方式。
|
||||
|
||||
- 名字怎么来的?
|
||||
|
||||
当我看到 WebView 时,我想"我真正想要的是围绕构建 WebView 应用程序工作,有点像 Rails 对于 Ruby"。因此,最初它是一个文字游戏(Webview on
|
||||
Rails)。碰巧也是我来自的 [国家](https://en.wikipedia.org/wiki/Wales) 的英文名字的同音。所以就是他了。
|
||||
Rails)。碰巧也是我来自的 [国家](https://en.wikipedia.org/wiki/Wales) 的英文名字的同音。所以就是它了。
|
||||
|
||||
<span id="nav-9"></span>
|
||||
|
||||
@@ -275,6 +319,7 @@ go get -u github.com/wailsapp/wails/cmd/wails
|
||||
<a href="https://github.com/Igogrek"><img src="https://github.com/Igogrek.png?size=40" width="40"/></a></a>
|
||||
<a href="https://github.com/aschey"><img src="https://github.com/aschey.png?size=40" width="40"/></a></a>
|
||||
<a href="https://github.com/akhudek"><img src="https://github.com/akhudek.png?size=40" width="40"/></a></a>
|
||||
<a href="https://github.com/s12chung"><img src="https://github.com/s12chung.png?size=40" width="40"/></a></a>
|
||||
|
||||
<span id="nav-10"></span>
|
||||
|
||||
@@ -284,9 +329,9 @@ go get -u github.com/wailsapp/wails/cmd/wails
|
||||
|
||||
- [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - 他的支持和反馈是巨大的。
|
||||
- [Serge Zaitsev](https://github.com/zserge) - Wails 窗口所使用的 [Webview](https://github.com/zserge/webview) 的作者。
|
||||
- [Byron](https://github.com/bh90210) - 有时,Byron 单枪匹马地保持这个项目活着。没有他令人难以置信的投入,我们永远不会得到 v1 。
|
||||
- [Byron](https://github.com/bh90210) - 有时,Byron 一个人保持这个项目活跃着。没有他令人难以置信的投入,我们永远不会得到 v1 。
|
||||
|
||||
This project was mainly coded to the following albums:
|
||||
编写项目代码时伴随着以下专辑:
|
||||
|
||||
- [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
|
||||
- [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
|
||||
@@ -304,17 +349,11 @@ This project was mainly coded to the following albums:
|
||||
|
||||
<span id="nav-11"></span>
|
||||
|
||||
## 许可协议
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
|
||||
|
||||
<span id="nav-12"></span>
|
||||
|
||||
## 特别感谢
|
||||
|
||||
<p align="center" style="text-align: center">
|
||||
<a href="https://pace.dev"><img src="pace.jpeg"/></a><br/>
|
||||
<i>非常<i/>感谢<a href="https://pace.dev">Pace</a>对项目的赞助,并帮助将 Wails 移植到 Apple Silicon !<br/><br/>
|
||||
<i>非常</i> 感谢<a href="https://pace.dev">Pace</a>对项目的赞助,并帮助将 Wails 移植到 Apple Silicon !<br/><br/>
|
||||
如果您正在寻找一个强大并且快速和易于使用的项目管理工具,可以看看他们!<br/><br/>
|
||||
</p>
|
||||
|
||||
|
||||
21
cmd/linux.go
21
cmd/linux.go
@@ -71,6 +71,11 @@ const (
|
||||
Crux
|
||||
// RHEL distribution
|
||||
RHEL
|
||||
// NixOS distribution
|
||||
NixOS
|
||||
// Artix linux distribution
|
||||
ArtixLinux
|
||||
|
||||
)
|
||||
|
||||
// DistroInfo contains all the information relating to a linux distribution
|
||||
@@ -183,6 +188,10 @@ func parseOsRelease(osRelease string) *DistroInfo {
|
||||
result.Distribution = EndeavourOS
|
||||
case "crux":
|
||||
result.Distribution = Crux
|
||||
case "nixos":
|
||||
result.Distribution = NixOS
|
||||
case "artix":
|
||||
result.Distribution = ArtixLinux
|
||||
default:
|
||||
result.Distribution = Unknown
|
||||
}
|
||||
@@ -274,6 +283,18 @@ func PrtGetInstalled(packageName string) (bool, error) {
|
||||
return exitCode == 0, nil
|
||||
}
|
||||
|
||||
// NixEnvInstalled uses nix-env to see if a package is installed
|
||||
func NixEnvInstalled(packageName string) (bool, error) {
|
||||
program := NewProgramHelper()
|
||||
nixEnv := program.FindProgram("nix-env")
|
||||
if nixEnv == nil {
|
||||
return false, fmt.Errorf("cannot check dependencies: nix-env not found")
|
||||
}
|
||||
packageName = strings.ReplaceAll(packageName, "+", `\+`)
|
||||
_, _, exitCode, _ := nixEnv.Run("-q", packageName)
|
||||
return exitCode == 0, nil
|
||||
}
|
||||
|
||||
// RequestSupportForDistribution promts the user to submit a request to support their
|
||||
// currently unsupported distribution
|
||||
func RequestSupportForDistribution(distroInfo *DistroInfo) error {
|
||||
|
||||
@@ -213,6 +213,15 @@ distributions:
|
||||
gccversioncommand: *gccdumpversion
|
||||
programs: *archdefaultprograms
|
||||
libraries: *archdefaultlibraries
|
||||
artix:
|
||||
id: artix
|
||||
releases:
|
||||
default:
|
||||
version: default
|
||||
name: Artix Linux
|
||||
gccversioncommand: *gccdumpversion
|
||||
programs: *archdefaultprograms
|
||||
libraries: *archdefaultlibraries
|
||||
ctlos:
|
||||
id: ctlos
|
||||
releases:
|
||||
@@ -345,3 +354,22 @@ distributions:
|
||||
help: Please install with `sudo prt-get depinst gtk3` and try again
|
||||
- name: webkitgtk
|
||||
help: Please install with `sudo prt-get depinst webkitgtk` and try again
|
||||
nixos:
|
||||
id: nixos
|
||||
releases:
|
||||
default:
|
||||
version: default
|
||||
name: NixOS
|
||||
gccversioncommand: *gccdumpversion
|
||||
programs:
|
||||
- name: gcc
|
||||
help: Please install with `nix-env -iA nixos.gcc`
|
||||
- name: pkg-config
|
||||
help: Please install with `nix-env -iA nixos.pkg-config`
|
||||
- name: npm
|
||||
help: Please install with `nix-env -iA nixos.nodejs`
|
||||
libraries:
|
||||
- name: gtk+3
|
||||
help: Please install with `nix-env -iA nixos.gtk3`
|
||||
- name: webkitgtk
|
||||
help: Please install with `nix-env -iA nixos.nodePackages.webkitgtk`
|
||||
|
||||
@@ -281,7 +281,7 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
||||
switch distroInfo.Distribution {
|
||||
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian, PopOS:
|
||||
libraryChecker = DpkgInstalled
|
||||
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM, EndeavourOS:
|
||||
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM, EndeavourOS, ArtixLinux:
|
||||
libraryChecker = PacmanInstalled
|
||||
case CentOS, Fedora, Tumbleweed, Leap, RHEL:
|
||||
libraryChecker = RpmInstalled
|
||||
@@ -293,6 +293,8 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
||||
libraryChecker = EOpkgInstalled
|
||||
case Crux:
|
||||
libraryChecker = PrtGetInstalled
|
||||
case NixOS:
|
||||
libraryChecker = NixEnvInstalled
|
||||
default:
|
||||
return false, RequestSupportForDistribution(distroInfo)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
|
||||
// Version - Wails version
|
||||
const Version = "v1.16.5"
|
||||
const Version = "v1.16.8"
|
||||
|
||||
@@ -15,6 +15,7 @@ func init() {
|
||||
projectOptions := projectHelper.NewProjectOptions()
|
||||
commandDescription := `Generates a new Wails project using the given flags.
|
||||
Any flags that are required and not given will be prompted for.`
|
||||
build := false
|
||||
|
||||
initCommand := app.Command("init", "Initialises a new Wails project").
|
||||
LongDescription(commandDescription).
|
||||
@@ -23,7 +24,8 @@ Any flags that are required and not given will be prompted for.`
|
||||
StringFlag("template", "Template name", &projectOptions.Template).
|
||||
StringFlag("name", "Project name", &projectOptions.Name).
|
||||
StringFlag("description", "Project description", &projectOptions.Description).
|
||||
StringFlag("output", "Output binary name", &projectOptions.BinaryName)
|
||||
StringFlag("output", "Output binary name", &projectOptions.BinaryName).
|
||||
BoolFlag("build", "Build project after generating", &build)
|
||||
|
||||
initCommand.Action(func() error {
|
||||
|
||||
@@ -64,6 +66,10 @@ Any flags that are required and not given will be prompted for.`
|
||||
return err
|
||||
}
|
||||
genSpinner.Success()
|
||||
if !build {
|
||||
logger.Yellow("Project '%s' initialised. Run `wails build` to build it.", projectOptions.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build the project
|
||||
cwd, _ := os.Getwd()
|
||||
|
||||
5
go.mod
5
go.mod
@@ -16,12 +16,13 @@ require (
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
|
||||
github.com/pkg/errors v0.8.1 // indirect
|
||||
github.com/sirupsen/logrus v1.4.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/objx v0.1.1 // indirect
|
||||
github.com/stretchr/testify v1.3.0 // indirect
|
||||
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba
|
||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359
|
||||
golang.org/x/text v0.3.0
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4
|
||||
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22
|
||||
|
||||
5
go.sum
5
go.sum
@@ -54,6 +54,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@@ -76,9 +78,12 @@ golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc=
|
||||
|
||||
@@ -54,7 +54,7 @@ extern "C"
|
||||
int ready;
|
||||
int js_busy;
|
||||
int should_exit;
|
||||
|
||||
|
||||
int min_width;
|
||||
int min_height;
|
||||
int max_width;
|
||||
@@ -179,7 +179,7 @@ struct webview_priv
|
||||
WEBVIEW_API int webview_inject_css(struct webview *w, const char *css);
|
||||
WEBVIEW_API void webview_set_title(struct webview *w, const char *title);
|
||||
WEBVIEW_API void webview_focus(struct webview *w);
|
||||
WEBVIEW_API void webview_minsize(struct webview *w, int width, int height);
|
||||
WEBVIEW_API void webview_minsize(struct webview *w, int width, int height);
|
||||
WEBVIEW_API void webview_maxsize(struct webview *w, int width, int height);
|
||||
WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen);
|
||||
WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g,
|
||||
@@ -342,12 +342,12 @@ struct webview_priv
|
||||
w->priv.should_exit = 0;
|
||||
w->priv.queue = g_async_queue_new();
|
||||
w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
|
||||
w->priv.min_width = -1;
|
||||
w->priv.min_height = -1;
|
||||
w->priv.max_width = -1;
|
||||
w->priv.max_height = -1;
|
||||
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(w->priv.window), w->title);
|
||||
|
||||
if (w->resizable)
|
||||
@@ -421,13 +421,13 @@ struct webview_priv
|
||||
}
|
||||
|
||||
WEBVIEW_API void webview_minsize(struct webview *w, int width, int height) {
|
||||
|
||||
|
||||
w->priv.min_width = width;
|
||||
w->priv.min_height = height;
|
||||
|
||||
GdkGeometry hints;
|
||||
GdkWindowHints usedHints = (GdkWindowHints) GDK_HINT_MIN_SIZE;
|
||||
|
||||
|
||||
hints.min_width = w->priv.min_width;
|
||||
hints.min_height = w->priv.min_height;
|
||||
if (w->priv.max_width != -1) {
|
||||
@@ -435,18 +435,18 @@ struct webview_priv
|
||||
hints.max_height = w->priv.max_height;
|
||||
usedHints = (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
|
||||
}
|
||||
|
||||
|
||||
gtk_window_set_geometry_hints(GTK_WINDOW(w->priv.window), w->priv.window, &hints, usedHints);
|
||||
}
|
||||
|
||||
WEBVIEW_API void webview_maxsize(struct webview *w, int width, int height) {
|
||||
|
||||
|
||||
w->priv.max_width = width;
|
||||
w->priv.max_height = height;
|
||||
|
||||
|
||||
GdkGeometry hints;
|
||||
GdkWindowHints usedHints = (GdkWindowHints) GDK_HINT_MAX_SIZE;
|
||||
|
||||
|
||||
if (w->priv.min_width != -1) {
|
||||
hints.min_width = w->priv.min_width;
|
||||
hints.min_height = w->priv.min_height;
|
||||
@@ -454,7 +454,7 @@ struct webview_priv
|
||||
}
|
||||
hints.max_width = w->priv.max_width;
|
||||
hints.max_height = w->priv.max_height;
|
||||
|
||||
|
||||
gtk_window_set_geometry_hints(GTK_WINDOW(w->priv.window), w->priv.window, &hints, usedHints);
|
||||
}
|
||||
|
||||
@@ -514,7 +514,6 @@ struct webview_priv
|
||||
}
|
||||
gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE);
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE);
|
||||
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE);
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dlg), TRUE);
|
||||
gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dlg), TRUE);
|
||||
gint response = gtk_dialog_run(GTK_DIALOG(dlg));
|
||||
@@ -1398,12 +1397,12 @@ struct webview_priv
|
||||
case WM_GETMINMAXINFO:
|
||||
{
|
||||
if (w != NULL) {
|
||||
// get pixel density
|
||||
// get pixel density
|
||||
HDC hDC = GetDC(NULL);
|
||||
double DPIScaleX = GetDeviceCaps(hDC, 88)/96.0;
|
||||
double DPIScaleY = GetDeviceCaps(hDC, 90)/96.0;
|
||||
ReleaseDC(NULL, hDC);
|
||||
|
||||
|
||||
RECT rcClient, rcWind;
|
||||
POINT ptDiff;
|
||||
GetClientRect(hwnd, &rcClient);
|
||||
@@ -1413,7 +1412,7 @@ struct webview_priv
|
||||
int heightExtra = (rcWind.bottom - rcWind.top) - rcClient.bottom;
|
||||
|
||||
LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
|
||||
|
||||
|
||||
if (w->priv.min_width != -1) {
|
||||
lpMMI->ptMinTrackSize.x = w->priv.min_width * DPIScaleX + widthExtra;
|
||||
lpMMI->ptMinTrackSize.y = w->priv.min_height * DPIScaleY + heightExtra;
|
||||
@@ -1423,7 +1422,7 @@ struct webview_priv
|
||||
lpMMI->ptMaxTrackSize.y = w->priv.max_height * DPIScaleY + heightExtra;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
case WM_DESTROY:
|
||||
@@ -2328,14 +2327,14 @@ struct webview_priv
|
||||
{
|
||||
[w->priv.window makeKeyWindow];
|
||||
}
|
||||
|
||||
|
||||
WEBVIEW_API void webview_minsize(struct webview *w, int width, int height) {
|
||||
NSSize size;
|
||||
size.width = width;
|
||||
size.height = height;
|
||||
[w->priv.window setMinSize:size];
|
||||
}
|
||||
|
||||
|
||||
WEBVIEW_API void webview_maxsize(struct webview *w, int width, int height) {
|
||||
NSSize size;
|
||||
size.width = width;
|
||||
@@ -2346,7 +2345,7 @@ struct webview_priv
|
||||
[button performSelectorOnMainThread:@selector(setEnabled:) withObject:NO
|
||||
waitUntilDone:NO];
|
||||
}
|
||||
|
||||
|
||||
WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen)
|
||||
{
|
||||
int b = ((([w->priv.window styleMask] & NSWindowStyleMaskFullScreen) ==
|
||||
@@ -2503,4 +2502,4 @@ struct webview_priv
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WEBVIEW_H */
|
||||
#endif /* WEBVIEW_H */
|
||||
|
||||
@@ -5,11 +5,15 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal"
|
||||
"github.com/wailsapp/wails/v2/internal/gomod"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/system"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
@@ -42,7 +46,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
|
||||
// Setup Platform flag
|
||||
platform := runtime.GOOS
|
||||
//command.StringFlag("platform", "Platform to target", &platform)
|
||||
command.StringFlag("platform", "Platform to target", &platform)
|
||||
|
||||
// Verbosity
|
||||
verbosity := 1
|
||||
@@ -95,14 +99,14 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
"darwin/amd64",
|
||||
"darwin/arm64",
|
||||
"darwin/universal",
|
||||
"linux",
|
||||
//"linux",
|
||||
//"linux/amd64",
|
||||
//"linux/arm-7",
|
||||
"windows",
|
||||
"windows/amd64",
|
||||
})
|
||||
if !validPlatformArch.Contains(platform) {
|
||||
return fmt.Errorf("platform %s is not supported", platform)
|
||||
return fmt.Errorf("platform %s is not supported. Platforms supported: %s", platform, validPlatformArch.Join(","))
|
||||
}
|
||||
|
||||
if compress && platform == "darwin/universal" {
|
||||
@@ -117,12 +121,20 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
}
|
||||
|
||||
// Tags
|
||||
experimental := false
|
||||
userTags := []string{}
|
||||
for _, tag := range strings.Split(tags, " ") {
|
||||
thisTag := strings.TrimSpace(tag)
|
||||
if thisTag != "" {
|
||||
userTags = append(userTags, thisTag)
|
||||
}
|
||||
if thisTag == "exp" {
|
||||
experimental = true
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" && !experimental {
|
||||
return fmt.Errorf("Linux version coming soon!")
|
||||
}
|
||||
|
||||
// Webview2 installer strategy (download by default)
|
||||
@@ -197,6 +209,11 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
fmt.Fprintf(w, "\n")
|
||||
w.Flush()
|
||||
|
||||
err = checkGoModVersion(logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return doBuild(buildOptions)
|
||||
})
|
||||
}
|
||||
@@ -220,3 +237,29 @@ func doBuild(buildOptions *build.Options) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkGoModVersion(logger *clilogger.CLILogger) error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gomodFilename := filepath.Join(cwd, "go.mod")
|
||||
gomodData, err := os.ReadFile(gomodFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outOfSync, err := gomod.GoModOutOfSync(gomodData, internal.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !outOfSync {
|
||||
return nil
|
||||
}
|
||||
gomodversion, err := gomod.GetWailsVersionFromModFile(gomodData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Println("Warning: go.mod is using Wails '%s' but the CLI is '%s'. Consider updating it.\n", gomodversion.String(), internal.Version)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ package dev
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal"
|
||||
"github.com/wailsapp/wails/v2/internal/gomod"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -18,7 +16,10 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/google/shlex"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal"
|
||||
"github.com/wailsapp/wails/v2/internal/gomod"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/project"
|
||||
|
||||
"github.com/pkg/browser"
|
||||
@@ -71,6 +72,7 @@ type devFlags struct {
|
||||
forceBuild bool
|
||||
debounceMS int
|
||||
devServerURL string
|
||||
appargs string
|
||||
}
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
@@ -92,12 +94,29 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
command.BoolFlag("f", "Force build application", &flags.forceBuild)
|
||||
command.IntFlag("debounce", "The amount of time to wait to trigger a reload on change", &flags.debounceMS)
|
||||
command.StringFlag("devserverurl", "The url of the dev server to use", &flags.devServerURL)
|
||||
command.StringFlag("appargs", "arguments to pass to the underlying app (quoted and space searated)", &flags.appargs)
|
||||
|
||||
command.Action(func() error {
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
app.PrintBanner()
|
||||
|
||||
experimental := false
|
||||
userTags := []string{}
|
||||
for _, tag := range strings.Split(flags.tags, " ") {
|
||||
thisTag := strings.TrimSpace(tag)
|
||||
if thisTag != "" {
|
||||
userTags = append(userTags, thisTag)
|
||||
}
|
||||
if thisTag == "exp" {
|
||||
experimental = true
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" && !experimental {
|
||||
return fmt.Errorf("Linux version coming soon!")
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -255,11 +274,11 @@ func defaultDevFlags() devFlags {
|
||||
|
||||
// generateBuildOptions creates a build.Options using the flags
|
||||
func generateBuildOptions(flags devFlags) *build.Options {
|
||||
return &build.Options{
|
||||
result := &build.Options{
|
||||
OutputType: "dev",
|
||||
Mode: build.Dev,
|
||||
Arch: runtime.GOARCH,
|
||||
Pack: true,
|
||||
Pack: false,
|
||||
Platform: runtime.GOOS,
|
||||
LDFlags: flags.ldflags,
|
||||
Compiler: flags.compilerCommand,
|
||||
@@ -268,6 +287,11 @@ func generateBuildOptions(flags devFlags) *build.Options {
|
||||
Verbosity: flags.verbosity,
|
||||
WailsJSDir: flags.wailsjsdir,
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
result.Pack = false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// loadAndMergeProjectConfig reconciles flags passed to the CLI with project config settings and updates
|
||||
@@ -331,6 +355,10 @@ func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, e
|
||||
shouldSaveConfig = true
|
||||
}
|
||||
|
||||
if flags.appargs == "" && projectConfig.AppArgs != "" {
|
||||
flags.appargs = projectConfig.AppArgs
|
||||
}
|
||||
|
||||
if shouldSaveConfig {
|
||||
err = projectConfig.Save()
|
||||
if err != nil {
|
||||
@@ -367,13 +395,15 @@ func runFrontendDevCommand(cwd string, devCommand string, wg *sync.WaitGroup) fu
|
||||
if runtime.GOOS == "windows" {
|
||||
// Credit: https://stackoverflow.com/a/44551450
|
||||
// For whatever reason, killing an npm script on windows just doesn't exit properly with cancel
|
||||
kill := exec.Command("TASKKILL", "/T", "/F", "/PID", strconv.Itoa(cmd.Process.Pid))
|
||||
kill.Stderr = os.Stderr
|
||||
kill.Stdout = os.Stdout
|
||||
err := kill.Run()
|
||||
if err != nil {
|
||||
if err.Error() != "exit status 1" {
|
||||
LogRed("Error from '%s': %s", devCommand, err.Error())
|
||||
if cmd != nil && cmd.Process != nil {
|
||||
kill := exec.Command("TASKKILL", "/T", "/F", "/PID", strconv.Itoa(cmd.Process.Pid))
|
||||
kill.Stderr = os.Stderr
|
||||
kill.Stdout = os.Stdout
|
||||
err := kill.Run()
|
||||
if err != nil {
|
||||
if err.Error() != "exit status 1" {
|
||||
LogRed("Error from '%s': %s", devCommand, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -439,16 +469,20 @@ func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process
|
||||
debugBinaryProcess = nil
|
||||
}
|
||||
|
||||
// parse appargs if any
|
||||
args, err := shlex.Split(flags.appargs)
|
||||
|
||||
if err != nil {
|
||||
buildOptions.Logger.Fatal("Unable to parse appargs: %s", err.Error())
|
||||
}
|
||||
|
||||
// Set environment variables accordingly
|
||||
os.Setenv("loglevel", flags.loglevel)
|
||||
os.Setenv("assetdir", flags.assetDir)
|
||||
os.Setenv("devserverurl", flags.devServerURL)
|
||||
|
||||
// Start up new binary with correct args
|
||||
args := slicer.StringSlicer{}
|
||||
args.Add("-loglevel", flags.loglevel)
|
||||
if flags.assetDir != "" {
|
||||
args.Add("-assetdir", flags.assetDir)
|
||||
}
|
||||
if flags.devServerURL != "" {
|
||||
args.Add("-devserverurl", flags.devServerURL)
|
||||
}
|
||||
newProcess := process.NewProcess(appBinary, args.AsSlice()...)
|
||||
newProcess := process.NewProcess(appBinary, args...)
|
||||
err = newProcess.Start(exitCodeChannel)
|
||||
if err != nil {
|
||||
// Remove binary
|
||||
|
||||
@@ -22,7 +22,7 @@ func (b *App) startup(ctx context.Context) {
|
||||
}
|
||||
|
||||
// domReady is called after the front-end dom has been loaded
|
||||
func (b App) domReady(ctx context.Context) {
|
||||
func (b *App) domReady(ctx context.Context) {
|
||||
// Add your action here
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
html {
|
||||
text-align: center;
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 255);
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -13,6 +13,7 @@ body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"embed"
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
@@ -13,6 +15,9 @@ import (
|
||||
//go:embed frontend/dist
|
||||
var assets embed.FS
|
||||
|
||||
//go:embed build/appicon.png
|
||||
var icon []byte
|
||||
|
||||
func main() {
|
||||
// Create an instance of the app structure
|
||||
app := NewApp()
|
||||
@@ -31,7 +36,7 @@ func main() {
|
||||
Frameless: false,
|
||||
StartHidden: false,
|
||||
HideWindowOnClose: false,
|
||||
RGBA: &options.RGBA{R: 255, G: 255, B: 255, A: 255},
|
||||
RGBA: &options.RGBA{R: 0, G: 0, B: 0, A: 0},
|
||||
Assets: assets,
|
||||
LogLevel: logger.DEBUG,
|
||||
OnStartup: app.startup,
|
||||
@@ -46,6 +51,17 @@ func main() {
|
||||
WindowIsTranslucent: false,
|
||||
DisableWindowIcon: false,
|
||||
},
|
||||
Mac: &mac.Options{
|
||||
TitleBar: mac.TitleBarHiddenInset(),
|
||||
Appearance: mac.NSAppearanceNameDarkAqua,
|
||||
WebviewIsTransparent: true,
|
||||
WindowIsTranslucent: true,
|
||||
About: &mac.AboutInfo{
|
||||
Title: "My Application",
|
||||
Message: "© 2021 Me",
|
||||
Icon: icon,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -22,7 +22,7 @@ func (b *App) startup(ctx context.Context) {
|
||||
}
|
||||
|
||||
// domReady is called after the front-end dom has been loaded
|
||||
func (b App) domReady(ctx context.Context) {
|
||||
func (b *App) domReady(ctx context.Context) {
|
||||
// Add your action here
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
html {
|
||||
background-color: rgba(33, 37, 43, 1);
|
||||
background-color: rgba(33, 37, 43, 0);
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
@@ -8,8 +8,9 @@ body {
|
||||
margin: 0;
|
||||
color: white;
|
||||
font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"embed"
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
@@ -13,6 +15,9 @@ import (
|
||||
//go:embed frontend/src
|
||||
var assets embed.FS
|
||||
|
||||
//go:embed build/appicon.png
|
||||
var icon []byte
|
||||
|
||||
func main() {
|
||||
// Create an instance of the app structure
|
||||
app := NewApp()
|
||||
@@ -31,7 +36,7 @@ func main() {
|
||||
Frameless: false,
|
||||
StartHidden: false,
|
||||
HideWindowOnClose: false,
|
||||
RGBA: &options.RGBA{R: 255, G: 255, B: 255, A: 255},
|
||||
RGBA: &options.RGBA{R: 0, G: 0, B: 0, A: 0},
|
||||
Assets: assets,
|
||||
LogLevel: logger.DEBUG,
|
||||
OnStartup: app.startup,
|
||||
@@ -46,6 +51,16 @@ func main() {
|
||||
WindowIsTranslucent: false,
|
||||
DisableWindowIcon: false,
|
||||
},
|
||||
Mac: &mac.Options{
|
||||
TitleBar: mac.TitleBarHiddenInset(),
|
||||
WebviewIsTransparent: true,
|
||||
WindowIsTranslucent: true,
|
||||
About: &mac.AboutInfo{
|
||||
Title: "Vanilla Template",
|
||||
Message: "Part of the Wails projects",
|
||||
Icon: icon,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package internal
|
||||
|
||||
var Version = "v2.0.0-beta.12"
|
||||
var Version = "v2.0.0-beta.15"
|
||||
|
||||
@@ -13,6 +13,7 @@ require (
|
||||
github.com/gofiber/fiber/v2 v2.17.0
|
||||
github.com/gofiber/websocket/v2 v2.0.8
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.1.2 // indirect
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/imdario/mergo v0.3.12
|
||||
@@ -21,7 +22,7 @@ require (
|
||||
github.com/leaanthony/debme v1.2.1
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211007095229-b1759d2e4ec7
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211022194343-1e4c8d4226f3
|
||||
github.com/leaanthony/gosod v1.0.3
|
||||
github.com/leaanthony/idgen v1.0.0
|
||||
github.com/leaanthony/slicer v1.5.0
|
||||
@@ -43,7 +44,7 @@ require (
|
||||
github.com/ztrue/tracerr v0.3.0
|
||||
golang.org/x/mod v0.4.1
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985
|
||||
golang.org/x/tools v0.1.0
|
||||
nhooyr.io/websocket v1.8.6
|
||||
)
|
||||
@@ -60,6 +61,7 @@ require (
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/klauspost/compress v1.12.2 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e // indirect
|
||||
github.com/mattn/go-runewidth v0.0.7 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
|
||||
19
v2/go.sum
19
v2/go.sum
@@ -75,6 +75,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -101,8 +103,9 @@ github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -115,10 +118,8 @@ github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQ
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd h1:6m4zZ/esiByaDbzgdvDxjsOaIDgtuG1q2cyhjAi6uAg=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211007095229-b1759d2e4ec7 h1:qw9f/UqPp2GQ318n8G0Ikawe8GRkdPpUNJMuYeeafGA=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211007095229-b1759d2e4ec7/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211022194343-1e4c8d4226f3 h1:qhgrg3MhFRAIvtaqoqI+SrT+0wDYpxDMp9e3cvcxMpI=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211022194343-1e4c8d4226f3/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM=
|
||||
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
|
||||
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
|
||||
github.com/leaanthony/idgen v1.0.0 h1:IZreR+JGEzFV4yeVuBZA25gM0keUoFy+RDUldncQ+Jw=
|
||||
@@ -161,6 +162,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f h1:PgA+Olipyj258EIEYnpFFONrrCcAIWNUNoFhUfMqAGY=
|
||||
github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f/go.mod h1:lHhJedqxCoHN+zMtwGNTXWmF0u9Jt363FYRhV6g0CdY=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
@@ -259,8 +262,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985 h1:LOlKVhfDyahgmqa97awczplwkjzNaELFg3zRIJ13RYo=
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -282,10 +285,12 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -5,7 +5,9 @@ package appng
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop"
|
||||
@@ -16,18 +18,14 @@ import (
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||
"github.com/wailsapp/wails/v2/internal/project"
|
||||
"github.com/wailsapp/wails/v2/internal/signal"
|
||||
pkglogger "github.com/wailsapp/wails/v2/pkg/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// App defines a Wails application structure
|
||||
type App struct {
|
||||
frontend frontend.Frontend
|
||||
logger *logger.Logger
|
||||
signal *signal.Manager
|
||||
options *options.App
|
||||
|
||||
menuManager *menumanager.Manager
|
||||
@@ -60,19 +58,20 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
||||
|
||||
// Check for CLI Flags
|
||||
assetdir := flag.String("assetdir", "", "Directory to serve assets")
|
||||
devServerURL := flag.String("devserverurl", "", "URL of development server")
|
||||
loglevel := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
||||
flag.Parse()
|
||||
if devServerURL != nil && *devServerURL != "" {
|
||||
ctx = context.WithValue(ctx, "devserverurl", *devServerURL)
|
||||
|
||||
assetdir := os.Getenv("assetdir")
|
||||
devServerURL := os.Getenv("devserverurl")
|
||||
loglevel := os.Getenv("loglevel")
|
||||
|
||||
if devServerURL != "" {
|
||||
ctx = context.WithValue(ctx, "devserverurl", devServerURL)
|
||||
}
|
||||
if assetdir != nil && *assetdir != "" {
|
||||
ctx = context.WithValue(ctx, "assetdir", *assetdir)
|
||||
if assetdir != "" {
|
||||
ctx = context.WithValue(ctx, "assetdir", assetdir)
|
||||
}
|
||||
|
||||
if loglevel != nil && *loglevel != "" {
|
||||
level, err := pkglogger.StringToLogLevel(*loglevel)
|
||||
if loglevel != "" {
|
||||
level, err := pkglogger.StringToLogLevel(loglevel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package appng
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop"
|
||||
@@ -12,7 +13,6 @@ import (
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||
"github.com/wailsapp/wails/v2/internal/signal"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
type App struct {
|
||||
frontend frontend.Frontend
|
||||
logger *logger.Logger
|
||||
signal *signal.Manager
|
||||
options *options.App
|
||||
|
||||
menuManager *menumanager.Manager
|
||||
@@ -36,9 +35,6 @@ type App struct {
|
||||
|
||||
func (a *App) Run() error {
|
||||
err := a.frontend.Run(a.ctx)
|
||||
if a.shutdownCallback != nil {
|
||||
a.shutdownCallback(a.ctx)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -1252,7 +1252,7 @@ void createDelegate(struct Application *app) {
|
||||
|
||||
app->delegate = delegate;
|
||||
|
||||
msg_id(app->application, s("setDelegate:"), delegate);
|
||||
msg_id(app->application, s("setDelegate:"), delegate);
|
||||
}
|
||||
|
||||
bool windowShouldClose(id self, SEL cmd, id sender) {
|
||||
|
||||
@@ -1 +1 @@
|
||||
The version of WebView2 used: 1.0.864.35
|
||||
The version of WebView2 SDK used: 1.0.992.28
|
||||
|
||||
@@ -7,7 +7,7 @@ set sdk_version=%1
|
||||
set native_dir="%~dp0\microsoft.web.webview2.%sdk_version%\build\native"
|
||||
copy "%native_dir%\include\*.h" .. >NUL
|
||||
copy "%native_dir%\x64\WebView2Loader.dll" "..\x64" >NUL
|
||||
@rd /S /Q "microsoft.web.webview2.%sdk_version%"
|
||||
@REM @rd /S /Q "microsoft.web.webview2.%sdk_version%"
|
||||
del /s version.txt >nul 2>&1
|
||||
echo The version of WebView2 SDK used: %sdk_version% > sdkversion.txt
|
||||
echo SDK updated to %sdk_version%
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/leaanthony/webview2runtime"
|
||||
)
|
||||
|
||||
const MinimumRuntimeVersion string = "91.0.864.48"
|
||||
const MinimumRuntimeVersion string = "91.0.992.28"
|
||||
|
||||
type installationStatus int
|
||||
|
||||
|
||||
21
v2/internal/frontend/desktop/darwin/AppDelegate.h
Normal file
21
v2/internal/frontend/desktop/darwin/AppDelegate.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// AppDelegate.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#ifndef AppDelegate_h
|
||||
#define AppDelegate_h
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "WailsContext.h"
|
||||
|
||||
@interface AppDelegate : NSResponder <NSTouchBarProvider>
|
||||
|
||||
@property bool alwaysOnTop;
|
||||
@property (retain) WailsWindow* mainWindow;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* AppDelegate_h */
|
||||
31
v2/internal/frontend/desktop/darwin/AppDelegate.m
Normal file
31
v2/internal/frontend/desktop/darwin/AppDelegate.m
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// AppDelegate.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
|
||||
return NO;
|
||||
}
|
||||
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification {
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
[self.mainWindow makeKeyAndOrderFront:self];
|
||||
if (self.alwaysOnTop) {
|
||||
[self.mainWindow setLevel:NSStatusWindowLevel];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
@synthesize touchBar;
|
||||
|
||||
@end
|
||||
61
v2/internal/frontend/desktop/darwin/Application.h
Normal file
61
v2/internal/frontend/desktop/darwin/Application.h
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Application.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#ifndef Application_h
|
||||
#define Application_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "WailsContext.h"
|
||||
|
||||
WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent, int debug);
|
||||
void Run(void*);
|
||||
|
||||
void SetTitle(void* ctx, const char *title);
|
||||
void Center(void* ctx);
|
||||
void SetSize(void* ctx, int width, int height);
|
||||
void SetMinSize(void* ctx, int width, int height);
|
||||
void SetMaxSize(void* ctx, int width, int height);
|
||||
void SetPosition(void* ctx, int x, int y);
|
||||
void Fullscreen(void* ctx);
|
||||
void UnFullscreen(void* ctx);
|
||||
void Minimise(void* ctx);
|
||||
void UnMinimise(void* ctx);
|
||||
void Maximise(void* ctx);
|
||||
void UnMaximise(void* ctx);
|
||||
void Hide(void* ctx);
|
||||
void Show(void* ctx);
|
||||
void SetRGBA(void* ctx, int r, int g, int b, int a);
|
||||
void ExecJS(void* ctx, const char*);
|
||||
void Quit(void*);
|
||||
|
||||
const char* GetSize(void *ctx);
|
||||
const char* GetPos(void *ctx);
|
||||
|
||||
void ProcessURLResponse(void *inctx, const char *url, const char *contentType, void* data, int datalength);
|
||||
|
||||
/* Dialogs */
|
||||
|
||||
void MessageDialog(void *inctx, const char* dialogType, const char* title, const char* message, const char* button1, const char* button2, const char* button3, const char* button4, const char* defaultButton, const char* cancelButton, void* iconData, int iconDataLength);
|
||||
void OpenFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int allowDirectories, int allowFiles, int canCreateDirectories, int treatPackagesAsDirectories, int resolveAliases, int showHiddenFiles, int allowMultipleSelection, const char* filters);
|
||||
void SaveFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int canCreateDirectories, int treatPackagesAsDirectories, int showHiddenFiles, const char* filters);
|
||||
|
||||
/* Application Menu */
|
||||
void* NewMenu(const char* name);
|
||||
void AppendSubmenu(void* parent, void* child);
|
||||
void AppendRole(void *inctx, void *inMenu, int role);
|
||||
void SetAsApplicationMenu(void *inctx, void *inMenu);
|
||||
void UpdateApplicationMenu(void *inctx);
|
||||
|
||||
void SetAbout(void *inctx, const char* title, const char* description, void* imagedata, int datalen);
|
||||
void* AppendMenuItem(void* inctx, void* nsmenu, const char* label, const char* shortcutKey, int modifiers, int disabled, int checked, int menuItemID);
|
||||
void AppendSeparator(void* inMenu);
|
||||
void UpdateMenuItem(void* nsmenuitem, int checked);
|
||||
|
||||
NSString* safeInit(const char* input);
|
||||
|
||||
#endif /* Application_h */
|
||||
314
v2/internal/frontend/desktop/darwin/Application.m
Normal file
314
v2/internal/frontend/desktop/darwin/Application.m
Normal file
@@ -0,0 +1,314 @@
|
||||
//
|
||||
// Application.m
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "WailsContext.h"
|
||||
#import "Application.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "WailsMenu.h"
|
||||
#import "WailsMenuItem.h"
|
||||
|
||||
WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent, int debug) {
|
||||
|
||||
[NSApplication sharedApplication];
|
||||
|
||||
WailsContext *result = [WailsContext new];
|
||||
|
||||
result.debug = debug;
|
||||
|
||||
[result CreateWindow:width :height :frameless :resizable :fullscreen :fullSizeContent :hideTitleBar :titlebarAppearsTransparent :hideTitle :useToolbar :hideToolbarSeparator :webviewIsTransparent :hideWindowOnClose :safeInit(appearance) :windowIsTranslucent];
|
||||
[result SetTitle:safeInit(title)];
|
||||
[result Center];
|
||||
|
||||
result.alwaysOnTop = alwaysOnTop;
|
||||
result.hideOnClose = hideWindowOnClose;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ProcessURLResponse(void *inctx, const char *url, const char *contentType, void* data, int datalength) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSString *nsurl = safeInit(url);
|
||||
NSString *nsContentType = safeInit(contentType);
|
||||
NSData *nsdata = [NSData dataWithBytes:data length:datalength];
|
||||
|
||||
[ctx processURLResponse:nsurl :nsContentType :nsdata];
|
||||
}
|
||||
|
||||
void ExecJS(void* inctx, const char *script) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSString *nsscript = safeInit(script);
|
||||
ON_MAIN_THREAD(
|
||||
[ctx ExecJS:nsscript];
|
||||
);
|
||||
}
|
||||
|
||||
void SetTitle(void* inctx, const char *title) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSString *_title = safeInit(title);
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetTitle:_title];
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void SetRGBA(void *inctx, int r, int g, int b, int a) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetRGBA:r :g :b :a];
|
||||
);
|
||||
}
|
||||
|
||||
void SetSize(void* inctx, int width, int height) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetSize:width :height];
|
||||
);
|
||||
}
|
||||
|
||||
void SetMinSize(void* inctx, int width, int height) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetMinSize:width :height];
|
||||
);
|
||||
}
|
||||
|
||||
void SetMaxSize(void* inctx, int width, int height) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetMaxSize:width :height];
|
||||
);
|
||||
}
|
||||
|
||||
void SetPosition(void* inctx, int x, int y) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetPosition:x :y];
|
||||
);
|
||||
}
|
||||
|
||||
void Center(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Center];
|
||||
);
|
||||
}
|
||||
|
||||
void Fullscreen(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Fullscreen];
|
||||
);
|
||||
}
|
||||
|
||||
void UnFullscreen(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx UnFullscreen];
|
||||
);
|
||||
}
|
||||
|
||||
void Minimise(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Minimise];
|
||||
);
|
||||
}
|
||||
|
||||
void UnMinimise(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx UnMinimise];
|
||||
);
|
||||
}
|
||||
|
||||
void Maximise(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Maximise];
|
||||
);
|
||||
}
|
||||
|
||||
const char* GetSize(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSRect frame = [ctx.mainWindow frame];
|
||||
NSString *result = [NSString stringWithFormat:@"%d,%d", (int)frame.size.width, (int)frame.size.height];
|
||||
return [result UTF8String];
|
||||
}
|
||||
|
||||
const char* GetPos(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSScreen* screen = [ctx getCurrentScreen];
|
||||
NSRect windowFrame = [ctx.mainWindow frame];
|
||||
NSRect screenFrame = [screen visibleFrame];
|
||||
int x = windowFrame.origin.x - screenFrame.origin.x;
|
||||
int y = windowFrame.origin.y - screenFrame.origin.y;
|
||||
y = screenFrame.size.height - y - windowFrame.size.height;
|
||||
NSString *result = [NSString stringWithFormat:@"%d,%d",x,y];
|
||||
return [result UTF8String];
|
||||
|
||||
}
|
||||
|
||||
void UnMaximise(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx UnMaximise];
|
||||
);
|
||||
}
|
||||
|
||||
void Quit(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
[NSApp stop:ctx];
|
||||
}
|
||||
|
||||
void Hide(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Hide];
|
||||
);
|
||||
}
|
||||
|
||||
void Show(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Show];
|
||||
);
|
||||
}
|
||||
|
||||
NSString* safeInit(const char* input) {
|
||||
NSString *result = nil;
|
||||
if (input != nil) {
|
||||
result = [NSString stringWithUTF8String:input];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void MessageDialog(void *inctx, const char* dialogType, const char* title, const char* message, const char* button1, const char* button2, const char* button3, const char* button4, const char* defaultButton, const char* cancelButton, void* iconData, int iconDataLength) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
|
||||
NSString *_dialogType = safeInit(dialogType);
|
||||
NSString *_title = safeInit(title);
|
||||
NSString *_message = safeInit(message);
|
||||
NSString *_button1 = safeInit(button1);
|
||||
NSString *_button2 = safeInit(button2);
|
||||
NSString *_button3 = safeInit(button3);
|
||||
NSString *_button4 = safeInit(button4);
|
||||
NSString *_defaultButton = safeInit(defaultButton);
|
||||
NSString *_cancelButton = safeInit(cancelButton);
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
[ctx MessageDialog:_dialogType :_title :_message :_button1 :_button2 :_button3 :_button4 :_defaultButton :_cancelButton :iconData :iconDataLength];
|
||||
)
|
||||
}
|
||||
|
||||
void OpenFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int allowDirectories, int allowFiles, int canCreateDirectories, int treatPackagesAsDirectories, int resolveAliases, int showHiddenFiles, int allowMultipleSelection, const char* filters) {
|
||||
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSString *_title = safeInit(title);
|
||||
NSString *_defaultFilename = safeInit(defaultFilename);
|
||||
NSString *_defaultDirectory = safeInit(defaultDirectory);
|
||||
NSString *_filters = safeInit(filters);
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
[ctx OpenFileDialog:_title :_defaultFilename :_defaultDirectory :allowDirectories :allowFiles :canCreateDirectories :treatPackagesAsDirectories :resolveAliases :showHiddenFiles :allowMultipleSelection :_filters];
|
||||
)
|
||||
}
|
||||
|
||||
void SaveFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int canCreateDirectories, int treatPackagesAsDirectories, int showHiddenFiles, const char* filters) {
|
||||
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSString *_title = safeInit(title);
|
||||
NSString *_defaultFilename = safeInit(defaultFilename);
|
||||
NSString *_defaultDirectory = safeInit(defaultDirectory);
|
||||
NSString *_filters = safeInit(filters);
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SaveFileDialog:_title :_defaultFilename :_defaultDirectory :canCreateDirectories :treatPackagesAsDirectories :showHiddenFiles :_filters];
|
||||
)
|
||||
}
|
||||
|
||||
void AppendRole(void *inctx, void *inMenu, int role) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
WailsMenu *menu = (__bridge WailsMenu*) inMenu;
|
||||
[menu appendRole :ctx :role];
|
||||
}
|
||||
|
||||
void* NewMenu(const char *name) {
|
||||
NSString *title = @"";
|
||||
if (name != nil) {
|
||||
title = [NSString stringWithUTF8String:name];
|
||||
}
|
||||
WailsMenu *result = [[WailsMenu new] initWithNSTitle:title];
|
||||
return result;
|
||||
}
|
||||
|
||||
void AppendSubmenu(void* inparent, void* inchild) {
|
||||
WailsMenu *parent = (__bridge WailsMenu*) inparent;
|
||||
WailsMenu *child = (__bridge WailsMenu*) inchild;
|
||||
[parent appendSubmenu:child];
|
||||
}
|
||||
|
||||
void SetAsApplicationMenu(void *inctx, void *inMenu) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
WailsMenu *menu = (__bridge WailsMenu*) inMenu;
|
||||
ctx.applicationMenu = menu;
|
||||
}
|
||||
|
||||
void UpdateApplicationMenu(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
[app setMainMenu:ctx.applicationMenu];
|
||||
)
|
||||
}
|
||||
|
||||
void SetAbout(void *inctx, const char* title, const char* description, void* imagedata, int datalen) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSString *_title = safeInit(title);
|
||||
NSString *_description = safeInit(description);
|
||||
|
||||
[ctx SetAbout :_title :_description :imagedata :datalen];
|
||||
}
|
||||
|
||||
void* AppendMenuItem(void* inctx, void* inMenu, const char* label, const char* shortcutKey, int modifiers, int disabled, int checked, int menuItemID) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
WailsMenu *menu = (__bridge WailsMenu*) inMenu;
|
||||
NSString *_label = safeInit(label);
|
||||
NSString *_shortcutKey = safeInit(shortcutKey);
|
||||
|
||||
return [menu AppendMenuItem:ctx :_label :_shortcutKey :modifiers :disabled :checked :menuItemID];
|
||||
}
|
||||
|
||||
void UpdateMenuItem(void* nsmenuitem, int checked) {
|
||||
ON_MAIN_THREAD(
|
||||
WailsMenuItem *menuItem = (__bridge WailsMenuItem*) nsmenuitem;
|
||||
[menuItem setState:(checked == 1?NSControlStateValueOn:NSControlStateValueOff)];
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
void AppendSeparator(void* inMenu) {
|
||||
WailsMenu *menu = (__bridge WailsMenu*) inMenu;
|
||||
[menu AppendSeparator];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Run(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
AppDelegate* delegate = [AppDelegate new];
|
||||
[app setDelegate:(id)delegate];
|
||||
ctx.appdelegate = delegate;
|
||||
delegate.mainWindow = ctx.mainWindow;
|
||||
delegate.alwaysOnTop = ctx.alwaysOnTop;
|
||||
|
||||
[ctx loadRequest:@"wails://wails/"];
|
||||
[app setMainMenu:ctx.applicationMenu];
|
||||
[app run];
|
||||
[ctx release];
|
||||
}
|
||||
16
v2/internal/frontend/desktop/darwin/Role.h
Normal file
16
v2/internal/frontend/desktop/darwin/Role.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Role.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 24/10/21.
|
||||
//
|
||||
|
||||
#ifndef Role_h
|
||||
#define Role_h
|
||||
|
||||
typedef int Role;
|
||||
|
||||
static const Role AppMenu = 1;
|
||||
static const Role EditMenu = 2;
|
||||
|
||||
#endif /* Role_h */
|
||||
18
v2/internal/frontend/desktop/darwin/WailsAlert.h
Normal file
18
v2/internal/frontend/desktop/darwin/WailsAlert.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// WailsAlert.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 20/10/21.
|
||||
//
|
||||
|
||||
#ifndef WailsAlert_h
|
||||
#define WailsAlert_h
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface WailsAlert : NSAlert
|
||||
- (void)addButton:(NSString*)text :(NSString*)defaultButton :(NSString*)cancelButton;
|
||||
@end
|
||||
|
||||
|
||||
#endif /* WailsAlert_h */
|
||||
30
v2/internal/frontend/desktop/darwin/WailsAlert.m
Normal file
30
v2/internal/frontend/desktop/darwin/WailsAlert.m
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// WailsAlert.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 20/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "WailsAlert.h"
|
||||
|
||||
@implementation WailsAlert
|
||||
|
||||
- (void)addButton:(NSString*)text :(NSString*)defaultButton :(NSString*)cancelButton {
|
||||
if( text == nil ) {
|
||||
return;
|
||||
}
|
||||
NSButton *button = [self addButtonWithTitle:text];
|
||||
if( defaultButton != nil && [text isEqualToString:defaultButton]) {
|
||||
[button setKeyEquivalent:@"\r"];
|
||||
} else if( cancelButton != nil && [text isEqualToString:cancelButton]) {
|
||||
[button setKeyEquivalent:@"\033"];
|
||||
} else {
|
||||
[button setKeyEquivalent:@""];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
82
v2/internal/frontend/desktop/darwin/WailsContext.h
Normal file
82
v2/internal/frontend/desktop/darwin/WailsContext.h
Normal file
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// WailsContext.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#ifndef WailsContext_h
|
||||
#define WailsContext_h
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
#define ON_MAIN_THREAD(str) dispatch_async(dispatch_get_main_queue(), ^{ str; });
|
||||
#define unicode(input) [NSString stringWithFormat:@"%C", input]
|
||||
|
||||
@interface WailsWindow : NSWindow
|
||||
- (BOOL)canBecomeKeyWindow;
|
||||
@end
|
||||
|
||||
@interface WailsContext : NSObject <WKURLSchemeHandler,WKScriptMessageHandler,WKNavigationDelegate>
|
||||
|
||||
@property (retain) WailsWindow* mainWindow;
|
||||
@property (retain) WKWebView* webview;
|
||||
@property (nonatomic, assign) id appdelegate;
|
||||
|
||||
@property bool hideOnClose;
|
||||
@property bool shuttingDown;
|
||||
|
||||
@property NSSize maxSize;
|
||||
@property NSSize minSize;
|
||||
|
||||
@property (retain) NSEvent* mouseEvent;
|
||||
|
||||
@property bool alwaysOnTop;
|
||||
|
||||
@property bool debug;
|
||||
|
||||
@property (retain) WKUserContentController* userContentController;
|
||||
@property (retain) NSMutableDictionary *urlRequests;
|
||||
|
||||
@property (retain) NSMenu* applicationMenu;
|
||||
|
||||
@property (retain) NSImage* aboutImage;
|
||||
@property (retain) NSString* aboutTitle;
|
||||
@property (retain) NSString* aboutDescription;
|
||||
|
||||
- (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(NSString *)appearance :(bool)windowIsTranslucent;
|
||||
- (void) SetSize:(int)width :(int)height;
|
||||
- (void) SetPosition:(int)x :(int) y;
|
||||
- (void) SetMinSize:(int)minWidth :(int)minHeight;
|
||||
- (void) SetMaxSize:(int)maxWidth :(int)maxHeight;
|
||||
- (void) SetTitle:(NSString*)title;
|
||||
- (void) Center;
|
||||
- (void) Fullscreen;
|
||||
- (void) UnFullscreen;
|
||||
- (void) Minimise;
|
||||
- (void) UnMinimise;
|
||||
- (void) Maximise;
|
||||
- (void) UnMaximise;
|
||||
- (void) SetRGBA:(int)r :(int)g :(int)b :(int)a;
|
||||
- (void) HideMouse;
|
||||
- (void) ShowMouse;
|
||||
- (void) Hide;
|
||||
- (void) Show;
|
||||
- (void) Quit;
|
||||
|
||||
-(void) MessageDialog :(NSString*)dialogType :(NSString*)title :(NSString*)message :(NSString*)button1 :(NSString*)button2 :(NSString*)button3 :(NSString*)button4 :(NSString*)defaultButton :(NSString*)cancelButton :(void*)iconData :(int)iconDataLength;
|
||||
- (void) OpenFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(NSString*)filters;
|
||||
- (void) SaveFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(NSString*)filters;
|
||||
|
||||
- (void) loadRequest:(NSString*)url;
|
||||
- (void) processURLResponse:(NSString *)url :(NSString *)contentType :(NSData*)data;
|
||||
- (void) ExecJS:(NSString*)script;
|
||||
- (NSScreen*) getCurrentScreen;
|
||||
|
||||
- (void) SetAbout :(NSString*)title :(NSString*)description :(void*)imagedata :(int)datalen;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* WailsContext_h */
|
||||
619
v2/internal/frontend/desktop/darwin/WailsContext.m
Normal file
619
v2/internal/frontend/desktop/darwin/WailsContext.m
Normal file
@@ -0,0 +1,619 @@
|
||||
//
|
||||
// WailsContext.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
#import "WailsContext.h"
|
||||
#import "WailsAlert.h"
|
||||
#import "WailsMenu.h"
|
||||
#import "WindowDelegate.h"
|
||||
#import "message.h"
|
||||
#import "Role.h"
|
||||
|
||||
@implementation WailsWindow
|
||||
|
||||
- (BOOL)canBecomeKeyWindow
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation WailsContext
|
||||
|
||||
- (void) SetSize:(int)width :(int)height {
|
||||
|
||||
if (self.shuttingDown) return;
|
||||
|
||||
NSRect frame = [self.mainWindow frame];
|
||||
frame.origin.y += frame.size.height - height;
|
||||
frame.size.width = width;
|
||||
frame.size.height = height;
|
||||
ON_MAIN_THREAD([self.mainWindow setFrame:frame display:TRUE animate:FALSE];);
|
||||
}
|
||||
|
||||
- (void) SetPosition:(int)x :(int)y {
|
||||
|
||||
if (self.shuttingDown) return;
|
||||
|
||||
NSScreen* screen = [self getCurrentScreen];
|
||||
NSRect windowFrame = [self.mainWindow frame];
|
||||
NSRect screenFrame = [screen frame];
|
||||
windowFrame.origin.x = screenFrame.origin.x + (float)x;
|
||||
windowFrame.origin.y = (screenFrame.origin.y + screenFrame.size.height) - windowFrame.size.height - (float)y;
|
||||
|
||||
ON_MAIN_THREAD([self.mainWindow setFrame:windowFrame display:TRUE animate:FALSE]; );
|
||||
}
|
||||
|
||||
- (void) SetMinSize:(int)minWidth :(int)minHeight {
|
||||
|
||||
if (self.shuttingDown) return;
|
||||
|
||||
NSSize size = { minWidth, minHeight };
|
||||
|
||||
self.minSize = size;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
[self.mainWindow setMinSize:size];
|
||||
[self adjustWindowSize];
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
- (void) SetMaxSize:(int)maxWidth :(int)maxHeight {
|
||||
|
||||
if (self.shuttingDown) return;
|
||||
|
||||
NSSize size = { FLT_MAX, FLT_MAX };
|
||||
|
||||
size.width = maxWidth > 0 ? maxWidth : FLT_MAX;
|
||||
size.height = maxHeight > 0 ? maxHeight : FLT_MAX;
|
||||
|
||||
self.maxSize = size;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
[self.mainWindow setMaxSize:size];
|
||||
[self adjustWindowSize];
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
- (void) adjustWindowSize {
|
||||
|
||||
if (self.shuttingDown) return;
|
||||
|
||||
NSRect currentFrame = [self.mainWindow frame];
|
||||
|
||||
if ( currentFrame.size.width > self.maxSize.width ) currentFrame.size.width = self.maxSize.width;
|
||||
if ( currentFrame.size.width < self.minSize.width ) currentFrame.size.width = self.minSize.width;
|
||||
if ( currentFrame.size.height > self.maxSize.height ) currentFrame.size.height = self.maxSize.height;
|
||||
if ( currentFrame.size.height < self.minSize.height ) currentFrame.size.height = self.minSize.height;
|
||||
|
||||
[self.mainWindow setFrame:currentFrame display:YES animate:FALSE];
|
||||
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
[super dealloc];
|
||||
[self.appdelegate release];
|
||||
[self.mainWindow release];
|
||||
[self.mouseEvent release];
|
||||
[self.userContentController release];
|
||||
[self.urlRequests release];
|
||||
[self.applicationMenu release];
|
||||
}
|
||||
|
||||
- (NSScreen*) getCurrentScreen {
|
||||
NSScreen* screen = [self.mainWindow screen];
|
||||
if( screen == NULL ) {
|
||||
screen = [NSScreen mainScreen];
|
||||
}
|
||||
return screen;
|
||||
}
|
||||
|
||||
- (void) SetTitle:(NSString*)title {
|
||||
ON_MAIN_THREAD([self.mainWindow setTitle:title];)
|
||||
}
|
||||
|
||||
- (void) Center {
|
||||
ON_MAIN_THREAD( [self.mainWindow center]; );
|
||||
}
|
||||
|
||||
- (BOOL) isFullscreen {
|
||||
NSWindowStyleMask masks = [self.mainWindow styleMask];
|
||||
if ( masks & NSWindowStyleMaskFullScreen ) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(NSString*)appearance :(bool)windowIsTranslucent {
|
||||
|
||||
self.urlRequests = [NSMutableDictionary new];
|
||||
|
||||
NSWindowStyleMask styleMask = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
|
||||
|
||||
if (frameless) {
|
||||
styleMask = NSWindowStyleMaskBorderless;
|
||||
titlebarAppearsTransparent = true;
|
||||
hideTitle = true;
|
||||
} else {
|
||||
if (!hideTitleBar) {
|
||||
styleMask |= NSWindowStyleMaskTitled;
|
||||
}
|
||||
|
||||
if (fullscreen) {
|
||||
styleMask |= NSWindowStyleMaskFullScreen;
|
||||
}
|
||||
|
||||
if( fullSizeContent || frameless || titlebarAppearsTransparent ) {
|
||||
styleMask |= NSWindowStyleMaskFullSizeContentView;
|
||||
}
|
||||
}
|
||||
|
||||
if (resizable) {
|
||||
styleMask |= NSWindowStyleMaskResizable;
|
||||
}
|
||||
|
||||
self.mainWindow = [[[WailsWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height)
|
||||
styleMask:styleMask backing:NSBackingStoreBuffered defer:NO]
|
||||
autorelease];
|
||||
|
||||
if (!frameless && useToolbar) {
|
||||
id toolbar = [[NSToolbar alloc] initWithIdentifier:@"wails.toolbar"];
|
||||
[toolbar autorelease];
|
||||
[toolbar setShowsBaselineSeparator:!hideToolbarSeparator];
|
||||
[self.mainWindow setToolbar:toolbar];
|
||||
|
||||
}
|
||||
|
||||
[self.mainWindow setTitleVisibility:hideTitle];
|
||||
[self.mainWindow setTitlebarAppearsTransparent:titlebarAppearsTransparent];
|
||||
|
||||
// [self.mainWindow canBecomeKeyWindow];
|
||||
|
||||
id contentView = [self.mainWindow contentView];
|
||||
if (windowIsTranslucent) {
|
||||
NSVisualEffectView *effectView = [NSVisualEffectView alloc];
|
||||
NSRect bounds = [contentView bounds];
|
||||
[effectView initWithFrame:bounds];
|
||||
[effectView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
[effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
|
||||
[effectView setState:NSVisualEffectStateActive];
|
||||
[contentView addSubview:effectView positioned:NSWindowBelow relativeTo:nil];
|
||||
}
|
||||
|
||||
if (appearance != nil) {
|
||||
NSAppearance *nsAppearance = [NSAppearance appearanceNamed:appearance];
|
||||
[self.mainWindow setAppearance:nsAppearance];
|
||||
}
|
||||
|
||||
// Set up min/max
|
||||
NSSize maxSize = { FLT_MAX, FLT_MAX };
|
||||
self.maxSize = maxSize;
|
||||
NSSize minSize = { 0, 0 };
|
||||
self.minSize = minSize;
|
||||
[self adjustWindowSize];
|
||||
|
||||
WindowDelegate *windowDelegate = [WindowDelegate new];
|
||||
windowDelegate.hideOnClose = hideWindowOnClose;
|
||||
[self.mainWindow setDelegate:windowDelegate];
|
||||
|
||||
// Webview stuff here!
|
||||
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
|
||||
config.suppressesIncrementalRendering = true;
|
||||
[config setURLSchemeHandler:self forURLScheme:@"wails"];
|
||||
|
||||
// [config.preferences setValue:[NSNumber numberWithBool:true] forKey:@"developerExtrasEnabled"];
|
||||
|
||||
WKUserContentController* userContentController = [WKUserContentController new];
|
||||
[userContentController addScriptMessageHandler:self name:@"external"];
|
||||
config.userContentController = userContentController;
|
||||
self.userContentController = userContentController;
|
||||
if (self.debug) {
|
||||
[config.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
|
||||
} else {
|
||||
// Disable default context menus
|
||||
WKUserScript *initScript = [WKUserScript new];
|
||||
[initScript initWithSource:@"window.wails.flags.disableWailsDefaultContextMenu = true;"
|
||||
injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
|
||||
forMainFrameOnly:false];
|
||||
[userContentController addUserScript:initScript];
|
||||
|
||||
}
|
||||
|
||||
self.webview = [WKWebView alloc];
|
||||
CGRect init = { 0,0,0,0 };
|
||||
[self.webview initWithFrame:init configuration:config];
|
||||
[contentView addSubview:self.webview];
|
||||
[self.webview setAutoresizingMask: NSViewWidthSizable|NSViewHeightSizable];
|
||||
CGRect contentViewBounds = [contentView bounds];
|
||||
[self.webview setFrame:contentViewBounds];
|
||||
|
||||
if (webviewIsTransparent) {
|
||||
[self.webview setValue:[NSNumber numberWithBool:!webviewIsTransparent] forKey:@"drawsBackground"];
|
||||
}
|
||||
|
||||
[self.webview setNavigationDelegate:self];
|
||||
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults setBool:FALSE forKey:@"NSAutomaticQuoteSubstitutionEnabled"];
|
||||
|
||||
// Mouse monitors
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) {
|
||||
id window = [event window];
|
||||
if (window == self.mainWindow) {
|
||||
self.mouseEvent = event;
|
||||
}
|
||||
return event;
|
||||
}];
|
||||
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseUp handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) {
|
||||
id window = [event window];
|
||||
if (window == self.mainWindow) {
|
||||
self.mouseEvent = nil;
|
||||
[self ShowMouse];
|
||||
}
|
||||
return event;
|
||||
}];
|
||||
|
||||
self.applicationMenu = [NSMenu new];
|
||||
|
||||
}
|
||||
|
||||
- (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags {
|
||||
NSMenuItem *result = [[[NSMenuItem alloc] initWithTitle:title action:selector keyEquivalent:key] autorelease];
|
||||
if( flags != 0 ) {
|
||||
[result setKeyEquivalentModifierMask:flags];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key {
|
||||
return [self newMenuItem :title :selector :key :0];
|
||||
}
|
||||
|
||||
- (NSMenu*) newMenu :(NSString*)title {
|
||||
WailsMenu *result = [[[WailsMenu new] initWithTitle:title] autorelease];
|
||||
[result setAutoenablesItems:NO];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void) Quit {
|
||||
processMessage("Q");
|
||||
}
|
||||
|
||||
- (void) loadRequest :(NSString*)url {
|
||||
NSURL *wkUrl = [NSURL URLWithString:url];
|
||||
NSURLRequest *wkRequest = [NSURLRequest requestWithURL:wkUrl];
|
||||
[self.webview loadRequest:wkRequest];
|
||||
}
|
||||
|
||||
- (void) SetRGBA:(int)r :(int)g :(int)b :(int)a {
|
||||
float red = r/255;
|
||||
float green = g/255;
|
||||
float blue = b/255;
|
||||
float alpha = a/255;
|
||||
|
||||
id colour = [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha ];
|
||||
|
||||
ON_MAIN_THREAD([self.mainWindow setBackgroundColor:colour];);
|
||||
}
|
||||
|
||||
- (void) HideMouse {
|
||||
[NSCursor hide];
|
||||
}
|
||||
|
||||
- (void) ShowMouse {
|
||||
[NSCursor unhide];
|
||||
}
|
||||
|
||||
- (bool) isFullScreen {
|
||||
long mask = [self.mainWindow styleMask];
|
||||
return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;
|
||||
}
|
||||
|
||||
- (bool) isMaximised {
|
||||
long mask = [self.mainWindow styleMask];
|
||||
return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;
|
||||
}
|
||||
|
||||
// Fullscreen sets the main window to be fullscreen
|
||||
- (void) Fullscreen {
|
||||
if( ! [self isFullScreen] ) {
|
||||
ON_MAIN_THREAD([self.mainWindow toggleFullScreen:nil];)
|
||||
}
|
||||
}
|
||||
|
||||
// UnFullscreen resets the main window after a fullscreen
|
||||
- (void) UnFullscreen {
|
||||
if( [self isFullScreen] ) {
|
||||
ON_MAIN_THREAD([self.mainWindow toggleFullScreen:nil];)
|
||||
}
|
||||
}
|
||||
|
||||
- (void) Minimise {
|
||||
ON_MAIN_THREAD([self.mainWindow miniaturize:nil];)
|
||||
}
|
||||
|
||||
- (void) UnMinimise {
|
||||
ON_MAIN_THREAD([self.mainWindow deminiaturize:nil];)
|
||||
}
|
||||
|
||||
- (void) Hide {
|
||||
ON_MAIN_THREAD([self.mainWindow orderOut:nil];)
|
||||
}
|
||||
|
||||
- (void) Show {
|
||||
ON_MAIN_THREAD(
|
||||
[self.mainWindow makeKeyAndOrderFront:nil];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
)
|
||||
}
|
||||
|
||||
- (void) Maximise {
|
||||
if (![self.mainWindow isZoomed]) {
|
||||
ON_MAIN_THREAD([self.mainWindow zoom:nil];)
|
||||
}
|
||||
}
|
||||
|
||||
- (void) UnMaximise {
|
||||
if ([self.mainWindow isZoomed]) {
|
||||
ON_MAIN_THREAD([self.mainWindow zoom:nil];)
|
||||
}
|
||||
}
|
||||
|
||||
- (void) ExecJS:(NSString*)script {
|
||||
ON_MAIN_THREAD(
|
||||
[self.webview evaluateJavaScript:script completionHandler:nil];
|
||||
)
|
||||
}
|
||||
|
||||
- (void) processURLResponse:(NSString *)url :(NSString *)contentType :(NSData *)data {
|
||||
id<WKURLSchemeTask> urlSchemeTask = self.urlRequests[url];
|
||||
NSURL *nsurl = [NSURL URLWithString:url];
|
||||
|
||||
NSHTTPURLResponse *response = [NSHTTPURLResponse new];
|
||||
NSMutableDictionary *headerFields = [NSMutableDictionary new];
|
||||
headerFields[@"content-type"] = contentType;
|
||||
[response initWithURL:nsurl statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headerFields];
|
||||
[urlSchemeTask didReceiveResponse:response];
|
||||
[urlSchemeTask didReceiveData:data];
|
||||
[urlSchemeTask didFinish];
|
||||
[self.urlRequests removeObjectForKey:url];
|
||||
}
|
||||
|
||||
- (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
|
||||
// Do something
|
||||
self.urlRequests[urlSchemeTask.request.URL.absoluteString] = urlSchemeTask;
|
||||
processURLRequest(self, [urlSchemeTask.request.URL.absoluteString UTF8String]);
|
||||
}
|
||||
|
||||
- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
|
||||
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
||||
processMessage("DomReady");
|
||||
}
|
||||
|
||||
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
|
||||
NSString *m = message.body;
|
||||
|
||||
// Check for drag
|
||||
if ( [m isEqualToString:@"drag"] ) {
|
||||
if( ! [self isFullScreen] ) {
|
||||
if( self.mouseEvent != nil ) {
|
||||
[self HideMouse];
|
||||
ON_MAIN_THREAD(
|
||||
[self.mainWindow performWindowDragWithEvent:self.mouseEvent];
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const char *_m = [m UTF8String];
|
||||
|
||||
processMessage(_m);
|
||||
}
|
||||
|
||||
|
||||
/***** Dialogs ******/
|
||||
-(void) MessageDialog :(NSString*)dialogType :(NSString*)title :(NSString*)message :(NSString*)button1 :(NSString*)button2 :(NSString*)button3 :(NSString*)button4 :(NSString*)defaultButton :(NSString*)cancelButton :(void*)iconData :(int)iconDataLength {
|
||||
|
||||
WailsAlert *alert = [WailsAlert new];
|
||||
|
||||
int style = NSAlertStyleInformational;
|
||||
if (dialogType != nil ) {
|
||||
if( [dialogType isEqualToString:@"warning"] ) {
|
||||
style = NSAlertStyleWarning;
|
||||
}
|
||||
if( [dialogType isEqualToString:@"error"] ) {
|
||||
style = NSAlertStyleCritical;
|
||||
}
|
||||
}
|
||||
[alert setAlertStyle:style];
|
||||
if( title != nil ) {
|
||||
[alert setMessageText:title];
|
||||
}
|
||||
if( message != nil ) {
|
||||
[alert setInformativeText:message];
|
||||
}
|
||||
|
||||
[alert addButton:button1 :defaultButton :cancelButton];
|
||||
[alert addButton:button2 :defaultButton :cancelButton];
|
||||
[alert addButton:button3 :defaultButton :cancelButton];
|
||||
[alert addButton:button4 :defaultButton :cancelButton];
|
||||
|
||||
NSImage *icon = nil;
|
||||
if (iconData != nil) {
|
||||
NSData *imageData = [NSData dataWithBytes:iconData length:iconDataLength];
|
||||
icon = [[NSImage alloc] initWithData:imageData];
|
||||
}
|
||||
ON_MAIN_THREAD(
|
||||
if( icon != nil) {
|
||||
[alert setIcon:icon];
|
||||
}
|
||||
[alert.window setLevel:NSFloatingWindowLevel];
|
||||
|
||||
long response = [alert runModal];
|
||||
int result;
|
||||
|
||||
if( response == NSAlertFirstButtonReturn ) {
|
||||
result = 0;
|
||||
}
|
||||
else if( response == NSAlertSecondButtonReturn ) {
|
||||
result = 1;
|
||||
}
|
||||
else if( response == NSAlertThirdButtonReturn ) {
|
||||
result = 2;
|
||||
} else {
|
||||
result = 3;
|
||||
}
|
||||
processMessageDialogResponse(result);
|
||||
)
|
||||
}
|
||||
|
||||
-(void) OpenFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(NSString*)filters {
|
||||
|
||||
|
||||
// Create the dialog
|
||||
NSOpenPanel *dialog = [NSOpenPanel openPanel];
|
||||
|
||||
// Valid but appears to do nothing.... :/
|
||||
if( title != nil ) {
|
||||
[dialog setTitle:title];
|
||||
}
|
||||
|
||||
// Filters - semicolon delimited list of file extensions
|
||||
if( allowFiles ) {
|
||||
if( filters != nil ) {
|
||||
filters = [filters stringByReplacingOccurrencesOfString:@"*." withString:@""];
|
||||
filters = [filters stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
NSArray *filterList = [filters componentsSeparatedByString:@";"];
|
||||
[dialog setAllowedFileTypes:filterList];
|
||||
} else {
|
||||
[dialog setAllowsOtherFileTypes:true];
|
||||
}
|
||||
// Default Filename
|
||||
if( defaultFilename != nil ) {
|
||||
[dialog setNameFieldStringValue:defaultFilename];
|
||||
}
|
||||
|
||||
[dialog setAllowsMultipleSelection: allowMultipleSelection];
|
||||
[dialog setShowsHiddenFiles: showHiddenFiles];
|
||||
|
||||
}
|
||||
|
||||
// Default Directory
|
||||
if( defaultDirectory != nil ) {
|
||||
NSURL *url = [NSURL fileURLWithPath:defaultDirectory];
|
||||
[dialog setDirectoryURL:url];
|
||||
}
|
||||
|
||||
|
||||
// Setup Options
|
||||
[dialog setCanChooseFiles: allowFiles];
|
||||
[dialog setCanChooseDirectories: allowDirectories];
|
||||
[dialog setCanCreateDirectories: canCreateDirectories];
|
||||
[dialog setResolvesAliases: resolveAliases];
|
||||
[dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories];
|
||||
|
||||
// Setup callback handler
|
||||
[dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) {
|
||||
NSMutableArray *arr = [NSMutableArray new];
|
||||
for (NSURL *url in [dialog URLs]) {
|
||||
[arr addObject:[url path]];
|
||||
}
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:arr options:0 error:nil];
|
||||
NSString *nsjson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
processOpenFileDialogResponse([nsjson UTF8String]);
|
||||
}];
|
||||
|
||||
ON_MAIN_THREAD([dialog runModal];)
|
||||
|
||||
}
|
||||
|
||||
|
||||
-(void) SaveFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(NSString*)filters; {
|
||||
|
||||
|
||||
// Create the dialog
|
||||
NSSavePanel *dialog = [NSOpenPanel savePanel];
|
||||
|
||||
// Valid but appears to do nothing.... :/
|
||||
if( title != nil ) {
|
||||
[dialog setTitle:title];
|
||||
}
|
||||
|
||||
// Filters - semicolon delimited list of file extensions
|
||||
if( filters != nil ) {
|
||||
filters = [filters stringByReplacingOccurrencesOfString:@"*." withString:@""];
|
||||
filters = [filters stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
NSArray *filterList = [filters componentsSeparatedByString:@";"];
|
||||
[dialog setAllowedFileTypes:filterList];
|
||||
} else {
|
||||
[dialog setAllowsOtherFileTypes:true];
|
||||
}
|
||||
// Default Filename
|
||||
if( defaultFilename != nil ) {
|
||||
[dialog setNameFieldStringValue:defaultFilename];
|
||||
}
|
||||
|
||||
// Default Directory
|
||||
if( defaultDirectory != nil ) {
|
||||
NSURL *url = [NSURL fileURLWithPath:defaultDirectory];
|
||||
[dialog setDirectoryURL:url];
|
||||
}
|
||||
|
||||
// Setup Options
|
||||
[dialog setCanCreateDirectories: canCreateDirectories];
|
||||
[dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories];
|
||||
[dialog setShowsHiddenFiles: showHiddenFiles];
|
||||
|
||||
// Setup callback handler
|
||||
[dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) {
|
||||
NSURL *url = [dialog URL];
|
||||
processSaveFileDialogResponse([url.path UTF8String]);
|
||||
}];
|
||||
|
||||
ON_MAIN_THREAD([dialog runModal];)
|
||||
|
||||
}
|
||||
|
||||
- (void) SetAbout :(NSString*)title :(NSString*)description :(void*)imagedata :(int)datalen {
|
||||
self.aboutTitle = title;
|
||||
self.aboutDescription = description;
|
||||
|
||||
NSData *imageData = [NSData dataWithBytes:imagedata length:datalen];
|
||||
self.aboutImage = [[NSImage alloc] initWithData:imageData];
|
||||
}
|
||||
|
||||
-(void) About {
|
||||
|
||||
WailsAlert *alert = [WailsAlert new];
|
||||
[alert setAlertStyle:NSAlertStyleInformational];
|
||||
if( self.aboutTitle != nil ) {
|
||||
[alert setMessageText:self.aboutTitle];
|
||||
}
|
||||
if( self.aboutDescription != nil ) {
|
||||
[alert setInformativeText:self.aboutDescription];
|
||||
}
|
||||
|
||||
|
||||
[alert.window setLevel:NSFloatingWindowLevel];
|
||||
if ( self.aboutImage != nil) {
|
||||
[alert setIcon:self.aboutImage];
|
||||
}
|
||||
|
||||
ON_MAIN_THREAD([alert runModal];)
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
30
v2/internal/frontend/desktop/darwin/WailsMenu.h
Normal file
30
v2/internal/frontend/desktop/darwin/WailsMenu.h
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// WailsMenu.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 25/10/21.
|
||||
//
|
||||
|
||||
#ifndef WailsMenu_h
|
||||
#define WailsMenu_h
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "Role.h"
|
||||
#import "WailsMenu.h"
|
||||
#import "WailsContext.h"
|
||||
|
||||
@interface WailsMenu : NSMenu
|
||||
|
||||
//- (void) AddMenuByRole :(Role)role;
|
||||
- (WailsMenu*) initWithNSTitle :(NSString*)title;
|
||||
- (void) appendSubmenu :(WailsMenu*)child;
|
||||
- (void) appendRole :(WailsContext*)ctx :(Role)role;
|
||||
|
||||
- (NSMenuItem*) newMenuItemWithContext :(WailsContext*)ctx :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags;
|
||||
- (void*) AppendMenuItem :(WailsContext*)ctx :(NSString*)label :(NSString *)shortcutKey :(int)modifiers :(bool)disabled :(bool)checked :(int)menuItemID;
|
||||
- (void) AppendSeparator;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* WailsMenu_h */
|
||||
318
v2/internal/frontend/desktop/darwin/WailsMenu.m
Normal file
318
v2/internal/frontend/desktop/darwin/WailsMenu.m
Normal file
@@ -0,0 +1,318 @@
|
||||
//
|
||||
// WailsMenu.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 25/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "WailsMenu.h"
|
||||
#import "WailsMenuItem.h"
|
||||
#import "Role.h"
|
||||
|
||||
@implementation WailsMenu
|
||||
|
||||
- (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags {
|
||||
NSMenuItem *result = [[[NSMenuItem alloc] initWithTitle:title action:selector keyEquivalent:key] autorelease];
|
||||
[result setKeyEquivalentModifierMask:flags];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSMenuItem*) newMenuItemWithContext :(WailsContext*)ctx :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags {
|
||||
NSMenuItem *result = [[NSMenuItem new] autorelease];
|
||||
if ( title != nil ) {
|
||||
[result setTitle:title];
|
||||
}
|
||||
if (selector != nil) {
|
||||
[result setAction:selector];
|
||||
}
|
||||
if (key) {
|
||||
[result setKeyEquivalent:key];
|
||||
}
|
||||
if( flags != 0 ) {
|
||||
[result setKeyEquivalentModifierMask:flags];
|
||||
}
|
||||
result.target = ctx;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key {
|
||||
return [self newMenuItem :title :selector :key :0];
|
||||
}
|
||||
|
||||
- (WailsMenu*) initWithNSTitle:(NSString *)title {
|
||||
if( title != nil ) {
|
||||
[super initWithTitle:title];
|
||||
}
|
||||
[self setAutoenablesItems:NO];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) appendSubmenu :(WailsMenu*)child {
|
||||
NSMenuItem *childMenuItem = [[NSMenuItem new] autorelease];
|
||||
[childMenuItem setTitle:[child title]];
|
||||
[self addItem:childMenuItem];
|
||||
[childMenuItem setSubmenu:child];
|
||||
}
|
||||
|
||||
- (void) appendRole :(WailsContext*)ctx :(Role)role {
|
||||
|
||||
switch(role) {
|
||||
case AppMenu:
|
||||
{
|
||||
NSString *appName = [NSRunningApplication currentApplication].localizedName;
|
||||
if( appName == nil ) {
|
||||
appName = [[NSProcessInfo processInfo] processName];
|
||||
}
|
||||
WailsMenu *appMenu = [[WailsMenu new] initWithNSTitle:appName];
|
||||
id quitTitle = [@"Quit " stringByAppendingString:appName];
|
||||
NSMenuItem* quitMenuItem = [self newMenuItem:quitTitle :@selector(Quit) :@"q" :NSEventModifierFlagCommand];
|
||||
quitMenuItem.target = ctx;
|
||||
if (ctx.aboutTitle != nil) {
|
||||
[appMenu addItem:[self newMenuItemWithContext :ctx :[@"About " stringByAppendingString:appName] :@selector(About) :nil :0]];
|
||||
}
|
||||
[appMenu addItem:quitMenuItem];
|
||||
[self appendSubmenu:appMenu];
|
||||
break;
|
||||
}
|
||||
case EditMenu:
|
||||
{
|
||||
WailsMenu *editMenu = [[WailsMenu new] initWithNSTitle:@"Edit"];
|
||||
[editMenu addItem:[self newMenuItem:@"Undo" :@selector(undoActionName) :@"z" :NSEventModifierFlagCommand]];
|
||||
[editMenu addItem:[self newMenuItem:@"Redo" :@selector(redoActionName) :@"z" :(NSEventModifierFlagShift | NSEventModifierFlagCommand)]];
|
||||
[editMenu addItem:[NSMenuItem separatorItem]];
|
||||
[editMenu addItem:[self newMenuItem:@"Cut" :@selector(cut:) :@"x" :NSEventModifierFlagCommand]];
|
||||
[editMenu addItem:[self newMenuItem:@"Copy" :@selector(copy:) :@"c" :NSEventModifierFlagCommand]];
|
||||
[editMenu addItem:[self newMenuItem:@"Paste" :@selector(paste:) :@"v" :NSEventModifierFlagCommand]];
|
||||
[editMenu addItem:[self newMenuItem:@"Paste and Match Style" :@selector(pasteAsRichText:) :@"v" :(NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagCommand)]];
|
||||
[editMenu addItem:[self newMenuItem:@"Delete" :@selector(delete:) :[self accel:@"backspace"] :0]];
|
||||
[editMenu addItem:[self newMenuItem:@"Select All" :@selector(selectAll:) :@"a" :NSEventModifierFlagCommand]];
|
||||
[editMenu addItem:[NSMenuItem separatorItem]];
|
||||
// NSMenuItem *speechMenuItem = [[NSMenuItem new] autorelease];
|
||||
// [speechMenuItem setTitle:@"Speech"];
|
||||
// [editMenu addItem:speechMenuItem];
|
||||
WailsMenu *speechMenu = [[WailsMenu new] initWithNSTitle:@"Speech"];
|
||||
[speechMenu addItem:[self newMenuItem:@"Start Speaking" :@selector(startSpeaking:) :@""]];
|
||||
[speechMenu addItem:[self newMenuItem:@"Stop Speaking" :@selector(stopSpeaking:) :@""]];
|
||||
[editMenu appendSubmenu:speechMenu];
|
||||
[self appendSubmenu:editMenu];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void*) AppendMenuItem :(WailsContext*)ctx :(NSString*)label :(NSString *)shortcutKey :(int)modifiers :(bool)disabled :(bool)checked :(int)menuItemID {
|
||||
|
||||
NSString *nslabel = @"";
|
||||
if (label != nil ) {
|
||||
nslabel = label;
|
||||
}
|
||||
WailsMenuItem *menuItem = [WailsMenuItem new];
|
||||
|
||||
// Label
|
||||
menuItem.title = nslabel;
|
||||
|
||||
// Process callback
|
||||
menuItem.menuItemID = menuItemID;
|
||||
menuItem.action = @selector(handleClick);
|
||||
menuItem.target = menuItem;
|
||||
|
||||
// Shortcut
|
||||
if (shortcutKey != nil) {
|
||||
[menuItem setKeyEquivalent:[self accel:shortcutKey]];
|
||||
[menuItem setKeyEquivalentModifierMask:modifiers];
|
||||
}
|
||||
|
||||
// Enabled/Disabled
|
||||
[menuItem setEnabled:!disabled];
|
||||
|
||||
// Checked
|
||||
[menuItem setState:(checked ? NSControlStateValueOn : NSControlStateValueOff)];
|
||||
|
||||
[self addItem:menuItem];
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
- (void) AppendSeparator {
|
||||
[self addItem:[NSMenuItem separatorItem]];
|
||||
}
|
||||
|
||||
|
||||
- (NSString*) accel :(NSString*)key {
|
||||
|
||||
// Guard against no accelerator key
|
||||
if( key == NULL ) {
|
||||
return @"";
|
||||
}
|
||||
|
||||
if( [key isEqualToString:@"backspace"] ) {
|
||||
return unicode(0x0008);
|
||||
}
|
||||
if( [key isEqualToString:@"tab"] ) {
|
||||
return unicode(0x0009);
|
||||
}
|
||||
if( [key isEqualToString:@"return"] ) {
|
||||
return unicode(0x000d);
|
||||
}
|
||||
if( [key isEqualToString:@"enter"] ) {
|
||||
return unicode(0x000d);
|
||||
}
|
||||
if( [key isEqualToString:@"escape"] ) {
|
||||
return unicode(0x001b);
|
||||
}
|
||||
if( [key isEqualToString:@"left"] ) {
|
||||
return unicode(0x001c);
|
||||
}
|
||||
if( [key isEqualToString:@"right"] ) {
|
||||
return unicode(0x001d);
|
||||
}
|
||||
if( [key isEqualToString:@"up"] ) {
|
||||
return unicode(0x001e);
|
||||
}
|
||||
if( [key isEqualToString:@"down"] ) {
|
||||
return unicode(0x001f);
|
||||
}
|
||||
if( [key isEqualToString:@"space"] ) {
|
||||
return unicode(0x0020);
|
||||
}
|
||||
if( [key isEqualToString:@"delete"] ) {
|
||||
return unicode(0x007f);
|
||||
}
|
||||
if( [key isEqualToString:@"home"] ) {
|
||||
return unicode(0x2196);
|
||||
}
|
||||
if( [key isEqualToString:@"end"] ) {
|
||||
return unicode(0x2198);
|
||||
}
|
||||
if( [key isEqualToString:@"page up"] ) {
|
||||
return unicode(0x21de);
|
||||
}
|
||||
if( [key isEqualToString:@"page down"] ) {
|
||||
return unicode(0x21df);
|
||||
}
|
||||
if( [key isEqualToString:@"f1"] ) {
|
||||
return unicode(0xf704);
|
||||
}
|
||||
if( [key isEqualToString:@"f2"] ) {
|
||||
return unicode(0xf705);
|
||||
}
|
||||
if( [key isEqualToString:@"f3"] ) {
|
||||
return unicode(0xf706);
|
||||
}
|
||||
if( [key isEqualToString:@"f4"] ) {
|
||||
return unicode(0xf707);
|
||||
}
|
||||
if( [key isEqualToString:@"f5"] ) {
|
||||
return unicode(0xf708);
|
||||
}
|
||||
if( [key isEqualToString:@"f6"] ) {
|
||||
return unicode(0xf709);
|
||||
}
|
||||
if( [key isEqualToString:@"f7"] ) {
|
||||
return unicode(0xf70a);
|
||||
}
|
||||
if( [key isEqualToString:@"f8"] ) {
|
||||
return unicode(0xf70b);
|
||||
}
|
||||
if( [key isEqualToString:@"f9"] ) {
|
||||
return unicode(0xf70c);
|
||||
}
|
||||
if( [key isEqualToString:@"f10"] ) {
|
||||
return unicode(0xf70d);
|
||||
}
|
||||
if( [key isEqualToString:@"f11"] ) {
|
||||
return unicode(0xf70e);
|
||||
}
|
||||
if( [key isEqualToString:@"f12"] ) {
|
||||
return unicode(0xf70f);
|
||||
}
|
||||
if( [key isEqualToString:@"f13"] ) {
|
||||
return unicode(0xf710);
|
||||
}
|
||||
if( [key isEqualToString:@"f14"] ) {
|
||||
return unicode(0xf711);
|
||||
}
|
||||
if( [key isEqualToString:@"f15"] ) {
|
||||
return unicode(0xf712);
|
||||
}
|
||||
if( [key isEqualToString:@"f16"] ) {
|
||||
return unicode(0xf713);
|
||||
}
|
||||
if( [key isEqualToString:@"f17"] ) {
|
||||
return unicode(0xf714);
|
||||
}
|
||||
if( [key isEqualToString:@"f18"] ) {
|
||||
return unicode(0xf715);
|
||||
}
|
||||
if( [key isEqualToString:@"f19"] ) {
|
||||
return unicode(0xf716);
|
||||
}
|
||||
if( [key isEqualToString:@"f20"] ) {
|
||||
return unicode(0xf717);
|
||||
}
|
||||
if( [key isEqualToString:@"f21"] ) {
|
||||
return unicode(0xf718);
|
||||
}
|
||||
if( [key isEqualToString:@"f22"] ) {
|
||||
return unicode(0xf719);
|
||||
}
|
||||
if( [key isEqualToString:@"f23"] ) {
|
||||
return unicode(0xf71a);
|
||||
}
|
||||
if( [key isEqualToString:@"f24"] ) {
|
||||
return unicode(0xf71b);
|
||||
}
|
||||
if( [key isEqualToString:@"f25"] ) {
|
||||
return unicode(0xf71c);
|
||||
}
|
||||
if( [key isEqualToString:@"f26"] ) {
|
||||
return unicode(0xf71d);
|
||||
}
|
||||
if( [key isEqualToString:@"f27"] ) {
|
||||
return unicode(0xf71e);
|
||||
}
|
||||
if( [key isEqualToString:@"f28"] ) {
|
||||
return unicode(0xf71f);
|
||||
}
|
||||
if( [key isEqualToString:@"f29"] ) {
|
||||
return unicode(0xf720);
|
||||
}
|
||||
if( [key isEqualToString:@"f30"] ) {
|
||||
return unicode(0xf721);
|
||||
}
|
||||
if( [key isEqualToString:@"f31"] ) {
|
||||
return unicode(0xf722);
|
||||
}
|
||||
if( [key isEqualToString:@"f32"] ) {
|
||||
return unicode(0xf723);
|
||||
}
|
||||
if( [key isEqualToString:@"f33"] ) {
|
||||
return unicode(0xf724);
|
||||
}
|
||||
if( [key isEqualToString:@"f34"] ) {
|
||||
return unicode(0xf725);
|
||||
}
|
||||
if( [key isEqualToString:@"f35"] ) {
|
||||
return unicode(0xf726);
|
||||
}
|
||||
// if( [key isEqualToString:@"Insert"] ) {
|
||||
// return unicode(0xf727);
|
||||
// }
|
||||
// if( [key isEqualToString:@"PrintScreen"] ) {
|
||||
// return unicode(0xf72e);
|
||||
// }
|
||||
// if( [key isEqualToString:@"ScrollLock"] ) {
|
||||
// return unicode(0xf72f);
|
||||
// }
|
||||
if( [key isEqualToString:@"numLock"] ) {
|
||||
return unicode(0xf739);
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
22
v2/internal/frontend/desktop/darwin/WailsMenuItem.h
Normal file
22
v2/internal/frontend/desktop/darwin/WailsMenuItem.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// WailsMenuItem.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 27/10/21.
|
||||
//
|
||||
|
||||
#ifndef WailsMenuItem_h
|
||||
#define WailsMenuItem_h
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface WailsMenuItem : NSMenuItem
|
||||
|
||||
@property int menuItemID;
|
||||
|
||||
- (void) handleClick;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* WailsMenuItem_h */
|
||||
20
v2/internal/frontend/desktop/darwin/WailsMenuItem.m
Normal file
20
v2/internal/frontend/desktop/darwin/WailsMenuItem.m
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// WailsMenuItem.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 27/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "WailsMenuItem.h"
|
||||
#include "message.h"
|
||||
|
||||
|
||||
@implementation WailsMenuItem
|
||||
|
||||
- (void) handleClick {
|
||||
processCallback(self.menuItemID);
|
||||
}
|
||||
|
||||
@end
|
||||
18
v2/internal/frontend/desktop/darwin/WindowDelegate.h
Normal file
18
v2/internal/frontend/desktop/darwin/WindowDelegate.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// WindowDelegate.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#ifndef WindowDelegate_h
|
||||
#define WindowDelegate_h
|
||||
|
||||
@interface WindowDelegate : NSObject <NSWindowDelegate>
|
||||
|
||||
@property bool hideOnClose;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* WindowDelegate_h */
|
||||
24
v2/internal/frontend/desktop/darwin/WindowDelegate.m
Normal file
24
v2/internal/frontend/desktop/darwin/WindowDelegate.m
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// WindowDelegate.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "WindowDelegate.h"
|
||||
#import "message.h"
|
||||
#import "WailsContext.h"
|
||||
|
||||
@implementation WindowDelegate
|
||||
|
||||
- (BOOL)windowShouldClose:(WailsWindow *)sender {
|
||||
[sender orderOut:nil];
|
||||
if( self.hideOnClose == false ) {
|
||||
processMessage("Q");
|
||||
}
|
||||
return !self.hideOnClose;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,4 +1,5 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
|
||||
51
v2/internal/frontend/desktop/darwin/callbacks.go
Normal file
51
v2/internal/frontend/desktop/darwin/callbacks.go
Normal file
@@ -0,0 +1,51 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Application.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
)
|
||||
|
||||
func (f *Frontend) handleCallback(menuItemID uint) error {
|
||||
|
||||
menuItem := getMenuItemForID(menuItemID)
|
||||
if menuItem == nil {
|
||||
return errors.New("unknown menuItem ID: " + strconv.Itoa(int(menuItemID)))
|
||||
}
|
||||
|
||||
wailsMenuItem := menuItem.wailsMenuItem
|
||||
if wailsMenuItem.Type == menu.CheckboxType {
|
||||
wailsMenuItem.Checked = !wailsMenuItem.Checked
|
||||
C.UpdateMenuItem(menuItem.nsmenuitem, bool2Cint(wailsMenuItem.Checked))
|
||||
}
|
||||
if wailsMenuItem.Type == menu.RadioType {
|
||||
// Ignore if we clicked the item that is already checked
|
||||
if !wailsMenuItem.Checked {
|
||||
for _, item := range menuItem.radioGroupMembers {
|
||||
if item.wailsMenuItem.Checked {
|
||||
item.wailsMenuItem.Checked = false
|
||||
C.UpdateMenuItem(item.nsmenuitem, C.int(0))
|
||||
}
|
||||
}
|
||||
wailsMenuItem.Checked = true
|
||||
C.UpdateMenuItem(menuItem.nsmenuitem, C.int(1))
|
||||
}
|
||||
}
|
||||
if wailsMenuItem.Click != nil {
|
||||
go wailsMenuItem.Click(&menu.CallbackData{MenuItem: wailsMenuItem})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
32
v2/internal/frontend/desktop/darwin/calloc.go
Normal file
32
v2/internal/frontend/desktop/darwin/calloc.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
// Calloc handles alloc/dealloc of C data
|
||||
type Calloc struct {
|
||||
pool []unsafe.Pointer
|
||||
}
|
||||
|
||||
// NewCalloc creates a new allocator
|
||||
func NewCalloc() Calloc {
|
||||
return Calloc{}
|
||||
}
|
||||
|
||||
// String creates a new C string and retains a reference to it
|
||||
func (c Calloc) String(in string) *C.char {
|
||||
result := C.CString(in)
|
||||
c.pool = append(c.pool, unsafe.Pointer(result))
|
||||
return result
|
||||
}
|
||||
|
||||
// Free frees all allocated C memory
|
||||
func (c Calloc) Free() {
|
||||
for _, str := range c.pool {
|
||||
C.free(str)
|
||||
}
|
||||
c.pool = []unsafe.Pointer{}
|
||||
}
|
||||
@@ -1,32 +1,193 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Application.h"
|
||||
#import "WailsContext.h"
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
)
|
||||
|
||||
// Obj-C dialog methods send the response to this channel
|
||||
var messageDialogResponse = make(chan int)
|
||||
var openFileDialogResponse = make(chan string)
|
||||
var saveFileDialogResponse = make(chan string)
|
||||
var dialogLock sync.Mutex
|
||||
|
||||
// OpenDirectoryDialog prompts the user to select a directory
|
||||
func (f *Frontend) OpenDirectoryDialog(options frontend.OpenDialogOptions) (string, error) {
|
||||
return "", nil
|
||||
results, err := f.openDialog(&options, false, false, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var selected string
|
||||
if len(results) > 0 {
|
||||
selected = results[0]
|
||||
}
|
||||
return selected, nil
|
||||
}
|
||||
|
||||
func (f *Frontend) openDialog(options *frontend.OpenDialogOptions, multiple bool, allowfiles bool, allowdirectories bool) ([]string, error) {
|
||||
dialogLock.Lock()
|
||||
defer dialogLock.Unlock()
|
||||
|
||||
c := NewCalloc()
|
||||
defer c.Free()
|
||||
title := c.String(options.Title)
|
||||
defaultFilename := c.String(options.DefaultFilename)
|
||||
defaultDirectory := c.String(options.DefaultDirectory)
|
||||
allowDirectories := bool2Cint(allowdirectories)
|
||||
allowFiles := bool2Cint(allowfiles)
|
||||
canCreateDirectories := bool2Cint(options.CanCreateDirectories)
|
||||
treatPackagesAsDirectories := bool2Cint(options.TreatPackagesAsDirectories)
|
||||
resolveAliases := bool2Cint(options.ResolvesAliases)
|
||||
showHiddenFiles := bool2Cint(options.ShowHiddenFiles)
|
||||
allowMultipleFileSelection := bool2Cint(multiple)
|
||||
|
||||
var filterStrings slicer.StringSlicer
|
||||
if options.Filters != nil {
|
||||
for _, filter := range options.Filters {
|
||||
thesePatterns := strings.Split(filter.Pattern, ";")
|
||||
for _, pattern := range thesePatterns {
|
||||
pattern = strings.TrimSpace(pattern)
|
||||
if pattern != "" {
|
||||
filterStrings.Add(pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
filterStrings.Deduplicate()
|
||||
}
|
||||
filters := filterStrings.Join(";")
|
||||
C.OpenFileDialog(f.mainWindow.context, title, defaultFilename, defaultDirectory, allowDirectories, allowFiles, canCreateDirectories, treatPackagesAsDirectories, resolveAliases, showHiddenFiles, allowMultipleFileSelection, c.String(filters))
|
||||
|
||||
var result = <-openFileDialogResponse
|
||||
|
||||
var parsedResults []string
|
||||
err := json.Unmarshal([]byte(result), &parsedResults)
|
||||
|
||||
return parsedResults, err
|
||||
}
|
||||
|
||||
// OpenFileDialog prompts the user to select a file
|
||||
func (f *Frontend) OpenFileDialog(options frontend.OpenDialogOptions) (string, error) {
|
||||
return "", nil
|
||||
results, err := f.openDialog(&options, false, options.AllowFiles, options.AllowDirectories)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var selected string
|
||||
if len(results) > 0 {
|
||||
selected = results[0]
|
||||
}
|
||||
return selected, nil
|
||||
}
|
||||
|
||||
// OpenMultipleFilesDialog prompts the user to select a file
|
||||
func (f *Frontend) OpenMultipleFilesDialog(dialogOptions frontend.OpenDialogOptions) ([]string, error) {
|
||||
return []string{}, nil
|
||||
func (f *Frontend) OpenMultipleFilesDialog(options frontend.OpenDialogOptions) ([]string, error) {
|
||||
return f.openDialog(&options, true, options.AllowFiles, options.AllowDirectories)
|
||||
}
|
||||
|
||||
// SaveFileDialog prompts the user to select a file
|
||||
func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (string, error) {
|
||||
return "", nil
|
||||
func (f *Frontend) SaveFileDialog(options frontend.SaveDialogOptions) (string, error) {
|
||||
dialogLock.Lock()
|
||||
defer dialogLock.Unlock()
|
||||
|
||||
c := NewCalloc()
|
||||
defer c.Free()
|
||||
title := c.String(options.Title)
|
||||
defaultFilename := c.String(options.DefaultFilename)
|
||||
defaultDirectory := c.String(options.DefaultDirectory)
|
||||
canCreateDirectories := bool2Cint(options.CanCreateDirectories)
|
||||
treatPackagesAsDirectories := bool2Cint(options.TreatPackagesAsDirectories)
|
||||
showHiddenFiles := bool2Cint(options.ShowHiddenFiles)
|
||||
|
||||
var filterStrings slicer.StringSlicer
|
||||
if options.Filters != nil {
|
||||
for _, filter := range options.Filters {
|
||||
thesePatterns := strings.Split(filter.Pattern, ";")
|
||||
for _, pattern := range thesePatterns {
|
||||
pattern = strings.TrimSpace(pattern)
|
||||
if pattern != "" {
|
||||
filterStrings.Add(pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
filterStrings.Deduplicate()
|
||||
}
|
||||
filters := filterStrings.Join(";")
|
||||
C.SaveFileDialog(f.mainWindow.context, title, defaultFilename, defaultDirectory, canCreateDirectories, treatPackagesAsDirectories, showHiddenFiles, c.String(filters))
|
||||
|
||||
var result = <-saveFileDialogResponse
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// MessageDialog show a message dialog to the user
|
||||
func (f *Frontend) MessageDialog(options frontend.MessageDialogOptions) (string, error) {
|
||||
return "", nil
|
||||
dialogLock.Lock()
|
||||
defer dialogLock.Unlock()
|
||||
|
||||
c := NewCalloc()
|
||||
defer c.Free()
|
||||
dialogType := c.String(string(options.Type))
|
||||
title := c.String(options.Title)
|
||||
message := c.String(options.Message)
|
||||
defaultButton := c.String(options.DefaultButton)
|
||||
cancelButton := c.String(options.CancelButton)
|
||||
const MaxButtons = 4
|
||||
var buttons [MaxButtons]*C.char
|
||||
for index, buttonText := range options.Buttons {
|
||||
if index == MaxButtons {
|
||||
return "", fmt.Errorf("max %d buttons supported (%d given)", MaxButtons, len(options.Buttons))
|
||||
}
|
||||
buttons[index] = c.String(buttonText)
|
||||
}
|
||||
|
||||
var iconData unsafe.Pointer
|
||||
var iconDataLength C.int
|
||||
if options.Icon != nil {
|
||||
iconData = unsafe.Pointer(&options.Icon[0])
|
||||
iconDataLength = C.int(len(options.Icon))
|
||||
}
|
||||
|
||||
C.MessageDialog(f.mainWindow.context, dialogType, title, message, buttons[0], buttons[1], buttons[2], buttons[3], defaultButton, cancelButton, iconData, iconDataLength)
|
||||
|
||||
var result = <-messageDialogResponse
|
||||
|
||||
selectedC := buttons[result]
|
||||
var selected string
|
||||
if selectedC != nil {
|
||||
selected = options.Buttons[result]
|
||||
}
|
||||
return selected, nil
|
||||
}
|
||||
|
||||
//export processMessageDialogResponse
|
||||
func processMessageDialogResponse(selection int) {
|
||||
messageDialogResponse <- selection
|
||||
}
|
||||
|
||||
//export processOpenFileDialogResponse
|
||||
func processOpenFileDialogResponse(cselection *C.char) {
|
||||
selection := C.GoString(cselection)
|
||||
openFileDialogResponse <- selection
|
||||
}
|
||||
|
||||
//export processSaveFileDialogResponse
|
||||
func processSaveFileDialogResponse(cselection *C.char) {
|
||||
selection := C.GoString(cselection)
|
||||
saveFileDialogResponse <- selection
|
||||
}
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Application.h"
|
||||
#import "WailsContext.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
"log"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
@@ -16,6 +29,15 @@ import (
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
type request struct {
|
||||
url *C.char
|
||||
ctx unsafe.Pointer
|
||||
}
|
||||
|
||||
var messageBuffer = make(chan string, 100)
|
||||
var requestBuffer = make(chan *request, 100)
|
||||
var callbackBuffer = make(chan uint, 10)
|
||||
|
||||
type Frontend struct {
|
||||
|
||||
// Context
|
||||
@@ -29,7 +51,7 @@ type Frontend struct {
|
||||
assets *assetserver.DesktopAssetServer
|
||||
|
||||
// main window handle
|
||||
//mainWindow *Window
|
||||
mainWindow *Window
|
||||
minWidth, minHeight, maxWidth, maxHeight int
|
||||
bindings *binding.Bindings
|
||||
dispatcher frontend.Dispatcher
|
||||
@@ -44,10 +66,6 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
||||
bindings: appBindings,
|
||||
dispatcher: dispatcher,
|
||||
ctx: ctx,
|
||||
minHeight: appoptions.MinHeight,
|
||||
minWidth: appoptions.MinWidth,
|
||||
maxHeight: appoptions.MaxHeight,
|
||||
maxWidth: appoptions.MaxWidth,
|
||||
}
|
||||
|
||||
// Check if we have been given a directory to serve assets from.
|
||||
@@ -69,9 +87,32 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
||||
}
|
||||
result.assets = assets
|
||||
|
||||
go result.startMessageProcessor()
|
||||
go result.startRequestProcessor()
|
||||
go result.startCallbackProcessor()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *Frontend) startMessageProcessor() {
|
||||
for message := range messageBuffer {
|
||||
f.processMessage(message)
|
||||
}
|
||||
}
|
||||
func (f *Frontend) startRequestProcessor() {
|
||||
for request := range requestBuffer {
|
||||
f.processRequest(request)
|
||||
}
|
||||
}
|
||||
func (f *Frontend) startCallbackProcessor() {
|
||||
for callback := range callbackBuffer {
|
||||
err := f.handleCallback(callback)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowReload() {
|
||||
f.ExecJS("runtime.WindowReload();")
|
||||
}
|
||||
@@ -80,222 +121,104 @@ func (f *Frontend) Run(ctx context.Context) error {
|
||||
|
||||
f.ctx = context.WithValue(ctx, "frontend", f)
|
||||
|
||||
//mainWindow := NewWindow(nil, f.frontendOptions)
|
||||
//f.mainWindow = mainWindow
|
||||
//
|
||||
//var _debug = ctx.Value("debug")
|
||||
//if _debug != nil {
|
||||
// f.debug = _debug.(bool)
|
||||
//}
|
||||
//
|
||||
//f.WindowCenter()
|
||||
//f.setupChromium()
|
||||
//
|
||||
//mainWindow.OnSize().Bind(func(arg *winc.Event) {
|
||||
// f.chromium.Resize()
|
||||
//})
|
||||
//
|
||||
//mainWindow.OnClose().Bind(func(arg *winc.Event) {
|
||||
// if f.frontendOptions.HideWindowOnClose {
|
||||
// f.WindowHide()
|
||||
// } else {
|
||||
// f.Quit()
|
||||
// }
|
||||
//})
|
||||
//
|
||||
//// TODO: Move this into a callback from frontend
|
||||
//go func() {
|
||||
// if f.frontendOptions.OnStartup != nil {
|
||||
// f.frontendOptions.OnStartup(f.ctx)
|
||||
// }
|
||||
//}()
|
||||
//
|
||||
//mainWindow.Run()
|
||||
var _debug = ctx.Value("debug")
|
||||
if _debug != nil {
|
||||
f.debug = _debug.(bool)
|
||||
}
|
||||
|
||||
mainWindow := NewWindow(f.frontendOptions, f.debug)
|
||||
f.mainWindow = mainWindow
|
||||
f.mainWindow.Center()
|
||||
|
||||
go func() {
|
||||
if f.frontendOptions.OnStartup != nil {
|
||||
f.frontendOptions.OnStartup(f.ctx)
|
||||
}
|
||||
}()
|
||||
mainWindow.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowCenter() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Center()
|
||||
f.mainWindow.Center()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowSetPos(x, y int) {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.SetPos(x, y)
|
||||
f.mainWindow.SetPos(x, y)
|
||||
}
|
||||
func (f *Frontend) WindowGetPos() (int, int) {
|
||||
runtime.LockOSThread()
|
||||
//return f.mainWindow.Pos()
|
||||
return 0, 0
|
||||
return f.mainWindow.Pos()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowSetSize(width, height int) {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.SetSize(width, height)
|
||||
f.mainWindow.SetSize(width, height)
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowGetSize() (int, int) {
|
||||
runtime.LockOSThread()
|
||||
//return f.mainWindow.Size()
|
||||
return 0, 0
|
||||
return f.mainWindow.Size()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowSetTitle(title string) {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.SetText(title)
|
||||
f.mainWindow.SetTitle(title)
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowFullscreen() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.SetMaxSize(0, 0)
|
||||
//f.mainWindow.SetMinSize(0, 0)
|
||||
//f.mainWindow.Fullscreen()
|
||||
f.mainWindow.SetMaxSize(0, 0)
|
||||
f.mainWindow.SetMinSize(0, 0)
|
||||
f.mainWindow.Fullscreen()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowUnFullscreen() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.UnFullscreen()
|
||||
//f.mainWindow.SetMaxSize(f.maxWidth, f.maxHeight)
|
||||
//f.mainWindow.SetMinSize(f.minWidth, f.minHeight)
|
||||
f.mainWindow.UnFullscreen()
|
||||
f.mainWindow.SetMaxSize(f.maxWidth, f.maxHeight)
|
||||
f.mainWindow.SetMinSize(f.minWidth, f.minHeight)
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowShow() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Show()
|
||||
f.mainWindow.Show()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowHide() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Hide()
|
||||
f.mainWindow.Hide()
|
||||
}
|
||||
func (f *Frontend) WindowMaximise() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Maximise()
|
||||
f.mainWindow.Maximise()
|
||||
}
|
||||
func (f *Frontend) WindowUnmaximise() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Restore()
|
||||
f.mainWindow.UnMaximise()
|
||||
}
|
||||
func (f *Frontend) WindowMinimise() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Minimise()
|
||||
f.mainWindow.Minimise()
|
||||
}
|
||||
func (f *Frontend) WindowUnminimise() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Restore()
|
||||
f.mainWindow.UnMinimise()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowSetMinSize(width int, height int) {
|
||||
runtime.LockOSThread()
|
||||
f.minWidth = width
|
||||
f.minHeight = height
|
||||
//f.mainWindow.SetMinSize(width, height)
|
||||
f.mainWindow.SetMinSize(width, height)
|
||||
}
|
||||
func (f *Frontend) WindowSetMaxSize(width int, height int) {
|
||||
runtime.LockOSThread()
|
||||
f.maxWidth = width
|
||||
f.maxHeight = height
|
||||
//f.mainWindow.SetMaxSize(width, height)
|
||||
f.mainWindow.SetMaxSize(width, height)
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowSetRGBA(col *options.RGBA) {
|
||||
runtime.LockOSThread()
|
||||
if col == nil {
|
||||
return
|
||||
}
|
||||
/*
|
||||
|
||||
//f.mainWindow.Dispatch(func() {
|
||||
controller := f.chromium.GetController()
|
||||
controller2 := controller.GetICoreWebView2Controller2()
|
||||
|
||||
backgroundCol := edge.COREWEBVIEW2_COLOR{
|
||||
A: col.A,
|
||||
R: col.R,
|
||||
G: col.G,
|
||||
B: col.B,
|
||||
}
|
||||
|
||||
// Webview2 only has 0 and 255 as valid values.
|
||||
if backgroundCol.A > 0 && backgroundCol.A < 255 {
|
||||
backgroundCol.A = 255
|
||||
}
|
||||
|
||||
if f.frontendOptions.Windows != nil && f.frontendOptions.Windows.WebviewIsTransparent {
|
||||
backgroundCol.A = 0
|
||||
}
|
||||
|
||||
err := controller2.PutDefaultBackgroundColor(backgroundCol)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
})
|
||||
*/
|
||||
f.mainWindow.SetRGBA(col.R, col.G, col.B, col.A)
|
||||
}
|
||||
|
||||
func (f *Frontend) Quit() {
|
||||
//winc.Exit()
|
||||
f.mainWindow.Quit()
|
||||
if f.frontendOptions.OnShutdown != nil {
|
||||
f.frontendOptions.OnShutdown(f.ctx)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
const (
|
||||
ctrlZ int = 90
|
||||
ctrlX = 88
|
||||
ctrlC = 67
|
||||
ctrlV = 86
|
||||
)
|
||||
|
||||
func (f *Frontend) setupChromium() {
|
||||
chromium := edge.NewChromium()
|
||||
f.chromium = chromium
|
||||
chromium.MessageCallback = f.processMessage
|
||||
chromium.WebResourceRequestedCallback = f.processRequest
|
||||
chromium.NavigationCompletedCallback = f.navigationCompleted
|
||||
acceleratorsWebviewShouldProcess := slicer.Int([]int{ctrlV, ctrlC, ctrlX, ctrlZ})
|
||||
chromium.AcceleratorKeyCallback = func(vkey uint) bool {
|
||||
// We want webview to handle ctrl-C, ctrl-Z, ctrl-v, ctrl-x
|
||||
if acceleratorsWebviewShouldProcess.Contains(int(vkey)) {
|
||||
return false
|
||||
}
|
||||
// Post keypress
|
||||
//w32.PostMessage(f.mainWindow.Handle(), w32.WM_KEYDOWN, uintptr(vkey), 0)
|
||||
return true
|
||||
}
|
||||
chromium.Embed(f.mainWindow.Handle())
|
||||
chromium.Resize()
|
||||
settings, err := chromium.GetSettings()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutAreDefaultContextMenusEnabled(f.debug)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutAreDevToolsEnabled(f.debug)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutIsZoomControlEnabled(false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutIsStatusBarEnabled(false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutIsStatusBarEnabled(false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Set background colour
|
||||
f.WindowSetRGBA(f.frontendOptions.RGBA)
|
||||
|
||||
chromium.AddWebResourceRequestedFilter("*", edge.COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL)
|
||||
chromium.Navigate("file://wails/")
|
||||
}
|
||||
*/
|
||||
|
||||
type EventNotify struct {
|
||||
Name string `json:"name"`
|
||||
Data []interface{} `json:"data"`
|
||||
@@ -314,47 +237,15 @@ func (f *Frontend) Notify(name string, data ...interface{}) {
|
||||
f.ExecJS(`window.wails.EventsNotify('` + template.JSEscapeString(string(payload)) + `');`)
|
||||
}
|
||||
|
||||
//func (f *Frontend) processRequest(req *edge.ICoreWebView2WebResourceRequest, args *edge.ICoreWebView2WebResourceRequestedEventArgs) {
|
||||
// //Get the request
|
||||
// uri, _ := req.GetUri()
|
||||
//
|
||||
// // Translate URI
|
||||
// uri = strings.TrimPrefix(uri, "file://wails")
|
||||
// if !strings.HasPrefix(uri, "/") {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // Load file from asset store
|
||||
// content, mimeType, err := f.assets.Load(uri)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// env := f.chromium.Environment()
|
||||
// headers := "Content-Type: " + mimeType
|
||||
// if f.servingFromDisk {
|
||||
// headers += "\nPragma: no-cache"
|
||||
// }
|
||||
// response, err := env.CreateWebResourceResponse(content, 200, "OK", headers)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// // Send response back
|
||||
// err = args.PutResponse(response)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// return
|
||||
//}
|
||||
|
||||
func (f *Frontend) processMessage(message string) {
|
||||
if message == "drag" {
|
||||
err := f.startDrag()
|
||||
if err != nil {
|
||||
f.logger.Error(err.Error())
|
||||
|
||||
if message == "DomReady" {
|
||||
if f.frontendOptions.OnDomReady != nil {
|
||||
f.frontendOptions.OnDomReady(f.ctx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
result, err := f.dispatcher.ProcessMessage(message, f)
|
||||
if err != nil {
|
||||
f.logger.Error(err.Error())
|
||||
@@ -375,44 +266,50 @@ func (f *Frontend) processMessage(message string) {
|
||||
}
|
||||
|
||||
func (f *Frontend) Callback(message string) {
|
||||
//f.mainWindow.Dispatch(func() {
|
||||
// f.chromium.Eval(`window.wails.Callback(` + strconv.Quote(message) + `);`)
|
||||
//})
|
||||
}
|
||||
|
||||
func (f *Frontend) startDrag() error {
|
||||
//if !w32.ReleaseCapture() {
|
||||
// return fmt.Errorf("unable to release mouse capture")
|
||||
//}
|
||||
//w32.SendMessage(f.mainWindow.Handle(), w32.WM_NCLBUTTONDOWN, w32.HTCAPTION, 0)
|
||||
return nil
|
||||
f.ExecJS(`window.wails.Callback(` + strconv.Quote(message) + `);`)
|
||||
}
|
||||
|
||||
func (f *Frontend) ExecJS(js string) {
|
||||
//f.mainWindow.Dispatch(func() {
|
||||
// f.chromium.Eval(js)
|
||||
//})
|
||||
f.mainWindow.ExecJS(js)
|
||||
}
|
||||
|
||||
//func (f *Frontend) navigationCompleted(sender *edge.ICoreWebView2, args *edge.ICoreWebView2NavigationCompletedEventArgs) {
|
||||
// if f.frontendOptions.OnDomReady != nil {
|
||||
// go f.frontendOptions.OnDomReady(f.ctx)
|
||||
// }
|
||||
//
|
||||
// // If you want to start hidden, return
|
||||
// if f.frontendOptions.StartHidden {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // Hack to make it visible: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1077#issuecomment-825375026
|
||||
// err := f.chromium.Hide()
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// err = f.chromium.Show()
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// f.mainWindow.Show()
|
||||
//
|
||||
//}
|
||||
func (f *Frontend) processRequest(r *request) {
|
||||
url := C.GoString(r.url)
|
||||
url = strings.TrimPrefix(url, "wails://wails")
|
||||
if !strings.HasPrefix(url, "/") {
|
||||
return
|
||||
}
|
||||
_contents, _mimetype, err := f.assets.Load(url)
|
||||
if err != nil {
|
||||
f.logger.Error(err.Error())
|
||||
//TODO: Handle errors
|
||||
return
|
||||
}
|
||||
var data unsafe.Pointer
|
||||
if _contents != nil {
|
||||
data = unsafe.Pointer(&_contents[0])
|
||||
}
|
||||
mimetype := C.CString(_mimetype)
|
||||
defer C.free(unsafe.Pointer(mimetype))
|
||||
|
||||
C.ProcessURLResponse(r.ctx, r.url, mimetype, data, C.int(len(_contents)))
|
||||
}
|
||||
|
||||
//export processMessage
|
||||
func processMessage(message *C.char) {
|
||||
goMessage := C.GoString(message)
|
||||
messageBuffer <- goMessage
|
||||
}
|
||||
|
||||
//export processURLRequest
|
||||
func processURLRequest(ctx unsafe.Pointer, url *C.char) {
|
||||
requestBuffer <- &request{
|
||||
url: url,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
//export processCallback
|
||||
func processCallback(callbackID uint) {
|
||||
callbackBuffer <- callbackID
|
||||
}
|
||||
|
||||
237
v2/internal/frontend/desktop/darwin/main.m
Normal file
237
v2/internal/frontend/desktop/darwin/main.m
Normal file
@@ -0,0 +1,237 @@
|
||||
//go:build ignore
|
||||
// main.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
// ****** This file is used for testing purposes only ******
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Application.h"
|
||||
|
||||
void processMessage(const char*t) {
|
||||
NSLog(@"processMessage called");
|
||||
}
|
||||
|
||||
void processMessageDialogResponse(int t) {
|
||||
NSLog(@"processMessage called");
|
||||
}
|
||||
|
||||
void processOpenFileDialogResponse(const char *t) {
|
||||
NSLog(@"processMessage called %s", t);
|
||||
}
|
||||
void processSaveFileDialogResponse(const char *t) {
|
||||
NSLog(@"processMessage called %s", t);
|
||||
}
|
||||
|
||||
void processCallback(int callbackID) {
|
||||
NSLog(@"Process callback %d", callbackID);
|
||||
}
|
||||
|
||||
void processURLRequest(void *ctx, const char* url) {
|
||||
NSLog(@"processURLRequest called");
|
||||
const char myByteArray[] = { 0x3c,0x68,0x31,0x3e,0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21,0x3c,0x2f,0x68,0x31,0x3e };
|
||||
ProcessURLResponse(ctx, url, "text/html", (void*)myByteArray, 21);
|
||||
}
|
||||
|
||||
unsigned char _Users_username_Pictures_SaltBae_png[] = {
|
||||
|
||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14,
|
||||
0x08, 0x06, 0x00, 0x00, 0x00, 0x8d, 0x89, 0x1d, 0x0d, 0x00, 0x00, 0x00,
|
||||
0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61,
|
||||
0x05, 0x00, 0x00, 0x00, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x00, 0x00, 0x7a,
|
||||
0x26, 0x00, 0x00, 0x80, 0x84, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0x80,
|
||||
0xe8, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0xea, 0x60, 0x00, 0x00, 0x3a,
|
||||
0x98, 0x00, 0x00, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b,
|
||||
0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x01, 0xd5, 0x69, 0x54,
|
||||
0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64,
|
||||
0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78,
|
||||
0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62,
|
||||
0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20,
|
||||
0x78, 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x58, 0x4d, 0x50,
|
||||
0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x22,
|
||||
0x3e, 0x0a, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44,
|
||||
0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d,
|
||||
0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
|
||||
0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f,
|
||||
0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79,
|
||||
0x6e, 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65,
|
||||
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64,
|
||||
0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78,
|
||||
0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x74, 0x69, 0x66, 0x66, 0x3d, 0x22, 0x68,
|
||||
0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f,
|
||||
0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x2f,
|
||||
0x31, 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f,
|
||||
0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c,
|
||||
0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65,
|
||||
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72,
|
||||
0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x31, 0x3c,
|
||||
0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x3c, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x50, 0x68,
|
||||
0x6f, 0x74, 0x6f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x49, 0x6e, 0x74,
|
||||
0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e,
|
||||
0x32, 0x3c, 0x2f, 0x74, 0x69, 0x66, 0x66, 0x3a, 0x50, 0x68, 0x6f, 0x74,
|
||||
0x6f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x49, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44,
|
||||
0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46,
|
||||
0x3e, 0x0a, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74,
|
||||
0x61, 0x3e, 0x0a, 0x02, 0xd8, 0x80, 0x05, 0x00, 0x00, 0x04, 0xdc, 0x49,
|
||||
0x44, 0x41, 0x54, 0x38, 0x11, 0x1d, 0x94, 0x49, 0x6c, 0x1b, 0x65, 0x18,
|
||||
0x86, 0x9f, 0x99, 0xf9, 0x67, 0xc6, 0x6b, 0xbc, 0x26, 0xce, 0xda, 0xa4,
|
||||
0x25, 0x69, 0x0b, 0x2d, 0x28, 0x34, 0x2c, 0x95, 0x00, 0x89, 0x45, 0x08,
|
||||
0x5a, 0x95, 0x03, 0x08, 0x09, 0x21, 0xe0, 0x80, 0x38, 0xc3, 0x85, 0x03,
|
||||
0xe2, 0x00, 0x47, 0xc4, 0x1d, 0x38, 0x70, 0xe3, 0xc6, 0x01, 0x01, 0x42,
|
||||
0x20, 0x54, 0x7a, 0x2a, 0x6b, 0x0b, 0x94, 0xd2, 0xd2, 0x25, 0x69, 0x9b,
|
||||
0xa4, 0x0d, 0x2d, 0xa9, 0xb3, 0x78, 0x89, 0x9d, 0xf1, 0x2c, 0x9e, 0x85,
|
||||
0x2f, 0xb5, 0x35, 0xb6, 0x35, 0x96, 0xde, 0x79, 0xdf, 0xef, 0x7f, 0x9f,
|
||||
0x4f, 0xfb, 0xe0, 0xad, 0x37, 0x12, 0xfd, 0xf0, 0xb3, 0x9c, 0xfb, 0xb7,
|
||||
0xc5, 0x8d, 0x46, 0x9b, 0x71, 0x5b, 0xf1, 0xd0, 0xf4, 0x18, 0xdb, 0xeb,
|
||||
0x4b, 0x1c, 0xff, 0xf1, 0x57, 0x98, 0xdc, 0x87, 0x72, 0x3a, 0x8c, 0x3a,
|
||||
0xcb, 0x8c, 0xea, 0x31, 0x35, 0xb7, 0xc3, 0x99, 0xba, 0xc3, 0xd7, 0xab,
|
||||
0x3e, 0x87, 0x2a, 0x8a, 0xb3, 0xff, 0xdc, 0xe0, 0x9b, 0x8f, 0x5f, 0xa2,
|
||||
0x1c, 0xc5, 0xfc, 0x72, 0xc9, 0x41, 0x99, 0x71, 0x48, 0xca, 0x84, 0x3c,
|
||||
0x3e, 0xda, 0xd2, 0x05, 0x9a, 0xb1, 0xc7, 0x35, 0x67, 0x1c, 0xdd, 0x4c,
|
||||
0x68, 0xeb, 0x26, 0xd9, 0x30, 0x26, 0x09, 0x23, 0x5c, 0x3f, 0xc2, 0xd3,
|
||||
0x43, 0xc2, 0x24, 0x21, 0x4e, 0x34, 0x40, 0x27, 0x89, 0x13, 0xf9, 0x1e,
|
||||
0x22, 0x6e, 0xd5, 0x45, 0x43, 0x63, 0xc6, 0xd2, 0x50, 0xa9, 0xc4, 0x67,
|
||||
0x24, 0x15, 0x72, 0xa9, 0x7e, 0x95, 0xfa, 0x4f, 0x27, 0x78, 0x64, 0x76,
|
||||
0x86, 0x23, 0x61, 0xc0, 0xf0, 0x58, 0x15, 0xc3, 0x29, 0x71, 0x06, 0x45,
|
||||
0x2e, 0xa5, 0x48, 0xbb, 0x0a, 0x3d, 0x89, 0xa0, 0x8f, 0x08, 0x8a, 0x8e,
|
||||
0x08, 0xbb, 0xc1, 0x8e, 0xb0, 0x8d, 0xdd, 0x0f, 0xc9, 0x84, 0x06, 0x65,
|
||||
0x34, 0xf4, 0xed, 0x8d, 0xff, 0x58, 0xbd, 0xfc, 0x27, 0x17, 0x2f, 0x9e,
|
||||
0xe3, 0xf0, 0x81, 0x49, 0x5e, 0xde, 0x5f, 0xe1, 0x9e, 0x82, 0xcd, 0xdc,
|
||||
0x78, 0x8d, 0xd9, 0xb2, 0xc9, 0x56, 0x12, 0x32, 0x94, 0x4f, 0x91, 0xcb,
|
||||
0x88, 0x68, 0xda, 0x42, 0x13, 0x77, 0x11, 0xa2, 0xa8, 0xc3, 0x5a, 0x5f,
|
||||
0x46, 0x30, 0x65, 0x52, 0x29, 0xe4, 0x24, 0x4d, 0x8e, 0xcc, 0x68, 0x19,
|
||||
0xe5, 0x76, 0xbb, 0xac, 0x5c, 0x98, 0xa7, 0xb3, 0xed, 0xd0, 0x37, 0x62,
|
||||
0xa2, 0xb0, 0xc7, 0x89, 0xe5, 0x2e, 0x03, 0x0d, 0x97, 0x95, 0x46, 0x8f,
|
||||
0x31, 0xd7, 0xa6, 0x63, 0x81, 0x65, 0x25, 0x84, 0xba, 0x45, 0x5f, 0x65,
|
||||
0x31, 0x2c, 0x71, 0x6b, 0x77, 0x69, 0xf5, 0x7a, 0xbc, 0xb0, 0x3b, 0xcd,
|
||||
0xf9, 0xa5, 0x90, 0xd1, 0xb0, 0xcd, 0xd4, 0xb0, 0xdc, 0xd7, 0xc4, 0xfa,
|
||||
0xf0, 0x78, 0x95, 0x7b, 0x27, 0xab, 0x5c, 0x5e, 0x6e, 0xd2, 0xee, 0x05,
|
||||
0xdc, 0xd8, 0xea, 0xf1, 0xf7, 0xe2, 0x1a, 0xc7, 0xee, 0x1a, 0x62, 0x2e,
|
||||
0x1f, 0xe3, 0xe8, 0xb6, 0xc4, 0x4c, 0xd3, 0x6d, 0x6e, 0xd0, 0x6b, 0xfc,
|
||||
0x4c, 0xe3, 0xd4, 0x1f, 0xc4, 0x4b, 0xf3, 0x1c, 0x2c, 0x65, 0x29, 0x67,
|
||||
0x4d, 0xbe, 0xfb, 0xad, 0x45, 0x65, 0x0c, 0xea, 0x7e, 0x1f, 0x15, 0x6b,
|
||||
0x09, 0x0b, 0x8b, 0xb7, 0x19, 0xc9, 0xa5, 0x78, 0x75, 0x6e, 0x18, 0xdf,
|
||||
0xf5, 0x79, 0x72, 0xd0, 0xa2, 0x2d, 0xb3, 0x3a, 0xbb, 0xb4, 0x41, 0x3e,
|
||||
0x53, 0xe6, 0xf4, 0xca, 0x3c, 0xa5, 0x7c, 0x86, 0xe9, 0xfd, 0x47, 0x18,
|
||||
0x2e, 0xbd, 0xce, 0xd1, 0x97, 0x26, 0x78, 0xbc, 0x7e, 0x1d, 0xff, 0xcc,
|
||||
0xa7, 0x5c, 0x71, 0x74, 0x16, 0xe3, 0x18, 0xd7, 0x1e, 0x23, 0xe8, 0xac,
|
||||
0xa3, 0x0c, 0xcd, 0x60, 0x22, 0x6f, 0x43, 0x36, 0x43, 0x3b, 0x19, 0xc6,
|
||||
0x08, 0x7a, 0xe0, 0x6c, 0xe3, 0x27, 0x8a, 0xdb, 0x4e, 0xc0, 0xd4, 0xa0,
|
||||
0xcd, 0x27, 0xaf, 0xbd, 0xcb, 0x86, 0x36, 0xc6, 0xcc, 0xfe, 0x59, 0xd2,
|
||||
0xca, 0x90, 0x93, 0x36, 0x70, 0xaf, 0x9c, 0xe4, 0xcb, 0x6f, 0x65, 0x54,
|
||||
0xd9, 0x47, 0x59, 0x70, 0xbb, 0x74, 0x1b, 0x0e, 0x89, 0xe7, 0xa3, 0xc7,
|
||||
0x12, 0x39, 0x63, 0xea, 0x68, 0x12, 0x6b, 0x53, 0x5c, 0x9e, 0xef, 0x76,
|
||||
0xf0, 0x55, 0x86, 0x0d, 0x17, 0x56, 0x9a, 0x4d, 0x94, 0x95, 0x65, 0xe6,
|
||||
0xbe, 0x67, 0x98, 0xbe, 0xfb, 0x21, 0x52, 0xd2, 0x43, 0xaf, 0x5d, 0x47,
|
||||
0x6b, 0x5c, 0xa3, 0x59, 0xbf, 0xc2, 0x62, 0xdd, 0x26, 0xa5, 0x12, 0x6a,
|
||||
0x41, 0x44, 0xdf, 0xbd, 0xcd, 0x92, 0x17, 0xa0, 0xb6, 0x03, 0x43, 0xba,
|
||||
0x66, 0x91, 0xe9, 0xdc, 0xc2, 0xce, 0xed, 0xa1, 0xfc, 0xc0, 0x2b, 0x14,
|
||||
0xff, 0xfd, 0x1e, 0x4b, 0xb3, 0xa9, 0x29, 0x87, 0x81, 0xd2, 0x04, 0x8e,
|
||||
0x66, 0x89, 0x58, 0x00, 0x7e, 0x07, 0xaf, 0xdb, 0xa4, 0xbb, 0xb5, 0x49,
|
||||
0xb9, 0xaa, 0x18, 0xb9, 0x77, 0x8e, 0xcd, 0xdb, 0x6d, 0x1e, 0x1c, 0xb5,
|
||||
0x38, 0x7d, 0xa5, 0xcf, 0xaa, 0x08, 0xeb, 0x77, 0x3f, 0x35, 0xc7, 0xda,
|
||||
0xfc, 0x02, 0xaa, 0xf6, 0x1c, 0xbb, 0x9f, 0x78, 0x9f, 0x89, 0x43, 0x47,
|
||||
0xa4, 0x6f, 0x3d, 0x06, 0xed, 0x90, 0x92, 0x79, 0x95, 0xd4, 0xe4, 0xfd,
|
||||
0x98, 0x66, 0x4a, 0x6a, 0xd7, 0xc7, 0x0b, 0x62, 0xa4, 0xe3, 0x8c, 0x4d,
|
||||
0xc4, 0xe8, 0x85, 0x98, 0xe5, 0x46, 0x44, 0x26, 0x97, 0x21, 0xe9, 0xf7,
|
||||
0xf9, 0x61, 0xc5, 0xe3, 0xd4, 0x66, 0x84, 0xd2, 0x70, 0xc9, 0xee, 0x79,
|
||||
0x98, 0x43, 0xc7, 0x5e, 0x27, 0xb6, 0x8a, 0xd2, 0x5a, 0x1f, 0xf3, 0xa9,
|
||||
0xf7, 0x88, 0xce, 0x7d, 0x85, 0x71, 0xe0, 0x79, 0x98, 0x7a, 0x90, 0x9e,
|
||||
0x1b, 0xd0, 0x13, 0x52, 0x4a, 0x66, 0x97, 0x7d, 0x33, 0x1e, 0xed, 0xae,
|
||||
0xc7, 0x87, 0x1f, 0x7d, 0xce, 0xc2, 0xd5, 0x3a, 0xe6, 0xde, 0x02, 0xcb,
|
||||
0xdb, 0x3e, 0xbe, 0xa6, 0x91, 0x95, 0x62, 0x6b, 0x2f, 0xce, 0x90, 0x3c,
|
||||
0xfd, 0xce, 0x71, 0x0e, 0xcc, 0x3e, 0x82, 0x13, 0xf4, 0x09, 0xd5, 0x00,
|
||||
0x16, 0x82, 0x98, 0xb3, 0x49, 0x24, 0xb1, 0x83, 0xc8, 0xc0, 0xd6, 0x3a,
|
||||
0x54, 0x33, 0xab, 0x14, 0x8c, 0x16, 0x4e, 0x38, 0xcc, 0xe5, 0xeb, 0x4d,
|
||||
0x5e, 0x7b, 0xfb, 0x4d, 0xaa, 0x79, 0xa1, 0x45, 0x1c, 0x9b, 0xd2, 0x94,
|
||||
0xcc, 0x0e, 0x8c, 0x52, 0x7a, 0x65, 0x17, 0xc7, 0xa9, 0x0c, 0x8e, 0xe2,
|
||||
0xf7, 0xba, 0xa8, 0xc8, 0x13, 0x87, 0x32, 0x87, 0x0b, 0x27, 0x30, 0x36,
|
||||
0x57, 0xe8, 0xea, 0x15, 0xce, 0x06, 0x65, 0x5e, 0x3d, 0x5a, 0x94, 0x53,
|
||||
0xb7, 0x59, 0x58, 0xdf, 0x25, 0xc4, 0xe4, 0xc9, 0x65, 0x3d, 0xb4, 0xb4,
|
||||
0x4e, 0x37, 0x0c, 0x29, 0x98, 0x4a, 0xe8, 0x11, 0xde, 0x85, 0x42, 0x43,
|
||||
0x1c, 0xaa, 0x38, 0x55, 0xc4, 0xb4, 0x2c, 0x22, 0x3d, 0xcd, 0xfa, 0xea,
|
||||
0x0d, 0xf4, 0x8d, 0x1f, 0xc9, 0x5f, 0xfa, 0x82, 0x6d, 0xc7, 0xe1, 0xa6,
|
||||
0x57, 0xe3, 0x56, 0x6e, 0x96, 0xbf, 0x16, 0x1f, 0xa3, 0x54, 0xaa, 0x91,
|
||||
0x16, 0x5a, 0xb2, 0xa9, 0x04, 0xaf, 0x67, 0xc9, 0xac, 0x6c, 0xfa, 0x32,
|
||||
0x9e, 0x48, 0xea, 0xa5, 0x0b, 0x89, 0x3b, 0x54, 0x47, 0xf2, 0xa1, 0xf2,
|
||||
0x2a, 0x4d, 0xeb, 0xf4, 0x17, 0xdc, 0xd4, 0x72, 0x6c, 0xb5, 0x36, 0x28,
|
||||
0xb6, 0x7e, 0x17, 0x04, 0xd3, 0xac, 0x7a, 0x42, 0xc1, 0xf4, 0x6e, 0x9e,
|
||||
0xbf, 0x6b, 0xb7, 0x3c, 0x3a, 0x21, 0x67, 0xcb, 0x41, 0x48, 0x07, 0x91,
|
||||
0xde, 0x1a, 0xe2, 0xaa, 0x9c, 0xb1, 0x59, 0xdb, 0x12, 0x25, 0xc1, 0x32,
|
||||
0x92, 0xea, 0xc9, 0xaf, 0x3b, 0x97, 0xca, 0xca, 0xfe, 0x5b, 0xfe, 0xe5,
|
||||
0x33, 0x29, 0xeb, 0x16, 0x95, 0xd2, 0x24, 0xeb, 0xda, 0x30, 0xeb, 0x95,
|
||||
0x1a, 0xd3, 0xf7, 0x0f, 0x51, 0x1c, 0xd9, 0x0b, 0x99, 0x12, 0x7a, 0x4a,
|
||||
0xd0, 0xd3, 0x25, 0x9a, 0x88, 0x45, 0xb1, 0x04, 0x33, 0x2c, 0x8a, 0x99,
|
||||
0x34, 0x6b, 0x75, 0x19, 0x91, 0x9d, 0x92, 0x29, 0x89, 0xa0, 0x2c, 0x8b,
|
||||
0x9d, 0xd8, 0x7a, 0x5e, 0x04, 0x07, 0x87, 0x66, 0x28, 0x56, 0x67, 0xb9,
|
||||
0xd6, 0xd2, 0x39, 0xd9, 0xec, 0x33, 0x30, 0xb2, 0x8b, 0xea, 0xae, 0x83,
|
||||
0x18, 0xb9, 0x31, 0x34, 0xbb, 0x42, 0x22, 0x0b, 0x21, 0x96, 0x3c, 0x61,
|
||||
0xac, 0xcb, 0x95, 0x60, 0x2a, 0xe9, 0x68, 0x79, 0x08, 0x36, 0x56, 0x65,
|
||||
0x27, 0x4a, 0xd9, 0x83, 0x00, 0xcf, 0x0b, 0xf1, 0xfc, 0x10, 0x15, 0x0a,
|
||||
0x6a, 0x75, 0x77, 0x8b, 0x86, 0xdc, 0x58, 0x57, 0x45, 0x52, 0xe9, 0x84,
|
||||
0x81, 0x7c, 0x91, 0x28, 0x55, 0x23, 0x96, 0x13, 0xd7, 0x24, 0xbe, 0xac,
|
||||
0x17, 0xfa, 0xf2, 0x78, 0x63, 0xc7, 0x82, 0x08, 0xda, 0xa6, 0xc5, 0x50,
|
||||
0x55, 0x04, 0xe5, 0x65, 0x5b, 0x06, 0xde, 0xce, 0xf0, 0x24, 0xf3, 0x4e,
|
||||
0x70, 0xb5, 0x15, 0x6a, 0x34, 0x7b, 0x11, 0x9d, 0xbe, 0x10, 0x53, 0xd0,
|
||||
0xa8, 0x86, 0x2e, 0x76, 0xb6, 0x2a, 0x9d, 0x2c, 0x48, 0x3c, 0x5b, 0xa2,
|
||||
0xc8, 0x3a, 0x37, 0xd4, 0x9d, 0xed, 0x6c, 0x4a, 0xab, 0x95, 0x6e, 0x08,
|
||||
0x66, 0x3d, 0x5a, 0xad, 0x4d, 0x18, 0xc8, 0xca, 0xfa, 0xd5, 0x85, 0x6f,
|
||||
0xf9, 0x5f, 0xde, 0x02, 0x30, 0xff, 0x03, 0x8c, 0x47, 0x35, 0xad, 0xbc,
|
||||
0xbf, 0x26, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
|
||||
0x42, 0x60, 0x82
|
||||
|
||||
};
|
||||
|
||||
unsigned int _Users_username_Pictures_SaltBae_png_len = 1863;
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
// insert code here...
|
||||
int frameless = 1;
|
||||
int resizable = 0;
|
||||
int fullscreen = 0;
|
||||
int fullSizeContent = 1;
|
||||
int hideTitleBar = 0;
|
||||
int titlebarAppearsTransparent = 0;
|
||||
int hideTitle = 0;
|
||||
int useToolbar = 0;
|
||||
int hideToolbarSeparator = 0;
|
||||
int webviewIsTransparent = 1;
|
||||
int alwaysOnTop = 0;
|
||||
int hideWindowOnClose = 0;
|
||||
const char* appearance = "NSAppearanceNameDarkAqua";
|
||||
int windowIsTranslucent = 1;
|
||||
int debug = 1;
|
||||
WailsContext *result = Create("OI OI!",400,400, frameless, resizable, fullscreen, fullSizeContent, hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent, alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, debug);
|
||||
SetRGBA(result, 255, 0, 0, 255);
|
||||
void *m = NewMenu("");
|
||||
SetAbout(result, "Fake title", "I am a description", _Users_username_Pictures_SaltBae_png, _Users_username_Pictures_SaltBae_png_len);
|
||||
// AddMenuByRole(result, 1);
|
||||
|
||||
AppendRole(result, m, 1);
|
||||
AppendRole(result, m, 2);
|
||||
void* submenu = NewMenu("test");
|
||||
void* menuITem = AppendMenuItem(result, submenu, "Woohoo", "p", 0, 0, 0, 470);
|
||||
AppendSubmenu(m, submenu);
|
||||
UpdateMenuItem(menuITem, 1);
|
||||
SetAsApplicationMenu(result, m);
|
||||
SetPosition(result, 100, 100);
|
||||
|
||||
|
||||
|
||||
Run((void*)CFBridgingRetain(result));
|
||||
return 0;
|
||||
}
|
||||
@@ -1,97 +1,135 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Application.h"
|
||||
#import "WailsContext.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
||||
)
|
||||
|
||||
type NSMenu struct {
|
||||
context unsafe.Pointer
|
||||
nsmenu unsafe.Pointer
|
||||
}
|
||||
|
||||
func NewNSMenu(context unsafe.Pointer, name string) *NSMenu {
|
||||
c := NewCalloc()
|
||||
defer c.Free()
|
||||
title := c.String(name)
|
||||
nsmenu := C.NewMenu(title)
|
||||
return &NSMenu{
|
||||
context: context,
|
||||
nsmenu: nsmenu,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *NSMenu) AddSubMenu(label string) *NSMenu {
|
||||
result := NewNSMenu(m.context, label)
|
||||
C.AppendSubmenu(m.nsmenu, result.nsmenu)
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *NSMenu) AppendRole(role menu.Role) {
|
||||
C.AppendRole(m.context, m.nsmenu, C.int(role))
|
||||
}
|
||||
|
||||
type MenuItem struct {
|
||||
id uint
|
||||
nsmenuitem unsafe.Pointer
|
||||
wailsMenuItem *menu.MenuItem
|
||||
radioGroupMembers []*MenuItem
|
||||
}
|
||||
|
||||
func (m *NSMenu) AddMenuItem(menuItem *menu.MenuItem) *MenuItem {
|
||||
c := NewCalloc()
|
||||
defer c.Free()
|
||||
var modifier C.int
|
||||
var key *C.char
|
||||
if menuItem.Accelerator != nil {
|
||||
modifier = C.int(keys.ToMacModifier(menuItem.Accelerator))
|
||||
key = c.String(menuItem.Accelerator.Key)
|
||||
}
|
||||
|
||||
result := &MenuItem{
|
||||
wailsMenuItem: menuItem,
|
||||
}
|
||||
|
||||
result.id = createMenuItemID(result)
|
||||
result.nsmenuitem = C.AppendMenuItem(m.context, m.nsmenu, c.String(menuItem.Label), key, modifier, bool2Cint(menuItem.Disabled), bool2Cint(menuItem.Checked), C.int(result.id))
|
||||
return result
|
||||
}
|
||||
|
||||
//func (w *Window) SetApplicationMenu(menu *menu.Menu) {
|
||||
//w.applicationMenu = menu
|
||||
//processMenu(w, menu)
|
||||
//}
|
||||
|
||||
//func processMenu(window *Window, menu *menu.Menu) {
|
||||
//mainMenu := window.NewMenu()
|
||||
//for _, menuItem := range menu.Items {
|
||||
// submenu := mainMenu.AddSubMenu(menuItem.Label)
|
||||
// for _, menuItem := range menuItem.SubMenu.Items {
|
||||
// processMenuItem(submenu, menuItem)
|
||||
// }
|
||||
//}
|
||||
//mainMenu.Show()
|
||||
//}
|
||||
func processMenu(parent *NSMenu, wailsMenu *menu.Menu) {
|
||||
var radioGroups []*MenuItem
|
||||
|
||||
//func processMenuItem(parent *winc.MenuItem, menuItem *menu.MenuItem) {
|
||||
// if menuItem.Hidden {
|
||||
// return
|
||||
// }
|
||||
// switch menuItem.Type {
|
||||
// case menu.SeparatorType:
|
||||
// parent.AddSeparator()
|
||||
// case menu.TextType:
|
||||
// shortcut := acceleratorToWincShortcut(menuItem.Accelerator)
|
||||
// newItem := parent.AddItem(menuItem.Label, shortcut)
|
||||
// if menuItem.Tooltip != "" {
|
||||
// newItem.SetToolTip(menuItem.Tooltip)
|
||||
// }
|
||||
// if menuItem.Click != nil {
|
||||
// newItem.OnClick().Bind(func(e *winc.Event) {
|
||||
// menuItem.Click(&menu.CallbackData{
|
||||
// MenuItem: menuItem,
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// newItem.SetEnabled(!menuItem.Disabled)
|
||||
//
|
||||
// case menu.CheckboxType:
|
||||
// shortcut := acceleratorToWincShortcut(menuItem.Accelerator)
|
||||
// newItem := parent.AddItem(menuItem.Label, shortcut)
|
||||
// newItem.SetCheckable(true)
|
||||
// newItem.SetChecked(menuItem.Checked)
|
||||
// if menuItem.Tooltip != "" {
|
||||
// newItem.SetToolTip(menuItem.Tooltip)
|
||||
// }
|
||||
// if menuItem.Click != nil {
|
||||
// newItem.OnClick().Bind(func(e *winc.Event) {
|
||||
// toggleCheckBox(menuItem)
|
||||
// menuItem.Click(&menu.CallbackData{
|
||||
// MenuItem: menuItem,
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// newItem.SetEnabled(!menuItem.Disabled)
|
||||
// addCheckBoxToMap(menuItem, newItem)
|
||||
// case menu.RadioType:
|
||||
// shortcut := acceleratorToWincShortcut(menuItem.Accelerator)
|
||||
// newItem := parent.AddItemRadio(menuItem.Label, shortcut)
|
||||
// newItem.SetCheckable(true)
|
||||
// newItem.SetChecked(menuItem.Checked)
|
||||
// if menuItem.Tooltip != "" {
|
||||
// newItem.SetToolTip(menuItem.Tooltip)
|
||||
// }
|
||||
// if menuItem.Click != nil {
|
||||
// newItem.OnClick().Bind(func(e *winc.Event) {
|
||||
// toggleRadioItem(menuItem)
|
||||
// menuItem.Click(&menu.CallbackData{
|
||||
// MenuItem: menuItem,
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// newItem.SetEnabled(!menuItem.Disabled)
|
||||
// addRadioItemToMap(menuItem, newItem)
|
||||
// case menu.SubmenuType:
|
||||
// submenu := parent.AddSubMenu(menuItem.Label)
|
||||
// for _, menuItem := range menuItem.SubMenu.Items {
|
||||
// processMenuItem(submenu, menuItem)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
for _, menuItem := range wailsMenu.Items {
|
||||
if menuItem.SubMenu != nil {
|
||||
if len(radioGroups) > 0 {
|
||||
processRadioGroups(radioGroups)
|
||||
radioGroups = []*MenuItem{}
|
||||
}
|
||||
submenu := parent.AddSubMenu(menuItem.Label)
|
||||
processMenu(submenu, menuItem.SubMenu)
|
||||
} else {
|
||||
lastMenuItem := processMenuItem(parent, menuItem)
|
||||
if menuItem.Type == menu.RadioType {
|
||||
radioGroups = append(radioGroups, lastMenuItem)
|
||||
} else {
|
||||
if len(radioGroups) > 0 {
|
||||
processRadioGroups(radioGroups)
|
||||
radioGroups = []*MenuItem{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func processRadioGroups(groups []*MenuItem) {
|
||||
for _, item := range groups {
|
||||
item.radioGroupMembers = groups
|
||||
}
|
||||
}
|
||||
|
||||
func processMenuItem(parent *NSMenu, menuItem *menu.MenuItem) *MenuItem {
|
||||
if menuItem.Hidden {
|
||||
return nil
|
||||
}
|
||||
if menuItem.Role != 0 {
|
||||
parent.AppendRole(menuItem.Role)
|
||||
return nil
|
||||
}
|
||||
if menuItem.Type == menu.SeparatorType {
|
||||
C.AppendSeparator(parent.nsmenu)
|
||||
return nil
|
||||
}
|
||||
|
||||
return parent.AddMenuItem(menuItem)
|
||||
|
||||
}
|
||||
|
||||
func (f *Frontend) MenuSetApplicationMenu(menu *menu.Menu) {
|
||||
//f.mainWindow.SetApplicationMenu(menu)
|
||||
f.mainWindow.SetApplicationMenu(menu)
|
||||
}
|
||||
|
||||
func (f *Frontend) MenuUpdateApplicationMenu() {
|
||||
//processMenu(f.mainWindow, f.mainWindow.applicationMenu)
|
||||
f.MenuSetApplicationMenu(f.frontendOptions.Menu)
|
||||
f.mainWindow.UpdateApplicationMenu()
|
||||
}
|
||||
|
||||
51
v2/internal/frontend/desktop/darwin/menuitem.go
Normal file
51
v2/internal/frontend/desktop/darwin/menuitem.go
Normal file
@@ -0,0 +1,51 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Application.h"
|
||||
#import "WailsContext.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"log"
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var menuItemToID = make(map[*MenuItem]uint)
|
||||
var idToMenuItem = make(map[uint]*MenuItem)
|
||||
var menuItemLock sync.Mutex
|
||||
var menuItemIDCounter uint = 0
|
||||
|
||||
func createMenuItemID(item *MenuItem) uint {
|
||||
menuItemLock.Lock()
|
||||
defer menuItemLock.Unlock()
|
||||
counter := 0
|
||||
for {
|
||||
menuItemIDCounter++
|
||||
value := idToMenuItem[menuItemIDCounter]
|
||||
if value == nil {
|
||||
break
|
||||
}
|
||||
counter++
|
||||
if counter == math.MaxInt {
|
||||
log.Fatal("insane amounts of menuitems detected! Aborting before the collapse of the world!")
|
||||
}
|
||||
}
|
||||
idToMenuItem[menuItemIDCounter] = item
|
||||
menuItemToID[item] = menuItemIDCounter
|
||||
return menuItemIDCounter
|
||||
}
|
||||
|
||||
func getMenuItemForID(id uint) *MenuItem {
|
||||
menuItemLock.Lock()
|
||||
defer menuItemLock.Unlock()
|
||||
return idToMenuItem[id]
|
||||
}
|
||||
29
v2/internal/frontend/desktop/darwin/message.h
Normal file
29
v2/internal/frontend/desktop/darwin/message.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// message.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 14/10/21.
|
||||
//
|
||||
|
||||
#ifndef export_h
|
||||
#define export_h
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
void processMessage(const char *);
|
||||
void processURLRequest(void*, const char *);
|
||||
void processMessageDialogResponse(int);
|
||||
void processOpenFileDialogResponse(const char*);
|
||||
void processSaveFileDialogResponse(const char*);
|
||||
void processCallback(int);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* export_h */
|
||||
226
v2/internal/frontend/desktop/darwin/window.go
Normal file
226
v2/internal/frontend/desktop/darwin/window.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Application.h"
|
||||
#import "WailsContext.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"log"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
type Window struct {
|
||||
context unsafe.Pointer
|
||||
}
|
||||
|
||||
func bool2Cint(value bool) C.int {
|
||||
if value {
|
||||
return C.int(1)
|
||||
}
|
||||
return C.int(0)
|
||||
}
|
||||
|
||||
func NewWindow(frontendOptions *options.App, debugMode bool) *Window {
|
||||
|
||||
c := NewCalloc()
|
||||
defer c.Free()
|
||||
|
||||
frameless := bool2Cint(frontendOptions.Frameless)
|
||||
resizable := bool2Cint(!frontendOptions.DisableResize)
|
||||
fullscreen := bool2Cint(frontendOptions.Fullscreen)
|
||||
alwaysOnTop := bool2Cint(frontendOptions.AlwaysOnTop)
|
||||
hideWindowOnClose := bool2Cint(frontendOptions.HideWindowOnClose)
|
||||
debug := bool2Cint(debugMode)
|
||||
|
||||
var fullSizeContent, hideTitleBar, hideTitle, useToolbar, webviewIsTransparent C.int
|
||||
var titlebarAppearsTransparent, hideToolbarSeparator, windowIsTranslucent C.int
|
||||
var appearance, title *C.char
|
||||
|
||||
width := C.int(frontendOptions.Width)
|
||||
height := C.int(frontendOptions.Height)
|
||||
|
||||
title = c.String(frontendOptions.Title)
|
||||
|
||||
if frontendOptions.Mac != nil {
|
||||
mac := frontendOptions.Mac
|
||||
if mac.TitleBar != nil {
|
||||
fullSizeContent = bool2Cint(mac.TitleBar.FullSizeContent)
|
||||
hideTitleBar = bool2Cint(mac.TitleBar.HideTitleBar)
|
||||
hideTitle = bool2Cint(mac.TitleBar.HideTitle)
|
||||
useToolbar = bool2Cint(mac.TitleBar.UseToolbar)
|
||||
titlebarAppearsTransparent = bool2Cint(mac.TitleBar.TitlebarAppearsTransparent)
|
||||
hideToolbarSeparator = bool2Cint(mac.TitleBar.HideToolbarSeparator)
|
||||
}
|
||||
windowIsTranslucent = bool2Cint(mac.WindowIsTranslucent)
|
||||
webviewIsTransparent = bool2Cint(mac.WebviewIsTransparent)
|
||||
|
||||
appearance = c.String(string(mac.Appearance))
|
||||
}
|
||||
var context *C.WailsContext = C.Create(title, width, height, frameless, resizable, fullscreen, fullSizeContent,
|
||||
hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent,
|
||||
alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, debug)
|
||||
|
||||
// Create menu
|
||||
result := &Window{
|
||||
context: unsafe.Pointer(context),
|
||||
}
|
||||
|
||||
if frontendOptions.RGBA != nil {
|
||||
result.SetRGBA(frontendOptions.RGBA.R, frontendOptions.RGBA.G, frontendOptions.RGBA.B, frontendOptions.RGBA.A)
|
||||
}
|
||||
|
||||
if frontendOptions.Mac != nil && frontendOptions.Mac.About != nil {
|
||||
title := c.String(frontendOptions.Mac.About.Title)
|
||||
description := c.String(frontendOptions.Mac.About.Message)
|
||||
var icon unsafe.Pointer
|
||||
var length C.int
|
||||
if frontendOptions.Mac.About.Icon != nil {
|
||||
icon = unsafe.Pointer(&frontendOptions.Mac.About.Icon[0])
|
||||
length = C.int(len(frontendOptions.Mac.About.Icon))
|
||||
}
|
||||
C.SetAbout(result.context, title, description, icon, length)
|
||||
}
|
||||
|
||||
if frontendOptions.Menu != nil {
|
||||
result.SetApplicationMenu(frontendOptions.Menu)
|
||||
}
|
||||
|
||||
result.SetMinSize(frontendOptions.MinWidth, frontendOptions.MinHeight)
|
||||
result.SetMaxSize(frontendOptions.MaxWidth, frontendOptions.MaxHeight)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (w *Window) Center() {
|
||||
C.Center(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) Run() {
|
||||
C.Run(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) Quit() {
|
||||
C.Quit(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) SetRGBA(r uint8, g uint8, b uint8, a uint8) {
|
||||
C.SetRGBA(w.context, C.int(r), C.int(g), C.int(b), C.int(a))
|
||||
}
|
||||
|
||||
func (w *Window) ExecJS(js string) {
|
||||
_js := C.CString(js)
|
||||
C.ExecJS(w.context, _js)
|
||||
C.free(unsafe.Pointer(_js))
|
||||
}
|
||||
|
||||
func (w *Window) SetPos(x int, y int) {
|
||||
C.SetPosition(w.context, C.int(x), C.int(y))
|
||||
}
|
||||
|
||||
func (w *Window) SetSize(width int, height int) {
|
||||
C.SetSize(w.context, C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
func (w *Window) SetTitle(title string) {
|
||||
t := C.CString(title)
|
||||
C.SetTitle(w.context, t)
|
||||
C.free(unsafe.Pointer(t))
|
||||
}
|
||||
|
||||
func (w *Window) Maximise() {
|
||||
C.Maximise(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) UnMaximise() {
|
||||
C.UnMaximise(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) Minimise() {
|
||||
C.Minimise(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) UnMinimise() {
|
||||
C.UnMinimise(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) SetMinSize(width int, height int) {
|
||||
if width == 0 && height == 0 {
|
||||
return
|
||||
}
|
||||
C.SetMinSize(w.context, C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
func (w *Window) SetMaxSize(width int, height int) {
|
||||
if width == 0 && height == 0 {
|
||||
return
|
||||
}
|
||||
C.SetMaxSize(w.context, C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
func (w *Window) Fullscreen() {
|
||||
C.Fullscreen(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) UnFullscreen() {
|
||||
C.UnFullscreen(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) Show() {
|
||||
C.Show(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) Hide() {
|
||||
C.Hide(w.context)
|
||||
}
|
||||
|
||||
func parseIntDuo(temp string) (int, int) {
|
||||
split := strings.Split(temp, ",")
|
||||
x, err := strconv.Atoi(split[0])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
y, err := strconv.Atoi(split[1])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (w *Window) Pos() (int, int) {
|
||||
var _result *C.char = C.GetPos(w.context)
|
||||
temp := C.GoString(_result)
|
||||
return parseIntDuo(temp)
|
||||
}
|
||||
|
||||
func (w *Window) Size() (int, int) {
|
||||
var _result *C.char = C.GetSize(w.context)
|
||||
temp := C.GoString(_result)
|
||||
return parseIntDuo(temp)
|
||||
}
|
||||
|
||||
func (w *Window) SetApplicationMenu(inMenu *menu.Menu) {
|
||||
mainMenu := NewNSMenu(w.context, "")
|
||||
processMenu(mainMenu, inMenu)
|
||||
C.SetAsApplicationMenu(w.context, mainMenu.nsmenu)
|
||||
}
|
||||
|
||||
func (w *Window) UpdateApplicationMenu() {
|
||||
C.UpdateApplicationMenu(w.context)
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
|
||||
@@ -135,6 +135,7 @@ func (f *Frontend) Run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
mainWindow.Run()
|
||||
mainWindow.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -257,19 +258,6 @@ func (f *Frontend) Quit() {
|
||||
winc.Exit()
|
||||
}
|
||||
|
||||
const (
|
||||
ctrlZ int = 90
|
||||
ctrlX = 88
|
||||
ctrlC = 67
|
||||
ctrlV = 86
|
||||
ctrlA = 65
|
||||
arrowUp = 38
|
||||
arrowDown = 40
|
||||
arrowRight = 39
|
||||
arrowLeft = 37
|
||||
keyDel = 46
|
||||
)
|
||||
|
||||
func (f *Frontend) setupChromium() {
|
||||
chromium := edge.NewChromium()
|
||||
f.chromium = chromium
|
||||
@@ -306,10 +294,15 @@ func (f *Frontend) setupChromium() {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutIsSwipeNavigationEnabled(false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Set background colour
|
||||
f.WindowSetRGBA(f.frontendOptions.RGBA)
|
||||
|
||||
chromium.SetGlobalPermission(edge.CoreWebView2PermissionStateAllow)
|
||||
chromium.AddWebResourceRequestedFilter("*", edge.COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL)
|
||||
chromium.Navigate(f.startURL)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ func NewWindow(parent winc.Controller, options *options.App) *Window {
|
||||
exStyle |= w32.WS_EX_NOREDIRECTIONBITMAP
|
||||
}
|
||||
}
|
||||
if options.AlwaysOnTop {
|
||||
exStyle |= w32.WS_EX_TOPMOST
|
||||
}
|
||||
|
||||
var dwStyle = w32.WS_OVERLAPPEDWINDOW
|
||||
if options.Frameless {
|
||||
|
||||
@@ -2,8 +2,9 @@ package dispatcher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
)
|
||||
|
||||
const systemCallPrefix = ":wails:"
|
||||
@@ -29,7 +30,7 @@ func (d *Dispatcher) processSystemCall(payload callMessage, sender frontend.Fron
|
||||
return &position{x, y}, nil
|
||||
case "WindowGetSize":
|
||||
w, h := sender.WindowGetSize()
|
||||
return &position{w, h}, nil
|
||||
return &size{w, h}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown systemcall message: %s", payload.Name)
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ type MessageDialogOptions struct {
|
||||
Buttons []string
|
||||
DefaultButton string
|
||||
CancelButton string
|
||||
Icon string
|
||||
Icon []byte
|
||||
}
|
||||
|
||||
type Frontend interface {
|
||||
|
||||
@@ -22,10 +22,18 @@ The electron alternative for Go
|
||||
while (obj && s.length) obj = obj[s.shift()];
|
||||
return obj;
|
||||
};
|
||||
window.WailsInvoke = _deeptest(["chrome", "webview", "postMessage"]) ||
|
||||
_deeptest(["webkit", "messageHandlers", "external", "postMessage"]);
|
||||
let windows = _deeptest(["chrome", "webview", "postMessage"]);
|
||||
let mac = _deeptest(["webkit", "messageHandlers", "external", "postMessage"]);
|
||||
|
||||
if (!window.WailsInvoke) {
|
||||
if (!windows && !mac) {
|
||||
console.error("Unsupported Platform");
|
||||
return;
|
||||
}
|
||||
|
||||
if (windows) {
|
||||
window.WailsInvoke = (message) => window.chrome.webview.postMessage(message);
|
||||
}
|
||||
if (mac) {
|
||||
window.WailsInvoke = (message) => window.webkit.messageHandlers.external.postMessage(message);
|
||||
}
|
||||
})();
|
||||
@@ -39,7 +39,11 @@ window.wails = {
|
||||
EventsNotify,
|
||||
SetBindings,
|
||||
eventListeners,
|
||||
callbacks
|
||||
callbacks,
|
||||
flags: {
|
||||
disableScrollbarDrag: false,
|
||||
disableWailsDefaultContextMenu: false,
|
||||
}
|
||||
};
|
||||
|
||||
// Set the bindings
|
||||
@@ -61,9 +65,23 @@ window.addEventListener('mousedown', (e) => {
|
||||
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
||||
break;
|
||||
} else if (currentElement.hasAttribute('data-wails-drag')) {
|
||||
if (window.wails.flags.disableScrollbarDrag) {
|
||||
// This checks for clicks on the scroll bar
|
||||
if (e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
window.WailsInvoke("drag");
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
currentElement = currentElement.parentElement;
|
||||
}
|
||||
});
|
||||
|
||||
// Setup context menu hook
|
||||
window.addEventListener('contextmenu', function (e) {
|
||||
if (window.wails.flags.disableWailsDefaultContextMenu) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
@@ -148,6 +148,7 @@
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.5.6.tgz",
|
||||
"integrity": "sha512-Bz8nU45FrT6sP/Tf3M2rQUuBGxnDSNSPZNIoYwSNt5H+wjSyo/t+zm94tgnOZsR6GgpDMbNQgo4jGbK0NLvEfw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"svelte": "^3.42.6"
|
||||
},
|
||||
@@ -155,7 +156,8 @@
|
||||
"svelte": {
|
||||
"version": "3.43.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.43.1.tgz",
|
||||
"integrity": "sha512-nvPIaKx4HLzYlSdquISZpgG1Kqr2VAWQjZOt3Iwm3UhbqmA0LnSx4k1YpRMEhjQYW3ZCqQoK8Egto9tv4YewMA=="
|
||||
"integrity": "sha512-nvPIaKx4HLzYlSdquISZpgG1Kqr2VAWQjZOt3Iwm3UhbqmA0LnSx4k1YpRMEhjQYW3ZCqQoK8Egto9tv4YewMA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1 +1 @@
|
||||
(()=>{(function(){let n=function(o){for(var e=window[o.shift()];e&&o.length;)e=e[o.shift()];return e};window.WailsInvoke=n(["chrome","webview","postMessage"])||n(["webkit","messageHandlers","external","postMessage"]),window.WailsInvoke||console.error("Unsupported Platform")})();})();
|
||||
(()=>{(function(){let o=function(e){for(var s=window[e.shift()];s&&e.length;)s=s[e.shift()];return s},t=o(["chrome","webview","postMessage"]),n=o(["webkit","messageHandlers","external","postMessage"]);if(!t&&!n){console.error("Unsupported Platform");return}t&&(window.WailsInvoke=e=>window.chrome.webview.postMessage(e)),n&&(window.WailsInvoke=e=>window.webkit.messageHandlers.external.postMessage(e))})();})();
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
(()=>{var g=Object.defineProperty;var h=o=>g(o,"__esModule",{value:!0});var f=(o,n)=>{h(o);for(var e in n)g(o,e,{get:n[e],enumerable:!0})};var W={};f(W,{LogDebug:()=>T,LogError:()=>D,LogFatal:()=>F,LogInfo:()=>C,LogLevel:()=>U,LogPrint:()=>B,LogTrace:()=>R,LogWarning:()=>J,SetLogLevel:()=>G});function l(o,n){window.WailsInvoke("L"+o+n)}function R(o){l("T",o)}function B(o){l("P",o)}function T(o){l("D",o)}function C(o){l("I",o)}function J(o){l("W",o)}function D(o){l("E",o)}function F(o){l("F",o)}function G(o){l("S",o)}var U={TRACE:1,DEBUG:2,INFO:3,WARNING:4,ERROR:5};var E=class{constructor(n,e){e=e||-1,this.Callback=t=>(n.apply(null,t),e===-1?!1:(e-=1,e===0))}},s={};function d(o,n,e){s[o]=s[o]||[];let t=new E(n,e);s[o].push(t)}function I(o,n){d(o,n,-1)}function k(o,n){d(o,n,1)}function m(o){let n=o.name;if(s[n]){let e=s[n].slice();for(let t=0;t<s[n].length;t+=1){let r=s[n][t],i=o.data;r.Callback(i)&&e.splice(t,1)}s[n]=e}}function S(o){let n;try{n=JSON.parse(o)}catch(e){let t="Invalid JSON passed to Notify: "+o;throw new Error(t)}m(n)}function y(o){let n={name:o,data:[].slice.apply(arguments).slice(1)};m(n),window.WailsInvoke("EE"+JSON.stringify(n))}function L(o){s.delete(o),window.WailsInvoke("EX"+o)}var c={};function z(){var o=new Uint32Array(1);return window.crypto.getRandomValues(o)[0]}function A(){return Math.random()*9007199254740991}var p;window.crypto?p=z:p=A;function a(o,n,e){return e==null&&(e=0),new Promise(function(t,r){var i;do i=o+"-"+p();while(c[i]);var w;e>0&&(w=setTimeout(function(){r(Error("Call to "+o+" timed out. Request ID: "+i))},e)),c[i]={timeoutHandle:w,reject:r,resolve:t};try{let u={name:o,args:n,callbackID:i};window.WailsInvoke("C"+JSON.stringify(u))}catch(u){console.error(u)}})}function O(o){var n;try{n=JSON.parse(o)}catch(r){let i=`Invalid JSON passed to callback: ${r.message}. Message: ${o}`;throw wails.LogDebug(i),new Error(i)}var e=n.callbackid,t=c[e];if(!t){let r=`Callback '${e}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(t.timeoutHandle),delete c[e],n.error?t.reject(n.error):t.resolve(n.result)}window.go={};function b(o){try{o=JSON.parse(o)}catch(n){console.error(n)}window.go=window.go||{},Object.keys(o).forEach(n=>{window.go[n]=window.go[n]||{},Object.keys(o[n]).forEach(e=>{window.go[n][e]=window.go[n][e]||{},Object.keys(o[n][e]).forEach(t=>{window.go[n][e][t]=function(){let r=0;function i(){let w=[].slice.call(arguments);return a([n,e,t].join("."),w,r)}return i.setTimeout=function(w){r=w},i.getTimeout=function(){return r},i}()})})})}var v={};f(v,{WindowCenter:()=>j,WindowFullscreen:()=>M,WindowGetPosition:()=>Z,WindowGetSize:()=>V,WindowHide:()=>K,WindowMaximise:()=>_,WindowMinimise:()=>no,WindowReload:()=>P,WindowSetMaxSize:()=>q,WindowSetMinSize:()=>N,WindowSetPosition:()=>X,WindowSetRGBA:()=>to,WindowSetSize:()=>Q,WindowSetTitle:()=>H,WindowShow:()=>Y,WindowUnFullscreen:()=>$,WindowUnmaximise:()=>oo,WindowUnminimise:()=>eo});function P(){window.location.reload()}function j(){window.WailsInvoke("Wc")}function H(o){window.WailsInvoke("WT"+o)}function M(){window.WailsInvoke("WF")}function $(){window.WailsInvoke("Wf")}function Q(o,n){window.WailsInvoke("Ws:"+o+":"+n)}function V(){return a(":wails:WindowGetSize")}function q(o,n){window.WailsInvoke("WZ:"+o+":"+n)}function N(o,n){window.WailsInvoke("Wz:"+o+":"+n)}function X(o,n){window.WailsInvoke("Wp:"+o+":"+n)}function Z(){return a(":wails:WindowGetPos")}function K(){window.WailsInvoke("WH")}function Y(){window.WailsInvoke("WS")}function _(){window.WailsInvoke("WM")}function oo(){window.WailsInvoke("WU")}function no(){window.WailsInvoke("Wm")}function eo(){window.WailsInvoke("Wu")}function to(o){let n=JSON.stringify(o);window.WailsInvoke("Wr:"+n)}var x={};f(x,{BrowserOpenURL:()=>io});function io(o){window.WailsInvoke("BO:"+o)}function ro(){window.WailsInvoke("Q")}window.runtime={...W,...v,...x,EventsOn:I,EventsOnce:k,EventsOnMultiple:d,EventsEmit:y,EventsOff:L,Quit:ro};window.wails={Callback:O,EventsNotify:S,SetBindings:b,eventListeners:s,callbacks:c};window.wails.SetBindings(window.wailsbindings);delete window.wails.SetBindings;window.addEventListener("mousedown",o=>{let n=o.target;for(;n!=null&&!n.hasAttribute("data-wails-no-drag");){if(n.hasAttribute("data-wails-drag")){window.WailsInvoke("drag");break}n=n.parentElement}});})();
|
||||
(()=>{var x=Object.defineProperty;var h=n=>x(n,"__esModule",{value:!0});var u=(n,o)=>{h(n);for(var e in o)x(n,e,{get:o[e],enumerable:!0})};var W={};u(W,{LogDebug:()=>C,LogError:()=>J,LogFatal:()=>F,LogInfo:()=>B,LogLevel:()=>U,LogPrint:()=>R,LogTrace:()=>D,LogWarning:()=>T,SetLogLevel:()=>G});function l(n,o){window.WailsInvoke("L"+n+o)}function D(n){l("T",n)}function R(n){l("P",n)}function C(n){l("D",n)}function B(n){l("I",n)}function T(n){l("W",n)}function J(n){l("E",n)}function F(n){l("F",n)}function G(n){l("S",n)}var U={TRACE:1,DEBUG:2,INFO:3,WARNING:4,ERROR:5};var E=class{constructor(o,e){e=e||-1,this.Callback=t=>(o.apply(null,t),e===-1?!1:(e-=1,e===0))}},s={};function d(n,o,e){s[n]=s[n]||[];let t=new E(o,e);s[n].push(t)}function I(n,o){d(n,o,-1)}function k(n,o){d(n,o,1)}function S(n){let o=n.name;if(s[o]){let e=s[o].slice();for(let t=0;t<s[o].length;t+=1){let r=s[o][t],i=n.data;r.Callback(i)&&e.splice(t,1)}s[o]=e}}function m(n){let o;try{o=JSON.parse(n)}catch(e){let t="Invalid JSON passed to Notify: "+n;throw new Error(t)}S(o)}function b(n){let o={name:n,data:[].slice.apply(arguments).slice(1)};S(o),window.WailsInvoke("EE"+JSON.stringify(o))}function y(n){s.delete(n),window.WailsInvoke("EX"+n)}var a={};function z(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}function A(){return Math.random()*9007199254740991}var p;window.crypto?p=z:p=A;function c(n,o,e){return e==null&&(e=0),new Promise(function(t,r){var i;do i=n+"-"+p();while(a[i]);var w;e>0&&(w=setTimeout(function(){r(Error("Call to "+n+" timed out. Request ID: "+i))},e)),a[i]={timeoutHandle:w,reject:r,resolve:t};try{let f={name:n,args:o,callbackID:i};window.WailsInvoke("C"+JSON.stringify(f))}catch(f){console.error(f)}})}function L(n){var o;try{o=JSON.parse(n)}catch(r){let i=`Invalid JSON passed to callback: ${r.message}. Message: ${n}`;throw wails.LogDebug(i),new Error(i)}var e=o.callbackid,t=a[e];if(!t){let r=`Callback '${e}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(t.timeoutHandle),delete a[e],o.error?t.reject(o.error):t.resolve(o.result)}window.go={};function O(n){try{n=JSON.parse(n)}catch(o){console.error(o)}window.go=window.go||{},Object.keys(n).forEach(o=>{window.go[o]=window.go[o]||{},Object.keys(n[o]).forEach(e=>{window.go[o][e]=window.go[o][e]||{},Object.keys(n[o][e]).forEach(t=>{window.go[o][e][t]=function(){let r=0;function i(){let w=[].slice.call(arguments);return c([o,e,t].join("."),w,r)}return i.setTimeout=function(w){r=w},i.getTimeout=function(){return r},i}()})})})}var v={};u(v,{WindowCenter:()=>M,WindowFullscreen:()=>j,WindowGetPosition:()=>Y,WindowGetSize:()=>V,WindowHide:()=>Z,WindowMaximise:()=>_,WindowMinimise:()=>on,WindowReload:()=>H,WindowSetMaxSize:()=>X,WindowSetMinSize:()=>q,WindowSetPosition:()=>N,WindowSetRGBA:()=>tn,WindowSetSize:()=>Q,WindowSetTitle:()=>P,WindowShow:()=>K,WindowUnFullscreen:()=>$,WindowUnmaximise:()=>nn,WindowUnminimise:()=>en});function H(){window.location.reload()}function M(){window.WailsInvoke("Wc")}function P(n){window.WailsInvoke("WT"+n)}function j(){window.WailsInvoke("WF")}function $(){window.WailsInvoke("Wf")}function Q(n,o){window.WailsInvoke("Ws:"+n+":"+o)}function V(){return c(":wails:WindowGetSize")}function X(n,o){window.WailsInvoke("WZ:"+n+":"+o)}function q(n,o){window.WailsInvoke("Wz:"+n+":"+o)}function N(n,o){window.WailsInvoke("Wp:"+n+":"+o)}function Y(){return c(":wails:WindowGetPos")}function Z(){window.WailsInvoke("WH")}function K(){window.WailsInvoke("WS")}function _(){window.WailsInvoke("WM")}function nn(){window.WailsInvoke("WU")}function on(){window.WailsInvoke("Wm")}function en(){window.WailsInvoke("Wu")}function tn(n){let o=JSON.stringify(n);window.WailsInvoke("Wr:"+o)}var g={};u(g,{BrowserOpenURL:()=>rn});function rn(n){window.WailsInvoke("BO:"+n)}function sn(){window.WailsInvoke("Q")}window.runtime={...W,...v,...g,EventsOn:I,EventsOnce:k,EventsOnMultiple:d,EventsEmit:b,EventsOff:y,Quit:sn};window.wails={Callback:L,EventsNotify:m,SetBindings:O,eventListeners:s,callbacks:a,flags:{disableScrollbarDrag:!1,disableWailsDefaultContextMenu:!1}};window.wails.SetBindings(window.wailsbindings);delete window.wails.SetBindings;window.addEventListener("mousedown",n=>{let o=n.target;for(;o!=null&&!o.hasAttribute("data-wails-no-drag");){if(o.hasAttribute("data-wails-drag")){if(window.wails.flags.disableScrollbarDrag&&(n.offsetX>n.target.clientWidth||n.offsetY>n.target.clientHeight))break;window.WailsInvoke("drag"),n.preventDefault();break}o=o.parentElement}});window.addEventListener("contextmenu",function(n){window.wails.flags.disableWailsDefaultContextMenu&&n.preventDefault()});})();
|
||||
|
||||
@@ -9,9 +9,9 @@ interface Size {
|
||||
}
|
||||
|
||||
interface RGBA {
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ const basicUpdated string = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.2 // indirect
|
||||
@@ -330,7 +330,7 @@ const multilineRequireUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -381,12 +381,12 @@ func TestUpdateGoModVersion(t *testing.T) {
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{"basic", args{[]byte(basic), "v2.0.0-beta.12"}, []byte(basicUpdated), false},
|
||||
{"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.12"}, []byte(multilineRequireUpdated), false},
|
||||
{"basicmultilinereplace", args{[]byte(multilineReplace), "v2.0.0-beta.12"}, []byte(multilineReplaceUpdated), false},
|
||||
{"basicmultilinereplaceblock", args{[]byte(multilineReplaceBlock), "v2.0.0-beta.12"}, []byte(multilineReplaceBlockUpdated), false},
|
||||
{"basicmultilinereplacenoversion", args{[]byte(multilineReplaceNoVersion), "v2.0.0-beta.12"}, []byte(multilineReplaceNoVersionUpdated), false},
|
||||
{"basicmultilinereplacenoversionblock", args{[]byte(multilineReplaceNoVersionBlock), "v2.0.0-beta.12"}, []byte(multilineReplaceNoVersionBlockUpdated), false},
|
||||
{"basic", args{[]byte(basic), "v2.0.0-beta.15"}, []byte(basicUpdated), false},
|
||||
{"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.15"}, []byte(multilineRequireUpdated), false},
|
||||
{"basicmultilinereplace", args{[]byte(multilineReplace), "v2.0.0-beta.15"}, []byte(multilineReplaceUpdated), false},
|
||||
{"basicmultilinereplaceblock", args{[]byte(multilineReplaceBlock), "v2.0.0-beta.15"}, []byte(multilineReplaceBlockUpdated), false},
|
||||
{"basicmultilinereplacenoversion", args{[]byte(multilineReplaceNoVersion), "v2.0.0-beta.15"}, []byte(multilineReplaceNoVersionUpdated), false},
|
||||
{"basicmultilinereplacenoversionblock", args{[]byte(multilineReplaceNoVersionBlock), "v2.0.0-beta.15"}, []byte(multilineReplaceNoVersionBlockUpdated), false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -413,8 +413,8 @@ func TestGoModOutOfSync(t *testing.T) {
|
||||
want bool
|
||||
wantErr bool
|
||||
}{
|
||||
{"basic", args{[]byte(basic), "v2.0.0-beta.12"}, true, false},
|
||||
{"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.12"}, true, false},
|
||||
{"basic", args{[]byte(basic), "v2.0.0-beta.15"}, true, false},
|
||||
{"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.15"}, true, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -433,7 +433,7 @@ const multilineReplaceUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -468,14 +468,14 @@ require (
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.12 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.15 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
const multilineReplaceNoVersionUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -517,7 +517,7 @@ const multilineReplaceNoVersionBlockUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -562,7 +562,7 @@ const multilineReplaceBlockUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -598,6 +598,6 @@ require (
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
)
|
||||
`
|
||||
|
||||
@@ -3,6 +3,7 @@ package menumanager
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
)
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ type Project struct {
|
||||
|
||||
// The url to use to server assets. Default "https://localhost:34115"
|
||||
DevServerURL string `json:"devserverurl"`
|
||||
|
||||
// Arguments that are forwared to the application in dev mode
|
||||
AppArgs string `json:"appargs"`
|
||||
}
|
||||
|
||||
func (p *Project) Save() error {
|
||||
|
||||
@@ -35,7 +35,7 @@ func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.
|
||||
|
||||
result := &Manager{
|
||||
bus: bus,
|
||||
logger: logger.CustomLogger("Event Manager"),
|
||||
logger: logger.CustomLogger("Signal Manager"),
|
||||
signalchannel: make(chan os.Signal, 2),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
@@ -49,7 +49,7 @@ func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.
|
||||
func (m *Manager) Start() {
|
||||
|
||||
// Hook into interrupts
|
||||
gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM)
|
||||
gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
|
||||
|
||||
m.wg.Add(1)
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@ package buildassets
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"github.com/leaanthony/debme"
|
||||
"github.com/leaanthony/gosod"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/leaanthony/debme"
|
||||
"github.com/leaanthony/gosod"
|
||||
)
|
||||
|
||||
//go:embed build
|
||||
@@ -50,3 +51,17 @@ func RegenerateAppIcon(target string) error {
|
||||
}
|
||||
return a.CopyFile("appicon.png", target, 0644)
|
||||
}
|
||||
|
||||
func RegeneratePlist(targetDir string, projectName string) error {
|
||||
darwinAssets, err := debme.FS(assets, "build/darwin")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
templateDir := gosod.New(darwinAssets)
|
||||
err = templateDir.Extract(targetDir, &assetData{Name: projectName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,9 +3,6 @@ package build
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/leaanthony/gosod"
|
||||
wailsRuntime "github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/runtime/wrapper"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -13,6 +10,10 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/leaanthony/gosod"
|
||||
wailsRuntime "github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/runtime/wrapper"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
@@ -215,8 +216,6 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||
commands.Add(`"all=-N -l"`)
|
||||
}
|
||||
|
||||
//commands.Add("-a")
|
||||
|
||||
var tags slicer.StringSlicer
|
||||
tags.Add(options.OutputType)
|
||||
tags.AddSlice(options.UserTags)
|
||||
@@ -299,7 +298,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||
if v != "" {
|
||||
v += " "
|
||||
}
|
||||
v += "-I" + buildBaseDir
|
||||
v += "-mmacosx-version-min=10.13"
|
||||
return v
|
||||
})
|
||||
// Use upsertEnv so we don't overwrite user's CGO_CXXFLAGS
|
||||
@@ -314,6 +313,17 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||
cmd.Env = upsertEnv(cmd.Env, "CGO_ENABLED", func(v string) string {
|
||||
return "1"
|
||||
})
|
||||
if runtime.GOOS == "darwin" {
|
||||
// Set the minimum Mac SDK to 10.13
|
||||
cmd.Env = upsertEnv(cmd.Env, "CGO_LDFLAGS", func(v string) string {
|
||||
if v != "" {
|
||||
v += " "
|
||||
}
|
||||
v += "-mmacosx-version-min=10.13"
|
||||
|
||||
return v
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
cmd.Env = upsertEnv(cmd.Env, "GOOS", func(v string) string {
|
||||
@@ -337,8 +347,6 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||
return err
|
||||
}
|
||||
|
||||
println("Done.")
|
||||
|
||||
if !options.Compress {
|
||||
return nil
|
||||
}
|
||||
@@ -520,17 +528,24 @@ func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
||||
}
|
||||
|
||||
// Check if there is a build command
|
||||
if b.projectData.BuildCommand == "" {
|
||||
var buildCommand string
|
||||
switch b.projectData.OutputType {
|
||||
case "dev":
|
||||
buildCommand = b.projectData.DevCommand
|
||||
default:
|
||||
buildCommand = b.projectData.BuildCommand
|
||||
}
|
||||
if buildCommand == "" {
|
||||
outputLogger.Println("No Build command. Skipping.")
|
||||
// No - ignore
|
||||
return nil
|
||||
}
|
||||
|
||||
outputLogger.Print("Compiling frontend: ")
|
||||
cmd := strings.Split(b.projectData.BuildCommand, " ")
|
||||
cmd := strings.Split(buildCommand, " ")
|
||||
if verbose {
|
||||
outputLogger.Println("")
|
||||
outputLogger.Println(" Build command: '" + strings.Join(cmd, " ") + "'")
|
||||
outputLogger.Println(" Build command: '" + buildCommand + "'")
|
||||
}
|
||||
stdout, stderr, err := shell.RunCommand(frontendDir, cmd[0], cmd[1:]...)
|
||||
if verbose || err != nil {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
|
||||
@@ -111,17 +112,11 @@ func Build(options *Options) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Build the base assets
|
||||
//err = builder.BuildAssets(options)
|
||||
//if err != nil {
|
||||
// return "", err
|
||||
//}
|
||||
|
||||
// If we are building for windows, we will need to generate the asset bundle before
|
||||
// compilation. This will be a .syso file in the project root
|
||||
if options.Pack && options.Platform == "windows" {
|
||||
outputLogger.Print("Generating bundle assets: ")
|
||||
err := packageApplication(options)
|
||||
err := packageApplicationForWindows(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -149,10 +144,10 @@ func Build(options *Options) (string, error) {
|
||||
options.OutputFile = amd64Filename
|
||||
options.CleanBuildDirectory = false
|
||||
if options.Verbosity == VERBOSE {
|
||||
println()
|
||||
println(" Building AMD64 Target:", filepath.Join(options.BuildDirectory, options.OutputFile))
|
||||
outputLogger.Println("\nBuilding AMD64 Target:", filepath.Join(options.BuildDirectory, options.OutputFile))
|
||||
}
|
||||
err = builder.CompileProject(options)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -161,15 +156,16 @@ func Build(options *Options) (string, error) {
|
||||
options.OutputFile = arm64Filename
|
||||
options.CleanBuildDirectory = false
|
||||
if options.Verbosity == VERBOSE {
|
||||
println(" Building ARM64 Target:", filepath.Join(options.BuildDirectory, options.OutputFile))
|
||||
outputLogger.Println("Building ARM64 Target:", filepath.Join(options.BuildDirectory, options.OutputFile))
|
||||
}
|
||||
err = builder.CompileProject(options)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Run lipo
|
||||
if options.Verbosity == VERBOSE {
|
||||
println(" Running lipo: ", "lipo", "-create", "-output", outputFile, amd64Filename, arm64Filename)
|
||||
outputLogger.Println(" Running lipo: ", "lipo", "-create", "-output", outputFile, amd64Filename, arm64Filename)
|
||||
}
|
||||
_, stderr, err := shell.RunCommand(options.BuildDirectory, "lipo", "-create", "-output", outputFile, amd64Filename, arm64Filename)
|
||||
if err != nil {
|
||||
@@ -193,6 +189,8 @@ func Build(options *Options) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
outputLogger.Println("Done.")
|
||||
|
||||
// Do we need to pack the app for non-windows?
|
||||
if options.Pack && options.Platform != "windows" {
|
||||
|
||||
@@ -212,6 +210,18 @@ func Build(options *Options) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return options.CompiledBinary, nil
|
||||
result := options.CompiledBinary
|
||||
|
||||
if options.Pack && options.Platform == "darwin" {
|
||||
sr := strings.Split(result, "/")
|
||||
for i := len(sr) - 1; i >= 0; i-- {
|
||||
if strings.Contains(sr[i], ".app") {
|
||||
result = strings.Join(sr[:i+1], "/")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -2,10 +2,19 @@ package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/leaanthony/winicon"
|
||||
"github.com/tc-hib/winres"
|
||||
|
||||
"github.com/jackmordaunt/icns"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wailsapp/wails/v2/pkg/buildassets"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
)
|
||||
|
||||
@@ -14,8 +23,10 @@ func packageProject(options *Options, platform string) error {
|
||||
|
||||
var err error
|
||||
switch platform {
|
||||
case "darwin", "windows":
|
||||
err = packageApplication(options)
|
||||
case "darwin":
|
||||
err = packageApplicationForDarwin(options)
|
||||
case "windows":
|
||||
err = packageApplicationForWindows(options)
|
||||
default:
|
||||
err = fmt.Errorf("packing not supported for %s yet", platform)
|
||||
}
|
||||
@@ -65,3 +76,225 @@ func getBuildBaseDirectory(options *Options) (string, error) {
|
||||
func getPackageAssetsDirectory() string {
|
||||
return fs.RelativePath("internal/packager", runtime.GOOS)
|
||||
}
|
||||
|
||||
func packageApplicationForDarwin(options *Options) error {
|
||||
|
||||
var err error
|
||||
|
||||
// Create directory structure
|
||||
bundlename := options.ProjectData.Name + ".app"
|
||||
|
||||
contentsDirectory := filepath.Join(options.BuildDirectory, bundlename, "/Contents")
|
||||
exeDir := filepath.Join(contentsDirectory, "/MacOS")
|
||||
err = fs.MkDirs(exeDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourceDir := filepath.Join(contentsDirectory, "/Resources")
|
||||
err = fs.MkDirs(resourceDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Copy binary
|
||||
packedBinaryPath := filepath.Join(exeDir, options.ProjectData.Name)
|
||||
err = fs.MoveFile(options.CompiledBinary, packedBinaryPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Cannot move file: "+options.ProjectData.OutputFilename)
|
||||
}
|
||||
|
||||
// Generate Info.plist
|
||||
err = processPList(options, contentsDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate Icons
|
||||
err = processApplicationIcon(resourceDir, options.ProjectData.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options.CompiledBinary = packedBinaryPath
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processPList(options *Options, contentsDirectory string) error {
|
||||
|
||||
// Check if plist already exists in project dir
|
||||
plistFileDir := filepath.Join(options.ProjectData.Path, "build", "darwin")
|
||||
plistFile := filepath.Join(plistFileDir, "Info.plist")
|
||||
// If the file doesn't exist, generate it
|
||||
if !fs.FileExists(plistFile) {
|
||||
err := buildassets.RegeneratePlist(plistFileDir, options.ProjectData.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Copy it to the contents directory
|
||||
targetFile := filepath.Join(contentsDirectory, "Info.plist")
|
||||
return fs.CopyFile(plistFile, targetFile)
|
||||
}
|
||||
|
||||
func processApplicationIcon(resourceDir string, iconsDir string) (err error) {
|
||||
|
||||
appIcon := filepath.Join(iconsDir, "appicon.png")
|
||||
|
||||
// Install default icon if one doesn't exist
|
||||
if !fs.FileExists(appIcon) {
|
||||
// No - Install default icon
|
||||
err = buildassets.RegenerateAppIcon(appIcon)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tgtBundle := path.Join(resourceDir, "iconfile.icns")
|
||||
imageFile, err := os.Open(appIcon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = imageFile.Close()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
srcImg, _, err := image.Decode(imageFile)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
dest, err := os.Create(tgtBundle)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
defer func() {
|
||||
err = dest.Close()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
return icns.Encode(dest, srcImg)
|
||||
}
|
||||
|
||||
func packageApplicationForWindows(options *Options) error {
|
||||
// Generate icon
|
||||
var err error
|
||||
err = generateIcoFile(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure Manifest is present
|
||||
err = generateManifest(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create syso file
|
||||
err = compileResources(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateManifest(options *Options) error {
|
||||
filename := options.ProjectData.Name + ".exe.manifest"
|
||||
manifestFile := filepath.Join(options.ProjectData.Path, "build", "windows", filename)
|
||||
if !fs.FileExists(manifestFile) {
|
||||
return buildassets.RegenerateManifest(manifestFile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateIcoFile(options *Options) error {
|
||||
// Check ico file exists already
|
||||
icoFile := filepath.Join(options.ProjectData.Path, "build", "windows", "icon.ico")
|
||||
if !fs.FileExists(icoFile) {
|
||||
// Check icon exists
|
||||
appicon := filepath.Join(options.ProjectData.Path, "build", "appicon.png")
|
||||
if !fs.FileExists(appicon) {
|
||||
return fmt.Errorf("application icon missing: %s", appicon)
|
||||
}
|
||||
// Load icon
|
||||
input, err := os.Open(appicon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
output, err := os.OpenFile(icoFile, os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = winicon.GenerateIcon(input, output, []int{256, 128, 64, 48, 32, 16})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func compileResources(options *Options) error {
|
||||
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
os.Chdir(currentDir)
|
||||
}()
|
||||
windowsDir := filepath.Join(options.ProjectData.Path, "build", "windows")
|
||||
err = os.Chdir(windowsDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rs := winres.ResourceSet{}
|
||||
icon := filepath.Join(windowsDir, "icon.ico")
|
||||
iconFile, err := os.Open(icon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iconFile.Close()
|
||||
ico, err := winres.LoadICO(iconFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rs.SetIcon(winres.RT_ICON, ico)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ManifestFilename := options.ProjectData.Name + ".exe.manifest"
|
||||
manifestData, err := os.ReadFile(ManifestFilename)
|
||||
xmlData, err := winres.AppManifestFromXML(manifestData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rs.SetManifest(xmlData)
|
||||
|
||||
targetFile := filepath.Join(options.ProjectData.Path, options.ProjectData.Name+"-res.syso")
|
||||
fout, err := os.Create(targetFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fout.Close()
|
||||
|
||||
archs := map[string]winres.Arch{
|
||||
"amd64": winres.ArchAMD64,
|
||||
}
|
||||
targetArch, supported := archs[options.Arch]
|
||||
if !supported {
|
||||
return fmt.Errorf("arch '%s' not supported", options.Arch)
|
||||
}
|
||||
|
||||
err = rs.WriteObject(fout, targetArch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/buildassets"
|
||||
|
||||
"github.com/jackmordaunt/icns"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
)
|
||||
|
||||
func packageApplication(options *Options) error {
|
||||
|
||||
var err error
|
||||
|
||||
// Create directory structure
|
||||
bundlename := options.ProjectData.Name + ".app"
|
||||
|
||||
contentsDirectory := filepath.Join(options.BuildDirectory, bundlename, "/Contents")
|
||||
exeDir := filepath.Join(contentsDirectory, "/MacOS")
|
||||
err = fs.MkDirs(exeDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourceDir := filepath.Join(contentsDirectory, "/Resources")
|
||||
err = fs.MkDirs(resourceDir, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Copy binary
|
||||
packedBinaryPath := filepath.Join(exeDir, options.ProjectData.Name)
|
||||
err = fs.MoveFile(options.CompiledBinary, packedBinaryPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Cannot move file: "+options.ProjectData.OutputFilename)
|
||||
}
|
||||
|
||||
// Generate Info.plist
|
||||
err = processPList(options, contentsDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate Icons
|
||||
err = processApplicationIcon(resourceDir, options.ProjectData.BuildDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options.CompiledBinary = packedBinaryPath
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processPList(options *Options, contentsDirectory string) error {
|
||||
|
||||
// Check if plist already exists in project dir
|
||||
plistFile := filepath.Join(options.ProjectData.BuildDir, "darwin", "Info.plist")
|
||||
|
||||
// If the file doesn't exist, generate it
|
||||
if !fs.FileExists(plistFile) {
|
||||
err := generateDefaultPlist(options, plistFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Copy it to the contents directory
|
||||
targetFile := filepath.Join(contentsDirectory, "Info.plist")
|
||||
return fs.CopyFile(plistFile, targetFile)
|
||||
}
|
||||
|
||||
func generateDefaultPlist(options *Options, targetPlistFile string) error {
|
||||
name := defaultString(options.ProjectData.Name, "WailsTest")
|
||||
exe := defaultString(options.OutputFile, name)
|
||||
version := "1.0.0"
|
||||
author := defaultString(options.ProjectData.Author.Name, "Anonymous")
|
||||
packageID := strings.Join([]string{"wails", name}, ".")
|
||||
plistData := newPlistData(name, exe, packageID, version, author)
|
||||
|
||||
tmpl := template.New("infoPlist")
|
||||
plistTemplate := fs.RelativePath("./internal/packager/darwin/Info.plist")
|
||||
infoPlist, err := ioutil.ReadFile(plistTemplate)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Cannot open plist template")
|
||||
}
|
||||
_, err = tmpl.Parse(string(infoPlist))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Write the template to a buffer
|
||||
var tpl bytes.Buffer
|
||||
err = tmpl.Execute(&tpl, plistData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the directory if it doesn't exist
|
||||
err = fs.MkDirs(filepath.Dir(targetPlistFile))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Save the file
|
||||
return ioutil.WriteFile(targetPlistFile, tpl.Bytes(), 0644)
|
||||
}
|
||||
|
||||
func defaultString(val string, defaultVal string) string {
|
||||
if val != "" {
|
||||
return val
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
type plistData struct {
|
||||
Title string
|
||||
Exe string
|
||||
PackageID string
|
||||
Version string
|
||||
Author string
|
||||
}
|
||||
|
||||
func newPlistData(title, exe, packageID, version, author string) *plistData {
|
||||
return &plistData{
|
||||
Title: title,
|
||||
Exe: exe,
|
||||
Version: version,
|
||||
PackageID: packageID,
|
||||
Author: author,
|
||||
}
|
||||
}
|
||||
|
||||
func processApplicationIcon(resourceDir string, iconsDir string) (err error) {
|
||||
|
||||
appIcon := filepath.Join(iconsDir, "appicon.png")
|
||||
|
||||
// Install default icon if one doesn't exist
|
||||
if !fs.FileExists(appIcon) {
|
||||
// No - Install default icon
|
||||
err = buildassets.RegenerateAppIcon(appIcon)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tgtBundle := path.Join(resourceDir, "iconfile.icns")
|
||||
imageFile, err := os.Open(appIcon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = imageFile.Close()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
srcImg, _, err := image.Decode(imageFile)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
dest, err := os.Create(tgtBundle)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
defer func() {
|
||||
err = dest.Close()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
return icns.Encode(dest, srcImg)
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leaanthony/winicon"
|
||||
"github.com/tc-hib/winres"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/pkg/buildassets"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func packageApplication(options *Options) error {
|
||||
// Generate icon
|
||||
var err error
|
||||
err = generateIcoFile(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure Manifest is present
|
||||
err = generateManifest(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create syso file
|
||||
err = compileResources(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateManifest(options *Options) error {
|
||||
filename := options.ProjectData.Name + ".exe.manifest"
|
||||
manifestFile := filepath.Join(options.ProjectData.Path, "build", "windows", filename)
|
||||
if !fs.FileExists(manifestFile) {
|
||||
return buildassets.RegenerateManifest(manifestFile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateIcoFile(options *Options) error {
|
||||
// Check ico file exists already
|
||||
icoFile := filepath.Join(options.ProjectData.Path, "build", "windows", "icon.ico")
|
||||
if !fs.FileExists(icoFile) {
|
||||
// Check icon exists
|
||||
appicon := filepath.Join(options.ProjectData.Path, "build", "appicon.png")
|
||||
if !fs.FileExists(appicon) {
|
||||
return fmt.Errorf("application icon missing: %s", appicon)
|
||||
}
|
||||
// Load icon
|
||||
input, err := os.Open(appicon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
output, err := os.OpenFile(icoFile, os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = winicon.GenerateIcon(input, output, []int{256, 128, 64, 48, 32, 16})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func compileResources(options *Options) error {
|
||||
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
os.Chdir(currentDir)
|
||||
}()
|
||||
windowsDir := filepath.Join(options.ProjectData.Path, "build", "windows")
|
||||
err = os.Chdir(windowsDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rs := winres.ResourceSet{}
|
||||
icon := filepath.Join(windowsDir, "icon.ico")
|
||||
iconFile, err := os.Open(icon)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer iconFile.Close()
|
||||
ico, err := winres.LoadICO(iconFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = rs.SetIcon(winres.RT_ICON, ico)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ManifestFilename := options.ProjectData.Name + ".exe.manifest"
|
||||
manifestData, err := os.ReadFile(ManifestFilename)
|
||||
xmlData, err := winres.AppManifestFromXML(manifestData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rs.SetManifest(xmlData)
|
||||
|
||||
targetFile := filepath.Join(options.ProjectData.Path, options.ProjectData.Name+"-res.syso")
|
||||
fout, err := os.Create(targetFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fout.Close()
|
||||
|
||||
archs := map[string]winres.Arch{
|
||||
"amd64": winres.ArchAMD64,
|
||||
}
|
||||
targetArch, supported := archs[options.Arch]
|
||||
if !supported {
|
||||
return fmt.Errorf("arch '%s' not supported", options.Arch)
|
||||
}
|
||||
|
||||
err = rs.WriteObject(fout, targetArch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
66
v2/pkg/menu/colours/colours.go
Normal file
66
v2/pkg/menu/colours/colours.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type Rgb struct {
|
||||
R uint8 `json:"r"`
|
||||
G uint8 `json:"g"`
|
||||
B uint8 `json:"b"`
|
||||
}
|
||||
|
||||
type Hsl struct {
|
||||
H float64 `json:"h"`
|
||||
S float64 `json:"s"`
|
||||
L float64 `json:"l"`
|
||||
}
|
||||
|
||||
type InputCol struct {
|
||||
Colorid uint8 `json:"colorId"`
|
||||
Hexstring string `json:"hexString"`
|
||||
Rgb Rgb `json:"rgb"`
|
||||
Hsl Hsl `json:"hsl"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
//go:embed gen.tmpl
|
||||
var Template string
|
||||
|
||||
func main() {
|
||||
|
||||
var Cols []InputCol
|
||||
|
||||
resp, err := http.Get("https://jonasjacek.github.io/colors/data.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = json.Unmarshal(data, &Cols)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
t, err := template.New("cols").Parse(Template)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
err = t.Execute(&buffer, Cols)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.WriteFile(filepath.Join("..", "cols.go"), buffer.Bytes(), 0755)
|
||||
}
|
||||
29
v2/pkg/menu/colours/gen.tmpl
Normal file
29
v2/pkg/menu/colours/gen.tmpl
Normal file
@@ -0,0 +1,29 @@
|
||||
package menu
|
||||
|
||||
type Rgb struct {
|
||||
R uint8 `json:"r"`
|
||||
G uint8 `json:"g"`
|
||||
B uint8 `json:"b"`
|
||||
}
|
||||
|
||||
type Hsl struct {
|
||||
H float64 `json:"h"`
|
||||
S float64 `json:"s"`
|
||||
L float64 `json:"l"`
|
||||
}
|
||||
|
||||
type Col struct {
|
||||
Hex string `json:"hex"`
|
||||
Rgb Rgb `json:"rgb"`
|
||||
Hsl Hsl `json:"hsl"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
var Cols = []*Col{ {{range $col := .}}
|
||||
{
|
||||
Hex: "{{.Hexstring}}",
|
||||
Rgb: Rgb{ {{.Rgb.R}}, {{.Rgb.G}}, {{.Rgb.B}} },
|
||||
Hsl: Hsl{ {{.Hsl.H}}, {{.Hsl.S}}, {{.Hsl.L}} },
|
||||
Name: "{{.Name}}",
|
||||
},{{end}}
|
||||
}
|
||||
1559
v2/pkg/menu/cols.go
Executable file
1559
v2/pkg/menu/cols.go
Executable file
File diff suppressed because it is too large
Load Diff
26
v2/pkg/menu/keys/macmodifiers.go
Normal file
26
v2/pkg/menu/keys/macmodifiers.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package keys
|
||||
|
||||
const (
|
||||
NSEventModifierFlagShift = 1 << 17 // Set if Shift key is pressed.
|
||||
NSEventModifierFlagControl = 1 << 18 // Set if Control key is pressed.
|
||||
NSEventModifierFlagOption = 1 << 19 // Set if Option or Alternate key is pressed.
|
||||
NSEventModifierFlagCommand = 1 << 20 // Set if Command key is pressed.
|
||||
)
|
||||
|
||||
var macModifierMap = map[Modifier]int{
|
||||
CmdOrCtrlKey: NSEventModifierFlagCommand,
|
||||
ControlKey: NSEventModifierFlagControl,
|
||||
OptionOrAltKey: NSEventModifierFlagOption,
|
||||
ShiftKey: NSEventModifierFlagShift,
|
||||
}
|
||||
|
||||
func ToMacModifier(accelerator *Accelerator) int {
|
||||
if accelerator == nil {
|
||||
return 0
|
||||
}
|
||||
result := 0
|
||||
for _, modifier := range accelerator.Modifiers {
|
||||
result |= macModifierMap[modifier]
|
||||
}
|
||||
return result
|
||||
}
|
||||
31
v2/pkg/menu/keys/macmodifiers_test.go
Normal file
31
v2/pkg/menu/keys/macmodifiers_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package keys
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestToMacModifier(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
accelerator *Accelerator
|
||||
want int
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
{"nil", nil, 0},
|
||||
{"empty", &Accelerator{}, 0},
|
||||
{"key", &Accelerator{Key: "p"}, 0},
|
||||
{"cmd", CmdOrCtrl(""), NSEventModifierFlagCommand},
|
||||
{"ctrl", Control(""), NSEventModifierFlagControl},
|
||||
{"shift", Shift(""), NSEventModifierFlagShift},
|
||||
{"option", OptionOrAlt(""), NSEventModifierFlagOption},
|
||||
{"cmd+ctrl", Combo("", CmdOrCtrlKey, ControlKey), NSEventModifierFlagCommand | NSEventModifierFlagControl},
|
||||
{"cmd+ctrl+shift", Combo("", CmdOrCtrlKey, ControlKey, ShiftKey), NSEventModifierFlagCommand | NSEventModifierFlagControl | NSEventModifierFlagShift},
|
||||
{"cmd+ctrl+shift+option", Combo("", CmdOrCtrlKey, ControlKey, ShiftKey, OptionOrAltKey), NSEventModifierFlagCommand | NSEventModifierFlagControl | NSEventModifierFlagShift | NSEventModifierFlagOption},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ToMacModifier(tt.accelerator); got != tt.want {
|
||||
t.Errorf("ToMacModifier() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,9 @@ type MenuItem struct {
|
||||
// Label is what appears as the menu text
|
||||
Label string
|
||||
// Role is a predefined menu type
|
||||
//Role Role `json:"Role,omitempty"`
|
||||
Role Role
|
||||
// Accelerator holds a representation of a key binding
|
||||
Accelerator *keys.Accelerator `json:"Accelerator,omitempty"`
|
||||
Accelerator *keys.Accelerator
|
||||
// Type of MenuItem, EG: Checkbox, Text, Separator, Radio, Submenu
|
||||
Type Type
|
||||
// Disabled makes the item unselectable
|
||||
@@ -24,10 +24,10 @@ type MenuItem struct {
|
||||
Checked bool
|
||||
// Submenu contains a list of menu items that will be shown as a submenu
|
||||
//SubMenu []*MenuItem `json:"SubMenu,omitempty"`
|
||||
SubMenu *Menu `json:"SubMenu,omitempty"`
|
||||
SubMenu *Menu
|
||||
|
||||
// Callback function when menu clicked
|
||||
Click Callback `json:"-"`
|
||||
Click Callback
|
||||
/*
|
||||
// Text Colour
|
||||
RGBA string
|
||||
@@ -267,16 +267,3 @@ func SubMenu(label string, menu *Menu) *MenuItem {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// SubMenuWithID is a helper to create Submenus with an ID
|
||||
func SubMenuWithID(label string, menu *Menu) *MenuItem {
|
||||
result := &MenuItem{
|
||||
Label: label,
|
||||
SubMenu: menu,
|
||||
Type: SubmenuType,
|
||||
}
|
||||
|
||||
menu.setParent(result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -3,37 +3,39 @@
|
||||
// Electron License: https://github.com/electron/electron/blob/master/LICENSE
|
||||
package menu
|
||||
|
||||
/*
|
||||
type Role string
|
||||
// Role is a type to identify menu roles
|
||||
type Role int
|
||||
|
||||
// These constants need to be kept in sync with `v2/internal/frontend/desktop/darwin/Role.h`
|
||||
const (
|
||||
AboutRole Role = "about"
|
||||
UndoRole Role = "undo"
|
||||
RedoRole Role = "redo"
|
||||
CutRole Role = "cut"
|
||||
CopyRole Role = "copy"
|
||||
PasteRole Role = "paste"
|
||||
PasteAndMatchStyleRole Role = "pasteAndMatchStyle"
|
||||
SelectAllRole Role = "selectAll"
|
||||
DeleteRole Role = "delete"
|
||||
MinimizeRole Role = "minimize"
|
||||
QuitRole Role = "quit"
|
||||
TogglefullscreenRole Role = "togglefullscreen"
|
||||
FileMenuRole Role = "fileMenu"
|
||||
EditMenuRole Role = "editMenu"
|
||||
ViewMenuRole Role = "viewMenu"
|
||||
WindowMenuRole Role = "windowMenu"
|
||||
AppMenuRole Role = "appMenu"
|
||||
HideRole Role = "hide"
|
||||
HideOthersRole Role = "hideOthers"
|
||||
UnhideRole Role = "unhide"
|
||||
FrontRole Role = "front"
|
||||
ZoomRole Role = "zoom"
|
||||
WindowSubMenuRole Role = "windowSubMenu"
|
||||
HelpSubMenuRole Role = "helpSubMenu"
|
||||
SeparatorItemRole Role = "separatorItem"
|
||||
AppMenuRole Role = 1
|
||||
EditMenuRole = 2
|
||||
//AboutRole Role = "about"
|
||||
//UndoRole Role = "undo"
|
||||
//RedoRole Role = "redo"
|
||||
//CutRole Role = "cut"
|
||||
//CopyRole Role = "copy"
|
||||
//PasteRole Role = "paste"
|
||||
//PasteAndMatchStyleRole Role = "pasteAndMatchStyle"
|
||||
//SelectAllRole Role = "selectAll"
|
||||
//DeleteRole Role = "delete"
|
||||
//MinimizeRole Role = "minimize"
|
||||
//QuitRole Role = "quit"
|
||||
//TogglefullscreenRole Role = "togglefullscreen"
|
||||
//FileMenuRole Role = "fileMenu"
|
||||
//ViewMenuRole Role = "viewMenu"
|
||||
//WindowMenuRole Role = "windowMenu"
|
||||
//HideRole Role = "hide"
|
||||
//HideOthersRole Role = "hideOthers"
|
||||
//UnhideRole Role = "unhide"
|
||||
//FrontRole Role = "front"
|
||||
//ZoomRole Role = "zoom"
|
||||
//WindowSubMenuRole Role = "windowSubMenu"
|
||||
//HelpSubMenuRole Role = "helpSubMenu"
|
||||
//SeparatorItemRole Role = "separatorItem"
|
||||
)
|
||||
|
||||
/*
|
||||
// About provides a MenuItem with the About role
|
||||
func About() *MenuItem {
|
||||
return &MenuItem{
|
||||
@@ -111,8 +113,8 @@ func Quit() *MenuItem {
|
||||
}
|
||||
}
|
||||
|
||||
// Togglefullscreen provides a MenuItem with the Togglefullscreen role
|
||||
func Togglefullscreen() *MenuItem {
|
||||
// ToggleFullscreen provides a MenuItem with the ToggleFullscreen role
|
||||
func ToggleFullscreen() *MenuItem {
|
||||
return &MenuItem{
|
||||
Role: TogglefullscreenRole,
|
||||
}
|
||||
@@ -124,6 +126,7 @@ func FileMenu() *MenuItem {
|
||||
Role: FileMenuRole,
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// EditMenu provides a MenuItem with the whole default "Edit" menu (Undo, Copy, etc.).
|
||||
func EditMenu() *MenuItem {
|
||||
@@ -132,6 +135,7 @@ func EditMenu() *MenuItem {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// ViewMenu provides a MenuItem with the whole default "View" menu (Reload, Toggle Developer Tools, etc.)
|
||||
func ViewMenu() *MenuItem {
|
||||
return &MenuItem{
|
||||
@@ -145,7 +149,7 @@ func WindowMenu() *MenuItem {
|
||||
Role: WindowMenuRole,
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
// These roles are Mac only
|
||||
|
||||
// AppMenu provides a MenuItem with the whole default "App" menu (About, Services, etc.)
|
||||
@@ -155,6 +159,7 @@ func AppMenu() *MenuItem {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Hide provides a MenuItem that maps to the hide action.
|
||||
func Hide() *MenuItem {
|
||||
return &MenuItem{
|
||||
@@ -169,8 +174,8 @@ func HideOthers() *MenuItem {
|
||||
}
|
||||
}
|
||||
|
||||
// Unhide provides a MenuItem that maps to the unhideAllApplications action.
|
||||
func Unhide() *MenuItem {
|
||||
// UnHide provides a MenuItem that maps to the unHideAllApplications action.
|
||||
func UnHide() *MenuItem {
|
||||
return &MenuItem{
|
||||
Role: UnhideRole,
|
||||
}
|
||||
|
||||
256
v2/pkg/menu/styledlabel.go
Normal file
256
v2/pkg/menu/styledlabel.go
Normal file
@@ -0,0 +1,256 @@
|
||||
package menu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TextStyle int
|
||||
|
||||
const (
|
||||
Bold TextStyle = 1 << 0
|
||||
Faint TextStyle = 1 << 1
|
||||
Italic TextStyle = 1 << 2
|
||||
Blinking TextStyle = 1 << 3
|
||||
Inversed TextStyle = 1 << 4
|
||||
Invisible TextStyle = 1 << 5
|
||||
Underlined TextStyle = 1 << 6
|
||||
Strikethrough TextStyle = 1 << 7
|
||||
)
|
||||
|
||||
type StyledText struct {
|
||||
Label string
|
||||
FgCol *Col
|
||||
BgCol *Col
|
||||
Style TextStyle
|
||||
}
|
||||
|
||||
func (s *StyledText) Bold() bool {
|
||||
return s.Style&Bold == Bold
|
||||
}
|
||||
func (s *StyledText) Faint() bool {
|
||||
return s.Style&Faint == Faint
|
||||
}
|
||||
func (s *StyledText) Italic() bool {
|
||||
return s.Style&Italic == Italic
|
||||
}
|
||||
func (s *StyledText) Blinking() bool {
|
||||
return s.Style&Blinking == Blinking
|
||||
}
|
||||
func (s *StyledText) Inversed() bool {
|
||||
return s.Style&Inversed == Inversed
|
||||
}
|
||||
func (s *StyledText) Invisible() bool {
|
||||
return s.Style&Invisible == Invisible
|
||||
}
|
||||
func (s *StyledText) Underlined() bool {
|
||||
return s.Style&Underlined == Underlined
|
||||
}
|
||||
func (s *StyledText) Strikethrough() bool {
|
||||
return s.Style&Strikethrough == Strikethrough
|
||||
}
|
||||
|
||||
var ansiColorMap = map[string]map[string]*Col{
|
||||
"Normal": {
|
||||
"30": Cols[0],
|
||||
"31": Cols[1],
|
||||
"32": Cols[2],
|
||||
"33": Cols[3],
|
||||
"34": Cols[4],
|
||||
"35": Cols[5],
|
||||
"36": Cols[6],
|
||||
"37": Cols[7],
|
||||
},
|
||||
"Bold": {
|
||||
"30": Cols[8],
|
||||
"31": Cols[9],
|
||||
"32": Cols[10],
|
||||
"33": Cols[11],
|
||||
"34": Cols[12],
|
||||
"35": Cols[13],
|
||||
"36": Cols[14],
|
||||
"37": Cols[15],
|
||||
},
|
||||
"Faint": {
|
||||
"30": Cols[0],
|
||||
"31": Cols[1],
|
||||
"32": Cols[2],
|
||||
"33": Cols[3],
|
||||
"34": Cols[4],
|
||||
"35": Cols[5],
|
||||
"36": Cols[6],
|
||||
"37": Cols[7],
|
||||
},
|
||||
}
|
||||
|
||||
func ParseANSI(input string) ([]*StyledText, error) {
|
||||
var result []*StyledText
|
||||
invalid := fmt.Errorf("invalid ansi string")
|
||||
missingTerminator := fmt.Errorf("missing escape terminator 'm'")
|
||||
invalidTrueColorSequence := fmt.Errorf("invalid TrueColor sequence")
|
||||
invalid256ColSequence := fmt.Errorf("invalid 256 colour sequence")
|
||||
index := 0
|
||||
var currentStyledText *StyledText = &StyledText{}
|
||||
|
||||
if len(input) == 0 {
|
||||
return nil, invalid
|
||||
}
|
||||
|
||||
for {
|
||||
// Read all chars to next escape code
|
||||
esc := strings.Index(input, "\033[")
|
||||
|
||||
// If no more esc chars, save what's left and return
|
||||
if esc == -1 {
|
||||
text := input[index:]
|
||||
if len(text) > 0 {
|
||||
currentStyledText.Label = text
|
||||
result = append(result, currentStyledText)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
label := input[:esc]
|
||||
if len(label) > 0 {
|
||||
currentStyledText.Label = label
|
||||
result = append(result, currentStyledText)
|
||||
currentStyledText = &StyledText{
|
||||
Label: "",
|
||||
FgCol: currentStyledText.FgCol,
|
||||
BgCol: currentStyledText.BgCol,
|
||||
Style: currentStyledText.Style,
|
||||
}
|
||||
}
|
||||
input = input[esc:]
|
||||
// skip
|
||||
input = input[2:]
|
||||
|
||||
// Read in params
|
||||
endesc := strings.Index(input, "m")
|
||||
if endesc == -1 {
|
||||
return nil, missingTerminator
|
||||
}
|
||||
paramText := input[:endesc]
|
||||
input = input[endesc+1:]
|
||||
params := strings.Split(paramText, ";")
|
||||
colourMap := ansiColorMap["Normal"]
|
||||
skip := 0
|
||||
for index, param := range params {
|
||||
if skip > 0 {
|
||||
skip--
|
||||
continue
|
||||
}
|
||||
switch param {
|
||||
case "0":
|
||||
// Reset styles
|
||||
if len(params) == 1 {
|
||||
if len(currentStyledText.Label) > 0 {
|
||||
result = append(result, currentStyledText)
|
||||
currentStyledText = &StyledText{
|
||||
Label: "",
|
||||
FgCol: currentStyledText.FgCol,
|
||||
BgCol: currentStyledText.BgCol,
|
||||
Style: currentStyledText.Style,
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
currentStyledText.Style = 0
|
||||
currentStyledText.FgCol = nil
|
||||
currentStyledText.BgCol = nil
|
||||
case "1":
|
||||
// Bold
|
||||
colourMap = ansiColorMap["Bold"]
|
||||
currentStyledText.Style |= Bold
|
||||
case "2":
|
||||
// Dim/Feint
|
||||
colourMap = ansiColorMap["Faint"]
|
||||
currentStyledText.Style |= Faint
|
||||
case "3":
|
||||
// Italic
|
||||
currentStyledText.Style |= Italic
|
||||
case "4":
|
||||
// Underlined
|
||||
currentStyledText.Style |= Underlined
|
||||
case "5":
|
||||
// Blinking
|
||||
currentStyledText.Style |= Blinking
|
||||
case "7":
|
||||
// Inverse
|
||||
currentStyledText.Style |= Inversed
|
||||
case "8":
|
||||
// Invisible
|
||||
currentStyledText.Style |= Invisible
|
||||
case "9":
|
||||
// Strikethrough
|
||||
currentStyledText.Style |= Strikethrough
|
||||
case "30", "31", "32", "33", "34", "35", "36", "37":
|
||||
currentStyledText.FgCol = colourMap[param]
|
||||
case "40", "41", "42", "43", "44", "45", "46", "47":
|
||||
currentStyledText.BgCol = colourMap[param]
|
||||
case "38", "48":
|
||||
if len(params)-index < 2 {
|
||||
return nil, invalid
|
||||
}
|
||||
// 256 colours
|
||||
if params[index+1] == "5" {
|
||||
skip = 2
|
||||
colIndexText := params[index+2]
|
||||
colIndex, err := strconv.Atoi(colIndexText)
|
||||
if err != nil {
|
||||
return nil, invalid256ColSequence
|
||||
}
|
||||
if colIndex < 0 || colIndex > 255 {
|
||||
return nil, invalid256ColSequence
|
||||
}
|
||||
if param == "38" {
|
||||
currentStyledText.FgCol = Cols[colIndex]
|
||||
continue
|
||||
}
|
||||
currentStyledText.BgCol = Cols[colIndex]
|
||||
continue
|
||||
}
|
||||
// we must have 4 params left
|
||||
if len(params)-index < 4 {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
if params[index+1] != "2" {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
var r, g, b uint8
|
||||
ri, err := strconv.Atoi(params[index+2])
|
||||
if err != nil {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
gi, err := strconv.Atoi(params[index+3])
|
||||
if err != nil {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
bi, err := strconv.Atoi(params[index+4])
|
||||
if err != nil {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
if bi > 255 || gi > 255 || ri > 255 {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
if bi < 0 || gi < 0 || ri < 0 {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
r = uint8(ri)
|
||||
g = uint8(gi)
|
||||
b = uint8(bi)
|
||||
skip = 4
|
||||
colvalue := fmt.Sprintf("#%02x%02x%02x", r, g, b)
|
||||
if param == "38" {
|
||||
currentStyledText.FgCol = &Col{Hex: colvalue, Rgb: Rgb{r, g, b}}
|
||||
continue
|
||||
}
|
||||
currentStyledText.BgCol = &Col{Hex: colvalue}
|
||||
default:
|
||||
return nil, invalid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
197
v2/pkg/menu/styledlabel_test.go
Normal file
197
v2/pkg/menu/styledlabel_test.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package menu
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
func TestParseAnsi16SingleColour(t *testing.T) {
|
||||
is := is.New(t)
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantText string
|
||||
wantColor string
|
||||
wantErr bool
|
||||
}{
|
||||
{"No formatting", "Hello World", "Hello World", "", false},
|
||||
{"Black", "\u001b[0;30mHello World\033[0m", "Hello World", "Black", false},
|
||||
{"Red", "\u001b[0;31mHello World\033[0m", "Hello World", "Maroon", false},
|
||||
{"Green", "\u001b[0;32m\033[0m", "", "Green", false},
|
||||
{"Yellow", "\u001b[0;33m😀\033[0m", "😀", "Olive", false},
|
||||
{"Blue", "\u001b[0;34m123\033[0m", "123", "Navy", false},
|
||||
{"Purple", "\u001b[0;35m👩🏽🔧\u001B[0m", "👩🏽🔧", "Purple", false},
|
||||
{"Cyan", "\033[0;36m😀\033[0m", "😀", "Teal", false},
|
||||
{"White", "\u001b[0;37m[0;37m\033[0m", "[0;37m", "Silver", false},
|
||||
{"Black Bold", "\u001b[1;30mHello World\033[0m", "Hello World", "Grey", false},
|
||||
{"Red Bold", "\u001b[1;31mHello World\033[0m", "Hello World", "Red", false},
|
||||
{"Green Bold", "\u001b[1;32m\033[0m", "", "Lime", false},
|
||||
{"Yellow Bold", "\u001b[1;33m😀\033[0m", "😀", "Yellow", false},
|
||||
{"Blue Bold", "\u001b[1;34m123\033[0m", "123", "Blue", false},
|
||||
{"Purple Bold", "\u001b[1;35m👩🏽🔧\u001B[0m", "👩🏽🔧", "Fuchsia", false},
|
||||
{"Cyan Bold", "\033[1;36m😀\033[0m", "😀", "Aqua", false},
|
||||
{"White Bold", "\u001b[1;37m[0;37m\033[0m", "[0;37m", "White", false},
|
||||
{"Blank", "", "", "", true},
|
||||
{"Emoji", "😀👩🏽🔧", "😀👩🏽🔧", "", false},
|
||||
{"Spaces", " ", " ", "", false},
|
||||
{"Bad code", "\u001b[1 ", "", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseANSI(tt.input)
|
||||
is.Equal(err != nil, tt.wantErr)
|
||||
expectedLength := 1
|
||||
if tt.wantErr {
|
||||
expectedLength = 0
|
||||
}
|
||||
is.Equal(len(got), expectedLength)
|
||||
if expectedLength == 1 {
|
||||
if len(tt.wantColor) > 0 {
|
||||
is.True(got[0].FgCol != nil)
|
||||
is.Equal(got[0].FgCol.Name, tt.wantColor)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAnsi16MultiColour(t *testing.T) {
|
||||
is := is.New(t)
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []*StyledText
|
||||
wantErr bool
|
||||
}{
|
||||
{"Black & Red", "\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Black"}},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Maroon"}},
|
||||
}, false},
|
||||
{"Text then Black & Red", "This is great!\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{
|
||||
{Label: "This is great!"},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Black"}},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Maroon"}},
|
||||
}, false},
|
||||
{"Text Reset then Black & Red", "This is great!\u001B[0m\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{
|
||||
{Label: "This is great!"},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Black"}},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Maroon"}},
|
||||
}, false},
|
||||
{"Black & Red no reset", "\u001B[0;30mHello World\u001B[0;31mHello World", []*StyledText{
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Black"}},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Maroon"}},
|
||||
}, false},
|
||||
{"Black,space,Red", "\u001B[0;30mHello World\u001B[0m \u001B[0;31mHello World\u001B[0m", []*StyledText{
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Black"}},
|
||||
{Label: " "},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Maroon"}},
|
||||
}, false},
|
||||
{"Black,Red,Blue,Green underlined", "\033[4;30mBlack\u001B[0m\u001B[4;31mRed\u001B[0m\u001B[4;34mBlue\u001B[0m\u001B[4;32mGreen\u001B[0m", []*StyledText{
|
||||
{Label: "Black", FgCol: &Col{Name: "Black"}, Style: Underlined},
|
||||
{Label: "Red", FgCol: &Col{Name: "Maroon"}, Style: Underlined},
|
||||
{Label: "Blue", FgCol: &Col{Name: "Navy"}, Style: Underlined},
|
||||
{Label: "Green", FgCol: &Col{Name: "Green"}, Style: Underlined},
|
||||
}, false},
|
||||
{"Black,Red,Blue,Green bold", "\033[1;30mBlack\u001B[0m\u001B[1;31mRed\u001B[0m\u001B[1;34mBlue\u001B[0m\u001B[1;32mGreen\u001B[0m", []*StyledText{
|
||||
{Label: "Black", FgCol: &Col{Name: "Grey"}, Style: Bold},
|
||||
{Label: "Red", FgCol: &Col{Name: "Red"}, Style: Bold},
|
||||
{Label: "Blue", FgCol: &Col{Name: "Blue"}, Style: Bold},
|
||||
{Label: "Green", FgCol: &Col{Name: "Lime"}, Style: Bold},
|
||||
}, false},
|
||||
{"Green Feint & Yellow Italic", "\u001B[2;32m👩🏽🔧\u001B[0m\u001B[0;3;33m👩🏽🔧\u001B[0m", []*StyledText{
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Green"}, Style: Faint},
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Olive"}, Style: Italic},
|
||||
}, false},
|
||||
{"Green Blinking & Yellow Inversed", "\u001B[5;32m👩🏽🔧\u001B[0m\u001B[0;7;33m👩🏽🔧\u001B[0m", []*StyledText{
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Green"}, Style: Blinking},
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Olive"}, Style: Inversed},
|
||||
}, false},
|
||||
{"Green Invisible & Yellow Invisible & Strikethrough", "\u001B[8;32m👩🏽🔧\u001B[0m\u001B[9;33m👩🏽🔧\u001B[0m", []*StyledText{
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Green"}, Style: Invisible},
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Olive"}, Style: Invisible | Strikethrough},
|
||||
}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseANSI(tt.input)
|
||||
is.Equal(err != nil, tt.wantErr)
|
||||
for index, w := range tt.want {
|
||||
is.Equal(got[index].Label, w.Label)
|
||||
if w.FgCol != nil {
|
||||
is.Equal(got[index].FgCol.Name, w.FgCol.Name)
|
||||
}
|
||||
is.Equal(got[index].Style, w.Style)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAnsi256(t *testing.T) {
|
||||
is := is.New(t)
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []*StyledText
|
||||
wantErr bool
|
||||
}{
|
||||
{"Grey93 & DarkViolet", "\u001B[38;5;255mGrey93\u001B[0m\u001B[38;5;128mDarkViolet\u001B[0m", []*StyledText{
|
||||
{Label: "Grey93", FgCol: &Col{Name: "Grey93"}},
|
||||
{Label: "DarkViolet", FgCol: &Col{Name: "DarkViolet"}},
|
||||
}, false},
|
||||
{"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;255mGrey93\u001B[0m\u001B[0;3;38;5;128mDarkViolet\u001B[0m", []*StyledText{
|
||||
{Label: "Grey93", FgCol: &Col{Name: "Grey93"}, Style: Bold},
|
||||
{Label: "DarkViolet", FgCol: &Col{Name: "DarkViolet"}, Style: Italic},
|
||||
}, false},
|
||||
{"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;256mGrey93\u001B[0m", nil, true},
|
||||
{"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;-1mGrey93\u001B[0m", nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseANSI(tt.input)
|
||||
is.Equal(err != nil, tt.wantErr)
|
||||
for index, w := range tt.want {
|
||||
is.Equal(got[index].Label, w.Label)
|
||||
if w.FgCol != nil {
|
||||
is.Equal(got[index].FgCol.Name, w.FgCol.Name)
|
||||
}
|
||||
is.Equal(got[index].Style, w.Style)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAnsiTrueColor(t *testing.T) {
|
||||
is := is.New(t)
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []*StyledText
|
||||
wantErr bool
|
||||
}{
|
||||
{"Red", "\u001B[38;2;255;0;0mRed\u001B[0m", []*StyledText{
|
||||
{Label: "Red", FgCol: &Col{Rgb: Rgb{255, 0, 0}, Hex: "#ff0000"}},
|
||||
}, false},
|
||||
{"Red, text, Green", "\u001B[38;2;255;0;0mRed\u001B[0mI am plain text\u001B[38;2;0;255;0mGreen\u001B[0m", []*StyledText{
|
||||
{Label: "Red", FgCol: &Col{Rgb: Rgb{255, 0, 0}, Hex: "#ff0000"}},
|
||||
{Label: "I am plain text"},
|
||||
{Label: "Green", FgCol: &Col{Rgb: Rgb{0, 255, 0}, Hex: "#00ff00"}},
|
||||
}, false},
|
||||
{"Bad 1", "\u001B[38;2;256;0;0mRed\u001B[0m", nil, true},
|
||||
{"Bad 2", "\u001B[38;2;-1;0;0mRed\u001B[0m", nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseANSI(tt.input)
|
||||
is.Equal(err != nil, tt.wantErr)
|
||||
for index, w := range tt.want {
|
||||
is.Equal(got[index].Label, w.Label)
|
||||
if w.FgCol != nil {
|
||||
is.Equal(got[index].FgCol.Hex, w.FgCol.Hex)
|
||||
is.Equal(got[index].FgCol.Rgb, w.FgCol.Rgb)
|
||||
}
|
||||
is.Equal(got[index].Style, w.Style)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package options
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
)
|
||||
|
||||
// Default options for creating the App
|
||||
@@ -11,3 +12,8 @@ var Default = &App{
|
||||
Logger: logger.NewDefaultLogger(),
|
||||
LogLevel: logger.INFO,
|
||||
}
|
||||
|
||||
var defaultMacMenu = menu.NewMenuFromItems(
|
||||
menu.AppMenu(),
|
||||
menu.EditMenu(),
|
||||
)
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package mac
|
||||
|
||||
type ActivationPolicy int
|
||||
//type ActivationPolicy int
|
||||
//
|
||||
//const (
|
||||
// NSApplicationActivationPolicyRegular ActivationPolicy = 0
|
||||
// NSApplicationActivationPolicyAccessory ActivationPolicy = 1
|
||||
// NSApplicationActivationPolicyProhibited ActivationPolicy = 2
|
||||
//)
|
||||
|
||||
const (
|
||||
NSApplicationActivationPolicyRegular ActivationPolicy = 0
|
||||
NSApplicationActivationPolicyAccessory ActivationPolicy = 1
|
||||
NSApplicationActivationPolicyProhibited ActivationPolicy = 2
|
||||
)
|
||||
type AboutInfo struct {
|
||||
Title string
|
||||
Message string
|
||||
Icon []byte
|
||||
}
|
||||
|
||||
// Options are options specific to Mac
|
||||
type Options struct {
|
||||
@@ -14,6 +20,7 @@ type Options struct {
|
||||
Appearance AppearanceType
|
||||
WebviewIsTransparent bool
|
||||
WindowIsTranslucent bool
|
||||
ActivationPolicy ActivationPolicy
|
||||
URLHandlers map[string]func(string)
|
||||
//ActivationPolicy ActivationPolicy
|
||||
About *AboutInfo
|
||||
//URLHandlers map[string]func(string)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@ package options
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
||||
"log"
|
||||
"runtime"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
|
||||
@@ -26,6 +29,7 @@ type App struct {
|
||||
MaxHeight int
|
||||
StartHidden bool
|
||||
HideWindowOnClose bool
|
||||
AlwaysOnTop bool
|
||||
RGBA *RGBA
|
||||
Assets embed.FS
|
||||
Menu *menu.Menu
|
||||
@@ -39,7 +43,7 @@ type App struct {
|
||||
//ContextMenus []*menu.ContextMenu
|
||||
//TrayMenus []*menu.TrayMenu
|
||||
Windows *windows.Options
|
||||
//Mac *mac.Options
|
||||
Mac *mac.Options
|
||||
}
|
||||
|
||||
type RGBA struct {
|
||||
@@ -91,4 +95,11 @@ func MergeDefaults(appoptions *App) {
|
||||
appoptions.Height = appoptions.MaxHeight
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
if appoptions.Menu == nil {
|
||||
appoptions.Menu = defaultMacMenu
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
@@ -53,6 +54,11 @@ func WindowSetSize(ctx context.Context, width int, height int) {
|
||||
appFrontend.WindowSetSize(width, height)
|
||||
}
|
||||
|
||||
func WindowGetSize(ctx context.Context) (int, int) {
|
||||
appFrontend := getFrontend(ctx)
|
||||
return appFrontend.WindowGetSize()
|
||||
}
|
||||
|
||||
// WindowSetMinSize sets the minimum size of the window
|
||||
func WindowSetMinSize(ctx context.Context, width int, height int) {
|
||||
appFrontend := getFrontend(ctx)
|
||||
@@ -71,6 +77,11 @@ func WindowSetPosition(ctx context.Context, x int, y int) {
|
||||
appFrontend.WindowSetPos(x, y)
|
||||
}
|
||||
|
||||
func WindowGetPos(ctx context.Context) (int, int) {
|
||||
appFrontend := getFrontend(ctx)
|
||||
return appFrontend.WindowGetPos()
|
||||
}
|
||||
|
||||
// WindowMaximise the window
|
||||
func WindowMaximise(ctx context.Context) {
|
||||
appFrontend := getFrontend(ctx)
|
||||
|
||||
161
website/blog/2021-11-06-v2-beta2-release-notes.mdx
Normal file
161
website/blog/2021-11-06-v2-beta2-release-notes.mdx
Normal file
@@ -0,0 +1,161 @@
|
||||
---
|
||||
slug: wails-v2-beta-for-mac
|
||||
title: Wails v2 Beta for MacOS
|
||||
authors: [leaanthony]
|
||||
tags: [wails, v2]
|
||||
---
|
||||
|
||||
|
||||
<div class="text--center">
|
||||
<img src="/img/wails-mac.png" width="60%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
Today marks the first beta release of Wails v2 for Mac! It's taken quite a while to get to this point and I'm hoping
|
||||
that today's release will give you something that's reasonably useful. There have been a number of twists and turns
|
||||
to get to this point and I'm hoping, with your help, to iron out the crinkles and get the Mac port polished for the
|
||||
final v2 release.
|
||||
|
||||
You mean this isn't ready for production? For your use case, it may well be ready, but there are still a number of
|
||||
known issues so keep your eye on [this project board](https://github.com/wailsapp/wails/projects/7) and if you would
|
||||
like to contribute, you'd be very welcome!
|
||||
|
||||
So what's new for Wails v2 for Mac vs v1? Hint: They're pretty similar to the Windows Beta :wink:
|
||||
|
||||
### New Features
|
||||
|
||||
<div class="text--center">
|
||||
<img src="/img/wails-menus-mac.png" width="80%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
There were a lot of requests for native menu support. Wails has finally got you covered. Application menus are now available
|
||||
and include support for most native menu features. This includes standard menu items, checkboxes, radio groups, submenus
|
||||
and separators.
|
||||
|
||||
There were a huge number of requests in v1 for the ability to have greater control of the window itself.
|
||||
I'm happy to announce that there's new runtime APIs specifically for this.
|
||||
It's feature-rich and supports multi-monitor configurations. There is also an improved dialogs API: Now, you can have modern, native
|
||||
dialogs with rich configuration to cater for all your dialog needs.
|
||||
|
||||
### Mac Specific Options
|
||||
|
||||
In addition to the normal application options, Wails v2 for Mac also brings some Mac extras:
|
||||
|
||||
- Make your window all funky and translucent, like all the pretty swift apps!
|
||||
- Highly customisable titlebar
|
||||
- We support the NSAppearance options for the application
|
||||
- Simple config to auto-create an "About" menu
|
||||
|
||||
### No requirement to bundle assets
|
||||
|
||||
A huge pain-point of v1 was the need to condense your entire application down to single JS & CSS files. I'm happy to
|
||||
announce that for v2, there is no requirement to bundle assets, in any way, shape or form. Want to load a local image? Use an
|
||||
`<img>` tag with a local src path. Want to use a cool font? Copy it in and add the path to it in your CSS.
|
||||
|
||||
> Wow, that sounds like a webserver...
|
||||
|
||||
Yes, it works just like a webserver, except it isn't.
|
||||
|
||||
> So how do I include my assets?
|
||||
|
||||
You just pass a single `embed.FS` that contains all your assets into your application configuration.
|
||||
They don't even need to be in the top directory - Wails will just work it out for you.
|
||||
|
||||
### New Development Experience
|
||||
|
||||
Now that assets don't need to be bundled, it's enabled a whole new development experience. The new `wails dev`
|
||||
command will build and run your application, but instead of using the assets in the `embed.FS`, it loads them directly
|
||||
from disk.
|
||||
|
||||
It also provides the additional features:
|
||||
|
||||
- Hot reload - Any changes to frontend assets will trigger and auto reload of the application frontend
|
||||
- Auto rebuild - Any changes to your Go code will rebuild and relaunch your application
|
||||
|
||||
In addition to this, a webserver will start on port 34115. This will serve your application to any browser that
|
||||
connects to it. All connected web browsers will respond to system events like hot reload on asset change.
|
||||
|
||||
In Go, we are used to dealing with structs in our applications. It's often useful to send structs to our frontend
|
||||
and use them as state in our application. In v1, this was a very manual process and a bit of a burden on the
|
||||
developer. I'm happy to announce that in v2, any application run in dev mode will automatically generate Typescript
|
||||
models for all structs that are input or output parameters to bound methods. This enables seamless interchange of data
|
||||
models between the two worlds.
|
||||
|
||||
In addition to this, another JS module is dynamically generated wrapping all your bound methods. This provides
|
||||
JSDoc for your methods, providing code completion and hinting in your IDE. It's really cool when you get data models
|
||||
auto-imported when hitting tab in an auto-generated module wrapping your Go code!
|
||||
|
||||
### Remote Templates
|
||||
|
||||
<div class="text--center">
|
||||
<img src="/img/remote-mac.png" width="80%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
Getting an application up and running quickly was always a key goal for the Wails project. When we launched, we tried
|
||||
to cover a lot of the modern frameworks at the time: react, vue and angular. The world of frontend development is very
|
||||
opinionated, fast moving and hard to keep on top of! As a result, we found our base templates getting out of date pretty
|
||||
quickly and this caused a maintenance headache. It also meant that we didn't have cool modern templates for the latest
|
||||
and greatest tech stacks.
|
||||
|
||||
With v2, I wanted to empower the community by giving you the ability to create and host templates yourselves, rather
|
||||
than rely on the Wails project. So now you can create projects using community supported templates! I hope this will
|
||||
inspire developers to create a vibrant ecosystem of project templates. I'm really quite excited about what our developer
|
||||
community can create!
|
||||
|
||||
### Native M1 Support
|
||||
|
||||
Thanks to the amazing support of [Mat Ryer](https://github.com/matryer/), the Wails project now supports M1 native
|
||||
builds:
|
||||
|
||||
<div class="text--center">
|
||||
<img src="/img/build-darwin-arm.png" width="80%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
You can also specify `darwin/amd64` as a target too:
|
||||
|
||||
<div class="text--center">
|
||||
<img src="/img/build-darwin-amd.png" width="80%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
Oh, I almost forgot.... you can also do `darwin/universal`.... :wink:
|
||||
|
||||
<div class="text--center">
|
||||
<img src="/img/build-darwin-universal.png" width="80%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
### Cross Compilation to Windows
|
||||
|
||||
Because Wails v2 for Windows is pure Go, you can target Windows builds without docker.
|
||||
|
||||
<div class="text--center">
|
||||
<img src="/img/build-cross-windows.png" width="80%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
|
||||
### WKWebView Renderer
|
||||
|
||||
V1 relied on a (now deprecated) WebView component. V2 uses the most recent WKWebKit component so expect the latest and greatest from Apple.
|
||||
|
||||
### In Conclusion
|
||||
|
||||
As I'd said in the Windows release notes, Wails v2 represents a new foundation for the project.
|
||||
The aim of this release is to get feedback on the new approach, and to iron out any bugs before a full release.
|
||||
Your input would be most welcome! Please direct any feedback to the [v2 Beta](https://github.com/wailsapp/wails/discussions/828)
|
||||
discussion board.
|
||||
|
||||
And finally, I'd like to give a special thank you to all the [project sponsors](/docs/credits#sponsors), including [JetBrains](https://www.jetbrains.com?from=Wails),
|
||||
whose support drive the project in many ways behind the scenes.
|
||||
|
||||
I look forward to seeing what people build with Wails in this next exciting phase of the project!
|
||||
|
||||
Lea.
|
||||
|
||||
PS: Linux users, you're next!
|
||||
|
||||
PPS: If you or your company find Wails useful, please consider [sponsoring the project](https://github.com/sponsors/leaanthony). Thanks!
|
||||
@@ -25,3 +25,6 @@ Example: `wails init -n "Your Project Name" -t https://github.com/misitebao/wail
|
||||
- [wails-vite-vue-ts](https://github.com/codydbentley/wails-vite-vue-ts) - Vue 3 TypeScript with Vite (and instructions to add features)
|
||||
- [wails-vite-vue-the-works](https://github.com/codydbentley/wails-vite-vue-the-works) - Vue 3 TypeScript with Vite, Vuex, Vue Router, Sass, and ESLint + Prettier
|
||||
|
||||
## Angular
|
||||
|
||||
- [wails-angular-template](https://github.com/TAINCER/wails-angular-template) - Angular with TypeScript, Sass, Hot-Reload, Code-Splitting and i18n
|
||||
|
||||
@@ -20,6 +20,9 @@ sidebar_position: 99
|
||||
<a href="https://github.com/snider" style="width:100px;">
|
||||
<img src="https://github.com/snider.png?size=100" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/codydbentley" style="width:100px">
|
||||
<img src="https://github.com/codydbentley.png?size=100" width="100"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://github.com/matryer" style="width:100px">
|
||||
@@ -58,11 +61,8 @@ sidebar_position: 99
|
||||
<a href="https://github.com/jugglingjsons" style="width:50px">
|
||||
<img src="https://github.com/jugglingjsons.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/marcus-crane" style="width:50px">
|
||||
<img src="https://github.com/marcus-crane.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/codydbentley" style="width:65px">
|
||||
<img src="https://github.com/codydbentley.png?size=65" width="65"/>
|
||||
<a href="https://github.com/marcus-crane" style="width:65px">
|
||||
<img src="https://github.com/marcus-crane.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/bbergshaven" style="width:45px">
|
||||
<img src="https://github.com/bbergshaven.png?size=45" width="45"/>
|
||||
@@ -70,6 +70,18 @@ sidebar_position: 99
|
||||
<a href="https://github.com/Gilgames000" style="width:45px">
|
||||
<img src="https://github.com/Gilgames000.png?size=45" width="45"/>
|
||||
</a>
|
||||
<a href="https://github.com/ilgityildirim" style="width:50px">
|
||||
<img src="https://github.com/ilgityildirim.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/ondoki" style="width:65px">
|
||||
<img src="https://github.com/ondoki.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/questrail" style="width:50px">
|
||||
<img src="https://github.com/questrail.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/DonTomato" style="width:45px">
|
||||
<img src="https://github.com/DonTomato.png?size=45" width="45"/>
|
||||
</a>
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -7,8 +7,9 @@ sidebar_position: 1
|
||||
## Supported Platforms
|
||||
|
||||
- Windows 10
|
||||
- MacOS x64 & arm64 (due October '21)
|
||||
- Linux (due December '21)
|
||||
- MacOS 10.13+ (amd64)
|
||||
- MacOS 11.0+ (arm64)
|
||||
- Linux (due Jan '22)
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -47,7 +48,11 @@ import TabItem from "@theme/TabItem";
|
||||
{ label: "Linux", value: "Linux" },
|
||||
]}
|
||||
>
|
||||
<TabItem value="MacOS">Coming Soon...</TabItem>
|
||||
<TabItem value="MacOS">
|
||||
Wails requires that the xcode command line tools are installed. This can be done by running:<br/>
|
||||
|
||||
<code>xcode-select --install</code>
|
||||
</TabItem>
|
||||
<TabItem value="Windows">
|
||||
Wails requires that the <a href="https://developer.microsoft.com/en-us/microsoft-edge/webview2/">WebView2</a>{" "}
|
||||
runtime is installed. Some Windows installations will already have this installed. You can check using the{" "}
|
||||
@@ -62,7 +67,7 @@ import TabItem from "@theme/TabItem";
|
||||
|
||||
## Installing Wails
|
||||
|
||||
Run `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.12` to install the Wails CLI.
|
||||
Run `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.15` to install the Wails CLI.
|
||||
|
||||
## System Check
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ The format of the file is slightly different. Here is a comparison:
|
||||
| frontend / serve | | Removed |
|
||||
| tags | | Removed |
|
||||
| | wailsjsdir | The directory to generate wailsjs modules |
|
||||
| | assetdir | The directory of the frontend assets for `dev` mode |
|
||||
| | assetdir | The directory of the compiled frontend assets for `dev` mode |
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
11
website/docs/guides/overscroll.mdx
Normal file
11
website/docs/guides/overscroll.mdx
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
# Overscroll
|
||||
|
||||
[Overscroll](https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior) is the "bounce effect" you sometimes
|
||||
get when you scroll beyond a page's content boundaries. This is common in mobile apps. This can be disabled using CSS:
|
||||
|
||||
```css
|
||||
body {
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
```
|
||||
@@ -74,6 +74,12 @@ Example:
|
||||
|
||||
`wails build -clean -o myproject.exe`
|
||||
|
||||
:::info UPX on Apple Silicon
|
||||
|
||||
There are [issues](https://github.com/upx/upx/issues/446) with using UPX with Apple Silicon.
|
||||
|
||||
:::
|
||||
|
||||
## doctor
|
||||
|
||||
`wails doctor` will run diagnostics to ensure that your system is ready for development.
|
||||
@@ -134,6 +140,8 @@ Your system is ready for Wails development!
|
||||
| -wailsjsdir | The directory to generate the generated Wails JS modules | Value in `wails.json` |
|
||||
| -debounce | The time to wait for reload after an asset change is detected | 100 (milliseconds) |
|
||||
| -devserverurl "url" | Use 3rd party dev server url, EG Vite | "http://localhost:34115" |
|
||||
| -appargs "args" | Arguments passed to the application in shell style | |
|
||||
| -platform "platform" | Platform/Arch to target | `runtime.GOOS` |
|
||||
|
||||
If the `assetdir`, `wailsjsdir`, `debounce` or `devserverurl` flags are provided on the command line, they are saved in
|
||||
`wails.json`, and become the defaults for subsequent invocations.
|
||||
|
||||
@@ -5,7 +5,7 @@ sidebar_position: 4
|
||||
# Menus
|
||||
|
||||
It is possible to add an application menu to Wails projects. This is achieved by defining a [Menu](#menu) struct and
|
||||
calling the runtime method [MenuSetApplicationMenu](/docs/reference/runtime/menu#menusetapplicationmenu).
|
||||
setting the [`Menu`](/docs/reference/options#menu) option, or by calling the runtime method [MenuSetApplicationMenu](/docs/reference/runtime/menu#menusetapplicationmenu).
|
||||
|
||||
It is also possible to dynamically update the menu, by updating the menu struct and calling
|
||||
[MenuUpdateApplicationMenu](/docs/reference/runtime/menu#menuupdateapplicationmenu).
|
||||
@@ -79,6 +79,7 @@ type MenuItem struct {
|
||||
| Checked | bool | Adds check to item (Checkbox & Radio types) |
|
||||
| SubMenu | [\*Menu](#menu) | Sets the submenu |
|
||||
| Click | [Callback](#callback) | Callback function when menu clicked |
|
||||
| Role | string | Defines a [role](#roles) for this menu item. Mac only for now. |
|
||||
|
||||
### Accelerator
|
||||
|
||||
@@ -231,3 +232,19 @@ type CallbackData struct {
|
||||
|
||||
The function is given a `CallbackData` struct which indicates which menu item triggered the callback. This is useful when
|
||||
using radio groups that may share a callback.
|
||||
|
||||
### Role
|
||||
|
||||
:::info Roles
|
||||
|
||||
Roles are currently supported on Mac only.
|
||||
|
||||
:::
|
||||
|
||||
A menu item may have a role, which is essentially a pre-defined menu item. We currently support the following roles:
|
||||
|
||||
| Role | Description |
|
||||
| ---- | ----------- |
|
||||
| AppMenuRole | The standard Mac application menu. Can be created using `menu.AppMenu()` |
|
||||
| EditMenuRole | The standard Mac edit menu. Can be created using `menu.EditMenu()` |
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ func main() {
|
||||
StartHidden: false,
|
||||
HideWindowOnClose: false,
|
||||
RGBA: &options.RGBA{R: 0, G: 0, B: 0, A: 255},
|
||||
AlwaysOnTop: false,
|
||||
Assets: assets,
|
||||
Menu: app.applicationMenu(),
|
||||
Logger: nil,
|
||||
@@ -43,6 +44,24 @@ func main() {
|
||||
WindowIsTranslucent: false,
|
||||
DisableWindowIcon: false,
|
||||
},
|
||||
Mac: &mac.Options{
|
||||
TitleBar: &mac.TitleBar{
|
||||
TitlebarAppearsTransparent: true,
|
||||
HideTitle: false,
|
||||
HideTitleBar: false,
|
||||
FullSizeContent: false,
|
||||
UseToolbar: false,
|
||||
HideToolbarSeparator: true,
|
||||
},
|
||||
Appearance: mac.NSAppearanceNameDarkAqua,
|
||||
WebviewIsTransparent: true,
|
||||
WindowIsTranslucent: false,
|
||||
About: &mac.AboutInfo{
|
||||
Title: "My Application",
|
||||
Message: "© 2021 Me",
|
||||
Icon: icon,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -168,6 +187,14 @@ Example: 0xFF000088 - Red at 50% transparency
|
||||
This value is the RGBA value to set the window by default.
|
||||
Default: 0xFFFFFFFF.
|
||||
|
||||
### AlwaysOnTop
|
||||
|
||||
Name: AlwaysOnTop
|
||||
|
||||
Type: bool
|
||||
|
||||
Indicates that the window should stay above other windows when losing focus.
|
||||
|
||||
### Assets
|
||||
|
||||
Name: Assets
|
||||
@@ -184,6 +211,8 @@ Type: \*menu.Menu
|
||||
|
||||
The menu to be used by the application. More details about Menus in the [Menu Reference](/docs/reference/runtime/menu).
|
||||
|
||||
NOTE: On Mac, if no menu is specified, a default menu will be created.
|
||||
|
||||
### Logger
|
||||
|
||||
Name: Logger
|
||||
@@ -273,3 +302,153 @@ Name: DisableWindowIcon
|
||||
Type: bool
|
||||
|
||||
Setting this to true will remove the icon in the top left corner of the title bar.
|
||||
|
||||
|
||||
## Mac Specific Options
|
||||
|
||||
### TitleBar
|
||||
|
||||
Name: TitleBar
|
||||
|
||||
Type: [*mac.TitleBar](#titlebar-struct)
|
||||
|
||||
The TitleBar struct provides the ability to configure the look and feel of the title bar.
|
||||
|
||||
### Appearance
|
||||
|
||||
Name: Appearance
|
||||
|
||||
Type: [AppearanceType](#appearance-type)
|
||||
|
||||
Appearance is used to set the style of your app in accordance with Apple's [NSAppearance](https://developer.apple.com/documentation/appkit/nsappearancename?language=objc) names.
|
||||
|
||||
### WebviewIsTransparent
|
||||
|
||||
Name: WebviewIsTransparent
|
||||
|
||||
Type: bool
|
||||
|
||||
Setting this to `true` will make the webview background transparent when an alpha value of `0` is used.
|
||||
This means that if you use `rgba(0,0,0,0)`, the host window will show through.
|
||||
Often combined with [WindowIsTranslucent](#WindowIsTranslucent) to make frosty-looking applications.
|
||||
|
||||
### WindowIsTranslucent
|
||||
|
||||
Name: WindowIsTranslucent
|
||||
|
||||
Type: bool
|
||||
|
||||
Setting this to `true` will make the window background translucent. Often combined
|
||||
with [WebviewIsTransparent](#WebviewIsTransparent) to make frosty-looking applications.
|
||||
|
||||
### About
|
||||
|
||||
Name: About
|
||||
|
||||
Type: [About](#about-struct)
|
||||
|
||||
This configuration lets you set the title, message and icon for the "About" menu item in the app menu created by the "AppMenu" role.
|
||||
|
||||
#### Titlebar struct
|
||||
|
||||
The titlebar of the application can be customised by using the TitleBar options:
|
||||
|
||||
```go
|
||||
type TitleBar struct {
|
||||
TitlebarAppearsTransparent bool
|
||||
HideTitle bool
|
||||
HideTitleBar bool
|
||||
FullSizeContent bool
|
||||
UseToolbar bool
|
||||
HideToolbarSeparator bool
|
||||
}
|
||||
```
|
||||
|
||||
| Name | Description |
|
||||
| ---- | ----------- |
|
||||
| TitlebarAppearsTransparent | Makes the titlebar transparent. [Apple Docs](https://developer.apple.com/documentation/appkit/nswindow/1419167-titlebarappearstransparent?language=objc) |
|
||||
| HideTitle | Hides the title of the window. [Apple Docs](https://developer.apple.com/documentation/appkit/nswindowtitlevisibility?language=objc) |
|
||||
| HideTitleBar | Removes [NSWindowStyleMaskTitled](https://developer.apple.com/documentation/appkit/nswindowstylemask/nswindowstylemasktitled/) from the style mask |
|
||||
| FullSizeContent | Makes the webview fill the entire window. [Apple Docs](https://developer.apple.com/documentation/appkit/nswindowstylemask/nswindowstylemaskfullsizecontentview)|
|
||||
| UseToolbar | Adds a default toolbar to the window. [Apple Docs](https://developer.apple.com/documentation/appkit/nstoolbar?language=objc) |
|
||||
| HideToolbarSeparator | Removes the line beneath the toolbar. [Apple Docs](https://developer.apple.com/documentation/appkit/nstoolbar/1516954-showsbaselineseparator?language=objc) |
|
||||
|
||||
Preconfigured titlebar settings are available:
|
||||
|
||||
| Setting | Example |
|
||||
| ------- | ------- |
|
||||
|`mac.TitleBarDefault()` |  |
|
||||
|`mac.TitleBarHidden()` |  |
|
||||
|`mac.TitleBarHiddenInset()` |  |
|
||||
|
||||
Example:
|
||||
```go
|
||||
Mac: &mac.Options{
|
||||
TitleBar: mac.TitleBarHiddenInset(),
|
||||
}
|
||||
```
|
||||
|
||||
Click [here](https://github.com/lukakerr/NSWindowStyles) for some inspiration on customising the titlebar.
|
||||
|
||||
#### Appearance type
|
||||
|
||||
You can specify the application's [appearance](https://developer.apple.com/documentation/appkit/nsappearance?language=objc).
|
||||
|
||||
| Value | Description |
|
||||
| --------------- | ------------------ |
|
||||
| DefaultAppearance | DefaultAppearance uses the default system value |
|
||||
| NSAppearanceNameAqua | The standard light system appearance |
|
||||
| NSAppearanceNameDarkAqua | The standard dark system appearance |
|
||||
| NSAppearanceNameVibrantLight | The light vibrant appearance |
|
||||
| NSAppearanceNameAccessibilityHighContrastAqua | A high-contrast version of the standard light system appearance |
|
||||
| NSAppearanceNameAccessibilityHighContrastDarkAqua | A high-contrast version of the standard dark system appearance |
|
||||
| NSAppearanceNameAccessibilityHighContrastVibrantLight | A high-contrast version of the light vibrant appearance |
|
||||
| NSAppearanceNameAccessibilityHighContrastVibrantDark | A high-contrast version of the dark vibrant appearance |
|
||||
|
||||
Example:
|
||||
```go
|
||||
Mac: &mac.Options{
|
||||
Appearance: mac.NSAppearanceNameDarkAqua,
|
||||
}
|
||||
```
|
||||
|
||||
#### About struct
|
||||
|
||||
```go
|
||||
type AboutInfo struct {
|
||||
Title string
|
||||
Message string
|
||||
Icon []byte
|
||||
}
|
||||
```
|
||||
If these settings are provided, an "About" menu item will appear in the app menu (when using the `AppMenu` role).
|
||||
Given this configuration:
|
||||
```go
|
||||
//go:embed build/appicon.png
|
||||
var icon []byte
|
||||
|
||||
func main() {
|
||||
err := wails.Run(&options.App{
|
||||
...
|
||||
Mac: &mac.Options{
|
||||
About: &mac.AboutInfo{
|
||||
Title: "My Application",
|
||||
Message: "© 2021 Me",
|
||||
Icon: icon,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
The "About" menu item will appear in the app menu:
|
||||
|
||||
<div class="text--center">
|
||||
<img src="/img/reference/about-menu.png" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
When clicked, that will open an about message box:
|
||||
|
||||
<div class="text--center">
|
||||
<img src="/img/reference/about-dialog.png" width="40%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
@@ -9,7 +9,7 @@ The project config resides in the `wails.json` file in the project directory. Th
|
||||
```json
|
||||
{
|
||||
"name": "[The project name]",
|
||||
"assetdir": "[Relative path to your assets directory]",
|
||||
"assetdir": "[Relative path to the directory containing the compiled assets]",
|
||||
"frontend:install": "[The command to install node dependencies, run in the frontend directory - often `npm install`]",
|
||||
"frontend:build": "[The command to build the assets, run in the frontend directory - often `npm run build`]",
|
||||
"frontend:dev": "[This command is run in a separate process on `wails dev`. Useful for 3rd party watchers]",
|
||||
@@ -17,7 +17,9 @@ The project config resides in the `wails.json` file in the project directory. Th
|
||||
"version": "[Project config version]",
|
||||
"outputfilename": "[The name of the binary]",
|
||||
"debounceMS": 100, // The default time the dev server waits to reload when it detects a vhange in assets
|
||||
"devserverurl": "[URL to the dev server serving local assets. Default: http://localhost:34115]"
|
||||
"devserverurl": "[URL to the dev server serving local assets. Default: http://localhost:34115]",
|
||||
"appargs": "[Arguments passed to the application in shell style when in dev mode]"
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -6,50 +6,59 @@ sidebar_position: 5
|
||||
|
||||
## Overview
|
||||
|
||||
This part of the runtime provides access to native dialogs, such as File Selectors and Message boxes.Context
|
||||
This part of the runtime provides access to native dialogs, such as File Selectors and Message boxes.
|
||||
|
||||
:::info Javascript
|
||||
Dialog is currently unsupported in the JS runtime.
|
||||
:::
|
||||
|
||||
### OpenDirectoryDialog
|
||||
|
||||
Opens a dialog that prompts the user to select a directory. Can be customised using [OpenDialogOptions](#opendialogoptions).
|
||||
|
||||
Go Signature: `OpenDirectoryDialog(ctx context.Context, dialogOptions OpenDialogOptions) (string, error)`
|
||||
|
||||
Returns: Selected directory (blank if the user cancelled) or an error
|
||||
|
||||
Opens a dialog that prompts the user to select a directory. Can be customised using [OpenDialogOptions](#OpenDialogOptions).
|
||||
|
||||
### OpenFileDialog
|
||||
|
||||
Opens a dialog that prompts the user to select a file. Can be customised using [OpenDialogOptions](#opendialogoptions).
|
||||
|
||||
Go Signature: `OpenFileDialog(ctx context.Context, dialogOptions OpenDialogOptions) (string, error)`
|
||||
|
||||
Returns: Selected file (blank if the user cancelled) or an error
|
||||
|
||||
Opens a dialog that prompts the user to select a file. Can be customised using [OpenDialogOptions](#OpenDialogOptions).
|
||||
|
||||
|
||||
### OpenMultipleFilesDialog
|
||||
|
||||
Opens a dialog that prompts the user to select multiple files. Can be customised using [OpenDialogOptions](#opendialogoptions).
|
||||
|
||||
Go Signature: `OpenMultipleFilesDialog(ctx context.Context, dialogOptions OpenDialogOptions) ([]string, error)`
|
||||
|
||||
Returns: Selected files (nil if the user cancelled) or an error
|
||||
|
||||
Opens a dialog that prompts the user to select multiple files. Can be customised using [OpenDialogOptions](#OpenDialogOptions).
|
||||
|
||||
|
||||
### SaveFileDialog
|
||||
|
||||
Opens a dialog that prompts the user to select a filename for the purposes of saving. Can be customised using [SaveDialogOptions](#savedialogoptions).
|
||||
|
||||
Go Signature: `SaveFileDialog(ctx context.Context, dialogOptions SaveDialogOptions) (string, error)`
|
||||
|
||||
Returns: The selected file (blank if the user cancelled) or an error
|
||||
|
||||
Opens a dialog that prompts the user to select a filename for the purposes of saving. Can be customised using [SaveDialogOptions](#SaveDialogOptions).
|
||||
|
||||
|
||||
### MessageDialog
|
||||
|
||||
Displays a message using a message dialog. Can be customised using [MessageDialogOptions](#messagedialogoptions).
|
||||
|
||||
Go Signature: `MessageDialog(ctx context.Context, dialogOptions MessageDialogOptions) (string, error)`
|
||||
|
||||
Returns: The text of the selected button or an error
|
||||
|
||||
Displays a message using a message dialog. Can be customised using [MessageDialogOptions](#MessageDialogOptions).
|
||||
|
||||
## Options
|
||||
|
||||
### OpenDialogOptions
|
||||
@@ -60,14 +69,27 @@ type OpenDialogOptions struct {
|
||||
DefaultFilename string
|
||||
Title string
|
||||
Filters []FileFilter
|
||||
AllowFiles bool // Mac Only
|
||||
AllowDirectories bool // Mac Only
|
||||
ShowHiddenFiles bool // Mac Only
|
||||
CanCreateDirectories bool // Mac Only
|
||||
ResolvesAliases bool // Mac Only
|
||||
TreatPackagesAsDirectories bool // Mac Only
|
||||
AllowFiles bool
|
||||
AllowDirectories bool
|
||||
ShowHiddenFiles bool
|
||||
CanCreateDirectories bool
|
||||
ResolvesAliases bool
|
||||
TreatPackagesAsDirectories bool
|
||||
}
|
||||
```
|
||||
| Field | Description | Win | Mac |
|
||||
| -------------------------- | ---------------------------------------------- | --- | --- |
|
||||
| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ |
|
||||
| DefaultFilename | The default filename | ✅ | ✅ |
|
||||
| Title | Title for the dialog | ✅ | ✅ |
|
||||
| [Filters](#filefilter) | A list of file filters | ✅ | ✅ |
|
||||
| AllowFiles | Allow files to be selected | | ✅ |
|
||||
| AllowDirectories | Allow directories to be selected | | ✅ |
|
||||
| ShowHiddenFiles | Show files hidden by the system | | ✅ |
|
||||
| CanCreateDirectories | Allow user to create directories | | ✅ |
|
||||
| ResolvesAliases | If true, returns the file not the alias | | ✅ |
|
||||
| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ |
|
||||
|
||||
|
||||
### SaveDialogOptions
|
||||
|
||||
@@ -77,12 +99,22 @@ type SaveDialogOptions struct {
|
||||
DefaultFilename string
|
||||
Title string
|
||||
Filters []FileFilter
|
||||
ShowHiddenFiles bool // Mac Only
|
||||
CanCreateDirectories bool // Mac Only
|
||||
TreatPackagesAsDirectories bool // Mac Only
|
||||
ShowHiddenFiles bool
|
||||
CanCreateDirectories bool
|
||||
TreatPackagesAsDirectories bool
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Description | Win | Mac |
|
||||
| -------------------------- | ---------------------------------------------- | --- | --- |
|
||||
| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ |
|
||||
| DefaultFilename | The default filename | ✅ | ✅ |
|
||||
| Title | Title for the dialog | ✅ | ✅ |
|
||||
| [Filters](#filefilter) | A list of file filters | ✅ | ✅ |
|
||||
| ShowHiddenFiles | Show files hidden by the system | | ✅ |
|
||||
| CanCreateDirectories | Allow user to create directories | | ✅ |
|
||||
| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ |
|
||||
|
||||
### MessageDialogOptions
|
||||
|
||||
```go
|
||||
@@ -91,33 +123,76 @@ type MessageDialogOptions struct {
|
||||
Title string
|
||||
Message string
|
||||
Buttons []string
|
||||
DefaultButton string // Mac Only
|
||||
CancelButton string // Mac Only
|
||||
Icon string // Mac Only
|
||||
DefaultButton string
|
||||
CancelButton string
|
||||
}
|
||||
```
|
||||
| Field | Description | Win | Mac |
|
||||
| ------------- | ------------------------------------------------------------------------- | --- | --- |
|
||||
| Type | The type of message dialog, eg question, info... | ✅ | ✅ |
|
||||
| Title | Title for the dialog | ✅ | ✅ |
|
||||
| Message | The message to show the user | ✅ | ✅ |
|
||||
| Buttons | A list of button titles | | ✅ |
|
||||
| DefaultButton | The button with this text should be treated as default. Bound to `return` | | ✅ |
|
||||
| CancelButton | The button with this text should be treated as cancel. Bound to `escape` | | ✅ |
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
#### Windows
|
||||
|
||||
Windows has standard dialog types in which the buttons are not customisable.
|
||||
The value returned will be one of: "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "Try Again" or "Continue"
|
||||
|
||||
#### Mac
|
||||
|
||||
A message dialog on Mac may specify up to 4 buttons. If no `DefaultButton` or `CancelButton` is given, the first button
|
||||
is considered default and is bound to the `return` key.
|
||||
|
||||
For the following code:
|
||||
```go
|
||||
selection, err := runtime.MessageDialog(b.ctx, runtime.MessageDialogOptions{
|
||||
Title: "It's your turn!",
|
||||
Message: "Select a number",
|
||||
Buttons: []string{"one", "two", "three", "four"},
|
||||
})
|
||||
```
|
||||
the first button is shown as default:
|
||||
<div class="text--center">
|
||||
<img src="/img/runtime/dialog_no_defaults.png" width="30%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
And if we specify `DefaultButton` to be "two":
|
||||
```go
|
||||
selection, err := runtime.MessageDialog(b.ctx, runtime.MessageDialogOptions{
|
||||
Title: "It's your turn!",
|
||||
Message: "Select a number",
|
||||
Buttons: []string{"one", "two", "three", "four"},
|
||||
DefaultButton: "two",
|
||||
})
|
||||
```
|
||||
the second button is shown as default. When `return` is pressed, the value "two" is returned.
|
||||
<div class="text--center">
|
||||
<img src="/img/runtime/dialog_default_button.png" width="30%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
If we now specify `CancelButton` to be "three":
|
||||
```go
|
||||
selection, err := runtime.MessageDialog(b.ctx, runtime.MessageDialogOptions{
|
||||
Title: "It's your turn!",
|
||||
Message: "Select a number",
|
||||
Buttons: []string{"one", "two", "three", "four"},
|
||||
DefaultButton: "two",
|
||||
CancelButton: "three",
|
||||
})
|
||||
```
|
||||
the button with "three" is shown at the bottom of the dialog. When `escape` is pressed, the value "three" is returned:
|
||||
<div class="text--center">
|
||||
<img src="/img/runtime/dialog_default_cancel.png" width="30%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<Tabs
|
||||
defaultValue="Windows"
|
||||
values={[
|
||||
{label: 'Windows', value: 'Windows'},
|
||||
{label: 'MacOS', value: 'MacOS'},
|
||||
{label: 'Linux', value: 'Linux'},
|
||||
]}>
|
||||
<TabItem value="MacOS">
|
||||
Both "DefaultButton" and "CancelButton" should match a value in "Buttons".
|
||||
</TabItem>
|
||||
<TabItem value="Windows">
|
||||
Windows has standard dialog types and the buttons are not customisable. The
|
||||
value returned will be one of: "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "Try Again" or "Continue"
|
||||
</TabItem>
|
||||
<TabItem value="Linux">
|
||||
Coming Soon...
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### DialogType
|
||||
|
||||
@@ -138,3 +213,37 @@ type FileFilter struct {
|
||||
Pattern string // semi-colon separated list of extensions, EG: "*.jpg;*.png"
|
||||
}
|
||||
```
|
||||
|
||||
#### Windows
|
||||
|
||||
Windows allows you to use multiple file filters in dialog boxes. Each FileFilter will show up as a separate entry in the
|
||||
dialog:
|
||||
|
||||
<div class="text--center">
|
||||
<img src="/img/runtime/dialog_win_filters.png" width="50%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
#### Mac
|
||||
|
||||
Mac dialogs only have the concept of a single set of patterns to filter files. If multiple FileFilters are provided,
|
||||
Wails will use all the Patterns defined.
|
||||
|
||||
Example:
|
||||
```go
|
||||
selection, err := runtime.OpenFileDialog(b.ctx, runtime.OpenDialogOptions{
|
||||
Title: "Select File",
|
||||
Filters: []runtime.FileFilter{
|
||||
{
|
||||
DisplayName: "Images (*.png;*.jpg)",
|
||||
Pattern: "*.png;*.jpg",
|
||||
}, {
|
||||
DisplayName: "Videos (*.mov;*.mp4)",
|
||||
Pattern: "*.mov;*.mp4",
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
This will result in the Open File dialog using `*.png,*.jpg,*.mov,*.mp4` as a filter.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user