Compare commits
60 Commits
develop
...
v2.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b03a758747 | ||
|
|
44597f2fbc | ||
|
|
0844113f3a | ||
|
|
79e99b68d6 | ||
|
|
c64b7bb79c | ||
|
|
e72b438ad2 | ||
|
|
3e4a112a3d | ||
|
|
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 |
105
README.md
@@ -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
|
||||
|
||||
@@ -142,7 +157,7 @@ This project is supported by these kind people / companies:
|
||||
<img src="https://github.com/DonTomato.png?size=45" width="45"/>
|
||||
</a>
|
||||
|
||||
<span id="nav-5"></span>
|
||||
<span id="nav-6"></span>
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -152,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
|
||||
|
||||
@@ -160,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
|
||||
|
||||
@@ -176,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
|
||||
|
||||
@@ -184,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
|
||||
|
||||
@@ -192,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
|
||||
|
||||
@@ -200,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
|
||||
|
||||
@@ -220,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).
|
||||
|
||||
@@ -345,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.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,13 +85,12 @@
|
||||
- 强大的命令行工具
|
||||
- 跨多个平台
|
||||
|
||||
<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>
|
||||
@@ -143,7 +160,7 @@
|
||||
<img src="https://github.com/DonTomato.png?size=45" width="45"/>
|
||||
</a>
|
||||
|
||||
<span id="nav-5"></span>
|
||||
<span id="nav-6"></span>
|
||||
|
||||
## 安装
|
||||
|
||||
@@ -152,7 +169,7 @@ Wails 使用 cgo 与原生渲染引擎结合,因此需要依赖一些平台的
|
||||
- Go 1.16
|
||||
- npm
|
||||
|
||||
<span id="nav-5-1"></span>
|
||||
<span id="nav-6-1"></span>
|
||||
|
||||
### MacOS
|
||||
|
||||
@@ -160,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
|
||||
|
||||
@@ -176,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
|
||||
|
||||
@@ -184,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
|
||||
|
||||
@@ -192,7 +209,7 @@ _也成功测试了: Manjaro & ArcoLinux_
|
||||
|
||||
_CentOS 6, 7_
|
||||
|
||||
<span id="nav-5-2-4"></span>
|
||||
<span id="nav-6-2-4"></span>
|
||||
|
||||
#### Fedora
|
||||
|
||||
@@ -200,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) 上面的文档.
|
||||
|
||||
@@ -244,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>
|
||||
|
||||
@@ -302,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>
|
||||
|
||||
@@ -311,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)
|
||||
@@ -331,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>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
|
||||
// Version - Wails version
|
||||
const Version = "v1.16.7"
|
||||
const Version = "v1.16.8"
|
||||
|
||||
@@ -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" {
|
||||
@@ -129,8 +133,8 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" && !experimental {
|
||||
return fmt.Errorf("MacOS version coming soon!")
|
||||
if runtime.GOOS == "linux" && !experimental {
|
||||
return fmt.Errorf("Linux version coming soon!")
|
||||
}
|
||||
|
||||
// Webview2 installer strategy (download by default)
|
||||
@@ -205,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)
|
||||
})
|
||||
}
|
||||
@@ -228,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
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/google/shlex"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal"
|
||||
"github.com/wailsapp/wails/v2/internal/gomod"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/project"
|
||||
|
||||
"github.com/pkg/browser"
|
||||
@@ -72,6 +72,7 @@ type devFlags struct {
|
||||
forceBuild bool
|
||||
debounceMS int
|
||||
devServerURL string
|
||||
appargs string
|
||||
}
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
@@ -93,6 +94,7 @@ 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
|
||||
@@ -111,8 +113,8 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" && !experimental {
|
||||
return fmt.Errorf("MacOS version coming soon!")
|
||||
if runtime.GOOS == "linux" && !experimental {
|
||||
return fmt.Errorf("Linux version coming soon!")
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
@@ -353,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 {
|
||||
@@ -463,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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
html {
|
||||
text-align: center;
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 255);
|
||||
background-color: rgba(33, 37, 43, 0.2);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -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: 33, G: 37, B: 43, A: 255},
|
||||
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 {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
html {
|
||||
background-color: rgba(33, 37, 43, 1);
|
||||
background-color: rgba(33, 37, 43, 0.2);
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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: 33, G: 37, B: 43, A: 255},
|
||||
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.15"
|
||||
var Version = "v2.0.0-beta.17"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
@@ -159,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=
|
||||
@@ -280,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
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
#define AppDelegate_h
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "WailsContext.h"
|
||||
|
||||
@interface AppDelegate : NSResponder <NSTouchBarProvider>
|
||||
|
||||
@property bool alwaysOnTop;
|
||||
@property (retain) NSWindow* mainWindow;
|
||||
@property (retain) WailsWindow* mainWindow;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -25,28 +25,6 @@
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
//
|
||||
//- (void) CreateMenu {
|
||||
// [NSApplication sharedApplication];
|
||||
// menubar = [[NSMenu new] autorelease];
|
||||
// id appMenuItem = [[NSMenuItem new] autorelease];
|
||||
// [menubar addItem:appMenuItem];
|
||||
// [NSApp setMainMenu:menubar];
|
||||
// id appMenu = [[NSMenu new] autorelease];
|
||||
// id appName = [[NSProcessInfo processInfo] processName];
|
||||
// id quitTitle = [@"Quit " stringByAppendingString:appName];
|
||||
// id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle
|
||||
// action:@selector(terminate:) keyEquivalent:@"q"]
|
||||
// autorelease];
|
||||
// [appMenu addItem:quitMenuItem];
|
||||
// [appMenuItem setSubmenu:appMenu];
|
||||
//}
|
||||
//
|
||||
//- (void) dealloc {
|
||||
// [super dealloc];
|
||||
// window = nil;
|
||||
// menubar = nil;
|
||||
//}
|
||||
|
||||
@synthesize touchBar;
|
||||
|
||||
|
||||
@@ -36,9 +36,26 @@ void Quit(void*);
|
||||
const char* GetSize(void *ctx);
|
||||
const char* GetPos(void *ctx);
|
||||
|
||||
void ProcessURLResponse(void *inctx, const char *url, const char *contentType, const char *data, int datalength);
|
||||
void ProcessURLResponse(void *inctx, const char *url, const char *contentType, void* data, int datalength);
|
||||
|
||||
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);
|
||||
/* 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 */
|
||||
|
||||
@@ -9,27 +9,31 @@
|
||||
#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 :appearance :windowIsTranslucent];
|
||||
[result SetTitle:title];
|
||||
[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, const char* data, int datalength) {
|
||||
void ProcessURLResponse(void *inctx, const char *url, const char *contentType, void* data, int datalength) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSString *nsurl = [[NSString alloc] initWithUTF8String:url];
|
||||
NSString *nsContentType = [[NSString alloc] initWithUTF8String:contentType];
|
||||
NSString *nsurl = safeInit(url);
|
||||
NSString *nsContentType = safeInit(contentType);
|
||||
NSData *nsdata = [NSData dataWithBytes:data length:datalength];
|
||||
|
||||
[ctx processURLResponse:nsurl :nsContentType :nsdata];
|
||||
@@ -37,15 +41,17 @@ void ProcessURLResponse(void *inctx, const char *url, const char *contentType, c
|
||||
|
||||
void ExecJS(void* inctx, const char *script) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSString *nsscript = safeInit(script);
|
||||
ON_MAIN_THREAD(
|
||||
[ctx ExecJS:script];
|
||||
[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];
|
||||
[ctx SetTitle:_title];
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,7 +87,7 @@ void SetMaxSize(void* inctx, int width, int height) {
|
||||
void SetPosition(void* inctx, int x, int y) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetSize:x :y];
|
||||
[ctx SetPosition:x :y];
|
||||
);
|
||||
}
|
||||
|
||||
@@ -173,42 +179,136 @@ void Show(void *inctx) {
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
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];
|
||||
[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];
|
||||
[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];
|
||||
[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 sharedApplication];
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
AppDelegate* delegate = [AppDelegate new];
|
||||
[NSApp setDelegate:(id)delegate];
|
||||
[app setDelegate:(id)delegate];
|
||||
ctx.appdelegate = delegate;
|
||||
delegate.mainWindow = ctx.mainWindow;
|
||||
delegate.alwaysOnTop = ctx.alwaysOnTop;
|
||||
|
||||
[ctx loadRequest:@"wails://wails/"];
|
||||
|
||||
[NSApp run];
|
||||
[app setMainMenu:ctx.applicationMenu];
|
||||
[app run];
|
||||
[ctx release];
|
||||
NSLog(@"Here");
|
||||
}
|
||||
|
||||
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 */
|
||||
@@ -11,7 +11,7 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface WailsAlert : NSAlert
|
||||
- (void)addButton:(const char*)text :(const char*)defaultButton :(const char*)cancelButton;
|
||||
- (void)addButton:(NSString*)text :(NSString*)defaultButton :(NSString*)cancelButton;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
|
||||
@implementation WailsAlert
|
||||
|
||||
- (void)addButton:(const char*)text :(const char*)defaultButton :(const char*)cancelButton {
|
||||
- (void)addButton:(NSString*)text :(NSString*)defaultButton :(NSString*)cancelButton {
|
||||
if( text == nil ) {
|
||||
return;
|
||||
}
|
||||
NSButton *button = [self addButtonWithTitle:[NSString stringWithUTF8String:text]];
|
||||
if( defaultButton != nil && strcmp(text, defaultButton) == 0) {
|
||||
NSButton *button = [self addButtonWithTitle:text];
|
||||
if( defaultButton != nil && [text isEqualToString:defaultButton]) {
|
||||
[button setKeyEquivalent:@"\r"];
|
||||
} else if( cancelButton != nil && strcmp(text, cancelButton) == 0) {
|
||||
} else if( cancelButton != nil && [text isEqualToString:cancelButton]) {
|
||||
[button setKeyEquivalent:@"\033"];
|
||||
} else {
|
||||
[button setKeyEquivalent:@""];
|
||||
|
||||
@@ -12,12 +12,13 @@
|
||||
#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>
|
||||
@interface WailsContext : NSObject <WKURLSchemeHandler,WKScriptMessageHandler,WKNavigationDelegate>
|
||||
|
||||
@property (retain) WailsWindow* mainWindow;
|
||||
@property (retain) WKWebView* webview;
|
||||
@@ -32,20 +33,24 @@
|
||||
@property (retain) NSEvent* mouseEvent;
|
||||
|
||||
@property bool alwaysOnTop;
|
||||
@property bool maximised;
|
||||
|
||||
@property bool debug;
|
||||
|
||||
@property (retain) WKUserContentController* userContentController;
|
||||
|
||||
@property (retain) NSMutableDictionary *urlRequests;
|
||||
|
||||
- (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 :(const char *)appearance :(bool)windowIsTranslucent;
|
||||
@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:(const char*)title;
|
||||
- (void) SetTitle:(NSString*)title;
|
||||
- (void) Center;
|
||||
- (void) Fullscreen;
|
||||
- (void) UnFullscreen;
|
||||
@@ -58,16 +63,19 @@
|
||||
- (void) ShowMouse;
|
||||
- (void) Hide;
|
||||
- (void) Show;
|
||||
- (void) Quit;
|
||||
|
||||
-(void) MessageDialog :(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) OpenFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(const char*)filters;
|
||||
-(void) SaveFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(const char*)filters;
|
||||
-(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:(const char*)script;
|
||||
- (void) ExecJS:(NSString*)script;
|
||||
- (NSScreen*) getCurrentScreen;
|
||||
|
||||
- (void) SetAbout :(NSString*)title :(NSString*)description :(void*)imagedata :(int)datalen;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
#import <WebKit/WebKit.h>
|
||||
#import "WailsContext.h"
|
||||
#import "WailsAlert.h"
|
||||
#import "WailsMenu.h"
|
||||
#import "WindowDelegate.h"
|
||||
#import "message.h"
|
||||
#import "Role.h"
|
||||
|
||||
@implementation WailsWindow
|
||||
|
||||
@@ -31,7 +33,7 @@
|
||||
frame.origin.y += frame.size.height - height;
|
||||
frame.size.width = width;
|
||||
frame.size.height = height;
|
||||
[self.mainWindow setFrame:frame display:TRUE animate:FALSE];
|
||||
ON_MAIN_THREAD([self.mainWindow setFrame:frame display:TRUE animate:FALSE];);
|
||||
}
|
||||
|
||||
- (void) SetPosition:(int)x :(int)y {
|
||||
@@ -40,11 +42,11 @@
|
||||
|
||||
NSScreen* screen = [self getCurrentScreen];
|
||||
NSRect windowFrame = [self.mainWindow frame];
|
||||
NSRect screenFrame = [screen visibleFrame];
|
||||
windowFrame.origin.x += screenFrame.origin.x + (float)x;
|
||||
windowFrame.origin.y += (screenFrame.origin.y + screenFrame.size.height) - windowFrame.size.height - (float)y;
|
||||
|
||||
[self.mainWindow setFrame:windowFrame display:TRUE animate:FALSE];
|
||||
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 {
|
||||
@@ -54,10 +56,11 @@
|
||||
NSSize size = { minWidth, minHeight };
|
||||
|
||||
self.minSize = size;
|
||||
|
||||
[self.mainWindow setMinSize:size];
|
||||
|
||||
[self adjustWindowSize];
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
[self.mainWindow setMinSize:size];
|
||||
[self adjustWindowSize];
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,10 +74,11 @@
|
||||
size.height = maxHeight > 0 ? maxHeight : FLT_MAX;
|
||||
|
||||
self.maxSize = size;
|
||||
|
||||
[self.mainWindow setMinSize:size];
|
||||
|
||||
[self adjustWindowSize];
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
[self.mainWindow setMaxSize:size];
|
||||
[self adjustWindowSize];
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,8 +92,8 @@
|
||||
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:TRUE animate:FALSE];
|
||||
|
||||
[self.mainWindow setFrame:currentFrame display:YES animate:FALSE];
|
||||
|
||||
}
|
||||
|
||||
@@ -100,6 +104,7 @@
|
||||
[self.mouseEvent release];
|
||||
[self.userContentController release];
|
||||
[self.urlRequests release];
|
||||
[self.applicationMenu release];
|
||||
}
|
||||
|
||||
- (NSScreen*) getCurrentScreen {
|
||||
@@ -110,55 +115,66 @@
|
||||
return screen;
|
||||
}
|
||||
|
||||
- (void) SetTitle:(const char *)title {
|
||||
NSString *_title = [NSString stringWithUTF8String:title];
|
||||
[self.mainWindow setTitle:_title];
|
||||
- (void) SetTitle:(NSString*)title {
|
||||
ON_MAIN_THREAD([self.mainWindow setTitle:title];)
|
||||
}
|
||||
|
||||
- (void) Center {
|
||||
[self.mainWindow center];
|
||||
ON_MAIN_THREAD( [self.mainWindow center]; );
|
||||
}
|
||||
|
||||
- (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 :(const char *)appearance :(bool)windowIsTranslucent {
|
||||
- (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 = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
|
||||
NSWindowStyleMask styleMask = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
|
||||
|
||||
if (frameless) {
|
||||
styleMask = NSWindowStyleMaskBorderless;
|
||||
titlebarAppearsTransparent = true;
|
||||
hideTitle = true;
|
||||
} else {
|
||||
if (resizable) {
|
||||
styleMask |= NSWindowStyleMaskResizable;
|
||||
if (!hideTitleBar) {
|
||||
styleMask |= NSWindowStyleMaskTitled;
|
||||
}
|
||||
|
||||
if (fullscreen) {
|
||||
styleMask |= NSWindowStyleMaskFullScreen;
|
||||
}
|
||||
|
||||
if( fullSizeContent || frameless || titlebarAppearsTransparent ) {
|
||||
styleMask |= NSWindowStyleMaskFullSizeContentView;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (useToolbar) {
|
||||
NSLog(@"Using Toolbar");
|
||||
|
||||
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];
|
||||
|
||||
// [self.mainWindow canBecomeKeyWindow];
|
||||
|
||||
id contentView = [self.mainWindow contentView];
|
||||
if (windowIsTranslucent) {
|
||||
@@ -172,8 +188,7 @@
|
||||
}
|
||||
|
||||
if (appearance != nil) {
|
||||
NSString *name = [NSString stringWithUTF8String:appearance];
|
||||
NSAppearance *nsAppearance = [NSAppearance appearanceNamed:name];
|
||||
NSAppearance *nsAppearance = [NSAppearance appearanceNamed:appearance];
|
||||
[self.mainWindow setAppearance:nsAppearance];
|
||||
}
|
||||
|
||||
@@ -193,7 +208,7 @@
|
||||
config.suppressesIncrementalRendering = true;
|
||||
[config setURLSchemeHandler:self forURLScheme:@"wails"];
|
||||
|
||||
[config.preferences setValue:[NSNumber numberWithBool:true] forKey:@"developerExtrasEnabled"];
|
||||
// [config.preferences setValue:[NSNumber numberWithBool:true] forKey:@"developerExtrasEnabled"];
|
||||
|
||||
WKUserContentController* userContentController = [WKUserContentController new];
|
||||
[userContentController addScriptMessageHandler:self name:@"external"];
|
||||
@@ -223,6 +238,8 @@
|
||||
[self.webview setValue:[NSNumber numberWithBool:!webviewIsTransparent] forKey:@"drawsBackground"];
|
||||
}
|
||||
|
||||
[self.webview setNavigationDelegate:self];
|
||||
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults setBool:FALSE forKey:@"NSAutomaticQuoteSubstitutionEnabled"];
|
||||
|
||||
@@ -244,6 +261,30 @@
|
||||
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 {
|
||||
@@ -260,7 +301,7 @@
|
||||
|
||||
id colour = [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha ];
|
||||
|
||||
[self.mainWindow setBackgroundColor:colour];
|
||||
ON_MAIN_THREAD([self.mainWindow setBackgroundColor:colour];);
|
||||
}
|
||||
|
||||
- (void) HideMouse {
|
||||
@@ -276,52 +317,60 @@
|
||||
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] ) {
|
||||
[self.mainWindow toggleFullScreen:nil];
|
||||
ON_MAIN_THREAD([self.mainWindow toggleFullScreen:nil];)
|
||||
}
|
||||
}
|
||||
|
||||
// UnFullscreen resets the main window after a fullscreen
|
||||
- (void) UnFullscreen {
|
||||
if( [self isFullScreen] ) {
|
||||
[self.mainWindow toggleFullScreen:nil];
|
||||
ON_MAIN_THREAD([self.mainWindow toggleFullScreen:nil];)
|
||||
}
|
||||
}
|
||||
|
||||
- (void) Minimise {
|
||||
[self.mainWindow miniaturize:nil];
|
||||
ON_MAIN_THREAD([self.mainWindow miniaturize:nil];)
|
||||
}
|
||||
|
||||
- (void) UnMinimise {
|
||||
[self.mainWindow deminiaturize:nil];
|
||||
ON_MAIN_THREAD([self.mainWindow deminiaturize:nil];)
|
||||
}
|
||||
|
||||
- (void) Hide {
|
||||
[self.mainWindow orderOut:nil];
|
||||
ON_MAIN_THREAD([self.mainWindow orderOut:nil];)
|
||||
}
|
||||
|
||||
- (void) Show {
|
||||
[self.mainWindow makeKeyAndOrderFront:nil];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
ON_MAIN_THREAD(
|
||||
[self.mainWindow makeKeyAndOrderFront:nil];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
)
|
||||
}
|
||||
|
||||
- (void) Maximise {
|
||||
if (! self.maximised) {
|
||||
[self.mainWindow zoom:nil];
|
||||
if (![self.mainWindow isZoomed]) {
|
||||
ON_MAIN_THREAD([self.mainWindow zoom:nil];)
|
||||
}
|
||||
}
|
||||
|
||||
- (void) UnMaximise {
|
||||
if (self.maximised) {
|
||||
[self.mainWindow zoom:nil];
|
||||
if ([self.mainWindow isZoomed]) {
|
||||
ON_MAIN_THREAD([self.mainWindow zoom:nil];)
|
||||
}
|
||||
}
|
||||
|
||||
- (void) ExecJS:(const char*)script {
|
||||
NSString *nsscript = [NSString stringWithUTF8String:script];
|
||||
[self.webview evaluateJavaScript:nsscript completionHandler:nil];
|
||||
- (void) ExecJS:(NSString*)script {
|
||||
ON_MAIN_THREAD(
|
||||
[self.webview evaluateJavaScript:script completionHandler:nil];
|
||||
)
|
||||
}
|
||||
|
||||
- (void) processURLResponse:(NSString *)url :(NSString *)contentType :(NSData *)data {
|
||||
@@ -348,6 +397,10 @@
|
||||
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
||||
processMessage("DomReady");
|
||||
}
|
||||
|
||||
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
|
||||
NSString *m = message.body;
|
||||
|
||||
@@ -371,25 +424,25 @@
|
||||
|
||||
|
||||
/***** Dialogs ******/
|
||||
-(void) MessageDialog :(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) 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( strcmp(dialogType, "warning") == 0 ) {
|
||||
if( [dialogType isEqualToString:@"warning"] ) {
|
||||
style = NSAlertStyleWarning;
|
||||
}
|
||||
if( strcmp(dialogType, "error") == 0) {
|
||||
if( [dialogType isEqualToString:@"error"] ) {
|
||||
style = NSAlertStyleCritical;
|
||||
}
|
||||
}
|
||||
[alert setAlertStyle:style];
|
||||
if( strlen(title) > 0 ) {
|
||||
[alert setMessageText:[NSString stringWithUTF8String:title]];
|
||||
if( title != nil ) {
|
||||
[alert setMessageText:title];
|
||||
}
|
||||
if( strlen(message) > 0 ) {
|
||||
[alert setInformativeText:[NSString stringWithUTF8String:message]];
|
||||
if( message != nil ) {
|
||||
[alert setInformativeText:message];
|
||||
}
|
||||
|
||||
[alert addButton:button1 :defaultButton :cancelButton];
|
||||
@@ -397,47 +450,59 @@
|
||||
[alert addButton:button3 :defaultButton :cancelButton];
|
||||
[alert addButton:button4 :defaultButton :cancelButton];
|
||||
|
||||
long response = [alert runModal];
|
||||
int result;
|
||||
|
||||
if( response == NSAlertFirstButtonReturn ) {
|
||||
result = 0;
|
||||
NSImage *icon = nil;
|
||||
if (iconData != nil) {
|
||||
NSData *imageData = [NSData dataWithBytes:iconData length:iconDataLength];
|
||||
icon = [[NSImage alloc] initWithData:imageData];
|
||||
}
|
||||
else if( response == NSAlertSecondButtonReturn ) {
|
||||
result = 1;
|
||||
}
|
||||
else if( response == NSAlertThirdButtonReturn ) {
|
||||
result = 2;
|
||||
} else {
|
||||
result = 3;
|
||||
}
|
||||
processMessageDialogResponse(result);
|
||||
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 :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(const char*)filters {
|
||||
-(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( strlen(title) > 0 ) {
|
||||
[dialog setTitle:[NSString stringWithUTF8String:title]];
|
||||
if( title != nil ) {
|
||||
[dialog setTitle:title];
|
||||
}
|
||||
|
||||
// Filters - semicolon delimited list of file extensions
|
||||
if( allowFiles ) {
|
||||
if( filters != nil && strlen(filters) > 0) {
|
||||
NSString *filterString = [[NSString stringWithUTF8String:filters] stringByReplacingOccurrencesOfString:@"*." withString:@""];
|
||||
filterString = [filterString stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
NSArray *filterList = [filterString componentsSeparatedByString:@";"];
|
||||
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 != NULL && strlen(defaultFilename) > 0 ) {
|
||||
[dialog setNameFieldStringValue:[NSString stringWithUTF8String:defaultFilename]];
|
||||
if( defaultFilename != nil ) {
|
||||
[dialog setNameFieldStringValue:defaultFilename];
|
||||
}
|
||||
|
||||
[dialog setAllowsMultipleSelection: allowMultipleSelection];
|
||||
@@ -446,8 +511,8 @@
|
||||
}
|
||||
|
||||
// Default Directory
|
||||
if( defaultDirectory != NULL && strlen(defaultDirectory) > 0 ) {
|
||||
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:defaultDirectory]];
|
||||
if( defaultDirectory != nil ) {
|
||||
NSURL *url = [NSURL fileURLWithPath:defaultDirectory];
|
||||
[dialog setDirectoryURL:url];
|
||||
}
|
||||
|
||||
@@ -470,40 +535,39 @@
|
||||
processOpenFileDialogResponse([nsjson UTF8String]);
|
||||
}];
|
||||
|
||||
|
||||
[dialog runModal];
|
||||
ON_MAIN_THREAD([dialog runModal];)
|
||||
|
||||
}
|
||||
|
||||
|
||||
-(void) SaveFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(const char*)filters; {
|
||||
-(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( strlen(title) > 0 ) {
|
||||
[dialog setTitle:[NSString stringWithUTF8String:title]];
|
||||
if( title != nil ) {
|
||||
[dialog setTitle:title];
|
||||
}
|
||||
|
||||
// Filters - semicolon delimited list of file extensions
|
||||
if( filters != nil && strlen(filters) > 0) {
|
||||
NSString *filterString = [[NSString stringWithUTF8String:filters] stringByReplacingOccurrencesOfString:@"*." withString:@""];
|
||||
filterString = [filterString stringByReplacingOccurrencesOfString:@" " withString:@""];
|
||||
NSArray *filterList = [filterString componentsSeparatedByString:@";"];
|
||||
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 != NULL && strlen(defaultFilename) > 0 ) {
|
||||
[dialog setNameFieldStringValue:[NSString stringWithUTF8String:defaultFilename]];
|
||||
if( defaultFilename != nil ) {
|
||||
[dialog setNameFieldStringValue:defaultFilename];
|
||||
}
|
||||
|
||||
// Default Directory
|
||||
if( defaultDirectory != NULL && strlen(defaultDirectory) > 0 ) {
|
||||
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:defaultDirectory]];
|
||||
if( defaultDirectory != nil ) {
|
||||
NSURL *url = [NSURL fileURLWithPath:defaultDirectory];
|
||||
[dialog setDirectoryURL:url];
|
||||
}
|
||||
|
||||
@@ -518,10 +582,38 @@
|
||||
processSaveFileDialogResponse([url.path UTF8String]);
|
||||
}];
|
||||
|
||||
[dialog runModal];
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -9,10 +9,11 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "WindowDelegate.h"
|
||||
#import "message.h"
|
||||
#import "WailsContext.h"
|
||||
|
||||
@implementation WindowDelegate
|
||||
|
||||
- (BOOL)windowShouldClose:(NSWindow *)sender {
|
||||
- (BOOL)windowShouldClose:(WailsWindow *)sender {
|
||||
[sender orderOut:nil];
|
||||
if( self.hideOnClose == false ) {
|
||||
processMessage("Q");
|
||||
|
||||
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
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
@@ -155,7 +156,14 @@ func (f *Frontend) MessageDialog(options frontend.MessageDialogOptions) (string,
|
||||
buttons[index] = c.String(buttonText)
|
||||
}
|
||||
|
||||
C.MessageDialog(f.mainWindow.context, dialogType, title, message, buttons[0], buttons[1], buttons[2], buttons[3], defaultButton, cancelButton)
|
||||
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
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ type request struct {
|
||||
|
||||
var messageBuffer = make(chan string, 100)
|
||||
var requestBuffer = make(chan *request, 100)
|
||||
var callbackBuffer = make(chan uint, 10)
|
||||
|
||||
type Frontend struct {
|
||||
|
||||
@@ -65,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.
|
||||
@@ -92,6 +89,7 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
||||
|
||||
go result.startMessageProcessor()
|
||||
go result.startRequestProcessor()
|
||||
go result.startCallbackProcessor()
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -106,6 +104,14 @@ func (f *Frontend) startRequestProcessor() {
|
||||
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();")
|
||||
@@ -208,6 +214,9 @@ func (f *Frontend) WindowSetRGBA(col *options.RGBA) {
|
||||
|
||||
func (f *Frontend) Quit() {
|
||||
f.mainWindow.Quit()
|
||||
if f.frontendOptions.OnShutdown != nil {
|
||||
f.frontendOptions.OnShutdown(f.ctx)
|
||||
}
|
||||
}
|
||||
|
||||
type EventNotify struct {
|
||||
@@ -229,13 +238,14 @@ func (f *Frontend) Notify(name string, data ...interface{}) {
|
||||
}
|
||||
|
||||
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())
|
||||
@@ -259,14 +269,6 @@ func (f *Frontend) Callback(message string) {
|
||||
f.ExecJS(`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
|
||||
}
|
||||
|
||||
func (f *Frontend) ExecJS(js string) {
|
||||
f.mainWindow.ExecJS(js)
|
||||
}
|
||||
@@ -283,8 +285,10 @@ func (f *Frontend) processRequest(r *request) {
|
||||
//TODO: Handle errors
|
||||
return
|
||||
}
|
||||
data := C.CString(string(_contents))
|
||||
defer C.free(unsafe.Pointer(data))
|
||||
var data unsafe.Pointer
|
||||
if _contents != nil {
|
||||
data = unsafe.Pointer(&_contents[0])
|
||||
}
|
||||
mimetype := C.CString(_mimetype)
|
||||
defer C.free(unsafe.Pointer(mimetype))
|
||||
|
||||
@@ -304,3 +308,8 @@ func processURLRequest(ctx unsafe.Pointer, url *C.char) {
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
//export processCallback
|
||||
func processCallback(callbackID uint) {
|
||||
callbackBuffer <- callbackID
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
void processMessage(const char*t) {
|
||||
NSLog(@"processMessage called");
|
||||
|
||||
}
|
||||
|
||||
void processMessageDialogResponse(int t) {
|
||||
@@ -22,33 +21,215 @@ void processMessageDialogResponse(int t) {
|
||||
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", myByteArray, 21);
|
||||
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 = 0;
|
||||
int resizable = 1;
|
||||
int frameless = 1;
|
||||
int resizable = 0;
|
||||
int fullscreen = 0;
|
||||
int fullSizeContent = 1;
|
||||
int hideTitleBar = 0;
|
||||
int titlebarAppearsTransparent = 1;
|
||||
int titlebarAppearsTransparent = 0;
|
||||
int hideTitle = 0;
|
||||
int useToolbar = 1;
|
||||
int hideToolbarSeparator = 1;
|
||||
int webviewIsTransparent = 0;
|
||||
int alwaysOnTop = 1;
|
||||
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));
|
||||
|
||||
@@ -3,96 +3,133 @@
|
||||
|
||||
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
@@ -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]
|
||||
}
|
||||
@@ -19,6 +19,7 @@ void processURLRequest(void*, const char *);
|
||||
void processMessageDialogResponse(int);
|
||||
void processOpenFileDialogResponse(const char*);
|
||||
void processSaveFileDialogResponse(const char*);
|
||||
void processCallback(int);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
@@ -46,10 +48,6 @@ func NewWindow(frontendOptions *options.App, debugMode bool) *Window {
|
||||
alwaysOnTop := bool2Cint(frontendOptions.AlwaysOnTop)
|
||||
hideWindowOnClose := bool2Cint(frontendOptions.HideWindowOnClose)
|
||||
debug := bool2Cint(debugMode)
|
||||
alpha := C.int(frontendOptions.RGBA.A)
|
||||
red := C.int(frontendOptions.RGBA.R)
|
||||
green := C.int(frontendOptions.RGBA.G)
|
||||
blue := C.int(frontendOptions.RGBA.B)
|
||||
|
||||
var fullSizeContent, hideTitleBar, hideTitle, useToolbar, webviewIsTransparent C.int
|
||||
var titlebarAppearsTransparent, hideToolbarSeparator, windowIsTranslucent C.int
|
||||
@@ -75,13 +73,39 @@ func NewWindow(frontendOptions *options.App, debugMode bool) *Window {
|
||||
|
||||
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)
|
||||
var context *C.WailsContext = C.Create(title, width, height, frameless, resizable, fullscreen, fullSizeContent,
|
||||
hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent,
|
||||
alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, debug)
|
||||
|
||||
C.SetRGBA(unsafe.Pointer(context), red, green, blue, alpha)
|
||||
|
||||
return &Window{
|
||||
// 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() {
|
||||
@@ -90,7 +114,6 @@ func (w *Window) Center() {
|
||||
|
||||
func (w *Window) Run() {
|
||||
C.Run(w.context)
|
||||
println("I exited!")
|
||||
}
|
||||
|
||||
func (w *Window) Quit() {
|
||||
@@ -138,10 +161,16 @@ func (w *Window) UnMinimise() {
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -185,3 +214,13 @@ func (w *Window) Size() (int, int) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ type MessageDialogOptions struct {
|
||||
Buttons []string
|
||||
DefaultButton string
|
||||
CancelButton string
|
||||
Icon string
|
||||
Icon []byte
|
||||
}
|
||||
|
||||
type Frontend interface {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package gomod
|
||||
|
||||
import (
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/matryer/is"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
const basic string = `module changeme
|
||||
@@ -75,7 +76,7 @@ const basicUpdated string = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.17
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.2 // indirect
|
||||
@@ -330,7 +331,7 @@ const multilineRequireUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.17
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -381,12 +382,12 @@ func TestUpdateGoModVersion(t *testing.T) {
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{"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},
|
||||
{"basic", args{[]byte(basic), "v2.0.0-beta.17"}, []byte(basicUpdated), false},
|
||||
{"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.17"}, []byte(multilineRequireUpdated), false},
|
||||
{"basicmultilinereplace", args{[]byte(multilineReplace), "v2.0.0-beta.17"}, []byte(multilineReplaceUpdated), false},
|
||||
{"basicmultilinereplaceblock", args{[]byte(multilineReplaceBlock), "v2.0.0-beta.17"}, []byte(multilineReplaceBlockUpdated), false},
|
||||
{"basicmultilinereplacenoversion", args{[]byte(multilineReplaceNoVersion), "v2.0.0-beta.17"}, []byte(multilineReplaceNoVersionUpdated), false},
|
||||
{"basicmultilinereplacenoversionblock", args{[]byte(multilineReplaceNoVersionBlock), "v2.0.0-beta.17"}, []byte(multilineReplaceNoVersionBlockUpdated), false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -413,8 +414,8 @@ func TestGoModOutOfSync(t *testing.T) {
|
||||
want bool
|
||||
wantErr bool
|
||||
}{
|
||||
{"basic", args{[]byte(basic), "v2.0.0-beta.15"}, true, false},
|
||||
{"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.15"}, true, false},
|
||||
{"basic", args{[]byte(basic), "v2.0.0-beta.17"}, true, false},
|
||||
{"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.17"}, true, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -433,7 +434,7 @@ const multilineReplaceUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.17
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -468,14 +469,14 @@ require (
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.15 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.17 => 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.15
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.17
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -517,7 +518,7 @@ const multilineReplaceNoVersionBlockUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.17
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -562,7 +563,7 @@ const multilineReplaceBlockUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.17
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -598,6 +599,6 @@ require (
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.17 => 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
|
||||
}
|
||||
@@ -534,10 +542,10 @@ func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
//ActivationPolicy ActivationPolicy
|
||||
About *AboutInfo
|
||||
//URLHandlers map[string]func(string)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"embed"
|
||||
"log"
|
||||
"runtime"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
||||
@@ -94,4 +95,11 @@ func MergeDefaults(appoptions *App) {
|
||||
appoptions.Height = appoptions.MaxHeight
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
if appoptions.Menu == nil {
|
||||
appoptions.Menu = defaultMacMenu
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
161
website/blog/2021-11-08-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: It's 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!
|
||||
@@ -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.15` to install the Wails CLI.
|
||||
Run `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.17` 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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -192,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
|
||||
@@ -281,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]"
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ const darkCodeTheme = require('prism-react-renderer/themes/palenight');
|
||||
({
|
||||
announcementBar: {
|
||||
id: 'beta-message',
|
||||
content: 'Wails v2 is currently Beta for Windows. Mac & Linux in progress.',
|
||||
content: 'Wails v2 is currently Beta for Windows & Mac. Linux in progress.',
|
||||
backgroundColor: '#b00',
|
||||
textColor: '#FFF',
|
||||
isCloseable: false,
|
||||
|
||||
@@ -10,7 +10,7 @@ sidebar_position: 1
|
||||
|
||||
Wails 是一个可让您使用 Go 和 Web 技术编写桌面应用的项目。
|
||||
|
||||
将它看作为轻量快速的 “Electron for Go”。 您可以使用 Go 的灵活性和强大功能,结合丰富的现代前端,轻松的构建应用程序。
|
||||
将它看作为 Go 的轻量级和快速的 Electron 替代品。 您可以使用 Go 的灵活性和强大功能,结合丰富的现代前端,轻松的构建应用程序。
|
||||
|
||||
Wails 一点也不弱!这是 [xbar](https://xbarapp.com) - 一个使用 Wails 编写的 MacOS 桌面应用。它使用 Mac 的系统原生菜单,支持浅色和深色桌面主题,主窗口使用半透明,使其具有原生应用的
|
||||
“冰霜” 效果。
|
||||
@@ -31,13 +31,13 @@ Wails 自动使您的 Go 方法可用于 Javascript,因此您可以从前端
|
||||
|
||||
## 运行时库
|
||||
|
||||
Wails 为 Go 和 Javascript 提供了一个运行时库,可以处理现代应用程序需要的很多东西,比如事件、日志记录、对话框等。
|
||||
Wails 为 Go 和 Javascript 提供了一个运行时库,可以处理现代应用程序需要的很多东西,比如事件、日志、对话框等。
|
||||
|
||||
## 实时开发体验
|
||||
|
||||
### 自动重新构建
|
||||
|
||||
当您在“dev”模式下运行您的应用程序时,Wails 会将您的应用程序构建为原生桌面应用程序,但会从磁盘读取您的资源。它将检测您的 Go 代码的任何更改,并自动重新构建和重新启动您的应用程序。
|
||||
当您在“dev“模式下运行您的应用程序时,Wails 会将您的应用程序构建为原生桌面应用程序,但会从磁盘读取您的资源。它将检测您的 Go 代码的任何更改,并自动重新构建和重新启动您的应用程序。
|
||||
|
||||
### 自动重新加载
|
||||
|
||||
@@ -47,7 +47,7 @@ Wails 为 Go 和 Javascript 提供了一个运行时库,可以处理现代应
|
||||
|
||||
如果您更喜欢在浏览器中调试和开发,那么 Wails 可以满足您的需求。正在运行的应用程序还有一个网络服务器,它将在连接到它的任何浏览器中运行您的应用程序。当您的资源在磁盘上发生变化时,它会刷新。
|
||||
|
||||
## 生成原生二进制文件
|
||||
## 可用于生产的原生二进制文件
|
||||
|
||||
当您准备好完成应用程序的最终构建时,CLI 会将其编译为单个可执行文件,并将所有资源打包到其中。在 Windows 和 MacOS
|
||||
上,可以创建用于分发的原生包。使用打包工具后生成的资源(图标、info.plist、清单文件等)是您项目的一部分,可以自定义,让您完全控制应用程序的构建方式。
|
||||
|
||||
@@ -11,7 +11,7 @@ sidebar_position: 2
|
||||
- [Gophers Slack Channel](https://gophers.slack.com/messages/CJ4P9F7MZ/)
|
||||
- [Gophers Slack Channel Invite](https://invite.slack.golangbridge.org/)
|
||||
- [Github Issues](https://github.com/wailsapp/wails/issues)
|
||||
- [v2 Beta Discussion Board](https://github.com/wailsapp/wails/discussions/828)
|
||||
- [v2 测试版讨论板](https://github.com/wailsapp/wails/discussions/828)
|
||||
|
||||
## 社交媒体
|
||||
|
||||
|
||||
@@ -23,3 +23,7 @@ sidebar_position: 1
|
||||
- [wails-template-vue](https://github.com/misitebao/wails-template-vue) - 基于 Vue 和 Vue-Router 的 Wails 模板
|
||||
- [wails-vite-vue-ts](https://github.com/codydbentley/wails-vite-vue-ts) - 使用 Vite 的 Vue 3 TypeScript(以及添加功能的说明)
|
||||
- [wails-vite-vue-the-works](https://github.com/codydbentley/wails-vite-vue-the-works) - 使用 Vite, Vuex, Vue Router, Sass, 和 ESLint + Prettier 的 Vue 3 TypeScript
|
||||
|
||||
## Angular
|
||||
|
||||
- [wails-angular-template](https://github.com/TAINCER/wails-angular-template) - 带有 TypeScript, Sass, 热重载, 代码拆分和 i18n 的 Angular
|
||||
|
||||
@@ -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,15 +61,27 @@ 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"/>
|
||||
</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>
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -46,6 +46,6 @@ Wails 项目具有以下布局:
|
||||
|
||||
`frontend`目录没有特定于 Wails 的内容,可以是您选择的任何前端项目。
|
||||
|
||||
`build`目录在构建过程中使用。这些文件可以更新以自定义您的构建。如果文件从构建目录中删除,将重新生成默认版本。
|
||||
`build`目录在构建过程中使用。这些文件可以修改以自定义您的构建。如果文件从构建目录中删除,将重新生成默认版本。
|
||||
|
||||
`go.mod`中的默认模块名称是“changeme”。您应该将其更改为更合适的内容。
|
||||
|
||||
@@ -30,7 +30,7 @@ Wails 有许多安装前需要的常见依赖项:
|
||||
|
||||
从[Node 下载页面](https://nodejs.org/en/download/)下载 Npm。最好使用最新版本,因为这是我们通常会测试的版本。
|
||||
|
||||
运行 `npm --version` 进行校验。
|
||||
运行 `npm --version` 进行验证。
|
||||
|
||||
## 平台指定依赖关系
|
||||
|
||||
@@ -63,7 +63,7 @@ import TabItem from "@theme/TabItem";
|
||||
|
||||
## 安装 Wails
|
||||
|
||||
运行 `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.15` 安装 Wails CLI。
|
||||
运行 `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.17` 安装 Wails CLI。
|
||||
|
||||
## 系统检查
|
||||
|
||||
|
||||
@@ -146,3 +146,20 @@ Wails v2 处理资源的方式的伟大之处在于它没有!您唯一需要
|
||||
第一个,如果有给定,将在`frontend`目录中执行以安装 node 模块。第二个,如果有给定,将在`frontend`目录中执行以构建前端项目。
|
||||
|
||||
如果没有给出这两个字段,那么 Wails 不会对前端做任何操作。它仅仅被用作`embed.FS`。
|
||||
|
||||
## 内置开发服务器
|
||||
|
||||
运行`wails dev`将启动内置的开发服务器,它将在您的项目目录中启动一个文件监听器。
|
||||
默认情况下,如果有任何文件更改,wails 会检查它是否是应用程序文件(默认:.go,可使用`-e`标志配置)。如果是,那么它将重新构建您的应用程序并重新启动它。如果更改的文件在`assetdir`目录中,它会在很短的时间后重新加载。
|
||||
|
||||
开发服务器使用一种称为“防抖”的技术,这意味着它不会立即重新加载,因为可能会在短时间内更改多个文件。当触发发生时,它会在发出重新加载之前等待一定的时间。
|
||||
如果发生另一个触发,它会再次重置为等待时间。默认情况下,此值为 100ms。如果此值不适用于您的项目,则可以使用`-debounce`标志进行配置。如果使用,此值将保存到您的项目配置中并成为默认值。
|
||||
|
||||
## 外部开发服务器
|
||||
|
||||
一些框架带有自己的实时重新加载服务器,但是它们将无法利用 Wails Go 绑定。在这种情况下,最好运行一个监听脚本,将项目重新构建到构建目录中,Wails 将监视该目录。
|
||||
有关示例,请参阅使用[rollup](https://rollupjs.org/guide/en/)的默认 svelte 模板。对于[create-react-app](https://create-react-app.dev/),可以使用[此脚本](https://gist.github.com/int128/e0cdec598c5b3db728ff35758abdbafd)来实现类似的结果。
|
||||
|
||||
## Go 模块
|
||||
|
||||
默认的 Wails 模板会生成一个包含模块名称“changeme”的`go.mod`文件。您应该在项目生成后将其更改为更合适的内容。
|
||||
|
||||
@@ -25,3 +25,7 @@ Wails 为拖动窗口提供了一个简单的解决方案:任何具有“data-
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
:::info 全屏
|
||||
如果您允许应用程序全屏显示,则此拖动功能将被禁用。
|
||||
:::
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# 滚动超出
|
||||
|
||||
[Overscroll](https://developer.mozilla.org/zh-CN/docs/Web/CSS/overscroll-behavior) 是当您滚动超出页面内容边界时有时会获得的“弹跳效果”。这在移动应用程序中很常见。这可以使用 CSS 禁用:
|
||||
|
||||
```css
|
||||
body {
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
```
|
||||
@@ -89,3 +89,4 @@ Renaming package-lock.json -> package-lock.tmpl.json...
|
||||
- 确保`template.json`完整,尤其是`helpurl`
|
||||
- 将文件推送到 GitHub
|
||||
- 在[社区模板](/docs/community/templates)页面上创建 PR
|
||||
- 在[模板公告](https://github.com/wailsapp/wails/discussions/825)讨论板上发布模板
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
|
||||
通过在构建时使用`-webview2`标志,您可以决定在未检测到合适的运行时的时候(包括安装的运行时是否太旧)应用程序将执行的操作。四个选项是:
|
||||
|
||||
1. 下载
|
||||
2. 内嵌
|
||||
3. 浏览器
|
||||
4. 错误
|
||||
1. Download(下载)
|
||||
2. Embed(内嵌)
|
||||
3. Browser(浏览器)
|
||||
4. Error(错误)
|
||||
|
||||
### 下载
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ Wails 应用程序是一个带有一个 webkit 前端的标准的 Go 应用程
|
||||
|
||||
### 概述
|
||||
|
||||
主应用程序由对`wails.Run()`的调用组成. 它接受描述应用程序窗口大小、窗口标题、要使用的资源等的应用程序配置。基本应用程序可能如下所示:
|
||||
主应用程序由对`wails.Run()`的调用组成. 它接受描述应用程序窗口大小、窗口标题、要使用的资源等应用程序配置。基本应用程序可能如下所示:
|
||||
|
||||
```go title="main.go"
|
||||
package main
|
||||
@@ -85,7 +85,7 @@ func (b *App) Greet(name string) string {
|
||||
#### 资源
|
||||
|
||||
`Assets` 选项是必须的,因为您不能拥有没有前端资源的 Wails 应用程序。这些资源可以是您希望在 Web 应用程序中找到的任何文件 - html、js、css、svg、png 等。
|
||||
**不需要生成资源包**- 纯文件即可。当应用程序启动时,它将尝试从您的资源中加载`index.html`,并且那时起前端基本上将作为浏览器工作。值得注意的是 embed.FS,
|
||||
**不需要生成资源包**- 纯文件即可。当应用程序启动时,它将尝试从您的资源中加载`index.html`,并且那时起前端基本上将作为浏览器工作。值得注意的是`embed.FS`,
|
||||
文件所在的位置没有要求。嵌入路径很可能使用了相对于您的主应用程序代码的嵌套目录,例如 `frontend/dist`:
|
||||
|
||||
```go title="main.go"
|
||||
@@ -95,10 +95,12 @@ var assets embed.FS
|
||||
|
||||
启动时,Wails 将遍历嵌入的文件,寻找包含的`index.html`. 所有其他资源将相对于该目录加载。
|
||||
|
||||
由于生产二进制文件使用中包含在`embed.FS`的文件,因此应用程序不需要附带任何外部文件。
|
||||
由于可用于生产的二进制文件使用包含在`embed.FS`中的文件,因此应用程序不需要附带任何外部文件。
|
||||
|
||||
当使用`wails dev`命令在”dev“模式下,资源从磁盘加载,任何更改都会导致“实时重新加载”。资源的位置需要使用`-assetdir`传递给`wails dev`命令,并且很可能与嵌入路径相同。
|
||||
希望`embed.FS`将来我们可以从它本身计算出来。更多细节可以在[应用程序开发指南](/docs/guides/application-development)中找到。
|
||||
希望将来我们可以从`embed.FS`本身计算出来。
|
||||
|
||||
更多细节可以在[应用程序开发指南](/docs/guides/application-development)中找到。
|
||||
|
||||
#### 应用程序生命周期回调
|
||||
|
||||
|
||||
@@ -13,25 +13,27 @@ Wails CLI 有许多用于管理项目的命令。所有命令都以此方式运
|
||||
|
||||
`wails init` 用于生成项目。
|
||||
|
||||
| 标志 | 描述 | 默认 |
|
||||
| :----------------- | :------------------------------------------------------------------ | :------ |
|
||||
| -n "project name" | 项目名. **强制必填**. | |
|
||||
| -d "project dir" | 要创建的项目目录 | 项目名 |
|
||||
| -g | 初始化 git 存储库 | |
|
||||
| -l | 可用项目模板列表 | |
|
||||
| -q | 禁止输出到控制台 | |
|
||||
| -t "template name" | 要使用的项目模板。这可能是在 github 上托管的远程模板的 URL 的名称。 | vanilla |
|
||||
| -ide | 生成 IDE 项目文件 | |
|
||||
| -f | 强制构建应用 | false |
|
||||
| 标志 | 描述 | 默认 |
|
||||
| :----------------- | :---------------------------------------------------------------------------------- | :------ |
|
||||
| -n "project name" | 项目名. **强制必填**. | |
|
||||
| -d "project dir" | 要创建的项目目录 | 项目名 |
|
||||
| -g | 初始化 git 存储库 | |
|
||||
| -l | 可用项目模板列表 | |
|
||||
| -q | 禁止输出到控制台 | |
|
||||
| -t "template name" | 要使用的项目模板。这可能是默认模板的名称或在 github 上托管的远程模板的 URL 的名称。 | vanilla |
|
||||
| -ide | 生成 IDE 项目文件 | |
|
||||
| -f | 强制构建应用 | false |
|
||||
|
||||
示例:
|
||||
`wails init -n test -d mytestproject -g -ide vscode -q`
|
||||
|
||||
这将在 "mytestproject" 目录生成一个名为 "test" 的项目,初始化 git,生成 vscode 项目文件并静默执行。
|
||||
|
||||
可以在[此处](/docs/guides/ides)找到有关在 Wails 中使用 IDE 的更多信息。
|
||||
|
||||
### 远程模板
|
||||
|
||||
支持远程模板(托管在 GitHub 上)并且可以使用模板的项目 URL 进行安装。
|
||||
支持远程模板(托管在 GitHub 上)并且可以使用模板项目的 URL 进行安装。
|
||||
|
||||
示例: `wails init -n test -t https://github.com/leaanthony/testtemplate`
|
||||
|
||||
@@ -66,6 +68,10 @@ Wails CLI 有许多用于管理项目的命令。所有命令都以此方式运
|
||||
|
||||
如果您更喜欢使用标准 Go 工具进行构建,请参阅[手动构建](/docs/guides/manual-builds)指南。
|
||||
|
||||
示例:
|
||||
|
||||
`wails build -clean -o myproject.exe`
|
||||
|
||||
## 诊断检查
|
||||
|
||||
`wails doctor` 将运行诊断程序以确保您的系统已准备好进行开发。
|
||||
@@ -111,23 +117,25 @@ Your system is ready for Wails development!
|
||||
- 所有应用程序资源都从磁盘加载。如果它们被更改,应用程序将自动重新加载(而不是重新构建)。所有连接的浏览器也将重新加载
|
||||
- 生成的 JS 模块提供以下内容:
|
||||
- 带有自动生成的 JSDoc 的 Go 方法的 Javascript 包装器,提供代码提示
|
||||
- 的 Go 结构体的 TypeScript 版本,可以构造并传递给您的后端方法
|
||||
- 您的 Go 结构体的 TypeScript 版本,可以构造并传递给您的 Go 方法
|
||||
- 生成第二个 JS 模块,为运行时提供包装器 + TS 声明
|
||||
|
||||
| 标志 | 描述 | 默认 |
|
||||
| :--------------------------- | :---------------------------------------------------- | :-------------------------- |
|
||||
| -assetdir "./path/to/assets" | 编译资源的路径 | Value in `wails.json` |
|
||||
| -browser | `http://localhost:34115`在启动时打开浏览器 | |
|
||||
| -compiler "compiler" | 使用不同的 go 编译器来构建,例如 go1.15beta1 | go |
|
||||
| -e | 触发重建的扩展(逗号分隔) | go |
|
||||
| -ldflags "flags" | 传递给编译器的额外 ldflags | |
|
||||
| -tags "extra tags" | 构建标签以传递给编译器(引号和空格分隔) | |
|
||||
| -loglevel "loglevel" | 要使用的日志级别 - Trace, Debug, Info, Warning, Error | Debug |
|
||||
| -noreload | 资源更改时禁用自动重新加载 | |
|
||||
| -v | 详细级别 (0 - silent, 1 - standard, 2 - verbose) | 1 |
|
||||
| -wailsjsdir | 生成生成的 Wails JS 模块的目录 | Value store in `wails.json` |
|
||||
| 标志 | 描述 | 默认 |
|
||||
| :--------------------------- | :---------------------------------------------------- | :----------------------- |
|
||||
| -assetdir "./path/to/assets" | 编译资源的路径 | `wails.json`中的值 |
|
||||
| -browser | 在启动时打开浏览器到`http://localhost:34115` | |
|
||||
| -compiler "compiler" | 使用不同的 go 编译器来构建,例如 go1.15beta1 | go |
|
||||
| -e | 触发重新构建的扩展(逗号分隔) | go |
|
||||
| -ldflags "flags" | 传递给编译器的额外 ldflags | |
|
||||
| -tags "extra tags" | 构建标签以传递给编译器(引号和空格分隔) | |
|
||||
| -loglevel "loglevel" | 要使用的日志级别 - Trace, Debug, Info, Warning, Error | Debug |
|
||||
| -noreload | 资源更改时禁用自动重新加载 | |
|
||||
| -v | 详细级别 (0 - silent, 1 - standard, 2 - verbose) | 1 |
|
||||
| -wailsjsdir | 生成生成的 Wails JS 模块的目录 | `wails.json`中的值 |
|
||||
| -debounce | 检测到资源更改后等待重新加载的时间 | 100 (毫秒) |
|
||||
| -devserverurl "url" | 使用第 3 方开发服务器 url, 例如 Vite | "http://localhost:34115" |
|
||||
|
||||
如果在命令行上提供了`-assetdir`或`-wailsjsdir`标志,它们将保存在`wails.json`中,并成为后续调用的默认值。
|
||||
如果在命令行上提供了`-assetdir`,`-wailsjsdir`,`debounce`或`devserverurl`标志,它们将保存在`wails.json`中,并成为后续调用的默认值。
|
||||
|
||||
示例:
|
||||
|
||||
@@ -136,10 +144,12 @@ Your system is ready for Wails development!
|
||||
此命令将执行以下操作:
|
||||
|
||||
- 构建应用程序并运行它(更多细节在[这里](/docs/guides/manual-builds))
|
||||
- 生成 Wails JS 模块 ``./frontend/src`
|
||||
- 注意文件的更新`./frontend/dist`并在任何更改时重新加载
|
||||
- 在`./frontend/src`中生成 Wails JS 模块
|
||||
- 监听`./frontend/dist`中文件的更新并在更改时重新加载
|
||||
- 打开浏览器并连接到应用程序
|
||||
|
||||
[此处](/docs/guides/application-development)提供了有关将此功能与现有框架脚本一起使用的更多信息。
|
||||
|
||||
## 生成
|
||||
|
||||
### 模板
|
||||
|
||||
@@ -155,6 +155,14 @@ func main() {
|
||||
|
||||
该值是默认设置窗口的 RGBA 值。默认值:0xFFFFFFFF。
|
||||
|
||||
### 窗口固定在最顶层
|
||||
|
||||
名称:AlwaysOnTop
|
||||
|
||||
类型:bool
|
||||
|
||||
窗口在失去焦点时应保持在其他窗口之上。
|
||||
|
||||
### 资源
|
||||
|
||||
名称:Assets
|
||||
@@ -219,7 +227,7 @@ func main() {
|
||||
|
||||
定义需要绑定到前端的方法的一部分结构实例。
|
||||
|
||||
### 窗口
|
||||
### Windows
|
||||
|
||||
名称:Windows
|
||||
|
||||
@@ -235,7 +243,7 @@ func main() {
|
||||
|
||||
类型:bool
|
||||
|
||||
将此设置为 true 将使 webview 背景透明,其中没有其他颜色呈现。通常与[窗口半透明](#窗口半透明)结合使用 以制作外观现代的应用程序。
|
||||
当 alpha 值为 0 时,设置为 true 将使 WebView 背景透明。这意味着如果你使用`rgba(0,0,0,0)`,主窗口将显示。通常与[窗口半透明](#窗口半透明)结合使用以制作冰霜效果的应用程序。
|
||||
|
||||
### 窗口半透明
|
||||
|
||||
@@ -243,7 +251,7 @@ func main() {
|
||||
|
||||
类型:bool
|
||||
|
||||
将此设置为 true 将使窗口半透明。通常与[网页透明](#网页透明) 结合使用以制作外观现代的应用程序。
|
||||
将此设置为 true 将使窗口半透明。通常与[网页透明](#网页透明) 结合使用以制作冰霜效果的应用程序。
|
||||
|
||||
### 禁用窗口图标
|
||||
|
||||
|
||||
@@ -11,13 +11,16 @@ sidebar_position: 5
|
||||
"name": "[项目名称]",
|
||||
"assetdir": "[资源目录的相对路径]",
|
||||
"frontend:install": "[安装node依赖的命令,在frontend目录下运行 - 通常是 `npm install`]",
|
||||
"frontend:dev": "[此命令在“Wails dev”上的单独进程中运行。适用于第三方观察者]",
|
||||
"frontend:build": "[构建资源的命令,在frontend目录下运行 - 通常是 `npm run build`]",
|
||||
"wailsjsdir": "[自动生成的JS模块将被创建的目录的相对路径]",
|
||||
"version": "[项目配置版本]",
|
||||
"outputfilename": "[二进制文件的名称]"
|
||||
"outputfilename": "[二进制文件的名称]",
|
||||
"debounceMS": 100, // 在检测到资源更改时,开发服务器等待重新加载的时间
|
||||
"devserverurl": "[用于服务本地资源的开发服务器URL。默认:http://localhost:34115]"
|
||||
}
|
||||
```
|
||||
|
||||
该文件将在运行`wails build`或`wails dev`时,由 Wails CLI 读取。
|
||||
|
||||
`wails build/dev`命令中的`assetdir`和`wailsjsdir`标志将覆盖项目配置并作为后续运行的默认值。
|
||||
`wails build/dev`命令中的`assetdir`,`wailsjsdir`,`debounceMS`和`devserverurl`标志将覆盖项目配置并作为后续运行的默认值。
|
||||
|
||||
@@ -15,44 +15,44 @@ JS 运行时当前不支持 Dialog。
|
||||
|
||||
### 打开选择目录对话框
|
||||
|
||||
打开一个对话框,提示用户选择目录。可以使用 [打开选择文件对话框参数选项](#打开选择文件对话框参数选项)进行自定义。
|
||||
|
||||
Go 方法签名: `OpenDirectoryDialog(ctx context.Context, dialogOptions OpenDialogOptions) (string, error)`
|
||||
|
||||
返回值: 所选目录(如果用户取消则为空白)或错误
|
||||
|
||||
打开一个对话框,提示用户选择目录。可以使用 [打开选择文件对话框参数选项](#打开选择文件对话框参数选项)进行自定义。
|
||||
|
||||
### 打开选择文件对话框
|
||||
|
||||
打开一个对话框,提示用户选择文件。可以使用[打开选择文件对话框参数选项](#打开选择文件对话框参数选项)进行自定义。
|
||||
|
||||
Go 方法签名: `OpenFileDialog(ctx context.Context, dialogOptions OpenDialogOptions) (string, error)`
|
||||
|
||||
返回值: 所选文件(如果用户取消则为空白)或错误
|
||||
|
||||
打开一个对话框,提示用户选择文件。可以使用[打开选择文件对话框参数选项](#打开选择文件对话框参数选项)进行自定义。
|
||||
|
||||
### 打开选择多个文件对话框
|
||||
|
||||
打开一个对话框,提示用户选择多个文件。可以使用[打开选择文件对话框参数选项](#打开选择文件对话框参数选项)进行自定义。
|
||||
|
||||
Go 方法签名: `OpenMultipleFilesDialog(ctx context.Context, dialogOptions OpenDialogOptions) ([]string, error)`
|
||||
|
||||
返回值: 选定的文件(如果用户取消则为零)或错误
|
||||
|
||||
打开一个对话框,提示用户选择多个文件。可以使用[打开选择文件对话框参数选项](#打开选择文件对话框参数选项)进行自定义。
|
||||
|
||||
### 保存文件对话框
|
||||
|
||||
打开一个对话框,提示用户选择文件名以进行保存。可以使用[保存文件对话框参数选项](#保存文件对话框参数选项)自定义。
|
||||
|
||||
Go 方法签名: `SaveFileDialog(ctx context.Context, dialogOptions SaveDialogOptions) (string, error)`
|
||||
|
||||
返回值: 所选文件(如果用户取消则为空白)或错误
|
||||
|
||||
打开一个对话框,提示用户选择文件名以进行保存。可以使用[保存文件对话框参数选项](#保存文件对话框参数选项)自定义。
|
||||
|
||||
### 消息对话框
|
||||
|
||||
使用消息对话框显示消息。可以使用[消息对话框参数选项](#消息对话框参数选项)进行自定义。
|
||||
|
||||
Go 方法签名: `MessageDialog(ctx context.Context, dialogOptions MessageDialogOptions) (string, error)`
|
||||
|
||||
返回值: 所选按钮的文本或错误
|
||||
|
||||
使用消息对话框显示消息。可以使用[消息对话框参数选项](#消息对话框参数选项)进行自定义。
|
||||
|
||||
## 参数选项
|
||||
|
||||
### 打开选择文件对话框参数选项
|
||||
@@ -63,15 +63,28 @@ 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
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 描述 | Win | Mac |
|
||||
| -------------------------- | --------------------------------- | --- | --- |
|
||||
| DefaultDirectory | 对话框打开时显示的目录 | ✅ | ✅ |
|
||||
| DefaultFilename | 默认文件名 | ✅ | ✅ |
|
||||
| Title | 对话框的标题 | ✅ | ✅ |
|
||||
| [Filters](#文件过滤) | 文件过滤器列表 | ✅ | ✅ |
|
||||
| AllowFiles | 允许选择文件 | | ✅ |
|
||||
| AllowDirectories | 允许选择目录 | | ✅ |
|
||||
| ShowHiddenFiles | 显示系统隐藏的文件 | | ✅ |
|
||||
| CanCreateDirectories | 允许用户创建目录 | | ✅ |
|
||||
| ResolvesAliases | 如果为 true,则返回文件而不是别名 | | ✅ |
|
||||
| TreatPackagesAsDirectories | 允许导航到包 | | ✅ |
|
||||
|
||||
### 保存文件对话框参数选项
|
||||
|
||||
```go
|
||||
@@ -80,12 +93,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
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 描述 | Win | Mac |
|
||||
| -------------------------- | ---------------------- | --- | --- |
|
||||
| DefaultDirectory | 对话框打开时显示的目录 | ✅ | ✅ |
|
||||
| DefaultFilename | 默认文件名 | ✅ | ✅ |
|
||||
| Title | 对话框的标题 | ✅ | ✅ |
|
||||
| [Filters](#文件过滤) | 文件过滤器列表 | ✅ | ✅ |
|
||||
| ShowHiddenFiles | 显示系统隐藏的文件 | | ✅ |
|
||||
| CanCreateDirectories | 允许用户创建目录 | | ✅ |
|
||||
| TreatPackagesAsDirectories | 允许导航到包 | | ✅ |
|
||||
|
||||
### 消息对话框参数选项
|
||||
|
||||
```go
|
||||
@@ -94,30 +117,96 @@ 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
|
||||
}
|
||||
```
|
||||
|
||||
import Tabs from "@theme/Tabs";
|
||||
import TabItem from "@theme/TabItem";
|
||||
| 字段 | 描述 | Win | Mac |
|
||||
| ------------- | ---------------------------------------------------- | --- | --- |
|
||||
| Type | 消息对话框的类型,例如问题、信息... | ✅ | ✅ |
|
||||
| Title | 对话框的标题 | ✅ | ✅ |
|
||||
| Message | 向用户显示的消息 | ✅ | ✅ |
|
||||
| Buttons | 按钮标题列表 | | ✅ |
|
||||
| DefaultButton | 带有此文本的按钮应被视为默认按钮。 Bound to `return` | | ✅ |
|
||||
| CancelButton | 带有此文本的按钮应被视为取消。Bound to `escape` | | ✅ |
|
||||
|
||||
<Tabs
|
||||
defaultValue="Windows"
|
||||
values={[
|
||||
{ label: "Windows", value: "Windows" },
|
||||
{ label: "MacOS", value: "MacOS" },
|
||||
{ label: "Linux", value: "Linux" },
|
||||
]}
|
||||
>
|
||||
<TabItem value="Windows">
|
||||
Windows
|
||||
具有标准对话框类型并且按钮不可定制。返回的值将是以下之一:“确定”、“取消”、“中止”、“重试”、“忽略”、“是”、“否”、“再试一次”或“继续”
|
||||
</TabItem>
|
||||
<TabItem value="MacOS">“DefaultButton”和“CancelButton”都应与“Buttons”中的值匹配。</TabItem>
|
||||
<TabItem value="Linux">即将推出...</TabItem>
|
||||
</Tabs>
|
||||
#### Windows
|
||||
|
||||
Windows 具有标准对话框类型,其中的按钮不可自定义。
|
||||
返回的值将是以下之一:"Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "Try Again" or "Continue"
|
||||
|
||||
#### Mac
|
||||
|
||||
Mac 上的消息对话框最多可以指定 4 个按钮。如果没有`DefaultButton`或`CancelButton`给出,第一个按钮被认为是默认的并绑定到`return`键。
|
||||
|
||||
对于以下代码:
|
||||
|
||||
```go
|
||||
selection, err := runtime.MessageDialog(b.ctx, runtime.MessageDialogOptions{
|
||||
Title: "It's your turn!",
|
||||
Message: "Select a number",
|
||||
Buttons: []string{"one", "two", "three", "four"},
|
||||
})
|
||||
```
|
||||
|
||||
第一个按钮显示为默认值:
|
||||
|
||||
<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 />
|
||||
|
||||
如果我们指定`DefaultButton`为“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",
|
||||
})
|
||||
```
|
||||
|
||||
第二个按钮显示为默认值。当 `return` 被按下时,则返回数值“two”。
|
||||
|
||||
<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 />
|
||||
|
||||
如果我们现在指定`CancelButton`为“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",
|
||||
})
|
||||
```
|
||||
|
||||
带有“three”的按钮显示在对话框的底部。当`escape`被按下时,则返回值“three”:
|
||||
|
||||
<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 />
|
||||
|
||||
#### 对话框类型
|
||||
|
||||
@@ -138,3 +227,41 @@ type FileFilter struct {
|
||||
Pattern string // semi-colon separated list of extensions, EG: "*.jpg;*.png"
|
||||
}
|
||||
```
|
||||
|
||||
#### Windows
|
||||
|
||||
Windows 允许您在对话框中使用多个文件过滤器。每个 FileFilter 将在对话框中显示为一个单独的条目:
|
||||
|
||||
<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 对话框只有一组模式来过滤文件的概念。如果提供了多个 FileFilters,Wails 将使用所有定义的模式。
|
||||
|
||||
示例:
|
||||
|
||||
```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",
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
这将导致使用`*.png,*.jpg,*.mov,*.mp4`作为过滤器打开文件对话框。
|
||||
|
||||
BIN
website/static/img/build-cross-windows.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
website/static/img/build-darwin-amd.png
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
website/static/img/build-darwin-arm.png
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
website/static/img/build-darwin-universal.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="50%" height="50%" viewBox="0 0 150 124" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
<svg width="50%" height="50%" viewBox="0 0 150 150" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xml:space="preserve"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1.2207,0,0,1.21688,0,0)">
|
||||
|
||||
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="50%" height="50%" viewBox="0 0 150 125" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
<svg width="50%" height="50%" viewBox="0 0 150 150" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xml:space="preserve"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1.2207,0,0,1.21688,0,0)">
|
||||
|
||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
BIN
website/static/img/reference/about-dialog.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
website/static/img/reference/about-menu.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
website/static/img/reference/titlebar-default.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
website/static/img/reference/titlebar-hidden-inset.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
website/static/img/reference/titlebar-hidden.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
website/static/img/remote-mac.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
website/static/img/wails-mac.png
Normal file
|
After Width: | Height: | Size: 514 KiB |
BIN
website/static/img/wails-menus-mac.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |