Compare commits

...

24 Commits

Author SHA1 Message Date
Lea Anthony
9735bd1b01 [website] v2.0.0-beta.21 2021-11-24 06:58:09 +11:00
Lea Anthony
cd8bad58cd [v] v2.0.0-beta.21 2021-11-24 06:42:34 +11:00
Lea Anthony
53a3cd9422 Merge pull request #965 from stffabi/feature/winguildflag-crosscompile
[v2] Add windowsgui ldflag when crosscompiling for windows
2021-11-23 10:39:10 -08:00
stffabi
5e2f25af9b [v2] Add windowsgui ldflag when crosscompiling for windows 2021-11-23 14:16:50 +01:00
Lea Anthony
f5f89c31eb Merge pull request #955 from letheanVPN/generated-project-helper
Helper NPM scripts for developers working on a Wails GUI project
2021-11-23 03:50:29 -08:00
Snider
46cb34f2ec Adds a package.json with basic start / setup npm scripts to assist non golang developers working on an end project. 2021-11-23 11:22:25 +00:00
Lea Anthony
f6f13540c8 Merge pull request #954 from stffabi/feature/fix-mac-bgcolor
[macOS] Fix background color
2021-11-23 03:06:33 -08:00
stffabi
21ce7709ab [macOS] Fix background color 2021-11-23 11:18:00 +01:00
Lea Anthony
d569e37b81 [mac] Fix open panel in dev 2021-11-23 21:04:01 +11:00
Lea Anthony
c9c6edeb84 [mac] Fix fullscreen / start fullscreen 2021-11-23 20:06:56 +11:00
Lea Anthony
9525667ebd [windows] Add Frameless resize 2021-11-22 06:47:09 +11:00
Lea Anthony
28a3d86348 Merge pull request #951 from letheanVPN/snider-patch-1
Update Lethean project url
2021-11-20 01:07:43 -08:00
Snider
fc8aa58e62 Update Lethean project url 2021-11-20 07:10:41 +00:00
Lea Anthony
cb2bbacae8 Fix website 2021-11-20 18:02:23 +11:00
Lea Anthony
c3c6261a2d Update sponsors 2021-11-20 17:55:17 +11:00
Lea Anthony
8bfec24108 Support slices + out params in Models.ts generation. Update website with runtime info 2021-11-18 17:54:09 +11:00
Lea Anthony
9ad2665ad8 [mac] Conditionally import UTType header 2021-11-17 21:13:25 +11:00
Lea Anthony
28894868e3 [mac] Fix for file filters MacOS 11+. Some memory leak fixes. 2021-11-17 21:03:40 +11:00
Lea Anthony
a8fcd994c9 Merge branch '930_-_default_window_state' 2021-11-16 18:23:43 +11:00
Lea Anthony
3a93c08813 [linux] basic windowing 2021-11-14 22:40:37 +11:00
Lea Anthony
ab1469638f [linux] get compiling working 2021-11-13 17:06:48 -08:00
Lea Anthony
9073caf287 Add build flag 2021-11-13 16:31:57 -08:00
Lea Anthony
1bed8234c9 [v2] Fix OnShutdown for production build 2021-11-11 06:05:50 +11:00
Lea Anthony
621c70253d [v2] Remove build constraint for DesktopIPC 2021-11-10 18:12:46 +11:00
49 changed files with 1297 additions and 211 deletions

View File

@@ -88,6 +88,14 @@ Click [here](https://wails.io) if you are interested in trying out v2 Beta for W
This project is supported by these kind people / companies:
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="sponsors/silver%20sponsor.png" width="100"/>
</a>
<a href="https://github.com/letheanVPN" style="width:100px;">
<img src="https://github.com/letheanVPN.png?size=100" width="100"/>
</a>
<br/>
<br/>
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="sponsors/bronze%20sponsor.png" width="100"/>
</a>

View File

@@ -91,6 +91,14 @@
这个项目由以下这些人或者公司支持:
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="sponsors/silver%20sponsor.png" width="100"/>
</a>
<a href="https://github.com/letheanVPN" style="width:100px;">
<img src="https://github.com/letheanVPN.png?size=100" width="100"/>
</a>
<br/>
<br/>
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="sponsors/bronze%20sponsor.png" width="100"/>
</a>

BIN
sponsors/silver sponsor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -99,7 +99,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
"darwin/amd64",
"darwin/arm64",
"darwin/universal",
//"linux",
"linux",
//"linux/amd64",
//"linux/arm-7",
"windows",

View File

@@ -278,7 +278,7 @@ func generateBuildOptions(flags devFlags) *build.Options {
OutputType: "dev",
Mode: build.Dev,
Arch: runtime.GOARCH,
Pack: false,
Pack: true,
Platform: runtime.GOOS,
LDFlags: flags.ldflags,
Compiler: flags.compilerCommand,
@@ -287,10 +287,7 @@ func generateBuildOptions(flags devFlags) *build.Options {
Verbosity: flags.verbosity,
WailsJSDir: flags.wailsjsdir,
}
switch runtime.GOOS {
case "darwin":
result.Pack = false
}
return result
}

View File

@@ -0,0 +1,12 @@
{
"private": true,
"scripts": {
"postinstall": "npm run setup && cd frontend && npm install",
"build": "wails build --clean",
"build:macos": "npm run build -- --platform darwin/universal",
"build:macos-arm": "npm run build -- --platform darwin/arm64",
"build:macos-intel": "npm run build -- --platform darwin",
"build:windows": "npm run build -- --platform windows/amd64",
"setup": "go install github.com/wailsapp/wails/v2/cmd/wails@latest"
}
}

View File

@@ -1,3 +1,3 @@
package internal
var Version = "v2.0.0-beta.20"
var Version = "v2.0.0-beta.21"

View File

@@ -49,6 +49,8 @@ require (
nhooyr.io/websocket v1.8.6
)
require github.com/gotk3/gotk3 v0.6.1
require (
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/andybalholm/brotli v1.0.2 // indirect

View File

@@ -82,6 +82,8 @@ github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gotk3/gotk3 v0.6.1 h1:GJ400a0ecEEWrzjBvzBzH+pB/esEMIGdB9zPSmBdoeo=
github.com/gotk3/gotk3 v0.6.1/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmAMQ=

View File

@@ -0,0 +1,32 @@
//go:build !dev && !production && !bindings && linux
package appng
import (
"fmt"
"github.com/wailsapp/wails/v2/pkg/options"
)
// App defines a Wails application structure
type App struct{}
func (a *App) Run() error {
return nil
}
// CreateApp creates the app!
func CreateApp(_ *options.App) (*App, error) {
// result := w32.MessageBox(0,
// `Wails applications will not build without the correct build tags.
//Please use "wails build" or press "OK" to open the documentation on how to use "go build"`,
// "Error",
// w32.MB_ICONERROR|w32.MB_OKCANCEL)
// if result == 1 {
// exec.Command("rundll32", "url.dll,FileProtocolHandler", "https://wails.io").Start()
// }
err := fmt.Errorf(`Wails applications will not build without the correct build tags.`)
return nil, err
}

View File

@@ -0,0 +1,15 @@
//go:build linux && !bindings
package appng
import (
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/pkg/options"
)
func PreflightChecks(options *options.App, logger *logger.Logger) error {
_ = options
return nil
}

View File

@@ -35,6 +35,9 @@ type App struct {
func (a *App) Run() error {
err := a.frontend.Run(a.ctx)
if a.shutdownCallback != nil {
a.shutdownCallback(a.ctx)
}
return err
}

View File

@@ -78,10 +78,16 @@ func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
input := methodType.In(inputIndex)
thisParam := newParameter("", input)
thisInput := input
if thisInput.Kind() == reflect.Slice {
thisInput = thisInput.Elem()
}
// Process struct pointer params
if input.Kind() == reflect.Ptr {
if input.Elem().Kind() == reflect.Struct {
typ := input.Elem()
if thisInput.Kind() == reflect.Ptr {
if thisInput.Elem().Kind() == reflect.Struct {
typ := thisInput.Elem()
a := reflect.New(typ)
s := reflect.Indirect(a).Interface()
b.converter.Add(s)
@@ -89,8 +95,8 @@ func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
}
// Process struct params
if input.Kind() == reflect.Struct {
a := reflect.New(input)
if thisInput.Kind() == reflect.Struct {
a := reflect.New(thisInput)
s := reflect.Indirect(a).Interface()
b.converter.Add(s)
}
@@ -108,6 +114,30 @@ func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ {
output := methodType.Out(outputIndex)
thisParam := newParameter("", output)
thisOutput := output
if thisOutput.Kind() == reflect.Slice {
thisOutput = thisOutput.Elem()
}
// Process struct pointer params
if thisOutput.Kind() == reflect.Ptr {
if thisOutput.Elem().Kind() == reflect.Struct {
typ := thisOutput.Elem()
a := reflect.New(typ)
s := reflect.Indirect(a).Interface()
b.converter.Add(s)
}
}
// Process struct params
if thisOutput.Kind() == reflect.Struct {
a := reflect.New(thisOutput)
s := reflect.Indirect(a).Interface()
b.converter.Add(s)
}
outputs = append(outputs, thisParam)
}
boundMethod.Outputs = outputs

View File

@@ -15,6 +15,7 @@
@property bool alwaysOnTop;
@property bool startHidden;
@property bool startFullscreen;
@property (retain) WailsWindow* mainWindow;
@end

View File

@@ -26,6 +26,12 @@
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSApp activateIgnoringOtherApps:YES];
if ( self.startFullscreen ) {
NSWindowCollectionBehavior behaviour = [self.mainWindow collectionBehavior];
behaviour |= NSWindowCollectionBehaviorFullScreenPrimary;
[self.mainWindow setCollectionBehavior:behaviour];
[self.mainWindow toggleFullScreen:nil];
}
}
- (void)dealloc {

View File

@@ -17,7 +17,7 @@
#define WindowStartsMinimised 2
#define WindowStartsFullscreen 3
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, int windowStartState, int startsHidden);
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, int windowStartState, int startsHidden, int minWidth, int minHeight, int maxWidth, int maxHeight);
void Run(void*);
void SetTitle(void* ctx, const char *title);

View File

@@ -12,7 +12,7 @@
#import "WailsMenu.h"
#import "WailsMenuItem.h"
WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent, int debug, int windowStartState, int startsHidden) {
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, int windowStartState, int startsHidden, int minWidth, int minHeight, int maxWidth, int maxHeight) {
[NSApplication sharedApplication];
@@ -24,7 +24,7 @@ WailsContext* Create(const char* title, int width, int height, int frameless, in
fullscreen = 1;
}
[result CreateWindow:width :height :frameless :resizable :fullscreen :fullSizeContent :hideTitleBar :titlebarAppearsTransparent :hideTitle :useToolbar :hideToolbarSeparator :webviewIsTransparent :hideWindowOnClose :safeInit(appearance) :windowIsTranslucent];
[result CreateWindow:width :height :frameless :resizable :fullscreen :fullSizeContent :hideTitleBar :titlebarAppearsTransparent :hideTitle :useToolbar :hideToolbarSeparator :webviewIsTransparent :hideWindowOnClose :safeInit(appearance) :windowIsTranslucent :minWidth :minHeight :maxWidth :maxHeight];
[result SetTitle:safeInit(title)];
[result Center];
@@ -41,6 +41,10 @@ WailsContext* Create(const char* title, int width, int height, int frameless, in
result.startHidden = true;
}
if ( fullscreen == 1 ) {
result.startFullscreen = true;
}
result.alwaysOnTop = alwaysOnTop;
result.hideOnClose = hideWindowOnClose;
@@ -324,6 +328,7 @@ void Run(void *inctx) {
delegate.mainWindow = ctx.mainWindow;
delegate.alwaysOnTop = ctx.alwaysOnTop;
delegate.startHidden = ctx.startHidden;
delegate.startFullscreen = ctx.startFullscreen;
[ctx loadRequest:@"wails://wails/"];
[app setMainMenu:ctx.applicationMenu];

View File

@@ -11,11 +11,21 @@
#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>
#if __has_include(<UniformTypeIdentifiers/UTType.h>)
#import <UniformTypeIdentifiers/UTType.h>
#endif
#define ON_MAIN_THREAD(str) dispatch_async(dispatch_get_main_queue(), ^{ str; });
#define unicode(input) [NSString stringWithFormat:@"%C", input]
@interface WailsWindow : NSWindow
- (BOOL)canBecomeKeyWindow;
@property NSSize userMinSize;
@property NSSize userMaxSize;
- (BOOL) canBecomeKeyWindow;
- (void) applyWindowConstraints;
- (void) disableWindowConstraints;
@end
@interface WailsContext : NSObject <WKURLSchemeHandler,WKScriptMessageHandler,WKNavigationDelegate>
@@ -27,9 +37,7 @@
@property bool hideOnClose;
@property bool shuttingDown;
@property bool startHidden;
@property NSSize maxSize;
@property NSSize minSize;
@property bool startFullscreen;
@property (retain) NSEvent* mouseEvent;
@@ -46,7 +54,7 @@
@property (retain) NSString* aboutTitle;
@property (retain) NSString* aboutDescription;
- (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(NSString *)appearance :(bool)windowIsTranslucent;
- (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(NSString *)appearance :(bool)windowIsTranslucent :(int)minWidth :(int)minHeight :(int)maxWidth :(int)maxHeight;
- (void) SetSize:(int)width :(int)height;
- (void) SetPosition:(int)x :(int) y;
- (void) SetMinSize:(int)minWidth :(int)minHeight;
@@ -76,6 +84,7 @@
- (NSScreen*) getCurrentScreen;
- (void) SetAbout :(NSString*)title :(NSString*)description :(void*)imagedata :(int)datalen;
- (void) dealloc;
@end

View File

@@ -21,6 +21,16 @@
return YES;
}
- (void) applyWindowConstraints {
[self setMinSize:self.userMinSize];
[self setMaxSize:self.userMaxSize];
}
- (void) disableWindowConstraints {
[self setMinSize:NSMakeSize(0, 0)];
[self setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
}
@end
@implementation WailsContext
@@ -33,7 +43,7 @@
frame.origin.y += frame.size.height - height;
frame.size.width = width;
frame.size.height = height;
ON_MAIN_THREAD([self.mainWindow setFrame:frame display:TRUE animate:FALSE];);
[self.mainWindow setFrame:frame display:TRUE animate:FALSE];
}
- (void) SetPosition:(int)x :(int)y {
@@ -45,8 +55,8 @@
NSRect screenFrame = [screen frame];
windowFrame.origin.x = screenFrame.origin.x + (float)x;
windowFrame.origin.y = (screenFrame.origin.y + screenFrame.size.height) - windowFrame.size.height - (float)y;
ON_MAIN_THREAD([self.mainWindow setFrame:windowFrame display:TRUE animate:FALSE]; );
[self.mainWindow setFrame:windowFrame display:TRUE animate:FALSE];
}
- (void) SetMinSize:(int)minWidth :(int)minHeight {
@@ -54,13 +64,9 @@
if (self.shuttingDown) return;
NSSize size = { minWidth, minHeight };
self.minSize = size;
ON_MAIN_THREAD(
[self.mainWindow setMinSize:size];
[self adjustWindowSize];
);
self.mainWindow.userMinSize = size;
[self.mainWindow setMinSize:size];
[self adjustWindowSize];
}
@@ -73,12 +79,9 @@
size.width = maxWidth > 0 ? maxWidth : FLT_MAX;
size.height = maxHeight > 0 ? maxHeight : FLT_MAX;
self.maxSize = size;
ON_MAIN_THREAD(
[self.mainWindow setMaxSize:size];
[self adjustWindowSize];
);
self.mainWindow.userMaxSize = size;
[self.mainWindow setMaxSize:size];
[self adjustWindowSize];
}
@@ -88,23 +91,23 @@
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;
if ( currentFrame.size.width > self.mainWindow.userMaxSize.width ) currentFrame.size.width = self.mainWindow.userMaxSize.width;
if ( currentFrame.size.width < self.mainWindow.userMinSize.width ) currentFrame.size.width = self.mainWindow.userMinSize.width;
if ( currentFrame.size.height > self.mainWindow.userMaxSize.height ) currentFrame.size.height = self.mainWindow.userMaxSize.height;
if ( currentFrame.size.height < self.mainWindow.userMinSize.height ) currentFrame.size.height = self.mainWindow.userMinSize.height;
[self.mainWindow setFrame:currentFrame display:YES animate:FALSE];
}
- (void) dealloc {
[super dealloc];
[self.appdelegate release];
[self.mainWindow release];
[self.mouseEvent release];
[self.userContentController release];
[self.urlRequests release];
[self.applicationMenu release];
[super dealloc];
}
- (NSScreen*) getCurrentScreen {
@@ -116,11 +119,11 @@
}
- (void) SetTitle:(NSString*)title {
ON_MAIN_THREAD([self.mainWindow setTitle:title];)
[self.mainWindow setTitle:title];
}
- (void) Center {
ON_MAIN_THREAD( [self.mainWindow center]; );
[self.mainWindow center];
}
- (BOOL) isFullscreen {
@@ -131,37 +134,29 @@
return NO;
}
- (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(NSString*)appearance :(bool)windowIsTranslucent {
- (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(NSString*)appearance :(bool)windowIsTranslucent :(int)minWidth :(int)minHeight :(int)maxWidth :(int)maxHeight {
self.urlRequests = [NSMutableDictionary new];
NSWindowStyleMask styleMask = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
NSWindowStyleMask styleMask = 0;
if (frameless) {
styleMask = NSWindowStyleMaskBorderless;
titlebarAppearsTransparent = true;
hideTitle = true;
} else {
if( !frameless ) {
if (!hideTitleBar) {
styleMask |= NSWindowStyleMaskTitled;
}
if (fullscreen) {
styleMask |= NSWindowStyleMaskFullScreen;
}
if( fullSizeContent || frameless || titlebarAppearsTransparent ) {
styleMask |= NSWindowStyleMaskFullSizeContentView;
}
styleMask |= NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
}
if( fullSizeContent || frameless || titlebarAppearsTransparent ) {
styleMask |= NSWindowStyleMaskFullSizeContentView;
}
if (resizable) {
styleMask |= NSWindowStyleMaskResizable;
}
self.mainWindow = [[[WailsWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height)
styleMask:styleMask backing:NSBackingStoreBuffered defer:NO]
autorelease];
self.mainWindow = [[WailsWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height)
styleMask:styleMask backing:NSBackingStoreBuffered defer:NO];
if (!frameless && useToolbar) {
id toolbar = [[NSToolbar alloc] initWithIdentifier:@"wails.toolbar"];
@@ -192,15 +187,25 @@
[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];
NSSize minSize = { minWidth, minHeight };
NSSize maxSize = { maxWidth, maxHeight };
if (maxSize.width == 0) {
maxSize.width = FLT_MAX;
}
if (maxSize.height == 0) {
maxSize.height = FLT_MAX;
}
self.mainWindow.userMaxSize = maxSize;
self.mainWindow.userMinSize = minSize;
if( !fullscreen ) {
[self.mainWindow applyWindowConstraints];
}
WindowDelegate *windowDelegate = [WindowDelegate new];
windowDelegate.hideOnClose = hideWindowOnClose;
windowDelegate.ctx = self;
[self.mainWindow setDelegate:windowDelegate];
// Webview stuff here!
@@ -278,7 +283,7 @@
}
- (NSMenu*) newMenu :(NSString*)title {
WailsMenu *result = [[[WailsMenu new] initWithTitle:title] autorelease];
WailsMenu *result = [[WailsMenu new] initWithTitle:title];
[result setAutoenablesItems:NO];
return result;
}
@@ -294,14 +299,14 @@
}
- (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;
float red = r/255.0;
float green = g/255.0;
float blue = b/255.0;
float alpha = a/255.0;
id colour = [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha ];
ON_MAIN_THREAD([self.mainWindow setBackgroundColor:colour];);
[self.mainWindow setBackgroundColor:colour];
}
- (void) HideMouse {
@@ -325,66 +330,64 @@
// Fullscreen sets the main window to be fullscreen
- (void) Fullscreen {
if( ! [self isFullScreen] ) {
ON_MAIN_THREAD([self.mainWindow toggleFullScreen:nil];)
[self.mainWindow disableWindowConstraints];
[self.mainWindow toggleFullScreen:nil];
}
}
// UnFullscreen resets the main window after a fullscreen
- (void) UnFullscreen {
if( [self isFullScreen] ) {
ON_MAIN_THREAD([self.mainWindow toggleFullScreen:nil];)
[self.mainWindow applyWindowConstraints];
[self.mainWindow toggleFullScreen:nil];
}
}
- (void) Minimise {
ON_MAIN_THREAD([self.mainWindow miniaturize:nil];)
[self.mainWindow miniaturize:nil];
}
- (void) UnMinimise {
ON_MAIN_THREAD([self.mainWindow deminiaturize:nil];)
[self.mainWindow deminiaturize:nil];
}
- (void) Hide {
ON_MAIN_THREAD([self.mainWindow orderOut:nil];)
[self.mainWindow orderOut:nil];
}
- (void) Show {
ON_MAIN_THREAD(
[self.mainWindow makeKeyAndOrderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
)
[self.mainWindow makeKeyAndOrderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
}
- (void) Maximise {
if (![self.mainWindow isZoomed]) {
ON_MAIN_THREAD([self.mainWindow zoom:nil];)
[self.mainWindow zoom:nil];
}
}
- (void) UnMaximise {
if ([self.mainWindow isZoomed]) {
ON_MAIN_THREAD([self.mainWindow zoom:nil];)
[self.mainWindow zoom:nil];
}
}
- (void) ExecJS:(NSString*)script {
ON_MAIN_THREAD(
[self.webview evaluateJavaScript:script completionHandler:nil];
)
[self.webview evaluateJavaScript:script completionHandler:nil];
}
- (void) processURLResponse:(NSString *)url :(NSString *)contentType :(NSData *)data {
id<WKURLSchemeTask> urlSchemeTask = self.urlRequests[url];
NSURL *nsurl = [NSURL URLWithString:url];
NSHTTPURLResponse *response = [NSHTTPURLResponse new];
NSMutableDictionary *headerFields = [NSMutableDictionary new];
headerFields[@"content-type"] = contentType;
[response initWithURL:nsurl statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headerFields];
NSHTTPURLResponse *response = [[NSHTTPURLResponse new] initWithURL:nsurl statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headerFields];
[urlSchemeTask didReceiveResponse:response];
[urlSchemeTask didReceiveData:data];
[urlSchemeTask didFinish];
[self.urlRequests removeObjectForKey:url];
[response release];
[headerFields release];
}
- (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
@@ -406,15 +409,13 @@
// Check for drag
if ( [m isEqualToString:@"drag"] ) {
if( ! [self isFullScreen] ) {
if( self.mouseEvent != nil ) {
[self HideMouse];
ON_MAIN_THREAD(
[self.mainWindow performWindowDragWithEvent:self.mouseEvent];
);
}
if( [self isFullScreen] ) {
return;
}
if( self.mouseEvent != nil ) {
[self.mainWindow performWindowDragWithEvent:self.mouseEvent];
}
return;
}
const char *_m = [m UTF8String];
@@ -455,28 +456,26 @@
NSData *imageData = [NSData dataWithBytes:iconData length:iconDataLength];
icon = [[NSImage alloc] initWithData:imageData];
}
ON_MAIN_THREAD(
if( icon != nil) {
[alert setIcon:icon];
}
[alert.window setLevel:NSFloatingWindowLevel];
if( icon != nil) {
[alert setIcon:icon];
}
[alert.window setLevel:NSFloatingWindowLevel];
long response = [alert runModal];
int result;
if( response == NSAlertFirstButtonReturn ) {
result = 0;
}
else if( response == NSAlertSecondButtonReturn ) {
result = 1;
}
else if( response == NSAlertThirdButtonReturn ) {
result = 2;
} else {
result = 3;
}
processMessageDialogResponse(result);
)
long response = [alert runModal];
int result;
if( response == NSAlertFirstButtonReturn ) {
result = 0;
}
else if( response == NSAlertSecondButtonReturn ) {
result = 1;
}
else if( response == NSAlertThirdButtonReturn ) {
result = 2;
} else {
result = 3;
}
processMessageDialogResponse(result);
}
-(void) OpenFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(NSString*)filters {
@@ -492,11 +491,20 @@
// Filters - semicolon delimited list of file extensions
if( allowFiles ) {
if( filters != nil ) {
if( filters != nil && [filters length] > 0) {
filters = [filters stringByReplacingOccurrencesOfString:@"*." withString:@""];
filters = [filters stringByReplacingOccurrencesOfString:@" " withString:@""];
NSArray *filterList = [filters componentsSeparatedByString:@";"];
[dialog setAllowedFileTypes:filterList];
if (@available(macOS 10.16, *)) {
NSMutableArray *contentTypes = [[NSMutableArray new] autorelease];
for (NSString *filter in filterList) {
UTType *t = [UTType typeWithFilenameExtension:filter];
[contentTypes addObject:t];
}
[dialog setAllowedContentTypes:contentTypes];
} else {
[dialog setAllowedFileTypes:filterList];
}
} else {
[dialog setAllowsOtherFileTypes:true];
}
@@ -613,8 +621,7 @@
[alert setIcon:self.aboutImage];
}
ON_MAIN_THREAD([alert runModal];)
[alert runModal];
}
@end

View File

@@ -64,7 +64,7 @@
if( appName == nil ) {
appName = [[NSProcessInfo processInfo] processName];
}
WailsMenu *appMenu = [[WailsMenu new] initWithNSTitle:appName];
WailsMenu *appMenu = [[[WailsMenu new] initWithNSTitle:appName] autorelease];
id quitTitle = [@"Quit " stringByAppendingString:appName];
NSMenuItem* quitMenuItem = [self newMenuItem:quitTitle :@selector(Quit) :@"q" :NSEventModifierFlagCommand];
quitMenuItem.target = ctx;
@@ -77,7 +77,7 @@
}
case EditMenu:
{
WailsMenu *editMenu = [[WailsMenu new] initWithNSTitle:@"Edit"];
WailsMenu *editMenu = [[[WailsMenu new] initWithNSTitle:@"Edit"] autorelease];
[editMenu addItem:[self newMenuItem:@"Undo" :@selector(undoActionName) :@"z" :NSEventModifierFlagCommand]];
[editMenu addItem:[self newMenuItem:@"Redo" :@selector(redoActionName) :@"z" :(NSEventModifierFlagShift | NSEventModifierFlagCommand)]];
[editMenu addItem:[NSMenuItem separatorItem]];
@@ -91,7 +91,7 @@
// NSMenuItem *speechMenuItem = [[NSMenuItem new] autorelease];
// [speechMenuItem setTitle:@"Speech"];
// [editMenu addItem:speechMenuItem];
WailsMenu *speechMenu = [[WailsMenu new] initWithNSTitle:@"Speech"];
WailsMenu *speechMenu = [[[WailsMenu new] initWithNSTitle:@"Speech"] autorelease];
[speechMenu addItem:[self newMenuItem:@"Start Speaking" :@selector(startSpeaking:) :@""]];
[speechMenu addItem:[self newMenuItem:@"Stop Speaking" :@selector(stopSpeaking:) :@""]];
[editMenu appendSubmenu:speechMenu];

View File

@@ -8,10 +8,17 @@
#ifndef WindowDelegate_h
#define WindowDelegate_h
#import "WailsContext.h"
@interface WindowDelegate : NSObject <NSWindowDelegate>
@property bool hideOnClose;
@property (assign) WailsContext* ctx;
- (void)windowDidExitFullScreen:(NSNotification *)notification;
@end

View File

@@ -21,4 +21,18 @@
return !self.hideOnClose;
}
- (void)windowDidExitFullScreen:(NSNotification *)notification {
[self.ctx.mainWindow applyWindowConstraints];
}
- (void)windowWillEnterFullScreen:(NSNotification *)notification {
[self.ctx.mainWindow disableWindowConstraints];
}
- (NSApplicationPresentationOptions)window:(WailsWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions {
return NSApplicationPresentationAutoHideToolbar | NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationFullScreen;
}
@end

View File

@@ -5,7 +5,7 @@ package darwin
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit -framework UniformTypeIdentifiers
#import <Foundation/Foundation.h>
#import "Application.h"
#import "WailsContext.h"
@@ -51,11 +51,10 @@ type Frontend struct {
assets *assetserver.DesktopAssetServer
// main window handle
mainWindow *Window
minWidth, minHeight, maxWidth, maxHeight int
bindings *binding.Bindings
dispatcher frontend.Dispatcher
servingFromDisk bool
mainWindow *Window
bindings *binding.Bindings
dispatcher frontend.Dispatcher
servingFromDisk bool
}
func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend {
@@ -163,15 +162,11 @@ func (f *Frontend) WindowSetTitle(title string) {
}
func (f *Frontend) WindowFullscreen() {
f.mainWindow.SetMaxSize(0, 0)
f.mainWindow.SetMinSize(0, 0)
f.mainWindow.Fullscreen()
}
func (f *Frontend) WindowUnFullscreen() {
f.mainWindow.UnFullscreen()
f.mainWindow.SetMaxSize(f.maxWidth, f.maxHeight)
f.mainWindow.SetMinSize(f.minWidth, f.minHeight)
}
func (f *Frontend) WindowShow() {
@@ -195,13 +190,9 @@ func (f *Frontend) WindowUnminimise() {
}
func (f *Frontend) WindowSetMinSize(width int, height int) {
f.minWidth = width
f.minHeight = height
f.mainWindow.SetMinSize(width, height)
}
func (f *Frontend) WindowSetMaxSize(width int, height int) {
f.maxWidth = width
f.maxHeight = height
f.mainWindow.SetMaxSize(width, height)
}
@@ -214,9 +205,6 @@ func (f *Frontend) WindowSetRGBA(col *options.RGBA) {
func (f *Frontend) Quit() {
f.mainWindow.Quit()
if f.frontendOptions.OnShutdown != nil {
f.frontendOptions.OnShutdown(f.ctx)
}
}
type EventNotify struct {
@@ -246,6 +234,11 @@ func (f *Frontend) processMessage(message string) {
return
}
//if strings.HasPrefix(message, "systemevent:") {
// f.processSystemEvent(message)
// return
//}
result, err := f.dispatcher.ProcessMessage(message, f)
if err != nil {
f.logger.Error(err.Error())
@@ -295,6 +288,22 @@ func (f *Frontend) processRequest(r *request) {
C.ProcessURLResponse(r.ctx, r.url, mimetype, data, C.int(len(_contents)))
}
//func (f *Frontend) processSystemEvent(message string) {
// sl := strings.Split(message, ":")
// if len(sl) != 2 {
// f.logger.Error("Invalid system message: %s", message)
// return
// }
// switch sl[1] {
// case "fullscreen":
// f.mainWindow.DisableSizeConstraints()
// case "unfullscreen":
// f.mainWindow.EnableSizeConstraints()
// default:
// f.logger.Error("Unknown system message: %s", message)
// }
//}
//export processMessage
func processMessage(message *C.char) {
goMessage := C.GoString(message)

View File

@@ -200,9 +200,9 @@ unsigned int _Users_username_Pictures_SaltBae_png_len = 1863;
int main(int argc, const char * argv[]) {
// insert code here...
int frameless = 1;
int resizable = 0;
int fullscreen = 0;
int frameless = 0;
int resizable = 1;
int fullscreen = 1;
int fullSizeContent = 1;
int hideTitleBar = 0;
int titlebarAppearsTransparent = 0;
@@ -215,7 +215,10 @@ int main(int argc, const char * argv[]) {
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);
int windowStartState = 0;
int startsHidden = 0;
WailsContext *result = Create("OI OI!",400,400, frameless, resizable, fullscreen, fullSizeContent, hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent, alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, debug, windowStartState,
startsHidden, 400, 400, 600, 600);
SetRGBA(result, 255, 0, 0, 255);
void *m = NewMenu("");
SetAbout(result, "Fake title", "I am a description", _Users_username_Pictures_SaltBae_png, _Users_username_Pictures_SaltBae_png_len);
@@ -228,7 +231,7 @@ int main(int argc, const char * argv[]) {
AppendSubmenu(m, submenu);
UpdateMenuItem(menuITem, 1);
SetAsApplicationMenu(result, m);
SetPosition(result, 100, 100);
// SetPosition(result, 100, 100);

View File

@@ -1,3 +1,6 @@
//go:build darwin
// +build darwin
package darwin
/*
@@ -56,6 +59,10 @@ func NewWindow(frontendOptions *options.App, debugMode bool) *Window {
width := C.int(frontendOptions.Width)
height := C.int(frontendOptions.Height)
minWidth := C.int(frontendOptions.MinWidth)
minHeight := C.int(frontendOptions.MinHeight)
maxWidth := C.int(frontendOptions.MaxWidth)
maxHeight := C.int(frontendOptions.MaxHeight)
windowStartState := C.int(int(frontendOptions.WindowStartState))
title = c.String(frontendOptions.Title)
@@ -77,7 +84,8 @@ func NewWindow(frontendOptions *options.App, debugMode bool) *Window {
}
var context *C.WailsContext = C.Create(title, width, height, frameless, resizable, fullscreen, fullSizeContent,
hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent,
alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, debug, windowStartState, startsHidden)
alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, debug, windowStartState, startsHidden,
minWidth, minHeight, maxWidth, maxHeight)
// Create menu
result := &Window{
@@ -104,9 +112,6 @@ func NewWindow(frontendOptions *options.App, debugMode bool) *Window {
result.SetApplicationMenu(frontendOptions.Menu)
}
result.SetMinSize(frontendOptions.MinWidth, frontendOptions.MinHeight)
result.SetMaxSize(frontendOptions.MaxWidth, frontendOptions.MaxHeight)
return result
}
@@ -163,16 +168,10 @@ func (w *Window) UnMinimise() {
}
func (w *Window) SetMinSize(width int, height int) {
if width == 0 && height == 0 {
return
}
C.SetMinSize(w.context, C.int(width), C.int(height))
}
func (w *Window) SetMaxSize(width int, height int) {
if width == 0 && height == 0 {
return
}
C.SetMaxSize(w.context, C.int(width), C.int(height))
}

View File

@@ -0,0 +1,17 @@
//go:build linux
// +build linux
package desktop
import (
"context"
"github.com/wailsapp/wails/v2/internal/binding"
"github.com/wailsapp/wails/v2/internal/frontend"
"github.com/wailsapp/wails/v2/internal/frontend/desktop/linux"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/pkg/options"
)
func NewFrontend(ctx context.Context, appoptions *options.App, logger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) frontend.Frontend {
return linux.NewFrontend(ctx, appoptions, logger, appBindings, dispatcher)
}

View File

@@ -0,0 +1,12 @@
//go:build linux
// +build linux
package linux
import "github.com/pkg/browser"
// BrowserOpenURL Use the default browser to open the url
func (f *Frontend) BrowserOpenURL(url string) {
// Specific method implementation
_ = browser.OpenURL(url)
}

View File

@@ -0,0 +1,26 @@
//go:build linux
// +build linux
package linux
import "github.com/wailsapp/wails/v2/internal/frontend"
func (f *Frontend) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) (string, error) {
panic("implement me")
}
func (f *Frontend) OpenMultipleFilesDialog(dialogOptions frontend.OpenDialogOptions) ([]string, error) {
panic("implement me")
}
func (f *Frontend) OpenDirectoryDialog(dialogOptions frontend.OpenDialogOptions) (string, error) {
panic("implement me")
}
func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (string, error) {
panic("implement me")
}
func (f *Frontend) MessageDialog(dialogOptions frontend.MessageDialogOptions) (string, error) {
panic("implement me")
}

View File

@@ -0,0 +1,401 @@
//go:build linux
// +build linux
package linux
import (
"context"
"encoding/json"
"log"
"strconv"
"text/template"
"github.com/wailsapp/wails/v2/internal/binding"
"github.com/wailsapp/wails/v2/internal/frontend"
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/gotk3/gotk3/gtk"
)
type Frontend struct {
// Context
ctx context.Context
frontendOptions *options.App
logger *logger.Logger
debug bool
// Assets
assets *assetserver.DesktopAssetServer
startURL string
// main window handle
mainWindow *Window
minWidth, minHeight, maxWidth, maxHeight int
bindings *binding.Bindings
dispatcher frontend.Dispatcher
servingFromDisk bool
}
func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend {
result := &Frontend{
frontendOptions: appoptions,
logger: myLogger,
bindings: appBindings,
dispatcher: dispatcher,
ctx: ctx,
minHeight: appoptions.MinHeight,
minWidth: appoptions.MinWidth,
maxHeight: appoptions.MaxHeight,
maxWidth: appoptions.MaxWidth,
startURL: "file://wails/",
}
bindingsJSON, err := appBindings.ToJSON()
if err != nil {
log.Fatal(err)
}
_devServerURL := ctx.Value("devserverurl")
if _devServerURL != nil {
devServerURL := _devServerURL.(string)
if len(devServerURL) > 0 && devServerURL != "http://localhost:34115" {
result.startURL = devServerURL
return result
}
}
// Check if we have been given a directory to serve assets from.
// If so, this means we are in dev mode and are serving assets off disk.
// We indicate this through the `servingFromDisk` flag to ensure requests
// aren't cached by WebView2 in dev mode
_assetdir := ctx.Value("assetdir")
if _assetdir != nil {
result.servingFromDisk = true
}
assets, err := assetserver.NewDesktopAssetServer(ctx, appoptions.Assets, bindingsJSON)
if err != nil {
log.Fatal(err)
}
result.assets = assets
// Initialise GTK
gtk.Init(nil)
return result
}
func (f *Frontend) WindowReload() {
f.ExecJS("runtime.WindowReload();")
}
func (f *Frontend) Run(ctx context.Context) error {
f.ctx = context.WithValue(ctx, "frontend", f)
mainWindow := NewWindow(f.frontendOptions)
f.mainWindow = mainWindow
var _debug = ctx.Value("debug")
if _debug != nil {
f.debug = _debug.(bool)
}
//f.WindowCenter()
//f.setupChromium()
//
//gtkWindow.OnSize().Bind(func(arg *winc.Event) {
// f.chromium.Resize()
//})
//
//gtkWindow.OnClose().Bind(func(arg *winc.Event) {
// if f.frontendOptions.HideWindowOnClose {
// f.WindowHide()
// } else {
// f.Quit()
// }
//})
go func() {
if f.frontendOptions.OnStartup != nil {
f.frontendOptions.OnStartup(f.ctx)
}
}()
if f.frontendOptions.Fullscreen {
mainWindow.Fullscreen()
}
mainWindow.Run()
mainWindow.Close()
return nil
}
func (f *Frontend) WindowCenter() {
f.mainWindow.Center()
}
func (f *Frontend) WindowSetPos(x, y int) {
f.mainWindow.SetPos(x, y)
}
func (f *Frontend) WindowGetPos() (int, int) {
return f.mainWindow.Pos()
}
func (f *Frontend) WindowSetSize(width, height int) {
f.mainWindow.SetSize(width, height)
}
func (f *Frontend) WindowGetSize() (int, int) {
return f.mainWindow.Size()
}
func (f *Frontend) WindowSetTitle(title string) {
f.mainWindow.SetText(title)
}
func (f *Frontend) WindowFullscreen() {
f.mainWindow.SetMaxSize(0, 0)
f.mainWindow.SetMinSize(0, 0)
f.mainWindow.Fullscreen()
}
func (f *Frontend) WindowUnFullscreen() {
f.mainWindow.UnFullscreen()
f.mainWindow.SetMaxSize(f.maxWidth, f.maxHeight)
f.mainWindow.SetMinSize(f.minWidth, f.minHeight)
}
func (f *Frontend) WindowShow() {
f.mainWindow.Show()
}
func (f *Frontend) WindowHide() {
f.mainWindow.Hide()
}
func (f *Frontend) WindowMaximise() {
f.mainWindow.Maximise()
}
func (f *Frontend) WindowUnmaximise() {
f.mainWindow.UnMaximise()
}
func (f *Frontend) WindowMinimise() {
f.mainWindow.Minimise()
}
func (f *Frontend) WindowUnminimise() {
f.mainWindow.UnMinimise()
}
func (f *Frontend) WindowSetMinSize(width int, height int) {
f.minWidth = width
f.minHeight = height
f.mainWindow.SetMinSize(width, height)
}
func (f *Frontend) WindowSetMaxSize(width int, height int) {
f.maxWidth = width
f.maxHeight = height
f.mainWindow.SetMaxSize(width, height)
}
func (f *Frontend) WindowSetRGBA(col *options.RGBA) {
if col == nil {
return
}
//
//f.gtkWindow.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)
// }
//})
}
func (f *Frontend) Quit() {
//winc.Exit()
}
//func (f *Frontend) setupChromium() {
// chromium := edge.NewChromium()
// f.chromium = chromium
// chromium.MessageCallback = f.processMessage
// chromium.WebResourceRequestedCallback = f.processRequest
// chromium.NavigationCompletedCallback = f.navigationCompleted
// chromium.AcceleratorKeyCallback = func(vkey uint) bool {
// w32.PostMessage(f.gtkWindow.Handle(), w32.WM_KEYDOWN, uintptr(vkey), 0)
// return false
// }
// chromium.Embed(f.gtkWindow.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.PutAreBrowserAcceleratorKeysEnabled(false)
// 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)
//}
type EventNotify struct {
Name string `json:"name"`
Data []interface{} `json:"data"`
}
func (f *Frontend) Notify(name string, data ...interface{}) {
notification := EventNotify{
Name: name,
Data: data,
}
payload, err := json.Marshal(notification)
if err != nil {
f.logger.Error(err.Error())
return
}
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" {
if !f.mainWindow.IsFullScreen() {
err := f.startDrag()
if err != nil {
f.logger.Error(err.Error())
}
}
return
}
result, err := f.dispatcher.ProcessMessage(message, f)
if err != nil {
f.logger.Error(err.Error())
f.Callback(result)
return
}
if result == "" {
return
}
switch result[0] {
case 'c':
// Callback from a method call
f.Callback(result[1:])
default:
f.logger.Info("Unknown message returned from dispatcher: %+v", result)
}
}
func (f *Frontend) Callback(message string) {
f.ExecJS(`window.wails.Callback(` + strconv.Quote(message) + `);`)
}
func (f *Frontend) startDrag() error {
//if !w32.ReleaseCapture() {
// return fmt.Errorf("unable to release mouse capture")
//}
//w32.SendMessage(f.gtkWindow.Handle(), w32.WM_NCLBUTTONDOWN, w32.HTCAPTION, 0)
return nil
}
func (f *Frontend) ExecJS(js string) {
//f.gtkWindow.Dispatch(func() {
// f.chromium.Eval(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
// }
//
// f.gtkWindow.Show()
//
//}

View File

@@ -0,0 +1,14 @@
//go:build linux
// +build linux
package linux
import "github.com/wailsapp/wails/v2/pkg/menu"
func (f *Frontend) MenuSetApplicationMenu(menu *menu.Menu) {
panic("implement me")
}
func (f *Frontend) MenuUpdateApplicationMenu() {
panic("implement me")
}

View File

@@ -0,0 +1,247 @@
//go:build linux
// +build linux
package linux
import (
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
"github.com/wailsapp/wails/v2/pkg/menu"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/linux"
"log"
"math"
"sync"
)
type Window struct {
frontendOptions *options.App
applicationMenu *menu.Menu
m sync.Mutex
application *gtk.Application
gtkWindow *gtk.ApplicationWindow
//dispatchq []func()
}
func NewWindow(options *options.App) *Window {
result := new(Window)
result.frontendOptions = options
var linuxOptions linux.Options
if options.Linux != nil {
linuxOptions = *options.Linux
}
appID := linuxOptions.AppID
if appID == "" {
appID = "io.wails"
}
println("AppID =", appID)
application, err := gtk.ApplicationNew(appID, glib.APPLICATION_FLAGS_NONE)
if err != nil {
log.Fatal("Could not create application:", err)
}
result.application = application
application.Connect("activate", func() {
window, err := gtk.ApplicationWindowNew(application)
if err != nil {
log.Fatal("Could not create application window:", err)
}
window.Connect("delete-event", func() {
if options.HideWindowOnClose {
result.gtkWindow.Hide()
return
}
result.gtkWindow.Close()
})
result.gtkWindow = window
window.SetTitle(options.Title)
window.SetDecorated(!options.Frameless)
window.SetDefaultSize(600, 300)
window.SetResizable(!options.DisableResize)
window.SetKeepAbove(options.AlwaysOnTop)
window.SetPosition(gtk.WIN_POS_CENTER)
if !options.StartHidden {
window.ShowAll()
}
})
//result.SetIsForm(true)
//
//var exStyle int
//if options.Windows != nil {
// exStyle = w32.WS_EX_CONTROLPARENT | w32.WS_EX_APPWINDOW
// if options.Windows.WindowIsTranslucent {
// exStyle |= w32.WS_EX_NOREDIRECTIONBITMAP
// }
//}
//if options.AlwaysOnTop {
// exStyle |= w32.WS_EX_TOPMOST
//}
//
//var dwStyle = w32.WS_OVERLAPPEDWINDOW
//if options.Frameless {
// dwStyle = w32.WS_POPUP
//}
//
//winc.RegClassOnlyOnce("wailsWindow")
//result.SetHandle(winc.CreateWindow("wailsWindow", parent, uint(exStyle), uint(dwStyle)))
//result.SetParent(parent)
//
//loadIcon := true
//if options.Windows != nil && options.Windows.DisableWindowIcon == true {
// loadIcon = false
//}
//if loadIcon {
// if ico, err := winc.NewIconFromResource(winc.GetAppInstance(), uint16(winc.AppIconID)); err == nil {
// result.SetIcon(0, ico)
// }
//}
//
//result.SetSize(options.Width, options.Height)
//result.SetText(options.Title)
//if options.Frameless == false && !options.Fullscreen {
// result.EnableMaxButton(!options.DisableResize)
// result.EnableSizable(!options.DisableResize)
// result.SetMinSize(options.MinWidth, options.MinHeight)
// result.SetMaxSize(options.MaxWidth, options.MaxHeight)
//}
//
//if options.Windows != nil {
// if options.Windows.WindowIsTranslucent {
// result.SetTranslucentBackground()
// }
//
// if options.Windows.DisableWindowIcon {
// result.DisableIcon()
// }
//}
//
//// Dlg forces display of focus rectangles, as soon as the user starts to type.
//w32.SendMessage(result.Handle(), w32.WM_CHANGEUISTATE, w32.UIS_INITIALIZE, 0)
//winc.RegMsgHandler(result)
//
//result.SetFont(winc.DefaultFont)
//
//if options.Menu != nil {
// result.SetApplicationMenu(options.Menu)
//}
return result
}
func (w *Window) Run() {
w.application.Run(nil)
}
func (w *Window) Dispatch(f func()) {
glib.IdleAdd(f)
}
func (w *Window) Fullscreen() {
w.gtkWindow.Fullscreen()
}
func (w *Window) UnFullscreen() {
w.gtkWindow.Unfullscreen()
}
func (w *Window) Close() {
w.application.Quit()
}
func (w *Window) Center() {
w.gtkWindow.SetPosition(gtk.WIN_POS_CENTER)
}
func (w *Window) SetPos(x int, y int) {
display, err := w.gtkWindow.GetDisplay()
if err != nil {
w.gtkWindow.Move(x, y)
return
}
window, err := w.gtkWindow.GetWindow()
if err != nil {
w.gtkWindow.Move(x, y)
return
}
monitor, err := display.GetMonitorAtWindow(window)
if err != nil {
w.gtkWindow.Move(x, y)
return
}
geom := monitor.GetGeometry()
w.gtkWindow.Move(geom.GetX()+x, geom.GetY()+y)
}
func (w *Window) Pos() (int, int) {
return w.gtkWindow.GetPosition()
}
func (w *Window) SetSize(width int, height int) {
w.gtkWindow.SetDefaultSize(width, height)
}
func (w *Window) Size() (int, int) {
return w.gtkWindow.GetSize()
}
func (w *Window) SetText(title string) {
w.gtkWindow.SetTitle(title)
}
func (w *Window) SetMaxSize(maxWidth int, maxHeight int) {
var geom gdk.Geometry
if maxWidth == 0 {
maxWidth = math.MaxInt
}
if maxHeight == 0 {
maxHeight = math.MaxInt
}
geom.SetMaxWidth(maxWidth)
geom.SetMaxHeight(maxHeight)
w.gtkWindow.SetGeometryHints(w.gtkWindow, geom, gdk.HINT_MAX_SIZE)
}
func (w *Window) SetMinSize(minWidth int, minHeight int) {
var geom gdk.Geometry
geom.SetMinWidth(minWidth)
geom.SetMinHeight(minHeight)
w.gtkWindow.SetGeometryHints(w.gtkWindow, geom, gdk.HINT_MIN_SIZE)
}
func (w *Window) Show() {
w.gtkWindow.ShowAll()
}
func (w *Window) Hide() {
w.gtkWindow.Hide()
}
func (w *Window) Maximise() {
w.gtkWindow.Maximize()
}
func (w *Window) UnMaximise() {
w.gtkWindow.Unmaximize()
}
func (w *Window) Minimise() {
w.gtkWindow.Iconify()
}
func (w *Window) UnMinimise() {
w.gtkWindow.Present()
}
func (w *Window) IsFullScreen() bool {
return false
}

View File

@@ -168,11 +168,17 @@ func (f *Frontend) WindowFullscreen() {
runtime.LockOSThread()
f.mainWindow.SetMaxSize(0, 0)
f.mainWindow.SetMinSize(0, 0)
if f.frontendOptions.Frameless && f.frontendOptions.DisableResize == false {
f.ExecJS("window.wails.flags.enableResize = false;")
}
f.mainWindow.Fullscreen()
}
func (f *Frontend) WindowUnFullscreen() {
runtime.LockOSThread()
if f.frontendOptions.Frameless && f.frontendOptions.DisableResize == false {
f.ExecJS("window.wails.flags.enableResize = true;")
}
f.mainWindow.UnFullscreen()
f.mainWindow.SetMaxSize(f.maxWidth, f.maxHeight)
f.mainWindow.SetMinSize(f.minWidth, f.minHeight)
@@ -354,6 +360,17 @@ func (f *Frontend) processRequest(req *edge.ICoreWebView2WebResourceRequest, arg
return
}
var edgeMap = map[string]uintptr{
"n-resize": w32.HTTOP,
"ne-resize": w32.HTTOPRIGHT,
"e-resize": w32.HTRIGHT,
"se-resize": w32.HTBOTTOMRIGHT,
"s-resize": w32.HTBOTTOM,
"sw-resize": w32.HTBOTTOMLEFT,
"w-resize": w32.HTLEFT,
"nw-resize": w32.HTTOPLEFT,
}
func (f *Frontend) processMessage(message string) {
if message == "drag" {
if !f.mainWindow.IsFullScreen() {
@@ -364,6 +381,21 @@ func (f *Frontend) processMessage(message string) {
}
return
}
if strings.HasPrefix(message, "resize:") {
if !f.mainWindow.IsFullScreen() {
sl := strings.Split(message, ":")
if len(sl) != 2 {
f.logger.Info("Unknown message returned from dispatcher: %+v", message)
return
}
edge := edgeMap[sl[1]]
err := f.startResize(edge)
if err != nil {
f.logger.Error(err.Error())
}
}
return
}
result, err := f.dispatcher.ProcessMessage(message, f)
if err != nil {
f.logger.Error(err.Error())
@@ -397,6 +429,14 @@ func (f *Frontend) startDrag() error {
return nil
}
func (f *Frontend) startResize(border uintptr) error {
if !w32.ReleaseCapture() {
return fmt.Errorf("unable to release mouse capture")
}
w32.SendMessage(f.mainWindow.Handle(), w32.WM_NCLBUTTONDOWN, border, 0)
return nil
}
func (f *Frontend) ExecJS(js string) {
f.mainWindow.Dispatch(func() {
f.chromium.Eval(js)
@@ -408,6 +448,10 @@ func (f *Frontend) navigationCompleted(sender *edge.ICoreWebView2, args *edge.IC
go f.frontendOptions.OnDomReady(f.ctx)
}
if f.frontendOptions.Frameless && f.frontendOptions.DisableResize == false {
f.ExecJS("window.wails.flags.enableResize = true;")
}
// Hack to make it visible: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1077#issuecomment-825375026
err := f.chromium.Hide()
if err != nil {

View File

@@ -43,6 +43,8 @@ window.wails = {
flags: {
disableScrollbarDrag: false,
disableWailsDefaultContextMenu: false,
enableResize: false,
defaultCursor: null
}
};
@@ -60,6 +62,15 @@ if (ENV === 0) {
// Setup drag handler
// Based on code from: https://github.com/patr0nus/DeskGap
window.addEventListener('mousedown', (e) => {
// Check for resizing
if (window.wails.flags.resizeEdge) {
window.WailsInvoke("resize:" + window.wails.flags.resizeEdge);
e.preventDefault();
return;
}
// Check for dragging
let currentElement = e.target;
while (currentElement != null) {
if (currentElement.hasAttribute('data-wails-no-drag')) {
@@ -79,6 +90,40 @@ window.addEventListener('mousedown', (e) => {
}
});
function setResize(cursor) {
document.body.style.cursor = cursor || window.wails.flags.defaultCursor;
window.wails.flags.resizeEdge = cursor;
}
window.addEventListener('mousemove', function (e) {
if (!window.wails.flags.enableResize) {
return;
}
if (window.wails.flags.defaultCursor == null) {
window.wails.flags.defaultCursor = document.body.style.cursor;
}
if (window.outerWidth - e.clientX < 16 && window.outerHeight - e.clientY < 16) {
document.body.style.cursor = "se-resize";
}
let rightBorder = window.outerWidth - e.clientX < 16;
let leftBorder = e.clientX < 16;
let topBorder = e.clientY < 16;
let bottomBorder = window.outerHeight - e.clientY < 16;
// If we aren't on an edge, but were, reset the cursor to default
if (!leftBorder && !rightBorder && !topBorder && !bottomBorder && window.wails.flags.resizeEdge !== undefined) {
setResize();
} else if (rightBorder && bottomBorder) setResize("se-resize");
else if (leftBorder && bottomBorder) setResize("sw-resize");
else if (leftBorder && topBorder) setResize("nw-resize");
else if (topBorder && rightBorder) setResize("ne-resize");
else if (leftBorder) setResize("w-resize");
else if (topBorder) setResize("n-resize");
else if (bottomBorder) setResize("s-resize");
else if (rightBorder) setResize("e-resize");
});
// Setup context menu hook
window.addEventListener('contextmenu', function (e) {
if (window.wails.flags.disableWailsDefaultContextMenu) {

View File

@@ -1,5 +1,3 @@
//go:build darwin || windows
package runtime
import _ "embed"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -243,7 +243,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
if options.Mode == Production {
ldflags.Add("-w", "-s")
if runtime.GOOS == "windows" {
if options.Platform == "windows" {
ldflags.Add("-H windowsgui")
}
}
@@ -295,10 +295,12 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
if options.Platform != "windows" {
// Use upsertEnv so we don't overwrite user's CGO_CFLAGS
cmd.Env = upsertEnv(cmd.Env, "CGO_CFLAGS", func(v string) string {
if v != "" {
v += " "
if options.Platform == "darwin" {
if v != "" {
v += " "
}
v += "-mmacosx-version-min=10.13"
}
v += "-mmacosx-version-min=10.13"
return v
})
// Use upsertEnv so we don't overwrite user's CGO_CXXFLAGS
@@ -313,7 +315,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
cmd.Env = upsertEnv(cmd.Env, "CGO_ENABLED", func(v string) string {
return "1"
})
if runtime.GOOS == "darwin" {
if options.Platform == "darwin" {
// Set the minimum Mac SDK to 10.13
cmd.Env = upsertEnv(cmd.Env, "CGO_LDFLAGS", func(v string) string {
if v != "" {

View File

@@ -6,7 +6,6 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
"github.com/wailsapp/wails/v2/internal/fs"
@@ -212,16 +211,6 @@ func Build(options *Options) (string, error) {
result := options.CompiledBinary
if options.Pack && options.Platform == "darwin" {
sr := strings.Split(result, "/")
for i := len(sr) - 1; i >= 0; i-- {
if strings.Contains(sr[i], ".app") {
result = strings.Join(sr[:i+1], "/")
break
}
}
}
return result, nil
}

View File

@@ -27,6 +27,8 @@ func packageProject(options *Options, platform string) error {
err = packageApplicationForDarwin(options)
case "windows":
err = packageApplicationForWindows(options)
case "linux":
err = packageApplicationForLinux(options)
default:
err = fmt.Errorf("packing not supported for %s yet", platform)
}
@@ -204,6 +206,29 @@ func packageApplicationForWindows(options *Options) error {
return nil
}
func packageApplicationForLinux(options *Options) error {
// Generate icon
//var err error
//err = generateIcoFile(options)
//if err != nil {
// return err
//}
//
//// Ensure Manifest is present
//err = generateManifest(options)
//if err != nil {
// return err
//}
//
//// Create syso file
//err = compileResources(options)
//if err != nil {
// return err
//}
return nil
}
func generateManifest(options *Options) error {
filename := options.ProjectData.Name + ".exe.manifest"
manifestFile := filepath.Join(options.ProjectData.Path, "build", "windows", filename)

View File

@@ -0,0 +1,7 @@
package linux
// Options specific to Linux builds
type Options struct {
// AppID is the gtk application id string. Defaults to a random uuid.
AppID string
}

View File

@@ -3,6 +3,7 @@ package options
import (
"context"
"embed"
"github.com/wailsapp/wails/v2/pkg/options/linux"
"log"
"runtime"
@@ -54,6 +55,7 @@ type App struct {
//TrayMenus []*menu.TrayMenu
Windows *windows.Options
Mac *mac.Options
Linux *linux.Options
}
type RGBA struct {

View File

@@ -14,6 +14,14 @@ sidebar_position: 99
<div
dangerouslySetInnerHTML={{
__html: `
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="/img/silver%20sponsor.png" width="100"/>
</a>
<a href="https://github.com/letheanVPN" style="width:100px;">
<img src="https://github.com/letheanVPN.png?size=100" width="100"/>
</a>
<br/>
<br/>
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="/img/bronze%20sponsor.png" width="100"/>
</a>

View File

@@ -67,7 +67,7 @@ import TabItem from "@theme/TabItem";
## Installing Wails
Run `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.20` to install the Wails CLI.
Run `go install github.com/wailsapp/wails/v2/cmd/wails@latest` to install the Wails CLI.
## System Check

View File

@@ -36,6 +36,7 @@ func main() {
OnStartup: app.startup,
OnDomReady: app.domready,
OnShutdown: app.shutdown,
WindowStartState: options.Maximised,
Bind: []interface{}{
app,
},
@@ -258,6 +259,20 @@ Type: func(ctx context.Context)
This callback is called after the frontend has been destroyed, just before the application terminates. It is given
the application context.
### WindowStartState
Name: WindowStartState
Type: options.WindowStartState
Defines how the window should present itself at startup.
| Value | Win | Mac |
| --------------- | --- | --- |
| Fullscreen | ✅ | ✅ |
| Maximised | ✅ | ✅ |
| Minimised | ✅ | |
### Bind
Name: Bind
@@ -452,3 +467,4 @@ When clicked, that will open an about message box:
<img src="/img/reference/about-dialog.png" width="40%" style={{"box-shadow": "rgb(255 255 255 / 20%) 0px 4px 8px 0px, rgb(104 104 104) 0px 6px 20px 0px"}}/>
</div>
<br/>

View File

@@ -8,9 +8,19 @@ The runtime is a library that provides utility methods for your application. The
and the aim is to try and keep them at parity where possible.
The Go Runtime is available through importing `github.com/wailsapp/wails/v2/pkg/runtime`. All methods in this package
take a context as the first parameter. This context can be obtained from the [OnStartup](/docs/reference/options#OnStartup)
or [OnDomReady](/docs/reference/options#OnDomReady) hooks.
take a context as the first parameter. This context can be obtained from the [OnStartup](/docs/reference/options#onstartup)
or [OnDomReady](/docs/reference/options#ondomready) hooks.
:::info Note
Whilst the context will be provided to the
[OnStartup](/docs/reference/options#onstartup) method, there's no guarantee the runtime will work in this method as
the window is initialising in a different thread. If
you wish to call runtime methods at startup, use [OnDomReady](/docs/reference/options#ondomready).
:::
The Javascript library is available to the frontend via the `window.runtime` map. There is a runtime package generated when using `dev`
mode that provides Typescript declarations for the runtime. This should be located in the `wailsjs` directory in your
frontend directory.

View File

@@ -14,6 +14,14 @@ sidebar_position: 99
<div
dangerouslySetInnerHTML={{
__html: `
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="/img/silver%20sponsor.png" width="100"/>
</a>
<a href="https://github.com/letheanVPN" style="width:100px;">
<img src="https://github.com/letheanVPN.png?size=100" width="100"/>
</a>
<br/>
<br/>
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="/img/bronze%20sponsor.png" width="100"/>
</a>

View File

@@ -63,7 +63,7 @@ import TabItem from "@theme/TabItem";
## 安装 Wails
运行 `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.20` 安装 Wails CLI。
运行 `go install github.com/wailsapp/wails/v2/cmd/wails@latest` 安装 Wails CLI。
## 系统检查

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB