mirror of
https://github.com/taigrr/wails.git
synced 2026-04-02 21:29:17 -07:00
Compare commits
54 Commits
v2.0.0-bet
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9e559f069 | ||
|
|
9a4c603001 | ||
|
|
98a95e99a5 | ||
|
|
d19c982eed | ||
|
|
a963836e75 | ||
|
|
00e9eb4b0b | ||
|
|
262b6281e1 | ||
|
|
f66c70f0be | ||
|
|
717d373668 | ||
|
|
d29fa94aa4 | ||
|
|
01dd0cd0b2 | ||
|
|
126cc78d1a | ||
|
|
5703d465fc | ||
|
|
0c2963cf53 | ||
|
|
b61fd16936 | ||
|
|
3a8ba96cb3 | ||
|
|
3b1d74cf84 | ||
|
|
2e0a6f95a0 | ||
|
|
8470bfb26b | ||
|
|
bea0c1446a | ||
|
|
35ebbdfa12 | ||
|
|
bb25b3f42f | ||
|
|
4a11f9bb20 | ||
|
|
c1a20d0509 | ||
|
|
32c3721b1b | ||
|
|
913cc56adf | ||
|
|
38f37e817b | ||
|
|
4e68f92083 | ||
|
|
3edbda313e | ||
|
|
04cde94c96 | ||
|
|
1faa962cf5 | ||
|
|
94a74520be | ||
|
|
27dd40fd29 | ||
|
|
616ecabb41 | ||
|
|
15cd325034 | ||
|
|
450eb2e7ae | ||
|
|
84622b829c | ||
|
|
a1323ce5e9 | ||
|
|
49629f6dc6 | ||
|
|
231848cb9e | ||
|
|
a51d8bb47d | ||
|
|
e0e4c0ae11 | ||
|
|
d47b3734af | ||
|
|
26d248a4b6 | ||
|
|
6413a6fb4d | ||
|
|
5e36f4fc7f | ||
|
|
b47c278c95 | ||
|
|
a94a720a68 | ||
|
|
3caa0f1438 | ||
|
|
b8ef90cb41 | ||
|
|
9efc648e3d | ||
|
|
baa96f47d8 | ||
|
|
184ce763c1 | ||
|
|
229ee95f91 |
68
README.md
68
README.md
@@ -1,5 +1,5 @@
|
||||
<p align="center" style="text-align: center">
|
||||
<img src="logo_cropped.png" width="40%"><br/>
|
||||
<img src="logo.png" width="55%"><br/>
|
||||
</p>
|
||||
<p align="center">
|
||||
Build desktop applications using Go & Web Technologies.<br/><br/>
|
||||
@@ -79,48 +79,49 @@ This project is supported by these kind people / companies:
|
||||
<a href="https://github.com/snider" style="width:100px;">
|
||||
<img src="https://github.com/snider.png?size=100" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/codydbentley" style="width:100px">
|
||||
<img src="https://github.com/codydbentley.png?size=100" width="100"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://github.com/matryer" style="width:100px">
|
||||
<img src="https://github.com/matryer.png" width="100"/>
|
||||
</a>
|
||||
<a href="https://www.jetbrains.com?from=Wails" style="width:100px">
|
||||
<img src="jetbrains-grayscale.png" width="100"/>
|
||||
<img src="/img/jetbrains-grayscale.png" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/tc-hib" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/tc-hib.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/tc-hib" style="width:55px">
|
||||
<img src="https://github.com/tc-hib.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/picatz" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/picatz.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/picatz" style="width:50px">
|
||||
<img src="https://github.com/picatz.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/tylertravisty" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/tylertravisty.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/tylertravisty" style="width:50px">
|
||||
<img src="https://github.com/tylertravisty.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/akhudek" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/akhudek.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/akhudek" style="width:50px">
|
||||
<img src="https://github.com/akhudek.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/trea" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/trea.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/trea" style="width:50px">
|
||||
<img src="https://github.com/trea.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/LanguageAgnostic" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/LanguageAgnostic.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/LanguageAgnostic" style="width:55px">
|
||||
<img src="https://github.com/LanguageAgnostic.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/fcjr" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/fcjr.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/fcjr" style="width:55px">
|
||||
<img src="https://github.com/fcjr.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/nickarellano" style="width:60px;border-radius: 50%">
|
||||
<img src="https://github.com/nickarellano.png?size=60" width="60" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/nickarellano" style="width:60px">
|
||||
<img src="https://github.com/nickarellano.png?size=60" width="60"/>
|
||||
</a>
|
||||
<a href="https://github.com/bglw" style="width:65px;border-radius: 50%">
|
||||
<img src="https://github.com/bglw.png?size=65" width="65" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/bglw" style="width:65px">
|
||||
<img src="https://github.com/bglw.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/jugglingjsons" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/jugglingjsons.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/jugglingjsons" style="width:50px">
|
||||
<img src="https://github.com/jugglingjsons.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/marcus-crane" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/marcus-crane.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
</a>
|
||||
<a href="https://github.com/codydbentley" style="width:65px">
|
||||
<img src="https://github.com/codydbentley.png?size=65" width="65"/>
|
||||
<a href="https://github.com/marcus-crane" style="width:65px">
|
||||
<img src="https://github.com/marcus-crane.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/bbergshaven" style="width:45px">
|
||||
<img src="https://github.com/bbergshaven.png?size=45" width="45"/>
|
||||
@@ -128,7 +129,18 @@ This project is supported by these kind people / companies:
|
||||
<a href="https://github.com/Gilgames000" style="width:45px">
|
||||
<img src="https://github.com/Gilgames000.png?size=45" width="45"/>
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/ilgityildirim" style="width:50px">
|
||||
<img src="https://github.com/ilgityildirim.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/ondoki" style="width:65px">
|
||||
<img src="https://github.com/ondoki.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/questrail" style="width:50px">
|
||||
<img src="https://github.com/questrail.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/DonTomato" style="width:45px">
|
||||
<img src="https://github.com/DonTomato.png?size=45" width="45"/>
|
||||
</a>
|
||||
|
||||
<span id="nav-5"></span>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p align="center" style="text-align: center">
|
||||
<img src="logo_cropped.png" width="40%"><br/>
|
||||
<img src="logo.png" width="40%"><br/>
|
||||
</p>
|
||||
<p align="center">
|
||||
使用 Go 和 Web 技术构建桌面应用程序。<br/><br/>
|
||||
@@ -73,47 +73,74 @@
|
||||
|
||||
这个项目由以下这些人或者公司支持:
|
||||
|
||||
|
||||
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
|
||||
<img src="sponsors/bronze%20sponsor.png" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/snider" style="width:100px;">
|
||||
<img src="https://github.com/snider.png?size=100" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/codydbentley" style="width:100px">
|
||||
<img src="https://github.com/codydbentley.png?size=100" width="100"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://github.com/matryer" style="width:100px">
|
||||
<img src="https://github.com/matryer.png" width="100"/>
|
||||
</a>
|
||||
<a href="https://www.jetbrains.com?from=Wails" style="width:100px">
|
||||
<img src="jetbrains-grayscale.png" width="100"/>
|
||||
<img src="/img/jetbrains-grayscale.png" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/tc-hib" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/tc-hib.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/tc-hib" style="width:55px">
|
||||
<img src="https://github.com/tc-hib.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/picatz" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/picatz.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/picatz" style="width:50px">
|
||||
<img src="https://github.com/picatz.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/tylertravisty" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/tylertravisty.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/tylertravisty" style="width:50px">
|
||||
<img src="https://github.com/tylertravisty.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/akhudek" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/akhudek.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/akhudek" style="width:50px">
|
||||
<img src="https://github.com/akhudek.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/trea" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/trea.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/trea" style="width:50px">
|
||||
<img src="https://github.com/trea.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/LanguageAgnostic" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/LanguageAgnostic.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/LanguageAgnostic" style="width:55px">
|
||||
<img src="https://github.com/LanguageAgnostic.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/snider" style="width:60px;border-radius: 50%">
|
||||
<img src="https://github.com/snider.png?size=60" width="60" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/fcjr" style="width:55px">
|
||||
<img src="https://github.com/fcjr.png?size=55" width="55"/>
|
||||
</a>
|
||||
<a href="https://github.com/fcjr" style="width:55px;border-radius: 50%">
|
||||
<img src="https://github.com/fcjr.png?size=55" width="55" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/nickarellano" style="width:60px">
|
||||
<img src="https://github.com/nickarellano.png?size=60" width="60"/>
|
||||
</a>
|
||||
<a href="https://github.com/nickarellano" style="width:60px;border-radius: 50%">
|
||||
<img src="https://github.com/nickarellano.png?size=60" width="60" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/bglw" style="width:65px">
|
||||
<img src="https://github.com/bglw.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/bglw" style="width:65px;border-radius: 50%">
|
||||
<img src="https://github.com/bglw.png?size=65" width="65" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/jugglingjsons" style="width:50px">
|
||||
<img src="https://github.com/jugglingjsons.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/jugglingjsons" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/jugglingjsons.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/marcus-crane" style="width:65px">
|
||||
<img src="https://github.com/marcus-crane.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/marcus-crane" style="width:50px;border-radius: 50%">
|
||||
<img src="https://github.com/marcus-crane.png?size=50" width="50" style="border-radius: 50%"/>
|
||||
<a href="https://github.com/bbergshaven" style="width:45px">
|
||||
<img src="https://github.com/bbergshaven.png?size=45" width="45"/>
|
||||
</a>
|
||||
<a href="https://github.com/Gilgames000" style="width:45px">
|
||||
<img src="https://github.com/Gilgames000.png?size=45" width="45"/>
|
||||
</a>
|
||||
<a href="https://github.com/ilgityildirim" style="width:50px">
|
||||
<img src="https://github.com/ilgityildirim.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/ondoki" style="width:65px">
|
||||
<img src="https://github.com/ondoki.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/questrail" style="width:50px">
|
||||
<img src="https://github.com/questrail.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/DonTomato" style="width:45px">
|
||||
<img src="https://github.com/DonTomato.png?size=45" width="45"/>
|
||||
</a>
|
||||
|
||||
<span id="nav-5"></span>
|
||||
|
||||
21
cmd/linux.go
21
cmd/linux.go
@@ -71,6 +71,11 @@ const (
|
||||
Crux
|
||||
// RHEL distribution
|
||||
RHEL
|
||||
// NixOS distribution
|
||||
NixOS
|
||||
// Artix linux distribution
|
||||
ArtixLinux
|
||||
|
||||
)
|
||||
|
||||
// DistroInfo contains all the information relating to a linux distribution
|
||||
@@ -183,6 +188,10 @@ func parseOsRelease(osRelease string) *DistroInfo {
|
||||
result.Distribution = EndeavourOS
|
||||
case "crux":
|
||||
result.Distribution = Crux
|
||||
case "nixos":
|
||||
result.Distribution = NixOS
|
||||
case "artix":
|
||||
result.Distribution = ArtixLinux
|
||||
default:
|
||||
result.Distribution = Unknown
|
||||
}
|
||||
@@ -274,6 +283,18 @@ func PrtGetInstalled(packageName string) (bool, error) {
|
||||
return exitCode == 0, nil
|
||||
}
|
||||
|
||||
// NixEnvInstalled uses nix-env to see if a package is installed
|
||||
func NixEnvInstalled(packageName string) (bool, error) {
|
||||
program := NewProgramHelper()
|
||||
nixEnv := program.FindProgram("nix-env")
|
||||
if nixEnv == nil {
|
||||
return false, fmt.Errorf("cannot check dependencies: nix-env not found")
|
||||
}
|
||||
packageName = strings.ReplaceAll(packageName, "+", `\+`)
|
||||
_, _, exitCode, _ := nixEnv.Run("-q", packageName)
|
||||
return exitCode == 0, nil
|
||||
}
|
||||
|
||||
// RequestSupportForDistribution promts the user to submit a request to support their
|
||||
// currently unsupported distribution
|
||||
func RequestSupportForDistribution(distroInfo *DistroInfo) error {
|
||||
|
||||
@@ -213,6 +213,15 @@ distributions:
|
||||
gccversioncommand: *gccdumpversion
|
||||
programs: *archdefaultprograms
|
||||
libraries: *archdefaultlibraries
|
||||
artix:
|
||||
id: artix
|
||||
releases:
|
||||
default:
|
||||
version: default
|
||||
name: Artix Linux
|
||||
gccversioncommand: *gccdumpversion
|
||||
programs: *archdefaultprograms
|
||||
libraries: *archdefaultlibraries
|
||||
ctlos:
|
||||
id: ctlos
|
||||
releases:
|
||||
@@ -345,3 +354,22 @@ distributions:
|
||||
help: Please install with `sudo prt-get depinst gtk3` and try again
|
||||
- name: webkitgtk
|
||||
help: Please install with `sudo prt-get depinst webkitgtk` and try again
|
||||
nixos:
|
||||
id: nixos
|
||||
releases:
|
||||
default:
|
||||
version: default
|
||||
name: NixOS
|
||||
gccversioncommand: *gccdumpversion
|
||||
programs:
|
||||
- name: gcc
|
||||
help: Please install with `nix-env -iA nixos.gcc`
|
||||
- name: pkg-config
|
||||
help: Please install with `nix-env -iA nixos.pkg-config`
|
||||
- name: npm
|
||||
help: Please install with `nix-env -iA nixos.nodejs`
|
||||
libraries:
|
||||
- name: gtk+3
|
||||
help: Please install with `nix-env -iA nixos.gtk3`
|
||||
- name: webkitgtk
|
||||
help: Please install with `nix-env -iA nixos.nodePackages.webkitgtk`
|
||||
|
||||
@@ -281,7 +281,7 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
||||
switch distroInfo.Distribution {
|
||||
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian, PopOS:
|
||||
libraryChecker = DpkgInstalled
|
||||
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM, EndeavourOS:
|
||||
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM, EndeavourOS, ArtixLinux:
|
||||
libraryChecker = PacmanInstalled
|
||||
case CentOS, Fedora, Tumbleweed, Leap, RHEL:
|
||||
libraryChecker = RpmInstalled
|
||||
@@ -293,6 +293,8 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
||||
libraryChecker = EOpkgInstalled
|
||||
case Crux:
|
||||
libraryChecker = PrtGetInstalled
|
||||
case NixOS:
|
||||
libraryChecker = NixEnvInstalled
|
||||
default:
|
||||
return false, RequestSupportForDistribution(distroInfo)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
|
||||
// Version - Wails version
|
||||
const Version = "v1.16.5"
|
||||
const Version = "v1.16.7"
|
||||
|
||||
@@ -15,6 +15,7 @@ func init() {
|
||||
projectOptions := projectHelper.NewProjectOptions()
|
||||
commandDescription := `Generates a new Wails project using the given flags.
|
||||
Any flags that are required and not given will be prompted for.`
|
||||
build := false
|
||||
|
||||
initCommand := app.Command("init", "Initialises a new Wails project").
|
||||
LongDescription(commandDescription).
|
||||
@@ -23,7 +24,8 @@ Any flags that are required and not given will be prompted for.`
|
||||
StringFlag("template", "Template name", &projectOptions.Template).
|
||||
StringFlag("name", "Project name", &projectOptions.Name).
|
||||
StringFlag("description", "Project description", &projectOptions.Description).
|
||||
StringFlag("output", "Output binary name", &projectOptions.BinaryName)
|
||||
StringFlag("output", "Output binary name", &projectOptions.BinaryName).
|
||||
BoolFlag("build", "Build project after generating", &build)
|
||||
|
||||
initCommand.Action(func() error {
|
||||
|
||||
@@ -64,6 +66,10 @@ Any flags that are required and not given will be prompted for.`
|
||||
return err
|
||||
}
|
||||
genSpinner.Success()
|
||||
if !build {
|
||||
logger.Yellow("Project '%s' initialised. Run `wails build` to build it.", projectOptions.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build the project
|
||||
cwd, _ := os.Getwd()
|
||||
|
||||
5
go.mod
5
go.mod
@@ -16,12 +16,13 @@ require (
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
|
||||
github.com/pkg/errors v0.8.1 // indirect
|
||||
github.com/sirupsen/logrus v1.4.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/objx v0.1.1 // indirect
|
||||
github.com/stretchr/testify v1.3.0 // indirect
|
||||
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba
|
||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359
|
||||
golang.org/x/text v0.3.0
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4
|
||||
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22
|
||||
|
||||
5
go.sum
5
go.sum
@@ -54,6 +54,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@@ -76,9 +78,12 @@ golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc=
|
||||
|
||||
@@ -54,7 +54,7 @@ extern "C"
|
||||
int ready;
|
||||
int js_busy;
|
||||
int should_exit;
|
||||
|
||||
|
||||
int min_width;
|
||||
int min_height;
|
||||
int max_width;
|
||||
@@ -179,7 +179,7 @@ struct webview_priv
|
||||
WEBVIEW_API int webview_inject_css(struct webview *w, const char *css);
|
||||
WEBVIEW_API void webview_set_title(struct webview *w, const char *title);
|
||||
WEBVIEW_API void webview_focus(struct webview *w);
|
||||
WEBVIEW_API void webview_minsize(struct webview *w, int width, int height);
|
||||
WEBVIEW_API void webview_minsize(struct webview *w, int width, int height);
|
||||
WEBVIEW_API void webview_maxsize(struct webview *w, int width, int height);
|
||||
WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen);
|
||||
WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g,
|
||||
@@ -342,12 +342,12 @@ struct webview_priv
|
||||
w->priv.should_exit = 0;
|
||||
w->priv.queue = g_async_queue_new();
|
||||
w->priv.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
|
||||
|
||||
w->priv.min_width = -1;
|
||||
w->priv.min_height = -1;
|
||||
w->priv.max_width = -1;
|
||||
w->priv.max_height = -1;
|
||||
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(w->priv.window), w->title);
|
||||
|
||||
if (w->resizable)
|
||||
@@ -421,13 +421,13 @@ struct webview_priv
|
||||
}
|
||||
|
||||
WEBVIEW_API void webview_minsize(struct webview *w, int width, int height) {
|
||||
|
||||
|
||||
w->priv.min_width = width;
|
||||
w->priv.min_height = height;
|
||||
|
||||
GdkGeometry hints;
|
||||
GdkWindowHints usedHints = (GdkWindowHints) GDK_HINT_MIN_SIZE;
|
||||
|
||||
|
||||
hints.min_width = w->priv.min_width;
|
||||
hints.min_height = w->priv.min_height;
|
||||
if (w->priv.max_width != -1) {
|
||||
@@ -435,18 +435,18 @@ struct webview_priv
|
||||
hints.max_height = w->priv.max_height;
|
||||
usedHints = (GdkWindowHints)(GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
|
||||
}
|
||||
|
||||
|
||||
gtk_window_set_geometry_hints(GTK_WINDOW(w->priv.window), w->priv.window, &hints, usedHints);
|
||||
}
|
||||
|
||||
WEBVIEW_API void webview_maxsize(struct webview *w, int width, int height) {
|
||||
|
||||
|
||||
w->priv.max_width = width;
|
||||
w->priv.max_height = height;
|
||||
|
||||
|
||||
GdkGeometry hints;
|
||||
GdkWindowHints usedHints = (GdkWindowHints) GDK_HINT_MAX_SIZE;
|
||||
|
||||
|
||||
if (w->priv.min_width != -1) {
|
||||
hints.min_width = w->priv.min_width;
|
||||
hints.min_height = w->priv.min_height;
|
||||
@@ -454,7 +454,7 @@ struct webview_priv
|
||||
}
|
||||
hints.max_width = w->priv.max_width;
|
||||
hints.max_height = w->priv.max_height;
|
||||
|
||||
|
||||
gtk_window_set_geometry_hints(GTK_WINDOW(w->priv.window), w->priv.window, &hints, usedHints);
|
||||
}
|
||||
|
||||
@@ -514,7 +514,6 @@ struct webview_priv
|
||||
}
|
||||
gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), FALSE);
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dlg), FALSE);
|
||||
gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dlg), TRUE);
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dlg), TRUE);
|
||||
gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dlg), TRUE);
|
||||
gint response = gtk_dialog_run(GTK_DIALOG(dlg));
|
||||
@@ -1398,12 +1397,12 @@ struct webview_priv
|
||||
case WM_GETMINMAXINFO:
|
||||
{
|
||||
if (w != NULL) {
|
||||
// get pixel density
|
||||
// get pixel density
|
||||
HDC hDC = GetDC(NULL);
|
||||
double DPIScaleX = GetDeviceCaps(hDC, 88)/96.0;
|
||||
double DPIScaleY = GetDeviceCaps(hDC, 90)/96.0;
|
||||
ReleaseDC(NULL, hDC);
|
||||
|
||||
|
||||
RECT rcClient, rcWind;
|
||||
POINT ptDiff;
|
||||
GetClientRect(hwnd, &rcClient);
|
||||
@@ -1413,7 +1412,7 @@ struct webview_priv
|
||||
int heightExtra = (rcWind.bottom - rcWind.top) - rcClient.bottom;
|
||||
|
||||
LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
|
||||
|
||||
|
||||
if (w->priv.min_width != -1) {
|
||||
lpMMI->ptMinTrackSize.x = w->priv.min_width * DPIScaleX + widthExtra;
|
||||
lpMMI->ptMinTrackSize.y = w->priv.min_height * DPIScaleY + heightExtra;
|
||||
@@ -1423,7 +1422,7 @@ struct webview_priv
|
||||
lpMMI->ptMaxTrackSize.y = w->priv.max_height * DPIScaleY + heightExtra;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
case WM_DESTROY:
|
||||
@@ -2328,14 +2327,14 @@ struct webview_priv
|
||||
{
|
||||
[w->priv.window makeKeyWindow];
|
||||
}
|
||||
|
||||
|
||||
WEBVIEW_API void webview_minsize(struct webview *w, int width, int height) {
|
||||
NSSize size;
|
||||
size.width = width;
|
||||
size.height = height;
|
||||
[w->priv.window setMinSize:size];
|
||||
}
|
||||
|
||||
|
||||
WEBVIEW_API void webview_maxsize(struct webview *w, int width, int height) {
|
||||
NSSize size;
|
||||
size.width = width;
|
||||
@@ -2346,7 +2345,7 @@ struct webview_priv
|
||||
[button performSelectorOnMainThread:@selector(setEnabled:) withObject:NO
|
||||
waitUntilDone:NO];
|
||||
}
|
||||
|
||||
|
||||
WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen)
|
||||
{
|
||||
int b = ((([w->priv.window styleMask] & NSWindowStyleMaskFullScreen) ==
|
||||
@@ -2503,4 +2502,4 @@ struct webview_priv
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WEBVIEW_H */
|
||||
#endif /* WEBVIEW_H */
|
||||
|
||||
@@ -117,12 +117,20 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
}
|
||||
|
||||
// Tags
|
||||
experimental := false
|
||||
userTags := []string{}
|
||||
for _, tag := range strings.Split(tags, " ") {
|
||||
thisTag := strings.TrimSpace(tag)
|
||||
if thisTag != "" {
|
||||
userTags = append(userTags, thisTag)
|
||||
}
|
||||
if thisTag == "exp" {
|
||||
experimental = true
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" && !experimental {
|
||||
return fmt.Errorf("MacOS version coming soon!")
|
||||
}
|
||||
|
||||
// Webview2 installer strategy (download by default)
|
||||
|
||||
@@ -3,8 +3,6 @@ package dev
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal"
|
||||
"github.com/wailsapp/wails/v2/internal/gomod"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -18,6 +16,9 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
|
||||
@@ -98,6 +99,22 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
logger := clilogger.New(w)
|
||||
app.PrintBanner()
|
||||
|
||||
experimental := false
|
||||
userTags := []string{}
|
||||
for _, tag := range strings.Split(flags.tags, " ") {
|
||||
thisTag := strings.TrimSpace(tag)
|
||||
if thisTag != "" {
|
||||
userTags = append(userTags, thisTag)
|
||||
}
|
||||
if thisTag == "exp" {
|
||||
experimental = true
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" && !experimental {
|
||||
return fmt.Errorf("MacOS version coming soon!")
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -255,11 +272,11 @@ func defaultDevFlags() devFlags {
|
||||
|
||||
// generateBuildOptions creates a build.Options using the flags
|
||||
func generateBuildOptions(flags devFlags) *build.Options {
|
||||
return &build.Options{
|
||||
result := &build.Options{
|
||||
OutputType: "dev",
|
||||
Mode: build.Dev,
|
||||
Arch: runtime.GOARCH,
|
||||
Pack: true,
|
||||
Pack: false,
|
||||
Platform: runtime.GOOS,
|
||||
LDFlags: flags.ldflags,
|
||||
Compiler: flags.compilerCommand,
|
||||
@@ -268,6 +285,11 @@ func generateBuildOptions(flags devFlags) *build.Options {
|
||||
Verbosity: flags.verbosity,
|
||||
WailsJSDir: flags.wailsjsdir,
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
result.Pack = false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// loadAndMergeProjectConfig reconciles flags passed to the CLI with project config settings and updates
|
||||
@@ -367,13 +389,15 @@ func runFrontendDevCommand(cwd string, devCommand string, wg *sync.WaitGroup) fu
|
||||
if runtime.GOOS == "windows" {
|
||||
// Credit: https://stackoverflow.com/a/44551450
|
||||
// For whatever reason, killing an npm script on windows just doesn't exit properly with cancel
|
||||
kill := exec.Command("TASKKILL", "/T", "/F", "/PID", strconv.Itoa(cmd.Process.Pid))
|
||||
kill.Stderr = os.Stderr
|
||||
kill.Stdout = os.Stdout
|
||||
err := kill.Run()
|
||||
if err != nil {
|
||||
if err.Error() != "exit status 1" {
|
||||
LogRed("Error from '%s': %s", devCommand, err.Error())
|
||||
if cmd != nil && cmd.Process != nil {
|
||||
kill := exec.Command("TASKKILL", "/T", "/F", "/PID", strconv.Itoa(cmd.Process.Pid))
|
||||
kill.Stderr = os.Stderr
|
||||
kill.Stdout = os.Stdout
|
||||
err := kill.Run()
|
||||
if err != nil {
|
||||
if err.Error() != "exit status 1" {
|
||||
LogRed("Error from '%s': %s", devCommand, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -22,7 +22,7 @@ func (b *App) startup(ctx context.Context) {
|
||||
}
|
||||
|
||||
// domReady is called after the front-end dom has been loaded
|
||||
func (b App) domReady(ctx context.Context) {
|
||||
func (b *App) domReady(ctx context.Context) {
|
||||
// Add your action here
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
|
||||
@@ -22,7 +22,7 @@ func (b *App) startup(ctx context.Context) {
|
||||
}
|
||||
|
||||
// domReady is called after the front-end dom has been loaded
|
||||
func (b App) domReady(ctx context.Context) {
|
||||
func (b *App) domReady(ctx context.Context) {
|
||||
// Add your action here
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@ body {
|
||||
margin: 0;
|
||||
color: white;
|
||||
font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package internal
|
||||
|
||||
var Version = "v2.0.0-beta.12"
|
||||
var Version = "v2.0.0-beta.15"
|
||||
|
||||
@@ -21,7 +21,7 @@ require (
|
||||
github.com/leaanthony/debme v1.2.1
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211007095229-b1759d2e4ec7
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211022194343-1e4c8d4226f3
|
||||
github.com/leaanthony/gosod v1.0.3
|
||||
github.com/leaanthony/idgen v1.0.0
|
||||
github.com/leaanthony/slicer v1.5.0
|
||||
@@ -43,7 +43,7 @@ require (
|
||||
github.com/ztrue/tracerr v0.3.0
|
||||
golang.org/x/mod v0.4.1
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985
|
||||
golang.org/x/tools v0.1.0
|
||||
nhooyr.io/websocket v1.8.6
|
||||
)
|
||||
|
||||
10
v2/go.sum
10
v2/go.sum
@@ -115,10 +115,8 @@ github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQ
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd h1:6m4zZ/esiByaDbzgdvDxjsOaIDgtuG1q2cyhjAi6uAg=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211007095229-b1759d2e4ec7 h1:qw9f/UqPp2GQ318n8G0Ikawe8GRkdPpUNJMuYeeafGA=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211007095229-b1759d2e4ec7/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211022194343-1e4c8d4226f3 h1:qhgrg3MhFRAIvtaqoqI+SrT+0wDYpxDMp9e3cvcxMpI=
|
||||
github.com/leaanthony/go-webview2 v0.0.0-20211022194343-1e4c8d4226f3/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM=
|
||||
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
|
||||
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
|
||||
github.com/leaanthony/idgen v1.0.0 h1:IZreR+JGEzFV4yeVuBZA25gM0keUoFy+RDUldncQ+Jw=
|
||||
@@ -259,8 +257,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985 h1:LOlKVhfDyahgmqa97awczplwkjzNaELFg3zRIJ13RYo=
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
@@ -1252,7 +1252,7 @@ void createDelegate(struct Application *app) {
|
||||
|
||||
app->delegate = delegate;
|
||||
|
||||
msg_id(app->application, s("setDelegate:"), delegate);
|
||||
msg_id(app->application, s("setDelegate:"), delegate);
|
||||
}
|
||||
|
||||
bool windowShouldClose(id self, SEL cmd, id sender) {
|
||||
|
||||
@@ -1 +1 @@
|
||||
The version of WebView2 used: 1.0.864.35
|
||||
The version of WebView2 SDK used: 1.0.992.28
|
||||
|
||||
@@ -7,7 +7,7 @@ set sdk_version=%1
|
||||
set native_dir="%~dp0\microsoft.web.webview2.%sdk_version%\build\native"
|
||||
copy "%native_dir%\include\*.h" .. >NUL
|
||||
copy "%native_dir%\x64\WebView2Loader.dll" "..\x64" >NUL
|
||||
@rd /S /Q "microsoft.web.webview2.%sdk_version%"
|
||||
@REM @rd /S /Q "microsoft.web.webview2.%sdk_version%"
|
||||
del /s version.txt >nul 2>&1
|
||||
echo The version of WebView2 SDK used: %sdk_version% > sdkversion.txt
|
||||
echo SDK updated to %sdk_version%
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/leaanthony/webview2runtime"
|
||||
)
|
||||
|
||||
const MinimumRuntimeVersion string = "91.0.864.48"
|
||||
const MinimumRuntimeVersion string = "91.0.992.28"
|
||||
|
||||
type installationStatus int
|
||||
|
||||
|
||||
20
v2/internal/frontend/desktop/darwin/AppDelegate.h
Normal file
20
v2/internal/frontend/desktop/darwin/AppDelegate.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// AppDelegate.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#ifndef AppDelegate_h
|
||||
#define AppDelegate_h
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface AppDelegate : NSResponder <NSTouchBarProvider>
|
||||
|
||||
@property bool alwaysOnTop;
|
||||
@property (retain) NSWindow* mainWindow;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* AppDelegate_h */
|
||||
53
v2/internal/frontend/desktop/darwin/AppDelegate.m
Normal file
53
v2/internal/frontend/desktop/darwin/AppDelegate.m
Normal file
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// AppDelegate.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
|
||||
return NO;
|
||||
}
|
||||
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification {
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
[self.mainWindow makeKeyAndOrderFront:self];
|
||||
if (self.alwaysOnTop) {
|
||||
[self.mainWindow setLevel:NSStatusWindowLevel];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
//
|
||||
//- (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;
|
||||
|
||||
@end
|
||||
44
v2/internal/frontend/desktop/darwin/Application.h
Normal file
44
v2/internal/frontend/desktop/darwin/Application.h
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Application.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#ifndef Application_h
|
||||
#define Application_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "WailsContext.h"
|
||||
|
||||
WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent, int debug);
|
||||
void Run(void*);
|
||||
|
||||
void SetTitle(void* ctx, const char *title);
|
||||
void Center(void* ctx);
|
||||
void SetSize(void* ctx, int width, int height);
|
||||
void SetMinSize(void* ctx, int width, int height);
|
||||
void SetMaxSize(void* ctx, int width, int height);
|
||||
void SetPosition(void* ctx, int x, int y);
|
||||
void Fullscreen(void* ctx);
|
||||
void UnFullscreen(void* ctx);
|
||||
void Minimise(void* ctx);
|
||||
void UnMinimise(void* ctx);
|
||||
void Maximise(void* ctx);
|
||||
void UnMaximise(void* ctx);
|
||||
void Hide(void* ctx);
|
||||
void Show(void* ctx);
|
||||
void SetRGBA(void* ctx, int r, int g, int b, int a);
|
||||
void ExecJS(void* ctx, const char*);
|
||||
void Quit(void*);
|
||||
|
||||
const char* GetSize(void *ctx);
|
||||
const char* GetPos(void *ctx);
|
||||
|
||||
void ProcessURLResponse(void *inctx, const char *url, const char *contentType, const char *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);
|
||||
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);
|
||||
#endif /* Application_h */
|
||||
214
v2/internal/frontend/desktop/darwin/Application.m
Normal file
214
v2/internal/frontend/desktop/darwin/Application.m
Normal file
@@ -0,0 +1,214 @@
|
||||
//
|
||||
// Application.m
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "WailsContext.h"
|
||||
#import "Application.h"
|
||||
#import "AppDelegate.h"
|
||||
|
||||
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) {
|
||||
|
||||
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 Center];
|
||||
|
||||
result.alwaysOnTop = alwaysOnTop;
|
||||
result.hideOnClose = hideWindowOnClose;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ProcessURLResponse(void *inctx, const char *url, const char *contentType, const char* data, int datalength) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSString *nsurl = [[NSString alloc] initWithUTF8String:url];
|
||||
NSString *nsContentType = [[NSString alloc] initWithUTF8String:contentType];
|
||||
NSData *nsdata = [NSData dataWithBytes:data length:datalength];
|
||||
|
||||
[ctx processURLResponse:nsurl :nsContentType :nsdata];
|
||||
}
|
||||
|
||||
void ExecJS(void* inctx, const char *script) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx ExecJS:script];
|
||||
);
|
||||
}
|
||||
|
||||
void SetTitle(void* inctx, const char *title) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetTitle:title];
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void SetRGBA(void *inctx, int r, int g, int b, int a) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetRGBA:r :g :b :a];
|
||||
);
|
||||
}
|
||||
|
||||
void SetSize(void* inctx, int width, int height) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetSize:width :height];
|
||||
);
|
||||
}
|
||||
|
||||
void SetMinSize(void* inctx, int width, int height) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetMinSize:width :height];
|
||||
);
|
||||
}
|
||||
|
||||
void SetMaxSize(void* inctx, int width, int height) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetMaxSize:width :height];
|
||||
);
|
||||
}
|
||||
|
||||
void SetPosition(void* inctx, int x, int y) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SetSize:x :y];
|
||||
);
|
||||
}
|
||||
|
||||
void Center(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Center];
|
||||
);
|
||||
}
|
||||
|
||||
void Fullscreen(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Fullscreen];
|
||||
);
|
||||
}
|
||||
|
||||
void UnFullscreen(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx UnFullscreen];
|
||||
);
|
||||
}
|
||||
|
||||
void Minimise(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Minimise];
|
||||
);
|
||||
}
|
||||
|
||||
void UnMinimise(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx UnMinimise];
|
||||
);
|
||||
}
|
||||
|
||||
void Maximise(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Maximise];
|
||||
);
|
||||
}
|
||||
|
||||
const char* GetSize(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSRect frame = [ctx.mainWindow frame];
|
||||
NSString *result = [NSString stringWithFormat:@"%d,%d", (int)frame.size.width, (int)frame.size.height];
|
||||
return [result UTF8String];
|
||||
}
|
||||
|
||||
const char* GetPos(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
NSScreen* screen = [ctx getCurrentScreen];
|
||||
NSRect windowFrame = [ctx.mainWindow frame];
|
||||
NSRect screenFrame = [screen visibleFrame];
|
||||
int x = windowFrame.origin.x - screenFrame.origin.x;
|
||||
int y = windowFrame.origin.y - screenFrame.origin.y;
|
||||
y = screenFrame.size.height - y - windowFrame.size.height;
|
||||
NSString *result = [NSString stringWithFormat:@"%d,%d",x,y];
|
||||
return [result UTF8String];
|
||||
|
||||
}
|
||||
|
||||
void UnMaximise(void* inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx UnMaximise];
|
||||
);
|
||||
}
|
||||
|
||||
void Quit(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
[NSApp stop:ctx];
|
||||
}
|
||||
|
||||
void Hide(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Hide];
|
||||
);
|
||||
}
|
||||
|
||||
void Show(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx Show];
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx MessageDialog:dialogType :title :message :button1 :button2 :button3 :button4 :defaultButton :cancelButton];
|
||||
)
|
||||
}
|
||||
|
||||
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;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx OpenFileDialog:title :defaultFilename :defaultDirectory :allowDirectories :allowFiles :canCreateDirectories :treatPackagesAsDirectories :resolveAliases :showHiddenFiles :allowMultipleSelection :filters];
|
||||
)
|
||||
}
|
||||
|
||||
void SaveFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int canCreateDirectories, int treatPackagesAsDirectories, int showHiddenFiles, const char* filters) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
ON_MAIN_THREAD(
|
||||
[ctx SaveFileDialog:title :defaultFilename :defaultDirectory :canCreateDirectories :treatPackagesAsDirectories :showHiddenFiles :filters];
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Run(void *inctx) {
|
||||
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||
[NSApplication sharedApplication];
|
||||
AppDelegate* delegate = [AppDelegate new];
|
||||
[NSApp setDelegate:(id)delegate];
|
||||
ctx.appdelegate = delegate;
|
||||
delegate.mainWindow = ctx.mainWindow;
|
||||
delegate.alwaysOnTop = ctx.alwaysOnTop;
|
||||
|
||||
[ctx loadRequest:@"wails://wails/"];
|
||||
|
||||
[NSApp run];
|
||||
[ctx release];
|
||||
NSLog(@"Here");
|
||||
}
|
||||
18
v2/internal/frontend/desktop/darwin/WailsAlert.h
Normal file
18
v2/internal/frontend/desktop/darwin/WailsAlert.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// WailsAlert.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 20/10/21.
|
||||
//
|
||||
|
||||
#ifndef WailsAlert_h
|
||||
#define WailsAlert_h
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface WailsAlert : NSAlert
|
||||
- (void)addButton:(const char*)text :(const char*)defaultButton :(const char*)cancelButton;
|
||||
@end
|
||||
|
||||
|
||||
#endif /* WailsAlert_h */
|
||||
30
v2/internal/frontend/desktop/darwin/WailsAlert.m
Normal file
30
v2/internal/frontend/desktop/darwin/WailsAlert.m
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// WailsAlert.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 20/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "WailsAlert.h"
|
||||
|
||||
@implementation WailsAlert
|
||||
|
||||
- (void)addButton:(const char*)text :(const char*)defaultButton :(const char*)cancelButton {
|
||||
if( text == nil ) {
|
||||
return;
|
||||
}
|
||||
NSButton *button = [self addButtonWithTitle:[NSString stringWithUTF8String:text]];
|
||||
if( defaultButton != nil && strcmp(text, defaultButton) == 0) {
|
||||
[button setKeyEquivalent:@"\r"];
|
||||
} else if( cancelButton != nil && strcmp(text, cancelButton) == 0) {
|
||||
[button setKeyEquivalent:@"\033"];
|
||||
} else {
|
||||
[button setKeyEquivalent:@""];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
74
v2/internal/frontend/desktop/darwin/WailsContext.h
Normal file
74
v2/internal/frontend/desktop/darwin/WailsContext.h
Normal file
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// WailsContext.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#ifndef WailsContext_h
|
||||
#define WailsContext_h
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
#define ON_MAIN_THREAD(str) dispatch_async(dispatch_get_main_queue(), ^{ str; });
|
||||
|
||||
@interface WailsWindow : NSWindow
|
||||
- (BOOL)canBecomeKeyWindow;
|
||||
@end
|
||||
|
||||
@interface WailsContext : NSObject <WKURLSchemeHandler,WKScriptMessageHandler>
|
||||
|
||||
@property (retain) WailsWindow* mainWindow;
|
||||
@property (retain) WKWebView* webview;
|
||||
@property (nonatomic, assign) id appdelegate;
|
||||
|
||||
@property bool hideOnClose;
|
||||
@property bool shuttingDown;
|
||||
|
||||
@property NSSize maxSize;
|
||||
@property NSSize minSize;
|
||||
|
||||
@property (retain) NSEvent* mouseEvent;
|
||||
|
||||
@property bool alwaysOnTop;
|
||||
@property bool 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;
|
||||
- (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) Center;
|
||||
- (void) Fullscreen;
|
||||
- (void) UnFullscreen;
|
||||
- (void) Minimise;
|
||||
- (void) UnMinimise;
|
||||
- (void) Maximise;
|
||||
- (void) UnMaximise;
|
||||
- (void) SetRGBA:(int)r :(int)g :(int)b :(int)a;
|
||||
- (void) HideMouse;
|
||||
- (void) ShowMouse;
|
||||
- (void) Hide;
|
||||
- (void) Show;
|
||||
|
||||
-(void) 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) loadRequest:(NSString*)url;
|
||||
- (void) processURLResponse:(NSString *)url :(NSString *)contentType :(NSData*)data;
|
||||
- (void) ExecJS:(const char*)script;
|
||||
- (NSScreen*) getCurrentScreen;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* WailsContext_h */
|
||||
527
v2/internal/frontend/desktop/darwin/WailsContext.m
Normal file
527
v2/internal/frontend/desktop/darwin/WailsContext.m
Normal file
@@ -0,0 +1,527 @@
|
||||
//
|
||||
// WailsContext.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
#import "WailsContext.h"
|
||||
#import "WailsAlert.h"
|
||||
#import "WindowDelegate.h"
|
||||
#import "message.h"
|
||||
|
||||
@implementation WailsWindow
|
||||
|
||||
- (BOOL)canBecomeKeyWindow
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation WailsContext
|
||||
|
||||
- (void) SetSize:(int)width :(int)height {
|
||||
|
||||
if (self.shuttingDown) return;
|
||||
|
||||
NSRect frame = [self.mainWindow frame];
|
||||
frame.origin.y += frame.size.height - height;
|
||||
frame.size.width = width;
|
||||
frame.size.height = height;
|
||||
[self.mainWindow setFrame:frame display:TRUE animate:FALSE];
|
||||
}
|
||||
|
||||
- (void) SetPosition:(int)x :(int)y {
|
||||
|
||||
if (self.shuttingDown) return;
|
||||
|
||||
NSScreen* screen = [self getCurrentScreen];
|
||||
NSRect windowFrame = [self.mainWindow frame];
|
||||
NSRect screenFrame = [screen 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];
|
||||
}
|
||||
|
||||
- (void) SetMinSize:(int)minWidth :(int)minHeight {
|
||||
|
||||
if (self.shuttingDown) return;
|
||||
|
||||
NSSize size = { minWidth, minHeight };
|
||||
|
||||
self.minSize = size;
|
||||
|
||||
[self.mainWindow setMinSize:size];
|
||||
|
||||
[self adjustWindowSize];
|
||||
}
|
||||
|
||||
|
||||
- (void) SetMaxSize:(int)maxWidth :(int)maxHeight {
|
||||
|
||||
if (self.shuttingDown) return;
|
||||
|
||||
NSSize size = { FLT_MAX, FLT_MAX };
|
||||
|
||||
size.width = maxWidth > 0 ? maxWidth : FLT_MAX;
|
||||
size.height = maxHeight > 0 ? maxHeight : FLT_MAX;
|
||||
|
||||
self.maxSize = size;
|
||||
|
||||
[self.mainWindow setMinSize:size];
|
||||
|
||||
[self adjustWindowSize];
|
||||
}
|
||||
|
||||
|
||||
- (void) adjustWindowSize {
|
||||
|
||||
if (self.shuttingDown) return;
|
||||
|
||||
NSRect currentFrame = [self.mainWindow frame];
|
||||
|
||||
if ( currentFrame.size.width > self.maxSize.width ) currentFrame.size.width = self.maxSize.width;
|
||||
if ( currentFrame.size.width < self.minSize.width ) currentFrame.size.width = self.minSize.width;
|
||||
if ( currentFrame.size.height > self.maxSize.height ) currentFrame.size.height = self.maxSize.height;
|
||||
if ( currentFrame.size.height < self.minSize.height ) currentFrame.size.height = self.minSize.height;
|
||||
|
||||
[self.mainWindow setFrame:currentFrame display:TRUE animate:FALSE];
|
||||
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
[super dealloc];
|
||||
[self.appdelegate release];
|
||||
[self.mainWindow release];
|
||||
[self.mouseEvent release];
|
||||
[self.userContentController release];
|
||||
[self.urlRequests release];
|
||||
}
|
||||
|
||||
- (NSScreen*) getCurrentScreen {
|
||||
NSScreen* screen = [self.mainWindow screen];
|
||||
if( screen == NULL ) {
|
||||
screen = [NSScreen mainScreen];
|
||||
}
|
||||
return screen;
|
||||
}
|
||||
|
||||
- (void) SetTitle:(const char *)title {
|
||||
NSString *_title = [NSString stringWithUTF8String:title];
|
||||
[self.mainWindow setTitle:_title];
|
||||
}
|
||||
|
||||
- (void) Center {
|
||||
[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 {
|
||||
|
||||
self.urlRequests = [NSMutableDictionary new];
|
||||
|
||||
NSWindowStyleMask styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
|
||||
|
||||
if (frameless) {
|
||||
styleMask = NSWindowStyleMaskBorderless;
|
||||
} else {
|
||||
if (resizable) {
|
||||
styleMask |= NSWindowStyleMaskResizable;
|
||||
}
|
||||
}
|
||||
if (fullscreen) {
|
||||
styleMask |= NSWindowStyleMaskFullScreen;
|
||||
}
|
||||
|
||||
if( fullSizeContent || frameless || titlebarAppearsTransparent ) {
|
||||
styleMask |= NSWindowStyleMaskFullSizeContentView;
|
||||
}
|
||||
|
||||
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");
|
||||
id toolbar = [[NSToolbar alloc] initWithIdentifier:@"wails.toolbar"];
|
||||
[toolbar autorelease];
|
||||
[toolbar setShowsBaselineSeparator:!hideToolbarSeparator];
|
||||
[self.mainWindow setToolbar:toolbar];
|
||||
}
|
||||
|
||||
[self.mainWindow setTitleVisibility:hideTitle];
|
||||
[self.mainWindow setTitlebarAppearsTransparent:titlebarAppearsTransparent];
|
||||
[self.mainWindow canBecomeKeyWindow];
|
||||
|
||||
id contentView = [self.mainWindow contentView];
|
||||
if (windowIsTranslucent) {
|
||||
NSVisualEffectView *effectView = [NSVisualEffectView alloc];
|
||||
NSRect bounds = [contentView bounds];
|
||||
[effectView initWithFrame:bounds];
|
||||
[effectView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
[effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
|
||||
[effectView setState:NSVisualEffectStateActive];
|
||||
[contentView addSubview:effectView positioned:NSWindowBelow relativeTo:nil];
|
||||
}
|
||||
|
||||
if (appearance != nil) {
|
||||
NSString *name = [NSString stringWithUTF8String:appearance];
|
||||
NSAppearance *nsAppearance = [NSAppearance appearanceNamed:name];
|
||||
[self.mainWindow setAppearance:nsAppearance];
|
||||
}
|
||||
|
||||
// Set up min/max
|
||||
NSSize maxSize = { FLT_MAX, FLT_MAX };
|
||||
self.maxSize = maxSize;
|
||||
NSSize minSize = { 0, 0 };
|
||||
self.minSize = minSize;
|
||||
[self adjustWindowSize];
|
||||
|
||||
WindowDelegate *windowDelegate = [WindowDelegate new];
|
||||
windowDelegate.hideOnClose = hideWindowOnClose;
|
||||
[self.mainWindow setDelegate:windowDelegate];
|
||||
|
||||
// Webview stuff here!
|
||||
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
|
||||
config.suppressesIncrementalRendering = true;
|
||||
[config setURLSchemeHandler:self forURLScheme:@"wails"];
|
||||
|
||||
[config.preferences setValue:[NSNumber numberWithBool:true] forKey:@"developerExtrasEnabled"];
|
||||
|
||||
WKUserContentController* userContentController = [WKUserContentController new];
|
||||
[userContentController addScriptMessageHandler:self name:@"external"];
|
||||
config.userContentController = userContentController;
|
||||
self.userContentController = userContentController;
|
||||
if (self.debug) {
|
||||
[config.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
|
||||
} else {
|
||||
// Disable default context menus
|
||||
WKUserScript *initScript = [WKUserScript new];
|
||||
[initScript initWithSource:@"window.wails.flags.disableWailsDefaultContextMenu = true;"
|
||||
injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
|
||||
forMainFrameOnly:false];
|
||||
[userContentController addUserScript:initScript];
|
||||
|
||||
}
|
||||
|
||||
self.webview = [WKWebView alloc];
|
||||
CGRect init = { 0,0,0,0 };
|
||||
[self.webview initWithFrame:init configuration:config];
|
||||
[contentView addSubview:self.webview];
|
||||
[self.webview setAutoresizingMask: NSViewWidthSizable|NSViewHeightSizable];
|
||||
CGRect contentViewBounds = [contentView bounds];
|
||||
[self.webview setFrame:contentViewBounds];
|
||||
|
||||
if (webviewIsTransparent) {
|
||||
[self.webview setValue:[NSNumber numberWithBool:!webviewIsTransparent] forKey:@"drawsBackground"];
|
||||
}
|
||||
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
[defaults setBool:FALSE forKey:@"NSAutomaticQuoteSubstitutionEnabled"];
|
||||
|
||||
// Mouse monitors
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) {
|
||||
id window = [event window];
|
||||
if (window == self.mainWindow) {
|
||||
self.mouseEvent = event;
|
||||
}
|
||||
return event;
|
||||
}];
|
||||
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseUp handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) {
|
||||
id window = [event window];
|
||||
if (window == self.mainWindow) {
|
||||
self.mouseEvent = nil;
|
||||
[self ShowMouse];
|
||||
}
|
||||
return event;
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (void) loadRequest :(NSString*)url {
|
||||
NSURL *wkUrl = [NSURL URLWithString:url];
|
||||
NSURLRequest *wkRequest = [NSURLRequest requestWithURL:wkUrl];
|
||||
[self.webview loadRequest:wkRequest];
|
||||
}
|
||||
|
||||
- (void) SetRGBA:(int)r :(int)g :(int)b :(int)a {
|
||||
float red = r/255;
|
||||
float green = g/255;
|
||||
float blue = b/255;
|
||||
float alpha = a/255;
|
||||
|
||||
id colour = [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha ];
|
||||
|
||||
[self.mainWindow setBackgroundColor:colour];
|
||||
}
|
||||
|
||||
- (void) HideMouse {
|
||||
[NSCursor hide];
|
||||
}
|
||||
|
||||
- (void) ShowMouse {
|
||||
[NSCursor unhide];
|
||||
}
|
||||
|
||||
- (bool) isFullScreen {
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
// UnFullscreen resets the main window after a fullscreen
|
||||
- (void) UnFullscreen {
|
||||
if( [self isFullScreen] ) {
|
||||
[self.mainWindow toggleFullScreen:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) Minimise {
|
||||
[self.mainWindow miniaturize:nil];
|
||||
}
|
||||
|
||||
- (void) UnMinimise {
|
||||
[self.mainWindow deminiaturize:nil];
|
||||
}
|
||||
|
||||
- (void) Hide {
|
||||
[self.mainWindow orderOut:nil];
|
||||
}
|
||||
|
||||
- (void) Show {
|
||||
[self.mainWindow makeKeyAndOrderFront:nil];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
- (void) Maximise {
|
||||
if (! self.maximised) {
|
||||
[self.mainWindow zoom:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) UnMaximise {
|
||||
if (self.maximised) {
|
||||
[self.mainWindow zoom:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) ExecJS:(const char*)script {
|
||||
NSString *nsscript = [NSString stringWithUTF8String:script];
|
||||
[self.webview evaluateJavaScript:nsscript completionHandler:nil];
|
||||
}
|
||||
|
||||
- (void) processURLResponse:(NSString *)url :(NSString *)contentType :(NSData *)data {
|
||||
id<WKURLSchemeTask> urlSchemeTask = self.urlRequests[url];
|
||||
NSURL *nsurl = [NSURL URLWithString:url];
|
||||
|
||||
NSHTTPURLResponse *response = [NSHTTPURLResponse new];
|
||||
NSMutableDictionary *headerFields = [NSMutableDictionary new];
|
||||
headerFields[@"content-type"] = contentType;
|
||||
[response initWithURL:nsurl statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headerFields];
|
||||
[urlSchemeTask didReceiveResponse:response];
|
||||
[urlSchemeTask didReceiveData:data];
|
||||
[urlSchemeTask didFinish];
|
||||
[self.urlRequests removeObjectForKey:url];
|
||||
}
|
||||
|
||||
- (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
|
||||
// Do something
|
||||
self.urlRequests[urlSchemeTask.request.URL.absoluteString] = urlSchemeTask;
|
||||
processURLRequest(self, [urlSchemeTask.request.URL.absoluteString UTF8String]);
|
||||
}
|
||||
|
||||
- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
|
||||
|
||||
}
|
||||
|
||||
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
|
||||
NSString *m = message.body;
|
||||
|
||||
// Check for drag
|
||||
if ( [m isEqualToString:@"drag"] ) {
|
||||
if( ! [self isFullScreen] ) {
|
||||
if( self.mouseEvent != nil ) {
|
||||
[self HideMouse];
|
||||
ON_MAIN_THREAD(
|
||||
[self.mainWindow performWindowDragWithEvent:self.mouseEvent];
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const char *_m = [m UTF8String];
|
||||
|
||||
processMessage(_m);
|
||||
}
|
||||
|
||||
|
||||
/***** Dialogs ******/
|
||||
-(void) MessageDialog :(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 {
|
||||
|
||||
WailsAlert *alert = [WailsAlert new];
|
||||
|
||||
int style = NSAlertStyleInformational;
|
||||
if (dialogType != nil ) {
|
||||
if( strcmp(dialogType, "warning") == 0 ) {
|
||||
style = NSAlertStyleWarning;
|
||||
}
|
||||
if( strcmp(dialogType, "error") == 0) {
|
||||
style = NSAlertStyleCritical;
|
||||
}
|
||||
}
|
||||
[alert setAlertStyle:style];
|
||||
if( strlen(title) > 0 ) {
|
||||
[alert setMessageText:[NSString stringWithUTF8String:title]];
|
||||
}
|
||||
if( strlen(message) > 0 ) {
|
||||
[alert setInformativeText:[NSString stringWithUTF8String:message]];
|
||||
}
|
||||
|
||||
[alert addButton:button1 :defaultButton :cancelButton];
|
||||
[alert addButton:button2 :defaultButton :cancelButton];
|
||||
[alert addButton:button3 :defaultButton :cancelButton];
|
||||
[alert addButton:button4 :defaultButton :cancelButton];
|
||||
|
||||
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 {
|
||||
|
||||
|
||||
// Create the dialog
|
||||
NSOpenPanel *dialog = [NSOpenPanel openPanel];
|
||||
|
||||
// Valid but appears to do nothing.... :/
|
||||
if( strlen(title) > 0 ) {
|
||||
[dialog setTitle:[NSString stringWithUTF8String: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:@";"];
|
||||
[dialog setAllowedFileTypes:filterList];
|
||||
} else {
|
||||
[dialog setAllowsOtherFileTypes:true];
|
||||
}
|
||||
// Default Filename
|
||||
if( defaultFilename != NULL && strlen(defaultFilename) > 0 ) {
|
||||
[dialog setNameFieldStringValue:[NSString stringWithUTF8String:defaultFilename]];
|
||||
}
|
||||
|
||||
[dialog setAllowsMultipleSelection: allowMultipleSelection];
|
||||
[dialog setShowsHiddenFiles: showHiddenFiles];
|
||||
|
||||
}
|
||||
|
||||
// Default Directory
|
||||
if( defaultDirectory != NULL && strlen(defaultDirectory) > 0 ) {
|
||||
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:defaultDirectory]];
|
||||
[dialog setDirectoryURL:url];
|
||||
}
|
||||
|
||||
|
||||
// Setup Options
|
||||
[dialog setCanChooseFiles: allowFiles];
|
||||
[dialog setCanChooseDirectories: allowDirectories];
|
||||
[dialog setCanCreateDirectories: canCreateDirectories];
|
||||
[dialog setResolvesAliases: resolveAliases];
|
||||
[dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories];
|
||||
|
||||
// Setup callback handler
|
||||
[dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) {
|
||||
NSMutableArray *arr = [NSMutableArray new];
|
||||
for (NSURL *url in [dialog URLs]) {
|
||||
[arr addObject:[url path]];
|
||||
}
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:arr options:0 error:nil];
|
||||
NSString *nsjson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
processOpenFileDialogResponse([nsjson UTF8String]);
|
||||
}];
|
||||
|
||||
|
||||
[dialog runModal];
|
||||
|
||||
}
|
||||
|
||||
|
||||
-(void) SaveFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(const char*)filters; {
|
||||
|
||||
|
||||
// Create the dialog
|
||||
NSSavePanel *dialog = [NSOpenPanel savePanel];
|
||||
|
||||
// Valid but appears to do nothing.... :/
|
||||
if( strlen(title) > 0 ) {
|
||||
[dialog setTitle:[NSString stringWithUTF8String: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:@";"];
|
||||
[dialog setAllowedFileTypes:filterList];
|
||||
} else {
|
||||
[dialog setAllowsOtherFileTypes:true];
|
||||
}
|
||||
// Default Filename
|
||||
if( defaultFilename != NULL && strlen(defaultFilename) > 0 ) {
|
||||
[dialog setNameFieldStringValue:[NSString stringWithUTF8String:defaultFilename]];
|
||||
}
|
||||
|
||||
// Default Directory
|
||||
if( defaultDirectory != NULL && strlen(defaultDirectory) > 0 ) {
|
||||
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:defaultDirectory]];
|
||||
[dialog setDirectoryURL:url];
|
||||
}
|
||||
|
||||
// Setup Options
|
||||
[dialog setCanCreateDirectories: canCreateDirectories];
|
||||
[dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories];
|
||||
[dialog setShowsHiddenFiles: showHiddenFiles];
|
||||
|
||||
// Setup callback handler
|
||||
[dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) {
|
||||
NSURL *url = [dialog URL];
|
||||
processSaveFileDialogResponse([url.path UTF8String]);
|
||||
}];
|
||||
|
||||
[dialog runModal];
|
||||
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
18
v2/internal/frontend/desktop/darwin/WindowDelegate.h
Normal file
18
v2/internal/frontend/desktop/darwin/WindowDelegate.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// WindowDelegate.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#ifndef WindowDelegate_h
|
||||
#define WindowDelegate_h
|
||||
|
||||
@interface WindowDelegate : NSObject <NSWindowDelegate>
|
||||
|
||||
@property bool hideOnClose;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* WindowDelegate_h */
|
||||
23
v2/internal/frontend/desktop/darwin/WindowDelegate.m
Normal file
23
v2/internal/frontend/desktop/darwin/WindowDelegate.m
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// WindowDelegate.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "WindowDelegate.h"
|
||||
#import "message.h"
|
||||
|
||||
@implementation WindowDelegate
|
||||
|
||||
- (BOOL)windowShouldClose:(NSWindow *)sender {
|
||||
[sender orderOut:nil];
|
||||
if( self.hideOnClose == false ) {
|
||||
processMessage("Q");
|
||||
}
|
||||
return !self.hideOnClose;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,4 +1,5 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
|
||||
32
v2/internal/frontend/desktop/darwin/calloc.go
Normal file
32
v2/internal/frontend/desktop/darwin/calloc.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
// Calloc handles alloc/dealloc of C data
|
||||
type Calloc struct {
|
||||
pool []unsafe.Pointer
|
||||
}
|
||||
|
||||
// NewCalloc creates a new allocator
|
||||
func NewCalloc() Calloc {
|
||||
return Calloc{}
|
||||
}
|
||||
|
||||
// String creates a new C string and retains a reference to it
|
||||
func (c Calloc) String(in string) *C.char {
|
||||
result := C.CString(in)
|
||||
c.pool = append(c.pool, unsafe.Pointer(result))
|
||||
return result
|
||||
}
|
||||
|
||||
// Free frees all allocated C memory
|
||||
func (c Calloc) Free() {
|
||||
for _, str := range c.pool {
|
||||
C.free(str)
|
||||
}
|
||||
c.pool = []unsafe.Pointer{}
|
||||
}
|
||||
@@ -1,32 +1,185 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Application.h"
|
||||
#import "WailsContext.h"
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
)
|
||||
|
||||
// Obj-C dialog methods send the response to this channel
|
||||
var messageDialogResponse = make(chan int)
|
||||
var openFileDialogResponse = make(chan string)
|
||||
var saveFileDialogResponse = make(chan string)
|
||||
var dialogLock sync.Mutex
|
||||
|
||||
// OpenDirectoryDialog prompts the user to select a directory
|
||||
func (f *Frontend) OpenDirectoryDialog(options frontend.OpenDialogOptions) (string, error) {
|
||||
return "", nil
|
||||
results, err := f.openDialog(&options, false, false, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var selected string
|
||||
if len(results) > 0 {
|
||||
selected = results[0]
|
||||
}
|
||||
return selected, nil
|
||||
}
|
||||
|
||||
func (f *Frontend) openDialog(options *frontend.OpenDialogOptions, multiple bool, allowfiles bool, allowdirectories bool) ([]string, error) {
|
||||
dialogLock.Lock()
|
||||
defer dialogLock.Unlock()
|
||||
|
||||
c := NewCalloc()
|
||||
defer c.Free()
|
||||
title := c.String(options.Title)
|
||||
defaultFilename := c.String(options.DefaultFilename)
|
||||
defaultDirectory := c.String(options.DefaultDirectory)
|
||||
allowDirectories := bool2Cint(allowdirectories)
|
||||
allowFiles := bool2Cint(allowfiles)
|
||||
canCreateDirectories := bool2Cint(options.CanCreateDirectories)
|
||||
treatPackagesAsDirectories := bool2Cint(options.TreatPackagesAsDirectories)
|
||||
resolveAliases := bool2Cint(options.ResolvesAliases)
|
||||
showHiddenFiles := bool2Cint(options.ShowHiddenFiles)
|
||||
allowMultipleFileSelection := bool2Cint(multiple)
|
||||
|
||||
var filterStrings slicer.StringSlicer
|
||||
if options.Filters != nil {
|
||||
for _, filter := range options.Filters {
|
||||
thesePatterns := strings.Split(filter.Pattern, ";")
|
||||
for _, pattern := range thesePatterns {
|
||||
pattern = strings.TrimSpace(pattern)
|
||||
if pattern != "" {
|
||||
filterStrings.Add(pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
filterStrings.Deduplicate()
|
||||
}
|
||||
filters := filterStrings.Join(";")
|
||||
C.OpenFileDialog(f.mainWindow.context, title, defaultFilename, defaultDirectory, allowDirectories, allowFiles, canCreateDirectories, treatPackagesAsDirectories, resolveAliases, showHiddenFiles, allowMultipleFileSelection, c.String(filters))
|
||||
|
||||
var result = <-openFileDialogResponse
|
||||
|
||||
var parsedResults []string
|
||||
err := json.Unmarshal([]byte(result), &parsedResults)
|
||||
|
||||
return parsedResults, err
|
||||
}
|
||||
|
||||
// OpenFileDialog prompts the user to select a file
|
||||
func (f *Frontend) OpenFileDialog(options frontend.OpenDialogOptions) (string, error) {
|
||||
return "", nil
|
||||
results, err := f.openDialog(&options, false, options.AllowFiles, options.AllowDirectories)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var selected string
|
||||
if len(results) > 0 {
|
||||
selected = results[0]
|
||||
}
|
||||
return selected, nil
|
||||
}
|
||||
|
||||
// OpenMultipleFilesDialog prompts the user to select a file
|
||||
func (f *Frontend) OpenMultipleFilesDialog(dialogOptions frontend.OpenDialogOptions) ([]string, error) {
|
||||
return []string{}, nil
|
||||
func (f *Frontend) OpenMultipleFilesDialog(options frontend.OpenDialogOptions) ([]string, error) {
|
||||
return f.openDialog(&options, true, options.AllowFiles, options.AllowDirectories)
|
||||
}
|
||||
|
||||
// SaveFileDialog prompts the user to select a file
|
||||
func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (string, error) {
|
||||
return "", nil
|
||||
func (f *Frontend) SaveFileDialog(options frontend.SaveDialogOptions) (string, error) {
|
||||
dialogLock.Lock()
|
||||
defer dialogLock.Unlock()
|
||||
|
||||
c := NewCalloc()
|
||||
defer c.Free()
|
||||
title := c.String(options.Title)
|
||||
defaultFilename := c.String(options.DefaultFilename)
|
||||
defaultDirectory := c.String(options.DefaultDirectory)
|
||||
canCreateDirectories := bool2Cint(options.CanCreateDirectories)
|
||||
treatPackagesAsDirectories := bool2Cint(options.TreatPackagesAsDirectories)
|
||||
showHiddenFiles := bool2Cint(options.ShowHiddenFiles)
|
||||
|
||||
var filterStrings slicer.StringSlicer
|
||||
if options.Filters != nil {
|
||||
for _, filter := range options.Filters {
|
||||
thesePatterns := strings.Split(filter.Pattern, ";")
|
||||
for _, pattern := range thesePatterns {
|
||||
pattern = strings.TrimSpace(pattern)
|
||||
if pattern != "" {
|
||||
filterStrings.Add(pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
filterStrings.Deduplicate()
|
||||
}
|
||||
filters := filterStrings.Join(";")
|
||||
C.SaveFileDialog(f.mainWindow.context, title, defaultFilename, defaultDirectory, canCreateDirectories, treatPackagesAsDirectories, showHiddenFiles, c.String(filters))
|
||||
|
||||
var result = <-saveFileDialogResponse
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// MessageDialog show a message dialog to the user
|
||||
func (f *Frontend) MessageDialog(options frontend.MessageDialogOptions) (string, error) {
|
||||
return "", nil
|
||||
dialogLock.Lock()
|
||||
defer dialogLock.Unlock()
|
||||
|
||||
c := NewCalloc()
|
||||
defer c.Free()
|
||||
dialogType := c.String(string(options.Type))
|
||||
title := c.String(options.Title)
|
||||
message := c.String(options.Message)
|
||||
defaultButton := c.String(options.DefaultButton)
|
||||
cancelButton := c.String(options.CancelButton)
|
||||
const MaxButtons = 4
|
||||
var buttons [MaxButtons]*C.char
|
||||
for index, buttonText := range options.Buttons {
|
||||
if index == MaxButtons {
|
||||
return "", fmt.Errorf("max %d buttons supported (%d given)", MaxButtons, len(options.Buttons))
|
||||
}
|
||||
buttons[index] = c.String(buttonText)
|
||||
}
|
||||
|
||||
C.MessageDialog(f.mainWindow.context, dialogType, title, message, buttons[0], buttons[1], buttons[2], buttons[3], defaultButton, cancelButton)
|
||||
|
||||
var result = <-messageDialogResponse
|
||||
|
||||
selectedC := buttons[result]
|
||||
var selected string
|
||||
if selectedC != nil {
|
||||
selected = options.Buttons[result]
|
||||
}
|
||||
return selected, nil
|
||||
}
|
||||
|
||||
//export processMessageDialogResponse
|
||||
func processMessageDialogResponse(selection int) {
|
||||
messageDialogResponse <- selection
|
||||
}
|
||||
|
||||
//export processOpenFileDialogResponse
|
||||
func processOpenFileDialogResponse(cselection *C.char) {
|
||||
selection := C.GoString(cselection)
|
||||
openFileDialogResponse <- selection
|
||||
}
|
||||
|
||||
//export processSaveFileDialogResponse
|
||||
func processSaveFileDialogResponse(cselection *C.char) {
|
||||
selection := C.GoString(cselection)
|
||||
saveFileDialogResponse <- selection
|
||||
}
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Application.h"
|
||||
#import "WailsContext.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
"log"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
@@ -16,6 +29,14 @@ import (
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
type request struct {
|
||||
url *C.char
|
||||
ctx unsafe.Pointer
|
||||
}
|
||||
|
||||
var messageBuffer = make(chan string, 100)
|
||||
var requestBuffer = make(chan *request, 100)
|
||||
|
||||
type Frontend struct {
|
||||
|
||||
// Context
|
||||
@@ -29,7 +50,7 @@ type Frontend struct {
|
||||
assets *assetserver.DesktopAssetServer
|
||||
|
||||
// main window handle
|
||||
//mainWindow *Window
|
||||
mainWindow *Window
|
||||
minWidth, minHeight, maxWidth, maxHeight int
|
||||
bindings *binding.Bindings
|
||||
dispatcher frontend.Dispatcher
|
||||
@@ -69,9 +90,23 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
|
||||
}
|
||||
result.assets = assets
|
||||
|
||||
go result.startMessageProcessor()
|
||||
go result.startRequestProcessor()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *Frontend) startMessageProcessor() {
|
||||
for message := range messageBuffer {
|
||||
f.processMessage(message)
|
||||
}
|
||||
}
|
||||
func (f *Frontend) startRequestProcessor() {
|
||||
for request := range requestBuffer {
|
||||
f.processRequest(request)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowReload() {
|
||||
f.ExecJS("runtime.WindowReload();")
|
||||
}
|
||||
@@ -80,222 +115,101 @@ func (f *Frontend) Run(ctx context.Context) error {
|
||||
|
||||
f.ctx = context.WithValue(ctx, "frontend", f)
|
||||
|
||||
//mainWindow := NewWindow(nil, f.frontendOptions)
|
||||
//f.mainWindow = mainWindow
|
||||
//
|
||||
//var _debug = ctx.Value("debug")
|
||||
//if _debug != nil {
|
||||
// f.debug = _debug.(bool)
|
||||
//}
|
||||
//
|
||||
//f.WindowCenter()
|
||||
//f.setupChromium()
|
||||
//
|
||||
//mainWindow.OnSize().Bind(func(arg *winc.Event) {
|
||||
// f.chromium.Resize()
|
||||
//})
|
||||
//
|
||||
//mainWindow.OnClose().Bind(func(arg *winc.Event) {
|
||||
// if f.frontendOptions.HideWindowOnClose {
|
||||
// f.WindowHide()
|
||||
// } else {
|
||||
// f.Quit()
|
||||
// }
|
||||
//})
|
||||
//
|
||||
//// TODO: Move this into a callback from frontend
|
||||
//go func() {
|
||||
// if f.frontendOptions.OnStartup != nil {
|
||||
// f.frontendOptions.OnStartup(f.ctx)
|
||||
// }
|
||||
//}()
|
||||
//
|
||||
//mainWindow.Run()
|
||||
var _debug = ctx.Value("debug")
|
||||
if _debug != nil {
|
||||
f.debug = _debug.(bool)
|
||||
}
|
||||
|
||||
mainWindow := NewWindow(f.frontendOptions, f.debug)
|
||||
f.mainWindow = mainWindow
|
||||
f.mainWindow.Center()
|
||||
|
||||
go func() {
|
||||
if f.frontendOptions.OnStartup != nil {
|
||||
f.frontendOptions.OnStartup(f.ctx)
|
||||
}
|
||||
}()
|
||||
mainWindow.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowCenter() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Center()
|
||||
f.mainWindow.Center()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowSetPos(x, y int) {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.SetPos(x, y)
|
||||
f.mainWindow.SetPos(x, y)
|
||||
}
|
||||
func (f *Frontend) WindowGetPos() (int, int) {
|
||||
runtime.LockOSThread()
|
||||
//return f.mainWindow.Pos()
|
||||
return 0, 0
|
||||
return f.mainWindow.Pos()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowSetSize(width, height int) {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.SetSize(width, height)
|
||||
f.mainWindow.SetSize(width, height)
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowGetSize() (int, int) {
|
||||
runtime.LockOSThread()
|
||||
//return f.mainWindow.Size()
|
||||
return 0, 0
|
||||
return f.mainWindow.Size()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowSetTitle(title string) {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.SetText(title)
|
||||
f.mainWindow.SetTitle(title)
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowFullscreen() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.SetMaxSize(0, 0)
|
||||
//f.mainWindow.SetMinSize(0, 0)
|
||||
//f.mainWindow.Fullscreen()
|
||||
f.mainWindow.SetMaxSize(0, 0)
|
||||
f.mainWindow.SetMinSize(0, 0)
|
||||
f.mainWindow.Fullscreen()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowUnFullscreen() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.UnFullscreen()
|
||||
//f.mainWindow.SetMaxSize(f.maxWidth, f.maxHeight)
|
||||
//f.mainWindow.SetMinSize(f.minWidth, f.minHeight)
|
||||
f.mainWindow.UnFullscreen()
|
||||
f.mainWindow.SetMaxSize(f.maxWidth, f.maxHeight)
|
||||
f.mainWindow.SetMinSize(f.minWidth, f.minHeight)
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowShow() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Show()
|
||||
f.mainWindow.Show()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowHide() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Hide()
|
||||
f.mainWindow.Hide()
|
||||
}
|
||||
func (f *Frontend) WindowMaximise() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Maximise()
|
||||
f.mainWindow.Maximise()
|
||||
}
|
||||
func (f *Frontend) WindowUnmaximise() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Restore()
|
||||
f.mainWindow.UnMaximise()
|
||||
}
|
||||
func (f *Frontend) WindowMinimise() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Minimise()
|
||||
f.mainWindow.Minimise()
|
||||
}
|
||||
func (f *Frontend) WindowUnminimise() {
|
||||
runtime.LockOSThread()
|
||||
//f.mainWindow.Restore()
|
||||
f.mainWindow.UnMinimise()
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowSetMinSize(width int, height int) {
|
||||
runtime.LockOSThread()
|
||||
f.minWidth = width
|
||||
f.minHeight = height
|
||||
//f.mainWindow.SetMinSize(width, height)
|
||||
f.mainWindow.SetMinSize(width, height)
|
||||
}
|
||||
func (f *Frontend) WindowSetMaxSize(width int, height int) {
|
||||
runtime.LockOSThread()
|
||||
f.maxWidth = width
|
||||
f.maxHeight = height
|
||||
//f.mainWindow.SetMaxSize(width, height)
|
||||
f.mainWindow.SetMaxSize(width, height)
|
||||
}
|
||||
|
||||
func (f *Frontend) WindowSetRGBA(col *options.RGBA) {
|
||||
runtime.LockOSThread()
|
||||
if col == nil {
|
||||
return
|
||||
}
|
||||
/*
|
||||
|
||||
//f.mainWindow.Dispatch(func() {
|
||||
controller := f.chromium.GetController()
|
||||
controller2 := controller.GetICoreWebView2Controller2()
|
||||
|
||||
backgroundCol := edge.COREWEBVIEW2_COLOR{
|
||||
A: col.A,
|
||||
R: col.R,
|
||||
G: col.G,
|
||||
B: col.B,
|
||||
}
|
||||
|
||||
// Webview2 only has 0 and 255 as valid values.
|
||||
if backgroundCol.A > 0 && backgroundCol.A < 255 {
|
||||
backgroundCol.A = 255
|
||||
}
|
||||
|
||||
if f.frontendOptions.Windows != nil && f.frontendOptions.Windows.WebviewIsTransparent {
|
||||
backgroundCol.A = 0
|
||||
}
|
||||
|
||||
err := controller2.PutDefaultBackgroundColor(backgroundCol)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
})
|
||||
*/
|
||||
f.mainWindow.SetRGBA(col.R, col.G, col.B, col.A)
|
||||
}
|
||||
|
||||
func (f *Frontend) Quit() {
|
||||
//winc.Exit()
|
||||
f.mainWindow.Quit()
|
||||
}
|
||||
|
||||
/*
|
||||
const (
|
||||
ctrlZ int = 90
|
||||
ctrlX = 88
|
||||
ctrlC = 67
|
||||
ctrlV = 86
|
||||
)
|
||||
|
||||
func (f *Frontend) setupChromium() {
|
||||
chromium := edge.NewChromium()
|
||||
f.chromium = chromium
|
||||
chromium.MessageCallback = f.processMessage
|
||||
chromium.WebResourceRequestedCallback = f.processRequest
|
||||
chromium.NavigationCompletedCallback = f.navigationCompleted
|
||||
acceleratorsWebviewShouldProcess := slicer.Int([]int{ctrlV, ctrlC, ctrlX, ctrlZ})
|
||||
chromium.AcceleratorKeyCallback = func(vkey uint) bool {
|
||||
// We want webview to handle ctrl-C, ctrl-Z, ctrl-v, ctrl-x
|
||||
if acceleratorsWebviewShouldProcess.Contains(int(vkey)) {
|
||||
return false
|
||||
}
|
||||
// Post keypress
|
||||
//w32.PostMessage(f.mainWindow.Handle(), w32.WM_KEYDOWN, uintptr(vkey), 0)
|
||||
return true
|
||||
}
|
||||
chromium.Embed(f.mainWindow.Handle())
|
||||
chromium.Resize()
|
||||
settings, err := chromium.GetSettings()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutAreDefaultContextMenusEnabled(f.debug)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutAreDevToolsEnabled(f.debug)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutIsZoomControlEnabled(false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutIsStatusBarEnabled(false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutIsStatusBarEnabled(false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Set background colour
|
||||
f.WindowSetRGBA(f.frontendOptions.RGBA)
|
||||
|
||||
chromium.AddWebResourceRequestedFilter("*", edge.COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL)
|
||||
chromium.Navigate("file://wails/")
|
||||
}
|
||||
*/
|
||||
|
||||
type EventNotify struct {
|
||||
Name string `json:"name"`
|
||||
Data []interface{} `json:"data"`
|
||||
@@ -314,39 +228,6 @@ func (f *Frontend) Notify(name string, data ...interface{}) {
|
||||
f.ExecJS(`window.wails.EventsNotify('` + template.JSEscapeString(string(payload)) + `');`)
|
||||
}
|
||||
|
||||
//func (f *Frontend) processRequest(req *edge.ICoreWebView2WebResourceRequest, args *edge.ICoreWebView2WebResourceRequestedEventArgs) {
|
||||
// //Get the request
|
||||
// uri, _ := req.GetUri()
|
||||
//
|
||||
// // Translate URI
|
||||
// uri = strings.TrimPrefix(uri, "file://wails")
|
||||
// if !strings.HasPrefix(uri, "/") {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // Load file from asset store
|
||||
// content, mimeType, err := f.assets.Load(uri)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// env := f.chromium.Environment()
|
||||
// headers := "Content-Type: " + mimeType
|
||||
// if f.servingFromDisk {
|
||||
// headers += "\nPragma: no-cache"
|
||||
// }
|
||||
// response, err := env.CreateWebResourceResponse(content, 200, "OK", headers)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// // Send response back
|
||||
// err = args.PutResponse(response)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// return
|
||||
//}
|
||||
|
||||
func (f *Frontend) processMessage(message string) {
|
||||
if message == "drag" {
|
||||
err := f.startDrag()
|
||||
@@ -375,9 +256,7 @@ func (f *Frontend) processMessage(message string) {
|
||||
}
|
||||
|
||||
func (f *Frontend) Callback(message string) {
|
||||
//f.mainWindow.Dispatch(func() {
|
||||
// f.chromium.Eval(`window.wails.Callback(` + strconv.Quote(message) + `);`)
|
||||
//})
|
||||
f.ExecJS(`window.wails.Callback(` + strconv.Quote(message) + `);`)
|
||||
}
|
||||
|
||||
func (f *Frontend) startDrag() error {
|
||||
@@ -389,30 +268,39 @@ func (f *Frontend) startDrag() error {
|
||||
}
|
||||
|
||||
func (f *Frontend) ExecJS(js string) {
|
||||
//f.mainWindow.Dispatch(func() {
|
||||
// f.chromium.Eval(js)
|
||||
//})
|
||||
f.mainWindow.ExecJS(js)
|
||||
}
|
||||
|
||||
//func (f *Frontend) navigationCompleted(sender *edge.ICoreWebView2, args *edge.ICoreWebView2NavigationCompletedEventArgs) {
|
||||
// if f.frontendOptions.OnDomReady != nil {
|
||||
// go f.frontendOptions.OnDomReady(f.ctx)
|
||||
// }
|
||||
//
|
||||
// // If you want to start hidden, return
|
||||
// if f.frontendOptions.StartHidden {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // Hack to make it visible: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1077#issuecomment-825375026
|
||||
// err := f.chromium.Hide()
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// err = f.chromium.Show()
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// f.mainWindow.Show()
|
||||
//
|
||||
//}
|
||||
func (f *Frontend) processRequest(r *request) {
|
||||
url := C.GoString(r.url)
|
||||
url = strings.TrimPrefix(url, "wails://wails")
|
||||
if !strings.HasPrefix(url, "/") {
|
||||
return
|
||||
}
|
||||
_contents, _mimetype, err := f.assets.Load(url)
|
||||
if err != nil {
|
||||
f.logger.Error(err.Error())
|
||||
//TODO: Handle errors
|
||||
return
|
||||
}
|
||||
data := C.CString(string(_contents))
|
||||
defer C.free(unsafe.Pointer(data))
|
||||
mimetype := C.CString(_mimetype)
|
||||
defer C.free(unsafe.Pointer(mimetype))
|
||||
|
||||
C.ProcessURLResponse(r.ctx, r.url, mimetype, data, C.int(len(_contents)))
|
||||
}
|
||||
|
||||
//export processMessage
|
||||
func processMessage(message *C.char) {
|
||||
goMessage := C.GoString(message)
|
||||
messageBuffer <- goMessage
|
||||
}
|
||||
|
||||
//export processURLRequest
|
||||
func processURLRequest(ctx unsafe.Pointer, url *C.char) {
|
||||
requestBuffer <- &request{
|
||||
url: url,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
56
v2/internal/frontend/desktop/darwin/main.m
Normal file
56
v2/internal/frontend/desktop/darwin/main.m
Normal file
@@ -0,0 +1,56 @@
|
||||
//go:build ignore
|
||||
// main.m
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 10/10/21.
|
||||
//
|
||||
|
||||
// ****** This file is used for testing purposes only ******
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Application.h"
|
||||
|
||||
void processMessage(const char*t) {
|
||||
NSLog(@"processMessage called");
|
||||
|
||||
}
|
||||
|
||||
void processMessageDialogResponse(int t) {
|
||||
NSLog(@"processMessage called");
|
||||
}
|
||||
|
||||
void processOpenFileDialogResponse(const char *t) {
|
||||
NSLog(@"processMessage called %s", t);
|
||||
}
|
||||
|
||||
void 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);
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
// insert code here...
|
||||
int frameless = 0;
|
||||
int resizable = 1;
|
||||
int fullscreen = 0;
|
||||
int fullSizeContent = 1;
|
||||
int hideTitleBar = 0;
|
||||
int titlebarAppearsTransparent = 1;
|
||||
int hideTitle = 0;
|
||||
int useToolbar = 1;
|
||||
int hideToolbarSeparator = 1;
|
||||
int webviewIsTransparent = 0;
|
||||
int alwaysOnTop = 1;
|
||||
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);
|
||||
|
||||
|
||||
|
||||
Run((void*)CFBridgingRetain(result));
|
||||
return 0;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package darwin
|
||||
|
||||
|
||||
28
v2/internal/frontend/desktop/darwin/message.h
Normal file
28
v2/internal/frontend/desktop/darwin/message.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// message.h
|
||||
// test
|
||||
//
|
||||
// Created by Lea Anthony on 14/10/21.
|
||||
//
|
||||
|
||||
#ifndef export_h
|
||||
#define export_h
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
void processMessage(const char *);
|
||||
void processURLRequest(void*, const char *);
|
||||
void processMessageDialogResponse(int);
|
||||
void processOpenFileDialogResponse(const char*);
|
||||
void processSaveFileDialogResponse(const char*);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* export_h */
|
||||
187
v2/internal/frontend/desktop/darwin/window.go
Normal file
187
v2/internal/frontend/desktop/darwin/window.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package darwin
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Application.h"
|
||||
#import "WailsContext.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"log"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
type Window struct {
|
||||
context unsafe.Pointer
|
||||
}
|
||||
|
||||
func bool2Cint(value bool) C.int {
|
||||
if value {
|
||||
return C.int(1)
|
||||
}
|
||||
return C.int(0)
|
||||
}
|
||||
|
||||
func NewWindow(frontendOptions *options.App, debugMode bool) *Window {
|
||||
|
||||
c := NewCalloc()
|
||||
defer c.Free()
|
||||
|
||||
frameless := bool2Cint(frontendOptions.Frameless)
|
||||
resizable := bool2Cint(!frontendOptions.DisableResize)
|
||||
fullscreen := bool2Cint(frontendOptions.Fullscreen)
|
||||
alwaysOnTop := bool2Cint(frontendOptions.AlwaysOnTop)
|
||||
hideWindowOnClose := bool2Cint(frontendOptions.HideWindowOnClose)
|
||||
debug := bool2Cint(debugMode)
|
||||
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
|
||||
var appearance, title *C.char
|
||||
|
||||
width := C.int(frontendOptions.Width)
|
||||
height := C.int(frontendOptions.Height)
|
||||
|
||||
title = c.String(frontendOptions.Title)
|
||||
|
||||
if frontendOptions.Mac != nil {
|
||||
mac := frontendOptions.Mac
|
||||
if mac.TitleBar != nil {
|
||||
fullSizeContent = bool2Cint(mac.TitleBar.FullSizeContent)
|
||||
hideTitleBar = bool2Cint(mac.TitleBar.HideTitleBar)
|
||||
hideTitle = bool2Cint(mac.TitleBar.HideTitle)
|
||||
useToolbar = bool2Cint(mac.TitleBar.UseToolbar)
|
||||
titlebarAppearsTransparent = bool2Cint(mac.TitleBar.TitlebarAppearsTransparent)
|
||||
hideToolbarSeparator = bool2Cint(mac.TitleBar.HideToolbarSeparator)
|
||||
}
|
||||
windowIsTranslucent = bool2Cint(mac.WindowIsTranslucent)
|
||||
webviewIsTransparent = bool2Cint(mac.WebviewIsTransparent)
|
||||
|
||||
appearance = c.String(string(mac.Appearance))
|
||||
}
|
||||
var context *C.WailsContext = C.Create(title, width, height, frameless, resizable, fullscreen, fullSizeContent, hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent, alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, debug)
|
||||
|
||||
C.SetRGBA(unsafe.Pointer(context), red, green, blue, alpha)
|
||||
|
||||
return &Window{
|
||||
context: unsafe.Pointer(context),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Window) Center() {
|
||||
C.Center(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) Run() {
|
||||
C.Run(w.context)
|
||||
println("I exited!")
|
||||
}
|
||||
|
||||
func (w *Window) Quit() {
|
||||
C.Quit(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) SetRGBA(r uint8, g uint8, b uint8, a uint8) {
|
||||
C.SetRGBA(w.context, C.int(r), C.int(g), C.int(b), C.int(a))
|
||||
}
|
||||
|
||||
func (w *Window) ExecJS(js string) {
|
||||
_js := C.CString(js)
|
||||
C.ExecJS(w.context, _js)
|
||||
C.free(unsafe.Pointer(_js))
|
||||
}
|
||||
|
||||
func (w *Window) SetPos(x int, y int) {
|
||||
C.SetPosition(w.context, C.int(x), C.int(y))
|
||||
}
|
||||
|
||||
func (w *Window) SetSize(width int, height int) {
|
||||
C.SetSize(w.context, C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
func (w *Window) SetTitle(title string) {
|
||||
t := C.CString(title)
|
||||
C.SetTitle(w.context, t)
|
||||
C.free(unsafe.Pointer(t))
|
||||
}
|
||||
|
||||
func (w *Window) Maximise() {
|
||||
C.Maximise(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) UnMaximise() {
|
||||
C.UnMaximise(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) Minimise() {
|
||||
C.Minimise(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) UnMinimise() {
|
||||
C.UnMinimise(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) SetMinSize(width int, height int) {
|
||||
C.SetMinSize(w.context, C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
func (w *Window) SetMaxSize(width int, height int) {
|
||||
C.SetMaxSize(w.context, C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
func (w *Window) Fullscreen() {
|
||||
C.Fullscreen(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) UnFullscreen() {
|
||||
C.UnFullscreen(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) Show() {
|
||||
C.Show(w.context)
|
||||
}
|
||||
|
||||
func (w *Window) Hide() {
|
||||
C.Hide(w.context)
|
||||
}
|
||||
|
||||
func parseIntDuo(temp string) (int, int) {
|
||||
split := strings.Split(temp, ",")
|
||||
x, err := strconv.Atoi(split[0])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
y, err := strconv.Atoi(split[1])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (w *Window) Pos() (int, int) {
|
||||
var _result *C.char = C.GetPos(w.context)
|
||||
temp := C.GoString(_result)
|
||||
return parseIntDuo(temp)
|
||||
}
|
||||
|
||||
func (w *Window) Size() (int, int) {
|
||||
var _result *C.char = C.GetSize(w.context)
|
||||
temp := C.GoString(_result)
|
||||
return parseIntDuo(temp)
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
|
||||
@@ -135,6 +135,7 @@ func (f *Frontend) Run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
mainWindow.Run()
|
||||
mainWindow.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -257,19 +258,6 @@ func (f *Frontend) Quit() {
|
||||
winc.Exit()
|
||||
}
|
||||
|
||||
const (
|
||||
ctrlZ int = 90
|
||||
ctrlX = 88
|
||||
ctrlC = 67
|
||||
ctrlV = 86
|
||||
ctrlA = 65
|
||||
arrowUp = 38
|
||||
arrowDown = 40
|
||||
arrowRight = 39
|
||||
arrowLeft = 37
|
||||
keyDel = 46
|
||||
)
|
||||
|
||||
func (f *Frontend) setupChromium() {
|
||||
chromium := edge.NewChromium()
|
||||
f.chromium = chromium
|
||||
@@ -306,10 +294,15 @@ func (f *Frontend) setupChromium() {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = settings.PutIsSwipeNavigationEnabled(false)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Set background colour
|
||||
f.WindowSetRGBA(f.frontendOptions.RGBA)
|
||||
|
||||
chromium.SetGlobalPermission(edge.CoreWebView2PermissionStateAllow)
|
||||
chromium.AddWebResourceRequestedFilter("*", edge.COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL)
|
||||
chromium.Navigate(f.startURL)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ func NewWindow(parent winc.Controller, options *options.App) *Window {
|
||||
exStyle |= w32.WS_EX_NOREDIRECTIONBITMAP
|
||||
}
|
||||
}
|
||||
if options.AlwaysOnTop {
|
||||
exStyle |= w32.WS_EX_TOPMOST
|
||||
}
|
||||
|
||||
var dwStyle = w32.WS_OVERLAPPEDWINDOW
|
||||
if options.Frameless {
|
||||
|
||||
@@ -2,8 +2,9 @@ package dispatcher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||
)
|
||||
|
||||
const systemCallPrefix = ":wails:"
|
||||
@@ -29,7 +30,7 @@ func (d *Dispatcher) processSystemCall(payload callMessage, sender frontend.Fron
|
||||
return &position{x, y}, nil
|
||||
case "WindowGetSize":
|
||||
w, h := sender.WindowGetSize()
|
||||
return &position{w, h}, nil
|
||||
return &size{w, h}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown systemcall message: %s", payload.Name)
|
||||
}
|
||||
|
||||
@@ -22,10 +22,18 @@ The electron alternative for Go
|
||||
while (obj && s.length) obj = obj[s.shift()];
|
||||
return obj;
|
||||
};
|
||||
window.WailsInvoke = _deeptest(["chrome", "webview", "postMessage"]) ||
|
||||
_deeptest(["webkit", "messageHandlers", "external", "postMessage"]);
|
||||
let windows = _deeptest(["chrome", "webview", "postMessage"]);
|
||||
let mac = _deeptest(["webkit", "messageHandlers", "external", "postMessage"]);
|
||||
|
||||
if (!window.WailsInvoke) {
|
||||
if (!windows && !mac) {
|
||||
console.error("Unsupported Platform");
|
||||
return;
|
||||
}
|
||||
|
||||
if (windows) {
|
||||
window.WailsInvoke = (message) => window.chrome.webview.postMessage(message);
|
||||
}
|
||||
if (mac) {
|
||||
window.WailsInvoke = (message) => window.webkit.messageHandlers.external.postMessage(message);
|
||||
}
|
||||
})();
|
||||
@@ -39,7 +39,11 @@ window.wails = {
|
||||
EventsNotify,
|
||||
SetBindings,
|
||||
eventListeners,
|
||||
callbacks
|
||||
callbacks,
|
||||
flags: {
|
||||
disableScrollbarDrag: false,
|
||||
disableWailsDefaultContextMenu: false,
|
||||
}
|
||||
};
|
||||
|
||||
// Set the bindings
|
||||
@@ -61,9 +65,23 @@ window.addEventListener('mousedown', (e) => {
|
||||
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
||||
break;
|
||||
} else if (currentElement.hasAttribute('data-wails-drag')) {
|
||||
if (window.wails.flags.disableScrollbarDrag) {
|
||||
// This checks for clicks on the scroll bar
|
||||
if (e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
window.WailsInvoke("drag");
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
currentElement = currentElement.parentElement;
|
||||
}
|
||||
});
|
||||
|
||||
// Setup context menu hook
|
||||
window.addEventListener('contextmenu', function (e) {
|
||||
if (window.wails.flags.disableWailsDefaultContextMenu) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
@@ -148,6 +148,7 @@
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.5.6.tgz",
|
||||
"integrity": "sha512-Bz8nU45FrT6sP/Tf3M2rQUuBGxnDSNSPZNIoYwSNt5H+wjSyo/t+zm94tgnOZsR6GgpDMbNQgo4jGbK0NLvEfw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"svelte": "^3.42.6"
|
||||
},
|
||||
@@ -155,7 +156,8 @@
|
||||
"svelte": {
|
||||
"version": "3.43.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.43.1.tgz",
|
||||
"integrity": "sha512-nvPIaKx4HLzYlSdquISZpgG1Kqr2VAWQjZOt3Iwm3UhbqmA0LnSx4k1YpRMEhjQYW3ZCqQoK8Egto9tv4YewMA=="
|
||||
"integrity": "sha512-nvPIaKx4HLzYlSdquISZpgG1Kqr2VAWQjZOt3Iwm3UhbqmA0LnSx4k1YpRMEhjQYW3ZCqQoK8Egto9tv4YewMA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1 +1 @@
|
||||
(()=>{(function(){let n=function(o){for(var e=window[o.shift()];e&&o.length;)e=e[o.shift()];return e};window.WailsInvoke=n(["chrome","webview","postMessage"])||n(["webkit","messageHandlers","external","postMessage"]),window.WailsInvoke||console.error("Unsupported Platform")})();})();
|
||||
(()=>{(function(){let o=function(e){for(var s=window[e.shift()];s&&e.length;)s=s[e.shift()];return s},t=o(["chrome","webview","postMessage"]),n=o(["webkit","messageHandlers","external","postMessage"]);if(!t&&!n){console.error("Unsupported Platform");return}t&&(window.WailsInvoke=e=>window.chrome.webview.postMessage(e)),n&&(window.WailsInvoke=e=>window.webkit.messageHandlers.external.postMessage(e))})();})();
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
(()=>{var g=Object.defineProperty;var h=o=>g(o,"__esModule",{value:!0});var f=(o,n)=>{h(o);for(var e in n)g(o,e,{get:n[e],enumerable:!0})};var W={};f(W,{LogDebug:()=>T,LogError:()=>D,LogFatal:()=>F,LogInfo:()=>C,LogLevel:()=>U,LogPrint:()=>B,LogTrace:()=>R,LogWarning:()=>J,SetLogLevel:()=>G});function l(o,n){window.WailsInvoke("L"+o+n)}function R(o){l("T",o)}function B(o){l("P",o)}function T(o){l("D",o)}function C(o){l("I",o)}function J(o){l("W",o)}function D(o){l("E",o)}function F(o){l("F",o)}function G(o){l("S",o)}var U={TRACE:1,DEBUG:2,INFO:3,WARNING:4,ERROR:5};var E=class{constructor(n,e){e=e||-1,this.Callback=t=>(n.apply(null,t),e===-1?!1:(e-=1,e===0))}},s={};function d(o,n,e){s[o]=s[o]||[];let t=new E(n,e);s[o].push(t)}function I(o,n){d(o,n,-1)}function k(o,n){d(o,n,1)}function m(o){let n=o.name;if(s[n]){let e=s[n].slice();for(let t=0;t<s[n].length;t+=1){let r=s[n][t],i=o.data;r.Callback(i)&&e.splice(t,1)}s[n]=e}}function S(o){let n;try{n=JSON.parse(o)}catch(e){let t="Invalid JSON passed to Notify: "+o;throw new Error(t)}m(n)}function y(o){let n={name:o,data:[].slice.apply(arguments).slice(1)};m(n),window.WailsInvoke("EE"+JSON.stringify(n))}function L(o){s.delete(o),window.WailsInvoke("EX"+o)}var c={};function z(){var o=new Uint32Array(1);return window.crypto.getRandomValues(o)[0]}function A(){return Math.random()*9007199254740991}var p;window.crypto?p=z:p=A;function a(o,n,e){return e==null&&(e=0),new Promise(function(t,r){var i;do i=o+"-"+p();while(c[i]);var w;e>0&&(w=setTimeout(function(){r(Error("Call to "+o+" timed out. Request ID: "+i))},e)),c[i]={timeoutHandle:w,reject:r,resolve:t};try{let u={name:o,args:n,callbackID:i};window.WailsInvoke("C"+JSON.stringify(u))}catch(u){console.error(u)}})}function O(o){var n;try{n=JSON.parse(o)}catch(r){let i=`Invalid JSON passed to callback: ${r.message}. Message: ${o}`;throw wails.LogDebug(i),new Error(i)}var e=n.callbackid,t=c[e];if(!t){let r=`Callback '${e}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(t.timeoutHandle),delete c[e],n.error?t.reject(n.error):t.resolve(n.result)}window.go={};function b(o){try{o=JSON.parse(o)}catch(n){console.error(n)}window.go=window.go||{},Object.keys(o).forEach(n=>{window.go[n]=window.go[n]||{},Object.keys(o[n]).forEach(e=>{window.go[n][e]=window.go[n][e]||{},Object.keys(o[n][e]).forEach(t=>{window.go[n][e][t]=function(){let r=0;function i(){let w=[].slice.call(arguments);return a([n,e,t].join("."),w,r)}return i.setTimeout=function(w){r=w},i.getTimeout=function(){return r},i}()})})})}var v={};f(v,{WindowCenter:()=>j,WindowFullscreen:()=>M,WindowGetPosition:()=>Z,WindowGetSize:()=>V,WindowHide:()=>K,WindowMaximise:()=>_,WindowMinimise:()=>no,WindowReload:()=>P,WindowSetMaxSize:()=>q,WindowSetMinSize:()=>N,WindowSetPosition:()=>X,WindowSetRGBA:()=>to,WindowSetSize:()=>Q,WindowSetTitle:()=>H,WindowShow:()=>Y,WindowUnFullscreen:()=>$,WindowUnmaximise:()=>oo,WindowUnminimise:()=>eo});function P(){window.location.reload()}function j(){window.WailsInvoke("Wc")}function H(o){window.WailsInvoke("WT"+o)}function M(){window.WailsInvoke("WF")}function $(){window.WailsInvoke("Wf")}function Q(o,n){window.WailsInvoke("Ws:"+o+":"+n)}function V(){return a(":wails:WindowGetSize")}function q(o,n){window.WailsInvoke("WZ:"+o+":"+n)}function N(o,n){window.WailsInvoke("Wz:"+o+":"+n)}function X(o,n){window.WailsInvoke("Wp:"+o+":"+n)}function Z(){return a(":wails:WindowGetPos")}function K(){window.WailsInvoke("WH")}function Y(){window.WailsInvoke("WS")}function _(){window.WailsInvoke("WM")}function oo(){window.WailsInvoke("WU")}function no(){window.WailsInvoke("Wm")}function eo(){window.WailsInvoke("Wu")}function to(o){let n=JSON.stringify(o);window.WailsInvoke("Wr:"+n)}var x={};f(x,{BrowserOpenURL:()=>io});function io(o){window.WailsInvoke("BO:"+o)}function ro(){window.WailsInvoke("Q")}window.runtime={...W,...v,...x,EventsOn:I,EventsOnce:k,EventsOnMultiple:d,EventsEmit:y,EventsOff:L,Quit:ro};window.wails={Callback:O,EventsNotify:S,SetBindings:b,eventListeners:s,callbacks:c};window.wails.SetBindings(window.wailsbindings);delete window.wails.SetBindings;window.addEventListener("mousedown",o=>{let n=o.target;for(;n!=null&&!n.hasAttribute("data-wails-no-drag");){if(n.hasAttribute("data-wails-drag")){window.WailsInvoke("drag");break}n=n.parentElement}});})();
|
||||
(()=>{var x=Object.defineProperty;var h=n=>x(n,"__esModule",{value:!0});var u=(n,o)=>{h(n);for(var e in o)x(n,e,{get:o[e],enumerable:!0})};var W={};u(W,{LogDebug:()=>C,LogError:()=>J,LogFatal:()=>F,LogInfo:()=>B,LogLevel:()=>U,LogPrint:()=>R,LogTrace:()=>D,LogWarning:()=>T,SetLogLevel:()=>G});function l(n,o){window.WailsInvoke("L"+n+o)}function D(n){l("T",n)}function R(n){l("P",n)}function C(n){l("D",n)}function B(n){l("I",n)}function T(n){l("W",n)}function J(n){l("E",n)}function F(n){l("F",n)}function G(n){l("S",n)}var U={TRACE:1,DEBUG:2,INFO:3,WARNING:4,ERROR:5};var E=class{constructor(o,e){e=e||-1,this.Callback=t=>(o.apply(null,t),e===-1?!1:(e-=1,e===0))}},s={};function d(n,o,e){s[n]=s[n]||[];let t=new E(o,e);s[n].push(t)}function I(n,o){d(n,o,-1)}function k(n,o){d(n,o,1)}function S(n){let o=n.name;if(s[o]){let e=s[o].slice();for(let t=0;t<s[o].length;t+=1){let r=s[o][t],i=n.data;r.Callback(i)&&e.splice(t,1)}s[o]=e}}function m(n){let o;try{o=JSON.parse(n)}catch(e){let t="Invalid JSON passed to Notify: "+n;throw new Error(t)}S(o)}function b(n){let o={name:n,data:[].slice.apply(arguments).slice(1)};S(o),window.WailsInvoke("EE"+JSON.stringify(o))}function y(n){s.delete(n),window.WailsInvoke("EX"+n)}var a={};function z(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}function A(){return Math.random()*9007199254740991}var p;window.crypto?p=z:p=A;function c(n,o,e){return e==null&&(e=0),new Promise(function(t,r){var i;do i=n+"-"+p();while(a[i]);var w;e>0&&(w=setTimeout(function(){r(Error("Call to "+n+" timed out. Request ID: "+i))},e)),a[i]={timeoutHandle:w,reject:r,resolve:t};try{let f={name:n,args:o,callbackID:i};window.WailsInvoke("C"+JSON.stringify(f))}catch(f){console.error(f)}})}function L(n){var o;try{o=JSON.parse(n)}catch(r){let i=`Invalid JSON passed to callback: ${r.message}. Message: ${n}`;throw wails.LogDebug(i),new Error(i)}var e=o.callbackid,t=a[e];if(!t){let r=`Callback '${e}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(t.timeoutHandle),delete a[e],o.error?t.reject(o.error):t.resolve(o.result)}window.go={};function O(n){try{n=JSON.parse(n)}catch(o){console.error(o)}window.go=window.go||{},Object.keys(n).forEach(o=>{window.go[o]=window.go[o]||{},Object.keys(n[o]).forEach(e=>{window.go[o][e]=window.go[o][e]||{},Object.keys(n[o][e]).forEach(t=>{window.go[o][e][t]=function(){let r=0;function i(){let w=[].slice.call(arguments);return c([o,e,t].join("."),w,r)}return i.setTimeout=function(w){r=w},i.getTimeout=function(){return r},i}()})})})}var v={};u(v,{WindowCenter:()=>M,WindowFullscreen:()=>j,WindowGetPosition:()=>Y,WindowGetSize:()=>V,WindowHide:()=>Z,WindowMaximise:()=>_,WindowMinimise:()=>on,WindowReload:()=>H,WindowSetMaxSize:()=>X,WindowSetMinSize:()=>q,WindowSetPosition:()=>N,WindowSetRGBA:()=>tn,WindowSetSize:()=>Q,WindowSetTitle:()=>P,WindowShow:()=>K,WindowUnFullscreen:()=>$,WindowUnmaximise:()=>nn,WindowUnminimise:()=>en});function H(){window.location.reload()}function M(){window.WailsInvoke("Wc")}function P(n){window.WailsInvoke("WT"+n)}function j(){window.WailsInvoke("WF")}function $(){window.WailsInvoke("Wf")}function Q(n,o){window.WailsInvoke("Ws:"+n+":"+o)}function V(){return c(":wails:WindowGetSize")}function X(n,o){window.WailsInvoke("WZ:"+n+":"+o)}function q(n,o){window.WailsInvoke("Wz:"+n+":"+o)}function N(n,o){window.WailsInvoke("Wp:"+n+":"+o)}function Y(){return c(":wails:WindowGetPos")}function Z(){window.WailsInvoke("WH")}function K(){window.WailsInvoke("WS")}function _(){window.WailsInvoke("WM")}function nn(){window.WailsInvoke("WU")}function on(){window.WailsInvoke("Wm")}function en(){window.WailsInvoke("Wu")}function tn(n){let o=JSON.stringify(n);window.WailsInvoke("Wr:"+o)}var g={};u(g,{BrowserOpenURL:()=>rn});function rn(n){window.WailsInvoke("BO:"+n)}function sn(){window.WailsInvoke("Q")}window.runtime={...W,...v,...g,EventsOn:I,EventsOnce:k,EventsOnMultiple:d,EventsEmit:b,EventsOff:y,Quit:sn};window.wails={Callback:L,EventsNotify:m,SetBindings:O,eventListeners:s,callbacks:a,flags:{disableScrollbarDrag:!1,disableWailsDefaultContextMenu:!1}};window.wails.SetBindings(window.wailsbindings);delete window.wails.SetBindings;window.addEventListener("mousedown",n=>{let o=n.target;for(;o!=null&&!o.hasAttribute("data-wails-no-drag");){if(o.hasAttribute("data-wails-drag")){if(window.wails.flags.disableScrollbarDrag&&(n.offsetX>n.target.clientWidth||n.offsetY>n.target.clientHeight))break;window.WailsInvoke("drag"),n.preventDefault();break}o=o.parentElement}});window.addEventListener("contextmenu",function(n){window.wails.flags.disableWailsDefaultContextMenu&&n.preventDefault()});})();
|
||||
|
||||
@@ -9,9 +9,9 @@ interface Size {
|
||||
}
|
||||
|
||||
interface RGBA {
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ const basicUpdated string = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
require github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.2 // indirect
|
||||
@@ -330,7 +330,7 @@ const multilineRequireUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -381,12 +381,12 @@ func TestUpdateGoModVersion(t *testing.T) {
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{"basic", args{[]byte(basic), "v2.0.0-beta.12"}, []byte(basicUpdated), false},
|
||||
{"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.12"}, []byte(multilineRequireUpdated), false},
|
||||
{"basicmultilinereplace", args{[]byte(multilineReplace), "v2.0.0-beta.12"}, []byte(multilineReplaceUpdated), false},
|
||||
{"basicmultilinereplaceblock", args{[]byte(multilineReplaceBlock), "v2.0.0-beta.12"}, []byte(multilineReplaceBlockUpdated), false},
|
||||
{"basicmultilinereplacenoversion", args{[]byte(multilineReplaceNoVersion), "v2.0.0-beta.12"}, []byte(multilineReplaceNoVersionUpdated), false},
|
||||
{"basicmultilinereplacenoversionblock", args{[]byte(multilineReplaceNoVersionBlock), "v2.0.0-beta.12"}, []byte(multilineReplaceNoVersionBlockUpdated), false},
|
||||
{"basic", args{[]byte(basic), "v2.0.0-beta.15"}, []byte(basicUpdated), false},
|
||||
{"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.15"}, []byte(multilineRequireUpdated), false},
|
||||
{"basicmultilinereplace", args{[]byte(multilineReplace), "v2.0.0-beta.15"}, []byte(multilineReplaceUpdated), false},
|
||||
{"basicmultilinereplaceblock", args{[]byte(multilineReplaceBlock), "v2.0.0-beta.15"}, []byte(multilineReplaceBlockUpdated), false},
|
||||
{"basicmultilinereplacenoversion", args{[]byte(multilineReplaceNoVersion), "v2.0.0-beta.15"}, []byte(multilineReplaceNoVersionUpdated), false},
|
||||
{"basicmultilinereplacenoversionblock", args{[]byte(multilineReplaceNoVersionBlock), "v2.0.0-beta.15"}, []byte(multilineReplaceNoVersionBlockUpdated), false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -413,8 +413,8 @@ func TestGoModOutOfSync(t *testing.T) {
|
||||
want bool
|
||||
wantErr bool
|
||||
}{
|
||||
{"basic", args{[]byte(basic), "v2.0.0-beta.12"}, true, false},
|
||||
{"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.12"}, true, false},
|
||||
{"basic", args{[]byte(basic), "v2.0.0-beta.15"}, true, false},
|
||||
{"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.15"}, true, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -433,7 +433,7 @@ const multilineReplaceUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -468,14 +468,14 @@ require (
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.12 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-beta.15 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
`
|
||||
const multilineReplaceNoVersionUpdated = `module changeme
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -517,7 +517,7 @@ const multilineReplaceNoVersionBlockUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -562,7 +562,7 @@ const multilineReplaceBlockUpdated = `module changeme
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -598,6 +598,6 @@ require (
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.12 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
github.com/wailsapp/wails/v2 v2.0.0-beta.15 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2
|
||||
)
|
||||
`
|
||||
|
||||
@@ -520,7 +520,14 @@ func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
||||
}
|
||||
|
||||
// Check if there is a build command
|
||||
if b.projectData.BuildCommand == "" {
|
||||
var buildCommand string
|
||||
switch b.projectData.OutputType {
|
||||
case "dev":
|
||||
buildCommand = b.projectData.DevCommand
|
||||
default:
|
||||
buildCommand = b.projectData.BuildCommand
|
||||
}
|
||||
if buildCommand == "" {
|
||||
outputLogger.Println("No Build command. Skipping.")
|
||||
// No - ignore
|
||||
return nil
|
||||
|
||||
66
v2/pkg/menu/colours/colours.go
Normal file
66
v2/pkg/menu/colours/colours.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type Rgb struct {
|
||||
R uint8 `json:"r"`
|
||||
G uint8 `json:"g"`
|
||||
B uint8 `json:"b"`
|
||||
}
|
||||
|
||||
type Hsl struct {
|
||||
H float64 `json:"h"`
|
||||
S float64 `json:"s"`
|
||||
L float64 `json:"l"`
|
||||
}
|
||||
|
||||
type InputCol struct {
|
||||
Colorid uint8 `json:"colorId"`
|
||||
Hexstring string `json:"hexString"`
|
||||
Rgb Rgb `json:"rgb"`
|
||||
Hsl Hsl `json:"hsl"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
//go:embed gen.tmpl
|
||||
var Template string
|
||||
|
||||
func main() {
|
||||
|
||||
var Cols []InputCol
|
||||
|
||||
resp, err := http.Get("https://jonasjacek.github.io/colors/data.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = json.Unmarshal(data, &Cols)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
t, err := template.New("cols").Parse(Template)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
err = t.Execute(&buffer, Cols)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.WriteFile(filepath.Join("..", "cols.go"), buffer.Bytes(), 0755)
|
||||
}
|
||||
29
v2/pkg/menu/colours/gen.tmpl
Normal file
29
v2/pkg/menu/colours/gen.tmpl
Normal file
@@ -0,0 +1,29 @@
|
||||
package menu
|
||||
|
||||
type Rgb struct {
|
||||
R uint8 `json:"r"`
|
||||
G uint8 `json:"g"`
|
||||
B uint8 `json:"b"`
|
||||
}
|
||||
|
||||
type Hsl struct {
|
||||
H float64 `json:"h"`
|
||||
S float64 `json:"s"`
|
||||
L float64 `json:"l"`
|
||||
}
|
||||
|
||||
type Col struct {
|
||||
Hex string `json:"hex"`
|
||||
Rgb Rgb `json:"rgb"`
|
||||
Hsl Hsl `json:"hsl"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
var Cols = []*Col{ {{range $col := .}}
|
||||
{
|
||||
Hex: "{{.Hexstring}}",
|
||||
Rgb: Rgb{ {{.Rgb.R}}, {{.Rgb.G}}, {{.Rgb.B}} },
|
||||
Hsl: Hsl{ {{.Hsl.H}}, {{.Hsl.S}}, {{.Hsl.L}} },
|
||||
Name: "{{.Name}}",
|
||||
},{{end}}
|
||||
}
|
||||
1559
v2/pkg/menu/cols.go
Executable file
1559
v2/pkg/menu/cols.go
Executable file
File diff suppressed because it is too large
Load Diff
256
v2/pkg/menu/styledlabel.go
Normal file
256
v2/pkg/menu/styledlabel.go
Normal file
@@ -0,0 +1,256 @@
|
||||
package menu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TextStyle int
|
||||
|
||||
const (
|
||||
Bold TextStyle = 1 << 0
|
||||
Faint TextStyle = 1 << 1
|
||||
Italic TextStyle = 1 << 2
|
||||
Blinking TextStyle = 1 << 3
|
||||
Inversed TextStyle = 1 << 4
|
||||
Invisible TextStyle = 1 << 5
|
||||
Underlined TextStyle = 1 << 6
|
||||
Strikethrough TextStyle = 1 << 7
|
||||
)
|
||||
|
||||
type StyledText struct {
|
||||
Label string
|
||||
FgCol *Col
|
||||
BgCol *Col
|
||||
Style TextStyle
|
||||
}
|
||||
|
||||
func (s *StyledText) Bold() bool {
|
||||
return s.Style&Bold == Bold
|
||||
}
|
||||
func (s *StyledText) Faint() bool {
|
||||
return s.Style&Faint == Faint
|
||||
}
|
||||
func (s *StyledText) Italic() bool {
|
||||
return s.Style&Italic == Italic
|
||||
}
|
||||
func (s *StyledText) Blinking() bool {
|
||||
return s.Style&Blinking == Blinking
|
||||
}
|
||||
func (s *StyledText) Inversed() bool {
|
||||
return s.Style&Inversed == Inversed
|
||||
}
|
||||
func (s *StyledText) Invisible() bool {
|
||||
return s.Style&Invisible == Invisible
|
||||
}
|
||||
func (s *StyledText) Underlined() bool {
|
||||
return s.Style&Underlined == Underlined
|
||||
}
|
||||
func (s *StyledText) Strikethrough() bool {
|
||||
return s.Style&Strikethrough == Strikethrough
|
||||
}
|
||||
|
||||
var ansiColorMap = map[string]map[string]*Col{
|
||||
"Normal": {
|
||||
"30": Cols[0],
|
||||
"31": Cols[1],
|
||||
"32": Cols[2],
|
||||
"33": Cols[3],
|
||||
"34": Cols[4],
|
||||
"35": Cols[5],
|
||||
"36": Cols[6],
|
||||
"37": Cols[7],
|
||||
},
|
||||
"Bold": {
|
||||
"30": Cols[8],
|
||||
"31": Cols[9],
|
||||
"32": Cols[10],
|
||||
"33": Cols[11],
|
||||
"34": Cols[12],
|
||||
"35": Cols[13],
|
||||
"36": Cols[14],
|
||||
"37": Cols[15],
|
||||
},
|
||||
"Faint": {
|
||||
"30": Cols[0],
|
||||
"31": Cols[1],
|
||||
"32": Cols[2],
|
||||
"33": Cols[3],
|
||||
"34": Cols[4],
|
||||
"35": Cols[5],
|
||||
"36": Cols[6],
|
||||
"37": Cols[7],
|
||||
},
|
||||
}
|
||||
|
||||
func ParseANSI(input string) ([]*StyledText, error) {
|
||||
var result []*StyledText
|
||||
invalid := fmt.Errorf("invalid ansi string")
|
||||
missingTerminator := fmt.Errorf("missing escape terminator 'm'")
|
||||
invalidTrueColorSequence := fmt.Errorf("invalid TrueColor sequence")
|
||||
invalid256ColSequence := fmt.Errorf("invalid 256 colour sequence")
|
||||
index := 0
|
||||
var currentStyledText *StyledText = &StyledText{}
|
||||
|
||||
if len(input) == 0 {
|
||||
return nil, invalid
|
||||
}
|
||||
|
||||
for {
|
||||
// Read all chars to next escape code
|
||||
esc := strings.Index(input, "\033[")
|
||||
|
||||
// If no more esc chars, save what's left and return
|
||||
if esc == -1 {
|
||||
text := input[index:]
|
||||
if len(text) > 0 {
|
||||
currentStyledText.Label = text
|
||||
result = append(result, currentStyledText)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
label := input[:esc]
|
||||
if len(label) > 0 {
|
||||
currentStyledText.Label = label
|
||||
result = append(result, currentStyledText)
|
||||
currentStyledText = &StyledText{
|
||||
Label: "",
|
||||
FgCol: currentStyledText.FgCol,
|
||||
BgCol: currentStyledText.BgCol,
|
||||
Style: currentStyledText.Style,
|
||||
}
|
||||
}
|
||||
input = input[esc:]
|
||||
// skip
|
||||
input = input[2:]
|
||||
|
||||
// Read in params
|
||||
endesc := strings.Index(input, "m")
|
||||
if endesc == -1 {
|
||||
return nil, missingTerminator
|
||||
}
|
||||
paramText := input[:endesc]
|
||||
input = input[endesc+1:]
|
||||
params := strings.Split(paramText, ";")
|
||||
colourMap := ansiColorMap["Normal"]
|
||||
skip := 0
|
||||
for index, param := range params {
|
||||
if skip > 0 {
|
||||
skip--
|
||||
continue
|
||||
}
|
||||
switch param {
|
||||
case "0":
|
||||
// Reset styles
|
||||
if len(params) == 1 {
|
||||
if len(currentStyledText.Label) > 0 {
|
||||
result = append(result, currentStyledText)
|
||||
currentStyledText = &StyledText{
|
||||
Label: "",
|
||||
FgCol: currentStyledText.FgCol,
|
||||
BgCol: currentStyledText.BgCol,
|
||||
Style: currentStyledText.Style,
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
currentStyledText.Style = 0
|
||||
currentStyledText.FgCol = nil
|
||||
currentStyledText.BgCol = nil
|
||||
case "1":
|
||||
// Bold
|
||||
colourMap = ansiColorMap["Bold"]
|
||||
currentStyledText.Style |= Bold
|
||||
case "2":
|
||||
// Dim/Feint
|
||||
colourMap = ansiColorMap["Faint"]
|
||||
currentStyledText.Style |= Faint
|
||||
case "3":
|
||||
// Italic
|
||||
currentStyledText.Style |= Italic
|
||||
case "4":
|
||||
// Underlined
|
||||
currentStyledText.Style |= Underlined
|
||||
case "5":
|
||||
// Blinking
|
||||
currentStyledText.Style |= Blinking
|
||||
case "7":
|
||||
// Inverse
|
||||
currentStyledText.Style |= Inversed
|
||||
case "8":
|
||||
// Invisible
|
||||
currentStyledText.Style |= Invisible
|
||||
case "9":
|
||||
// Strikethrough
|
||||
currentStyledText.Style |= Strikethrough
|
||||
case "30", "31", "32", "33", "34", "35", "36", "37":
|
||||
currentStyledText.FgCol = colourMap[param]
|
||||
case "40", "41", "42", "43", "44", "45", "46", "47":
|
||||
currentStyledText.BgCol = colourMap[param]
|
||||
case "38", "48":
|
||||
if len(params)-index < 2 {
|
||||
return nil, invalid
|
||||
}
|
||||
// 256 colours
|
||||
if params[index+1] == "5" {
|
||||
skip = 2
|
||||
colIndexText := params[index+2]
|
||||
colIndex, err := strconv.Atoi(colIndexText)
|
||||
if err != nil {
|
||||
return nil, invalid256ColSequence
|
||||
}
|
||||
if colIndex < 0 || colIndex > 255 {
|
||||
return nil, invalid256ColSequence
|
||||
}
|
||||
if param == "38" {
|
||||
currentStyledText.FgCol = Cols[colIndex]
|
||||
continue
|
||||
}
|
||||
currentStyledText.BgCol = Cols[colIndex]
|
||||
continue
|
||||
}
|
||||
// we must have 4 params left
|
||||
if len(params)-index < 4 {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
if params[index+1] != "2" {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
var r, g, b uint8
|
||||
ri, err := strconv.Atoi(params[index+2])
|
||||
if err != nil {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
gi, err := strconv.Atoi(params[index+3])
|
||||
if err != nil {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
bi, err := strconv.Atoi(params[index+4])
|
||||
if err != nil {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
if bi > 255 || gi > 255 || ri > 255 {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
if bi < 0 || gi < 0 || ri < 0 {
|
||||
return nil, invalidTrueColorSequence
|
||||
}
|
||||
r = uint8(ri)
|
||||
g = uint8(gi)
|
||||
b = uint8(bi)
|
||||
skip = 4
|
||||
colvalue := fmt.Sprintf("#%02x%02x%02x", r, g, b)
|
||||
if param == "38" {
|
||||
currentStyledText.FgCol = &Col{Hex: colvalue, Rgb: Rgb{r, g, b}}
|
||||
continue
|
||||
}
|
||||
currentStyledText.BgCol = &Col{Hex: colvalue}
|
||||
default:
|
||||
return nil, invalid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
197
v2/pkg/menu/styledlabel_test.go
Normal file
197
v2/pkg/menu/styledlabel_test.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package menu
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
func TestParseAnsi16SingleColour(t *testing.T) {
|
||||
is := is.New(t)
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantText string
|
||||
wantColor string
|
||||
wantErr bool
|
||||
}{
|
||||
{"No formatting", "Hello World", "Hello World", "", false},
|
||||
{"Black", "\u001b[0;30mHello World\033[0m", "Hello World", "Black", false},
|
||||
{"Red", "\u001b[0;31mHello World\033[0m", "Hello World", "Maroon", false},
|
||||
{"Green", "\u001b[0;32m\033[0m", "", "Green", false},
|
||||
{"Yellow", "\u001b[0;33m😀\033[0m", "😀", "Olive", false},
|
||||
{"Blue", "\u001b[0;34m123\033[0m", "123", "Navy", false},
|
||||
{"Purple", "\u001b[0;35m👩🏽🔧\u001B[0m", "👩🏽🔧", "Purple", false},
|
||||
{"Cyan", "\033[0;36m😀\033[0m", "😀", "Teal", false},
|
||||
{"White", "\u001b[0;37m[0;37m\033[0m", "[0;37m", "Silver", false},
|
||||
{"Black Bold", "\u001b[1;30mHello World\033[0m", "Hello World", "Grey", false},
|
||||
{"Red Bold", "\u001b[1;31mHello World\033[0m", "Hello World", "Red", false},
|
||||
{"Green Bold", "\u001b[1;32m\033[0m", "", "Lime", false},
|
||||
{"Yellow Bold", "\u001b[1;33m😀\033[0m", "😀", "Yellow", false},
|
||||
{"Blue Bold", "\u001b[1;34m123\033[0m", "123", "Blue", false},
|
||||
{"Purple Bold", "\u001b[1;35m👩🏽🔧\u001B[0m", "👩🏽🔧", "Fuchsia", false},
|
||||
{"Cyan Bold", "\033[1;36m😀\033[0m", "😀", "Aqua", false},
|
||||
{"White Bold", "\u001b[1;37m[0;37m\033[0m", "[0;37m", "White", false},
|
||||
{"Blank", "", "", "", true},
|
||||
{"Emoji", "😀👩🏽🔧", "😀👩🏽🔧", "", false},
|
||||
{"Spaces", " ", " ", "", false},
|
||||
{"Bad code", "\u001b[1 ", "", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseANSI(tt.input)
|
||||
is.Equal(err != nil, tt.wantErr)
|
||||
expectedLength := 1
|
||||
if tt.wantErr {
|
||||
expectedLength = 0
|
||||
}
|
||||
is.Equal(len(got), expectedLength)
|
||||
if expectedLength == 1 {
|
||||
if len(tt.wantColor) > 0 {
|
||||
is.True(got[0].FgCol != nil)
|
||||
is.Equal(got[0].FgCol.Name, tt.wantColor)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAnsi16MultiColour(t *testing.T) {
|
||||
is := is.New(t)
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []*StyledText
|
||||
wantErr bool
|
||||
}{
|
||||
{"Black & Red", "\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Black"}},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Maroon"}},
|
||||
}, false},
|
||||
{"Text then Black & Red", "This is great!\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{
|
||||
{Label: "This is great!"},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Black"}},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Maroon"}},
|
||||
}, false},
|
||||
{"Text Reset then Black & Red", "This is great!\u001B[0m\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{
|
||||
{Label: "This is great!"},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Black"}},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Maroon"}},
|
||||
}, false},
|
||||
{"Black & Red no reset", "\u001B[0;30mHello World\u001B[0;31mHello World", []*StyledText{
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Black"}},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Maroon"}},
|
||||
}, false},
|
||||
{"Black,space,Red", "\u001B[0;30mHello World\u001B[0m \u001B[0;31mHello World\u001B[0m", []*StyledText{
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Black"}},
|
||||
{Label: " "},
|
||||
{Label: "Hello World", FgCol: &Col{Name: "Maroon"}},
|
||||
}, false},
|
||||
{"Black,Red,Blue,Green underlined", "\033[4;30mBlack\u001B[0m\u001B[4;31mRed\u001B[0m\u001B[4;34mBlue\u001B[0m\u001B[4;32mGreen\u001B[0m", []*StyledText{
|
||||
{Label: "Black", FgCol: &Col{Name: "Black"}, Style: Underlined},
|
||||
{Label: "Red", FgCol: &Col{Name: "Maroon"}, Style: Underlined},
|
||||
{Label: "Blue", FgCol: &Col{Name: "Navy"}, Style: Underlined},
|
||||
{Label: "Green", FgCol: &Col{Name: "Green"}, Style: Underlined},
|
||||
}, false},
|
||||
{"Black,Red,Blue,Green bold", "\033[1;30mBlack\u001B[0m\u001B[1;31mRed\u001B[0m\u001B[1;34mBlue\u001B[0m\u001B[1;32mGreen\u001B[0m", []*StyledText{
|
||||
{Label: "Black", FgCol: &Col{Name: "Grey"}, Style: Bold},
|
||||
{Label: "Red", FgCol: &Col{Name: "Red"}, Style: Bold},
|
||||
{Label: "Blue", FgCol: &Col{Name: "Blue"}, Style: Bold},
|
||||
{Label: "Green", FgCol: &Col{Name: "Lime"}, Style: Bold},
|
||||
}, false},
|
||||
{"Green Feint & Yellow Italic", "\u001B[2;32m👩🏽🔧\u001B[0m\u001B[0;3;33m👩🏽🔧\u001B[0m", []*StyledText{
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Green"}, Style: Faint},
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Olive"}, Style: Italic},
|
||||
}, false},
|
||||
{"Green Blinking & Yellow Inversed", "\u001B[5;32m👩🏽🔧\u001B[0m\u001B[0;7;33m👩🏽🔧\u001B[0m", []*StyledText{
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Green"}, Style: Blinking},
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Olive"}, Style: Inversed},
|
||||
}, false},
|
||||
{"Green Invisible & Yellow Invisible & Strikethrough", "\u001B[8;32m👩🏽🔧\u001B[0m\u001B[9;33m👩🏽🔧\u001B[0m", []*StyledText{
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Green"}, Style: Invisible},
|
||||
{Label: "👩🏽🔧", FgCol: &Col{Name: "Olive"}, Style: Invisible | Strikethrough},
|
||||
}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseANSI(tt.input)
|
||||
is.Equal(err != nil, tt.wantErr)
|
||||
for index, w := range tt.want {
|
||||
is.Equal(got[index].Label, w.Label)
|
||||
if w.FgCol != nil {
|
||||
is.Equal(got[index].FgCol.Name, w.FgCol.Name)
|
||||
}
|
||||
is.Equal(got[index].Style, w.Style)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAnsi256(t *testing.T) {
|
||||
is := is.New(t)
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []*StyledText
|
||||
wantErr bool
|
||||
}{
|
||||
{"Grey93 & DarkViolet", "\u001B[38;5;255mGrey93\u001B[0m\u001B[38;5;128mDarkViolet\u001B[0m", []*StyledText{
|
||||
{Label: "Grey93", FgCol: &Col{Name: "Grey93"}},
|
||||
{Label: "DarkViolet", FgCol: &Col{Name: "DarkViolet"}},
|
||||
}, false},
|
||||
{"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;255mGrey93\u001B[0m\u001B[0;3;38;5;128mDarkViolet\u001B[0m", []*StyledText{
|
||||
{Label: "Grey93", FgCol: &Col{Name: "Grey93"}, Style: Bold},
|
||||
{Label: "DarkViolet", FgCol: &Col{Name: "DarkViolet"}, Style: Italic},
|
||||
}, false},
|
||||
{"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;256mGrey93\u001B[0m", nil, true},
|
||||
{"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;-1mGrey93\u001B[0m", nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseANSI(tt.input)
|
||||
is.Equal(err != nil, tt.wantErr)
|
||||
for index, w := range tt.want {
|
||||
is.Equal(got[index].Label, w.Label)
|
||||
if w.FgCol != nil {
|
||||
is.Equal(got[index].FgCol.Name, w.FgCol.Name)
|
||||
}
|
||||
is.Equal(got[index].Style, w.Style)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAnsiTrueColor(t *testing.T) {
|
||||
is := is.New(t)
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []*StyledText
|
||||
wantErr bool
|
||||
}{
|
||||
{"Red", "\u001B[38;2;255;0;0mRed\u001B[0m", []*StyledText{
|
||||
{Label: "Red", FgCol: &Col{Rgb: Rgb{255, 0, 0}, Hex: "#ff0000"}},
|
||||
}, false},
|
||||
{"Red, text, Green", "\u001B[38;2;255;0;0mRed\u001B[0mI am plain text\u001B[38;2;0;255;0mGreen\u001B[0m", []*StyledText{
|
||||
{Label: "Red", FgCol: &Col{Rgb: Rgb{255, 0, 0}, Hex: "#ff0000"}},
|
||||
{Label: "I am plain text"},
|
||||
{Label: "Green", FgCol: &Col{Rgb: Rgb{0, 255, 0}, Hex: "#00ff00"}},
|
||||
}, false},
|
||||
{"Bad 1", "\u001B[38;2;256;0;0mRed\u001B[0m", nil, true},
|
||||
{"Bad 2", "\u001B[38;2;-1;0;0mRed\u001B[0m", nil, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseANSI(tt.input)
|
||||
is.Equal(err != nil, tt.wantErr)
|
||||
for index, w := range tt.want {
|
||||
is.Equal(got[index].Label, w.Label)
|
||||
if w.FgCol != nil {
|
||||
is.Equal(got[index].FgCol.Hex, w.FgCol.Hex)
|
||||
is.Equal(got[index].FgCol.Rgb, w.FgCol.Rgb)
|
||||
}
|
||||
is.Equal(got[index].Style, w.Style)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -15,5 +15,5 @@ type Options struct {
|
||||
WebviewIsTransparent bool
|
||||
WindowIsTranslucent bool
|
||||
ActivationPolicy ActivationPolicy
|
||||
URLHandlers map[string]func(string)
|
||||
//URLHandlers map[string]func(string)
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package options
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
@@ -26,6 +28,7 @@ type App struct {
|
||||
MaxHeight int
|
||||
StartHidden bool
|
||||
HideWindowOnClose bool
|
||||
AlwaysOnTop bool
|
||||
RGBA *RGBA
|
||||
Assets embed.FS
|
||||
Menu *menu.Menu
|
||||
@@ -39,7 +42,7 @@ type App struct {
|
||||
//ContextMenus []*menu.ContextMenu
|
||||
//TrayMenus []*menu.TrayMenu
|
||||
Windows *windows.Options
|
||||
//Mac *mac.Options
|
||||
Mac *mac.Options
|
||||
}
|
||||
|
||||
type RGBA struct {
|
||||
|
||||
@@ -2,6 +2,7 @@ package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
@@ -53,6 +54,11 @@ func WindowSetSize(ctx context.Context, width int, height int) {
|
||||
appFrontend.WindowSetSize(width, height)
|
||||
}
|
||||
|
||||
func WindowGetSize(ctx context.Context) (int, int) {
|
||||
appFrontend := getFrontend(ctx)
|
||||
return appFrontend.WindowGetSize()
|
||||
}
|
||||
|
||||
// WindowSetMinSize sets the minimum size of the window
|
||||
func WindowSetMinSize(ctx context.Context, width int, height int) {
|
||||
appFrontend := getFrontend(ctx)
|
||||
@@ -71,6 +77,11 @@ func WindowSetPosition(ctx context.Context, x int, y int) {
|
||||
appFrontend.WindowSetPos(x, y)
|
||||
}
|
||||
|
||||
func WindowGetPos(ctx context.Context) (int, int) {
|
||||
appFrontend := getFrontend(ctx)
|
||||
return appFrontend.WindowGetPos()
|
||||
}
|
||||
|
||||
// WindowMaximise the window
|
||||
func WindowMaximise(ctx context.Context) {
|
||||
appFrontend := getFrontend(ctx)
|
||||
|
||||
@@ -25,3 +25,6 @@ Example: `wails init -n "Your Project Name" -t https://github.com/misitebao/wail
|
||||
- [wails-vite-vue-ts](https://github.com/codydbentley/wails-vite-vue-ts) - Vue 3 TypeScript with Vite (and instructions to add features)
|
||||
- [wails-vite-vue-the-works](https://github.com/codydbentley/wails-vite-vue-the-works) - Vue 3 TypeScript with Vite, Vuex, Vue Router, Sass, and ESLint + Prettier
|
||||
|
||||
## Angular
|
||||
|
||||
- [wails-angular-template](https://github.com/TAINCER/wails-angular-template) - Angular with TypeScript, Sass, Hot-Reload, Code-Splitting and i18n
|
||||
|
||||
@@ -20,6 +20,9 @@ sidebar_position: 99
|
||||
<a href="https://github.com/snider" style="width:100px;">
|
||||
<img src="https://github.com/snider.png?size=100" width="100"/>
|
||||
</a>
|
||||
<a href="https://github.com/codydbentley" style="width:100px">
|
||||
<img src="https://github.com/codydbentley.png?size=100" width="100"/>
|
||||
</a>
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://github.com/matryer" style="width:100px">
|
||||
@@ -58,11 +61,8 @@ sidebar_position: 99
|
||||
<a href="https://github.com/jugglingjsons" style="width:50px">
|
||||
<img src="https://github.com/jugglingjsons.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/marcus-crane" style="width:50px">
|
||||
<img src="https://github.com/marcus-crane.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/codydbentley" style="width:65px">
|
||||
<img src="https://github.com/codydbentley.png?size=65" width="65"/>
|
||||
<a href="https://github.com/marcus-crane" style="width:65px">
|
||||
<img src="https://github.com/marcus-crane.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/bbergshaven" style="width:45px">
|
||||
<img src="https://github.com/bbergshaven.png?size=45" width="45"/>
|
||||
@@ -70,6 +70,18 @@ sidebar_position: 99
|
||||
<a href="https://github.com/Gilgames000" style="width:45px">
|
||||
<img src="https://github.com/Gilgames000.png?size=45" width="45"/>
|
||||
</a>
|
||||
<a href="https://github.com/ilgityildirim" style="width:50px">
|
||||
<img src="https://github.com/ilgityildirim.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/ondoki" style="width:65px">
|
||||
<img src="https://github.com/ondoki.png?size=65" width="65"/>
|
||||
</a>
|
||||
<a href="https://github.com/questrail" style="width:50px">
|
||||
<img src="https://github.com/questrail.png?size=50" width="50"/>
|
||||
</a>
|
||||
<a href="https://github.com/DonTomato" style="width:45px">
|
||||
<img src="https://github.com/DonTomato.png?size=45" width="45"/>
|
||||
</a>
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -62,7 +62,7 @@ import TabItem from "@theme/TabItem";
|
||||
|
||||
## Installing Wails
|
||||
|
||||
Run `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.12` to install the Wails CLI.
|
||||
Run `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.15` to install the Wails CLI.
|
||||
|
||||
## System Check
|
||||
|
||||
|
||||
11
website/docs/guides/overscroll.mdx
Normal file
11
website/docs/guides/overscroll.mdx
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
# Overscroll
|
||||
|
||||
[Overscroll](https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior) is the "bounce effect" you sometimes
|
||||
get when you scroll beyond a page's content boundaries. This is common in mobile apps. This can be disabled using CSS:
|
||||
|
||||
```css
|
||||
body {
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
```
|
||||
@@ -168,6 +168,14 @@ Example: 0xFF000088 - Red at 50% transparency
|
||||
This value is the RGBA value to set the window by default.
|
||||
Default: 0xFFFFFFFF.
|
||||
|
||||
### AlwaysOnTop
|
||||
|
||||
Name: AlwaysOnTop
|
||||
|
||||
Type: bool
|
||||
|
||||
Indicates that the window should stay above other windows when losing focus.
|
||||
|
||||
### Assets
|
||||
|
||||
Name: Assets
|
||||
|
||||
@@ -6,50 +6,59 @@ sidebar_position: 5
|
||||
|
||||
## Overview
|
||||
|
||||
This part of the runtime provides access to native dialogs, such as File Selectors and Message boxes.Context
|
||||
This part of the runtime provides access to native dialogs, such as File Selectors and Message boxes.
|
||||
|
||||
:::info Javascript
|
||||
Dialog is currently unsupported in the JS runtime.
|
||||
:::
|
||||
|
||||
### OpenDirectoryDialog
|
||||
|
||||
Opens a dialog that prompts the user to select a directory. Can be customised using [OpenDialogOptions](#opendialogoptions).
|
||||
|
||||
Go Signature: `OpenDirectoryDialog(ctx context.Context, dialogOptions OpenDialogOptions) (string, error)`
|
||||
|
||||
Returns: Selected directory (blank if the user cancelled) or an error
|
||||
|
||||
Opens a dialog that prompts the user to select a directory. Can be customised using [OpenDialogOptions](#OpenDialogOptions).
|
||||
|
||||
### OpenFileDialog
|
||||
|
||||
Opens a dialog that prompts the user to select a file. Can be customised using [OpenDialogOptions](#opendialogoptions).
|
||||
|
||||
Go Signature: `OpenFileDialog(ctx context.Context, dialogOptions OpenDialogOptions) (string, error)`
|
||||
|
||||
Returns: Selected file (blank if the user cancelled) or an error
|
||||
|
||||
Opens a dialog that prompts the user to select a file. Can be customised using [OpenDialogOptions](#OpenDialogOptions).
|
||||
|
||||
|
||||
### OpenMultipleFilesDialog
|
||||
|
||||
Opens a dialog that prompts the user to select multiple files. Can be customised using [OpenDialogOptions](#opendialogoptions).
|
||||
|
||||
Go Signature: `OpenMultipleFilesDialog(ctx context.Context, dialogOptions OpenDialogOptions) ([]string, error)`
|
||||
|
||||
Returns: Selected files (nil if the user cancelled) or an error
|
||||
|
||||
Opens a dialog that prompts the user to select multiple files. Can be customised using [OpenDialogOptions](#OpenDialogOptions).
|
||||
|
||||
|
||||
### SaveFileDialog
|
||||
|
||||
Opens a dialog that prompts the user to select a filename for the purposes of saving. Can be customised using [SaveDialogOptions](#savedialogoptions).
|
||||
|
||||
Go Signature: `SaveFileDialog(ctx context.Context, dialogOptions SaveDialogOptions) (string, error)`
|
||||
|
||||
Returns: The selected file (blank if the user cancelled) or an error
|
||||
|
||||
Opens a dialog that prompts the user to select a filename for the purposes of saving. Can be customised using [SaveDialogOptions](#SaveDialogOptions).
|
||||
|
||||
|
||||
### MessageDialog
|
||||
|
||||
Displays a message using a message dialog. Can be customised using [MessageDialogOptions](#messagedialogoptions).
|
||||
|
||||
Go Signature: `MessageDialog(ctx context.Context, dialogOptions MessageDialogOptions) (string, error)`
|
||||
|
||||
Returns: The text of the selected button or an error
|
||||
|
||||
Displays a message using a message dialog. Can be customised using [MessageDialogOptions](#MessageDialogOptions).
|
||||
|
||||
## Options
|
||||
|
||||
### OpenDialogOptions
|
||||
@@ -60,14 +69,27 @@ type OpenDialogOptions struct {
|
||||
DefaultFilename string
|
||||
Title string
|
||||
Filters []FileFilter
|
||||
AllowFiles bool // Mac Only
|
||||
AllowDirectories bool // Mac Only
|
||||
ShowHiddenFiles bool // Mac Only
|
||||
CanCreateDirectories bool // Mac Only
|
||||
ResolvesAliases bool // Mac Only
|
||||
TreatPackagesAsDirectories bool // Mac Only
|
||||
AllowFiles bool
|
||||
AllowDirectories bool
|
||||
ShowHiddenFiles bool
|
||||
CanCreateDirectories bool
|
||||
ResolvesAliases bool
|
||||
TreatPackagesAsDirectories bool
|
||||
}
|
||||
```
|
||||
| Field | Description | Win | Mac |
|
||||
| -------------------------- | ---------------------------------------------- | --- | --- |
|
||||
| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ |
|
||||
| DefaultFilename | The default filename | ✅ | ✅ |
|
||||
| Title | Title for the dialog | ✅ | ✅ |
|
||||
| [Filters](#filefilter) | A list of file filters | ✅ | ✅ |
|
||||
| AllowFiles | Allow files to be selected | | ✅ |
|
||||
| AllowDirectories | Allow directories to be selected | | ✅ |
|
||||
| ShowHiddenFiles | Show files hidden by the system | | ✅ |
|
||||
| CanCreateDirectories | Allow user to create directories | | ✅ |
|
||||
| ResolvesAliases | If true, returns the file not the alias | | ✅ |
|
||||
| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ |
|
||||
|
||||
|
||||
### SaveDialogOptions
|
||||
|
||||
@@ -77,12 +99,22 @@ type SaveDialogOptions struct {
|
||||
DefaultFilename string
|
||||
Title string
|
||||
Filters []FileFilter
|
||||
ShowHiddenFiles bool // Mac Only
|
||||
CanCreateDirectories bool // Mac Only
|
||||
TreatPackagesAsDirectories bool // Mac Only
|
||||
ShowHiddenFiles bool
|
||||
CanCreateDirectories bool
|
||||
TreatPackagesAsDirectories bool
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Description | Win | Mac |
|
||||
| -------------------------- | ---------------------------------------------- | --- | --- |
|
||||
| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ |
|
||||
| DefaultFilename | The default filename | ✅ | ✅ |
|
||||
| Title | Title for the dialog | ✅ | ✅ |
|
||||
| [Filters](#filefilter) | A list of file filters | ✅ | ✅ |
|
||||
| ShowHiddenFiles | Show files hidden by the system | | ✅ |
|
||||
| CanCreateDirectories | Allow user to create directories | | ✅ |
|
||||
| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ |
|
||||
|
||||
### MessageDialogOptions
|
||||
|
||||
```go
|
||||
@@ -91,33 +123,76 @@ type MessageDialogOptions struct {
|
||||
Title string
|
||||
Message string
|
||||
Buttons []string
|
||||
DefaultButton string // Mac Only
|
||||
CancelButton string // Mac Only
|
||||
Icon string // Mac Only
|
||||
DefaultButton string
|
||||
CancelButton string
|
||||
}
|
||||
```
|
||||
| Field | Description | Win | Mac |
|
||||
| ------------- | ------------------------------------------------------------------------- | --- | --- |
|
||||
| Type | The type of message dialog, eg question, info... | ✅ | ✅ |
|
||||
| Title | Title for the dialog | ✅ | ✅ |
|
||||
| Message | The message to show the user | ✅ | ✅ |
|
||||
| Buttons | A list of button titles | | ✅ |
|
||||
| DefaultButton | The button with this text should be treated as default. Bound to `return` | | ✅ |
|
||||
| CancelButton | The button with this text should be treated as cancel. Bound to `escape` | | ✅ |
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
#### Windows
|
||||
|
||||
Windows has standard dialog types in which the buttons are not customisable.
|
||||
The value returned will be one of: "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "Try Again" or "Continue"
|
||||
|
||||
#### Mac
|
||||
|
||||
A message dialog on Mac may specify up to 4 buttons. If no `DefaultButton` or `CancelButton` is given, the first button
|
||||
is considered default and is bound to the `return` key.
|
||||
|
||||
For the following code:
|
||||
```go
|
||||
selection, err := runtime.MessageDialog(b.ctx, runtime.MessageDialogOptions{
|
||||
Title: "It's your turn!",
|
||||
Message: "Select a number",
|
||||
Buttons: []string{"one", "two", "three", "four"},
|
||||
})
|
||||
```
|
||||
the first button is shown as default:
|
||||
<div class="text--center">
|
||||
<img src="/img/runtime/dialog_no_defaults.png" width="30%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
And if we specify `DefaultButton` to be "two":
|
||||
```go
|
||||
selection, err := runtime.MessageDialog(b.ctx, runtime.MessageDialogOptions{
|
||||
Title: "It's your turn!",
|
||||
Message: "Select a number",
|
||||
Buttons: []string{"one", "two", "three", "four"},
|
||||
DefaultButton: "two",
|
||||
})
|
||||
```
|
||||
the second button is shown as default. When `return` is pressed, the value "two" is returned.
|
||||
<div class="text--center">
|
||||
<img src="/img/runtime/dialog_default_button.png" width="30%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
If we now specify `CancelButton` to be "three":
|
||||
```go
|
||||
selection, err := runtime.MessageDialog(b.ctx, runtime.MessageDialogOptions{
|
||||
Title: "It's your turn!",
|
||||
Message: "Select a number",
|
||||
Buttons: []string{"one", "two", "three", "four"},
|
||||
DefaultButton: "two",
|
||||
CancelButton: "three",
|
||||
})
|
||||
```
|
||||
the button with "three" is shown at the bottom of the dialog. When `escape` is pressed, the value "three" is returned:
|
||||
<div class="text--center">
|
||||
<img src="/img/runtime/dialog_default_cancel.png" width="30%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<Tabs
|
||||
defaultValue="Windows"
|
||||
values={[
|
||||
{label: 'Windows', value: 'Windows'},
|
||||
{label: 'MacOS', value: 'MacOS'},
|
||||
{label: 'Linux', value: 'Linux'},
|
||||
]}>
|
||||
<TabItem value="MacOS">
|
||||
Both "DefaultButton" and "CancelButton" should match a value in "Buttons".
|
||||
</TabItem>
|
||||
<TabItem value="Windows">
|
||||
Windows has standard dialog types and the buttons are not customisable. The
|
||||
value returned will be one of: "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "Try Again" or "Continue"
|
||||
</TabItem>
|
||||
<TabItem value="Linux">
|
||||
Coming Soon...
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### DialogType
|
||||
|
||||
@@ -138,3 +213,37 @@ type FileFilter struct {
|
||||
Pattern string // semi-colon separated list of extensions, EG: "*.jpg;*.png"
|
||||
}
|
||||
```
|
||||
|
||||
#### Windows
|
||||
|
||||
Windows allows you to use multiple file filters in dialog boxes. Each FileFilter will show up as a separate entry in the
|
||||
dialog:
|
||||
|
||||
<div class="text--center">
|
||||
<img src="/img/runtime/dialog_win_filters.png" width="50%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
#### Mac
|
||||
|
||||
Mac dialogs only have the concept of a single set of patterns to filter files. If multiple FileFilters are provided,
|
||||
Wails will use all the Patterns defined.
|
||||
|
||||
Example:
|
||||
```go
|
||||
selection, err := runtime.OpenFileDialog(b.ctx, runtime.OpenDialogOptions{
|
||||
Title: "Select File",
|
||||
Filters: []runtime.FileFilter{
|
||||
{
|
||||
DisplayName: "Images (*.png;*.jpg)",
|
||||
Pattern: "*.png;*.jpg",
|
||||
}, {
|
||||
DisplayName: "Videos (*.mov;*.mp4)",
|
||||
Pattern: "*.mov;*.mp4",
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
This will result in the Open File dialog using `*.png,*.jpg,*.mov,*.mp4` as a filter.
|
||||
@@ -44,8 +44,8 @@ This method sets up a listener for the given event name, but will only trigger a
|
||||
|
||||
### EventsEmit
|
||||
|
||||
Go Signature: `Events(ctx context.Context, eventName string, optionalData ...interface{})`
|
||||
Go Signature: `EventsEmit(ctx context.Context, eventName string, optionalData ...interface{})`
|
||||
|
||||
JS Signature: `Events(ctx context, optionalData function(optionalData?: any))`
|
||||
JS Signature: `EventsEmit(ctx context, optionalData function(optionalData?: any))`
|
||||
|
||||
This method emits the given event. Optional data may be passed with the event. This will trigger any event listeners.
|
||||
|
||||
@@ -174,9 +174,9 @@ interface Size {
|
||||
|
||||
```ts
|
||||
interface RGBA {
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -63,7 +63,7 @@ import TabItem from "@theme/TabItem";
|
||||
|
||||
## 安装 Wails
|
||||
|
||||
运行 `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.12` 安装 Wails CLI。
|
||||
运行 `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.15` 安装 Wails CLI。
|
||||
|
||||
## 系统检查
|
||||
|
||||
|
||||
@@ -189,9 +189,9 @@ interface Size {
|
||||
|
||||
```ts
|
||||
interface RGBA {
|
||||
r;
|
||||
g;
|
||||
b;
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
a: number;
|
||||
}
|
||||
```
|
||||
|
||||
15652
website/package-lock.json
generated
15652
website/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
BIN
website/static/img/runtime/dialog_default_button.png
Normal file
BIN
website/static/img/runtime/dialog_default_button.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
website/static/img/runtime/dialog_default_cancel.png
Normal file
BIN
website/static/img/runtime/dialog_default_cancel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
BIN
website/static/img/runtime/dialog_no_defaults.png
Normal file
BIN
website/static/img/runtime/dialog_no_defaults.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
BIN
website/static/img/runtime/dialog_win_filters.png
Normal file
BIN
website/static/img/runtime/dialog_win_filters.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
Reference in New Issue
Block a user