mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Merge branch 'master' into feature/webkitgtk6
This commit is contained in:
commit
e4c15da18a
24 changed files with 523 additions and 169 deletions
|
|
@ -123,7 +123,15 @@ func (b *Bindings) GenerateModels() ([]byte, error) {
|
|||
// if we have enums for this package, add them as well
|
||||
var enums, enumsExist = b.enumsToGenerateTS[packageName]
|
||||
if enumsExist {
|
||||
for enumName, enum := range enums {
|
||||
// Sort the enum names first to make the output deterministic
|
||||
sortedEnumNames := make([]string, 0, len(enums))
|
||||
for enumName := range enums {
|
||||
sortedEnumNames = append(sortedEnumNames, enumName)
|
||||
}
|
||||
sort.Strings(sortedEnumNames)
|
||||
|
||||
for _, enumName := range sortedEnumNames {
|
||||
enum := enums[enumName]
|
||||
fqemumname := packageName + "." + enumName
|
||||
if seen.Contains(fqemumname) {
|
||||
continue
|
||||
|
|
@ -172,7 +180,7 @@ func (b *Bindings) GenerateModels() ([]byte, error) {
|
|||
}
|
||||
|
||||
// Sort the package names first to make the output deterministic
|
||||
sortedPackageNames := make([]string, 0)
|
||||
sortedPackageNames := make([]string, 0, len(models))
|
||||
for packageName := range models {
|
||||
sortedPackageNames = append(sortedPackageNames, packageName)
|
||||
}
|
||||
|
|
|
|||
271
v2/internal/binding/binding_test/binding_enum_ordering_test.go
Normal file
271
v2/internal/binding/binding_test/binding_enum_ordering_test.go
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
package binding_test
|
||||
|
||||
// Test for PR #4664: Fix generated enums ordering
|
||||
// This test ensures that enum output is deterministic regardless of map iteration order
|
||||
|
||||
// ZFirstEnum - named with Z prefix to test alphabetical sorting
|
||||
type ZFirstEnum int
|
||||
|
||||
const (
|
||||
ZFirstEnumValue1 ZFirstEnum = iota
|
||||
ZFirstEnumValue2
|
||||
)
|
||||
|
||||
var AllZFirstEnumValues = []struct {
|
||||
Value ZFirstEnum
|
||||
TSName string
|
||||
}{
|
||||
{ZFirstEnumValue1, "ZValue1"},
|
||||
{ZFirstEnumValue2, "ZValue2"},
|
||||
}
|
||||
|
||||
// ASecondEnum - named with A prefix to test alphabetical sorting
|
||||
type ASecondEnum int
|
||||
|
||||
const (
|
||||
ASecondEnumValue1 ASecondEnum = iota
|
||||
ASecondEnumValue2
|
||||
)
|
||||
|
||||
var AllASecondEnumValues = []struct {
|
||||
Value ASecondEnum
|
||||
TSName string
|
||||
}{
|
||||
{ASecondEnumValue1, "AValue1"},
|
||||
{ASecondEnumValue2, "AValue2"},
|
||||
}
|
||||
|
||||
// MMiddleEnum - named with M prefix to test alphabetical sorting
|
||||
type MMiddleEnum int
|
||||
|
||||
const (
|
||||
MMiddleEnumValue1 MMiddleEnum = iota
|
||||
MMiddleEnumValue2
|
||||
)
|
||||
|
||||
var AllMMiddleEnumValues = []struct {
|
||||
Value MMiddleEnum
|
||||
TSName string
|
||||
}{
|
||||
{MMiddleEnumValue1, "MValue1"},
|
||||
{MMiddleEnumValue2, "MValue2"},
|
||||
}
|
||||
|
||||
type EntityWithMultipleEnums struct {
|
||||
Name string `json:"name"`
|
||||
EnumZ ZFirstEnum `json:"enumZ"`
|
||||
EnumA ASecondEnum `json:"enumA"`
|
||||
EnumM MMiddleEnum `json:"enumM"`
|
||||
}
|
||||
|
||||
func (e EntityWithMultipleEnums) Get() EntityWithMultipleEnums {
|
||||
return e
|
||||
}
|
||||
|
||||
// EnumOrderingTest tests that multiple enums in the same package are output
|
||||
// in alphabetical order by enum name. Before PR #4664, the order was
|
||||
// non-deterministic due to Go map iteration order.
|
||||
var EnumOrderingTest = BindingTest{
|
||||
name: "EnumOrderingTest",
|
||||
structs: []interface{}{
|
||||
&EntityWithMultipleEnums{},
|
||||
},
|
||||
enums: []interface{}{
|
||||
// Intentionally add enums in non-alphabetical order
|
||||
AllZFirstEnumValues,
|
||||
AllASecondEnumValues,
|
||||
AllMMiddleEnumValues,
|
||||
},
|
||||
exemptions: nil,
|
||||
shouldError: false,
|
||||
TsGenerationOptionsTest: TsGenerationOptionsTest{
|
||||
TsPrefix: "",
|
||||
TsSuffix: "",
|
||||
},
|
||||
// Expected output should have enums in alphabetical order: ASecondEnum, MMiddleEnum, ZFirstEnum
|
||||
want: `export namespace binding_test {
|
||||
|
||||
export enum ASecondEnum {
|
||||
AValue1 = 0,
|
||||
AValue2 = 1,
|
||||
}
|
||||
export enum MMiddleEnum {
|
||||
MValue1 = 0,
|
||||
MValue2 = 1,
|
||||
}
|
||||
export enum ZFirstEnum {
|
||||
ZValue1 = 0,
|
||||
ZValue2 = 1,
|
||||
}
|
||||
export class EntityWithMultipleEnums {
|
||||
name: string;
|
||||
enumZ: ZFirstEnum;
|
||||
enumA: ASecondEnum;
|
||||
enumM: MMiddleEnum;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new EntityWithMultipleEnums(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.name = source["name"];
|
||||
this.enumZ = source["enumZ"];
|
||||
this.enumA = source["enumA"];
|
||||
this.enumM = source["enumM"];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
||||
// EnumElementOrderingEnum tests sorting of enum elements by TSName
|
||||
type EnumElementOrderingEnum string
|
||||
|
||||
const (
|
||||
EnumElementZ EnumElementOrderingEnum = "z_value"
|
||||
EnumElementA EnumElementOrderingEnum = "a_value"
|
||||
EnumElementM EnumElementOrderingEnum = "m_value"
|
||||
)
|
||||
|
||||
// AllEnumElementOrderingValues intentionally lists values out of alphabetical order
|
||||
// to test that AddEnum sorts them
|
||||
var AllEnumElementOrderingValues = []struct {
|
||||
Value EnumElementOrderingEnum
|
||||
TSName string
|
||||
}{
|
||||
{EnumElementZ, "Zebra"},
|
||||
{EnumElementA, "Apple"},
|
||||
{EnumElementM, "Mango"},
|
||||
}
|
||||
|
||||
type EntityWithUnorderedEnumElements struct {
|
||||
Name string `json:"name"`
|
||||
Enum EnumElementOrderingEnum `json:"enum"`
|
||||
}
|
||||
|
||||
func (e EntityWithUnorderedEnumElements) Get() EntityWithUnorderedEnumElements {
|
||||
return e
|
||||
}
|
||||
|
||||
// EnumElementOrderingTest tests that enum elements are sorted alphabetically
|
||||
// by their TSName within an enum. Before PR #4664, elements appeared in the
|
||||
// order they were added, which could be arbitrary.
|
||||
var EnumElementOrderingTest = BindingTest{
|
||||
name: "EnumElementOrderingTest",
|
||||
structs: []interface{}{
|
||||
&EntityWithUnorderedEnumElements{},
|
||||
},
|
||||
enums: []interface{}{
|
||||
AllEnumElementOrderingValues,
|
||||
},
|
||||
exemptions: nil,
|
||||
shouldError: false,
|
||||
TsGenerationOptionsTest: TsGenerationOptionsTest{
|
||||
TsPrefix: "",
|
||||
TsSuffix: "",
|
||||
},
|
||||
// Expected output should have enum elements sorted: Apple, Mango, Zebra
|
||||
want: `export namespace binding_test {
|
||||
|
||||
export enum EnumElementOrderingEnum {
|
||||
Apple = "a_value",
|
||||
Mango = "m_value",
|
||||
Zebra = "z_value",
|
||||
}
|
||||
export class EntityWithUnorderedEnumElements {
|
||||
name: string;
|
||||
enum: EnumElementOrderingEnum;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new EntityWithUnorderedEnumElements(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.name = source["name"];
|
||||
this.enum = source["enum"];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
||||
// TSNameEnumElementOrdering tests sorting with TSName() method enum
|
||||
type TSNameEnumElementOrdering string
|
||||
|
||||
const (
|
||||
TSNameEnumZ TSNameEnumElementOrdering = "z_value"
|
||||
TSNameEnumA TSNameEnumElementOrdering = "a_value"
|
||||
TSNameEnumM TSNameEnumElementOrdering = "m_value"
|
||||
)
|
||||
|
||||
func (v TSNameEnumElementOrdering) TSName() string {
|
||||
switch v {
|
||||
case TSNameEnumZ:
|
||||
return "Zebra"
|
||||
case TSNameEnumA:
|
||||
return "Apple"
|
||||
case TSNameEnumM:
|
||||
return "Mango"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// AllTSNameEnumValues intentionally out of order
|
||||
var AllTSNameEnumValues = []TSNameEnumElementOrdering{TSNameEnumZ, TSNameEnumA, TSNameEnumM}
|
||||
|
||||
type EntityWithTSNameEnumOrdering struct {
|
||||
Name string `json:"name"`
|
||||
Enum TSNameEnumElementOrdering `json:"enum"`
|
||||
}
|
||||
|
||||
func (e EntityWithTSNameEnumOrdering) Get() EntityWithTSNameEnumOrdering {
|
||||
return e
|
||||
}
|
||||
|
||||
// TSNameEnumElementOrderingTest tests that enums using TSName() method
|
||||
// also have their elements sorted alphabetically by the TSName.
|
||||
var TSNameEnumElementOrderingTest = BindingTest{
|
||||
name: "TSNameEnumElementOrderingTest",
|
||||
structs: []interface{}{
|
||||
&EntityWithTSNameEnumOrdering{},
|
||||
},
|
||||
enums: []interface{}{
|
||||
AllTSNameEnumValues,
|
||||
},
|
||||
exemptions: nil,
|
||||
shouldError: false,
|
||||
TsGenerationOptionsTest: TsGenerationOptionsTest{
|
||||
TsPrefix: "",
|
||||
TsSuffix: "",
|
||||
},
|
||||
// Expected output should have enum elements sorted: Apple, Mango, Zebra
|
||||
want: `export namespace binding_test {
|
||||
|
||||
export enum TSNameEnumElementOrdering {
|
||||
Apple = "a_value",
|
||||
Mango = "m_value",
|
||||
Zebra = "z_value",
|
||||
}
|
||||
export class EntityWithTSNameEnumOrdering {
|
||||
name: string;
|
||||
enum: TSNameEnumElementOrdering;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new EntityWithTSNameEnumOrdering(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.name = source["name"];
|
||||
this.enum = source["enum"];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
|
@ -55,6 +55,10 @@ func TestBindings_GenerateModels(t *testing.T) {
|
|||
Generics2Test,
|
||||
IgnoredTest,
|
||||
DeepElementsTest,
|
||||
// PR #4664: Enum ordering tests
|
||||
EnumOrderingTest,
|
||||
EnumElementOrderingTest,
|
||||
TSNameEnumElementOrderingTest,
|
||||
}
|
||||
|
||||
testLogger := &logger.Logger{}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "CustomProtocol.h"
|
||||
#import "message.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
|
|
@ -19,6 +20,17 @@
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<NSUserActivityRestoring>> * _Nullable))restorationHandler {
|
||||
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
NSURL *url = userActivity.webpageURL;
|
||||
if (url) {
|
||||
HandleOpenURL((char*)[[url absoluteString] UTF8String]);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
|
||||
return NO;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
extern void HandleCustomProtocol(char*);
|
||||
extern void HandleOpenURL(char*);
|
||||
|
||||
@interface CustomProtocolSchemeHandler : NSObject
|
||||
+ (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
NSString *urlStr = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
|
||||
|
||||
HandleCustomProtocol((char*)[[[event paramDescriptorForKeyword:keyDirectObject] stringValue] UTF8String]);
|
||||
HandleOpenURL((char*)[[[event paramDescriptorForKeyword:keyDirectObject] stringValue] UTF8String]);
|
||||
}
|
||||
@end
|
||||
|
||||
|
|
|
|||
|
|
@ -518,8 +518,8 @@ func HandleOpenFile(filePath *C.char) {
|
|||
openFilepathBuffer <- goFilepath
|
||||
}
|
||||
|
||||
//export HandleCustomProtocol
|
||||
func HandleCustomProtocol(url *C.char) {
|
||||
//export HandleOpenURL
|
||||
func HandleOpenURL(url *C.char) {
|
||||
goUrl := C.GoString(url)
|
||||
openUrlBuffer <- goUrl
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package typescriptify
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"cmp"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
|
@ -9,6 +10,7 @@ import (
|
|||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -372,6 +374,9 @@ func (t *TypeScriptify) AddEnum(values interface{}) *TypeScriptify {
|
|||
|
||||
elements = append(elements, el)
|
||||
}
|
||||
slices.SortFunc(elements, func(a, b enumElement) int {
|
||||
return cmp.Compare(a.name, b.name)
|
||||
})
|
||||
ty := reflect.TypeOf(elements[0].value)
|
||||
t.enums[ty] = elements
|
||||
t.enumTypes = append(t.enumTypes, EnumType{Type: ty})
|
||||
|
|
@ -516,9 +521,6 @@ func (t TypeScriptify) ConvertToFile(fileName string, packageName string) error
|
|||
if _, err := f.WriteString(converted); err != nil {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ The pattern used by the default templates are that `main.go` is used for configu
|
|||
The `app.go` file will define a struct that has 2 methods which act as hooks into the main application:
|
||||
|
||||
```go title="app.go"
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
|
@ -28,7 +32,7 @@ func (a *App) shutdown(ctx context.Context) {
|
|||
|
||||
- The startup method is called as soon as Wails allocates the resources it needs and is a good place for creating resources,
|
||||
setting up event listeners and anything else the application needs at startup.
|
||||
It is given a `context.Context` which is usually saved in a struct field. This context is needed for calling the
|
||||
It is given a [`context.Context`](https://pkg.go.dev/context) which is usually saved in a struct field. This context is needed for calling the
|
||||
[runtime](../reference/runtime/intro.mdx). If this method returns an error, the application will terminate.
|
||||
In dev mode, the error will be output to the console.
|
||||
|
||||
|
|
@ -55,7 +59,6 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
More information on application lifecycle hooks can be found [here](../howdoesitwork.mdx#application-lifecycle-callbacks).
|
||||
|
|
@ -65,7 +68,12 @@ More information on application lifecycle hooks can be found [here](../howdoesit
|
|||
It is likely that you will want to call Go methods from the frontend. This is normally done by adding public methods to
|
||||
the already defined struct in `app.go`:
|
||||
|
||||
```go {16-18} title="app.go"
|
||||
```go {3,21-23} title="app.go"
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
|
@ -82,7 +90,7 @@ func (a *App) shutdown(ctx context.Context) {
|
|||
}
|
||||
|
||||
func (a *App) Greet(name string) string {
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -99,15 +107,14 @@ func main() {
|
|||
Height: 600,
|
||||
OnStartup: app.startup,
|
||||
OnShutdown: app.shutdown,
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
This will bind all public methods in our `App` struct (it will never bind the startup and shutdown methods).
|
||||
|
|
@ -133,10 +140,10 @@ func main() {
|
|||
otherStruct.SetContext(ctx)
|
||||
},
|
||||
OnShutdown: app.shutdown,
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
otherStruct
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
@ -187,18 +194,17 @@ func main() {
|
|||
Height: 600,
|
||||
OnStartup: app.startup,
|
||||
OnShutdown: app.shutdown,
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
EnumBind: []interface{}{
|
||||
AllWeekdays,
|
||||
},
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
EnumBind: []interface{}{
|
||||
AllWeekdays,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
This will add missing enums to your `model.ts` file.
|
||||
|
|
@ -223,15 +229,14 @@ func main() {
|
|||
OnStartup: app.startup,
|
||||
OnShutdown: app.shutdown,
|
||||
Menu: app.menu(),
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Assets
|
||||
|
|
|
|||
|
|
@ -59,6 +59,24 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
If you want to handle universal links as well, follow this [guide](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app) to add required entitlements, add required keys to Info.plist and configure `apple-app-site-association` on your website.
|
||||
|
||||
Here is example for Info.plist:
|
||||
```xml
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>NSUserActivityTypeBrowsingWeb</string>
|
||||
</array>
|
||||
```
|
||||
|
||||
And for entitlements.plist
|
||||
```xml
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:myawesomeapp.com</string>
|
||||
</array>
|
||||
```
|
||||
|
||||
|
||||
### Windows
|
||||
On Windows Custom Protocol Schemes is supported only with NSIS installer. During installation, the installer will create a
|
||||
|
|
|
|||
|
|
@ -63,17 +63,17 @@ func main() {
|
|||
|
||||
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (b *App) startup(ctx context.Context) {
|
||||
b.ctx = ctx
|
||||
b.ctx = ctx
|
||||
}
|
||||
|
||||
func (b *App) shutdown(ctx context.Context) {}
|
||||
|
||||
func (b *App) Greet(name string) string {
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -178,18 +178,18 @@ func main() {
|
|||
|
||||
|
||||
type App struct {
|
||||
ctx context.Context
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (a *App) Greet(name string) string {
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
}
|
||||
```
|
||||
|
||||
You may bind as many structs as you like. Just make sure you create an instance of it and pass it in `Bind`:
|
||||
|
||||
```go {10-12}
|
||||
//...
|
||||
//...
|
||||
err := wails.Run(&options.App{
|
||||
Title: "Basic Demo",
|
||||
Width: 1024,
|
||||
|
|
@ -203,7 +203,6 @@ You may bind as many structs as you like. Just make sure you create an instance
|
|||
&mystruct2{},
|
||||
},
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
You may bind enums types as well.
|
||||
|
|
@ -237,7 +236,7 @@ var AllWeekdays = []struct {
|
|||
```
|
||||
|
||||
```go {10-12}
|
||||
//...
|
||||
//...
|
||||
err := wails.Run(&options.App{
|
||||
Title: "Basic Demo",
|
||||
Width: 1024,
|
||||
|
|
@ -250,11 +249,10 @@ var AllWeekdays = []struct {
|
|||
&mystruct1{},
|
||||
&mystruct2{},
|
||||
},
|
||||
EnumBind: []interface{}{
|
||||
AllWeekdays,
|
||||
},
|
||||
EnumBind: []interface{}{
|
||||
AllWeekdays,
|
||||
},
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
When you run `wails dev` (or `wails generate module`), a frontend module will be generated containing the following:
|
||||
|
|
|
|||
|
|
@ -8,10 +8,23 @@ It is possible to add an application menu to Wails projects. This is achieved by
|
|||
setting it in the [`Menu`](../reference/options.mdx#menu) application config, or by calling the runtime method
|
||||
[MenuSetApplicationMenu](../reference/runtime/menu.mdx#menusetapplicationmenu).
|
||||
|
||||
An example of how to create a menu:
|
||||
An example of how to create a menu, using [the `NewApp` scaffold](../guides/application-development.mdx):
|
||||
|
||||
```go
|
||||
```go title="main.go"
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"runtime"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
rt "github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := NewApp()
|
||||
|
||||
AppMenu := menu.NewMenu()
|
||||
|
|
@ -29,7 +42,7 @@ An example of how to create a menu:
|
|||
})
|
||||
|
||||
if runtime.GOOS == "darwin" {
|
||||
AppMenu.Append(menu.EditMenu()) // On macOS platform, EditMenu should be appended to enable Cmd+C, Cmd+V, Cmd+Z... shortcuts
|
||||
AppMenu.Append(menu.EditMenu()) // On macOS platform, EditMenu should be appended to enable Cmd+C, Cmd+V, Cmd+Z... shortcuts
|
||||
}
|
||||
|
||||
err := wails.Run(&options.App{
|
||||
|
|
@ -40,8 +53,11 @@ An example of how to create a menu:
|
|||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
)
|
||||
// ...
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It is also possible to dynamically update the menu, by updating the menu struct and calling
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ Go: `WindowSetDarkTheme(ctx context.Context)`<br/> JS: `WindowSetDarkTheme()`
|
|||
|
||||
Go: `WindowShow(ctx context.Context)`<br/> JS: `WindowShow()`
|
||||
|
||||
### WindowShow 隐藏窗口
|
||||
### WindowHide 隐藏窗口
|
||||
|
||||
如果当前可见,则隐藏窗口。
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ Go: `WindowSetDarkTheme(ctx context.Context)`<br/> JS: `WindowSetDarkTheme()`
|
|||
|
||||
Go: `WindowShow(ctx context.Context)`<br/> JS: `WindowShow()`
|
||||
|
||||
### WindowShow 隐藏窗口
|
||||
### WindowHide 隐藏窗口
|
||||
|
||||
如果当前可见,则隐藏窗口。
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ Go: `WindowSetDarkTheme(ctx context.Context)`<br/> JS: `WindowSetDarkTheme()`
|
|||
|
||||
Go: `WindowShow(ctx context.Context)`<br/> JS: `WindowShow()`
|
||||
|
||||
### WindowShow 隐藏窗口
|
||||
### WindowHide 隐藏窗口
|
||||
|
||||
如果当前可见,则隐藏窗口。
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ Go: `WindowSetDarkTheme(ctx context.Context)`<br/> JS: `WindowSetDarkTheme()`
|
|||
|
||||
Go: `WindowShow(ctx context.Context)`<br/> JS: `WindowShow()`
|
||||
|
||||
### WindowShow 隐藏窗口
|
||||
### WindowHide 隐藏窗口
|
||||
|
||||
如果当前可见,则隐藏窗口。
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ Go: `WindowSetDarkTheme(ctx context.Context)`<br/> JS: `WindowSetDarkTheme()`
|
|||
|
||||
Go: `WindowShow(ctx context.Context)`<br/> JS: `WindowShow()`
|
||||
|
||||
### WindowShow 隐藏窗口
|
||||
### WindowHide 隐藏窗口
|
||||
|
||||
如果当前可见,则隐藏窗口。
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ Go: `WindowSetDarkTheme(ctx context.Context)`<br/> JS: `WindowSetDarkTheme()`
|
|||
|
||||
Go: `WindowShow(ctx context.Context)`<br/> JS: `WindowShow()`
|
||||
|
||||
### WindowShow 隐藏窗口
|
||||
### WindowHide 隐藏窗口
|
||||
|
||||
如果当前可见,则隐藏窗口。
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ Go: `WindowSetDarkTheme(ctx context.Context)`<br/> JS: `WindowSetDarkTheme()`
|
|||
|
||||
Go: `WindowShow(ctx context.Context)`<br/> JS: `WindowShow()`
|
||||
|
||||
### WindowShow 隐藏窗口
|
||||
### WindowHide 隐藏窗口
|
||||
|
||||
如果当前可见,则隐藏窗口。
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Updated menu reference docs with complete imports by @agilgur5 in [#4727](https://github.com/wailsapp/wails/pull/4727) and [#4742](https://github.com/wailsapp/wails/pull/4742)
|
||||
- Fixed menu reference syntax by @agilgur5 in [#4726](https://github.com/wailsapp/wails/pull/4726)
|
||||
- Fixed indentation in Application Development guide by @agilgur5 in [#4730](https://github.com/wailsapp/wails/pull/4730)
|
||||
- Updated Application Development guide to show imports in the `app.go` snippets by @agilgur5 in [#4731](https://github.com/wailsapp/wails/pull/4731)
|
||||
- Fixed link to CoC in Community Guide when there was a trailing slash by @agilgur5 in [#4732](https://github.com/wailsapp/wails/pull/4732)
|
||||
- Fixed indentation in "How does it work?" page by @agilgur5 in [#4733](https://github.com/wailsapp/wails/pull/4733)
|
||||
- Updated wails installation documentation to allow copying the `install wails` command with one click by @tilak999 in [#4692](https://github.com/wailsapp/wails/pull/4692)
|
||||
|
||||
## v2.11.0 - 2025-11-08
|
||||
|
||||
### Added
|
||||
|
|
@ -23,12 +33,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Added `ContentProtection` option to allow hiding the application window from screen sharing software [#4241](https://github.com/wailsapp/wails/pull/4241) by [@Taiterbase](https://github.com/Taiterbase)
|
||||
- Added `build:tags` to project specification for automatically adding compilation tags by @symball in [PR](https://github.com/wailsapp/wails/pull/4439)
|
||||
- Support for binding generics in [PR](https://github.dev/wailsapp/wails/pull/3626) by @ktsivkov
|
||||
- Add universal link support for macOS by @APshenkin in [PR](https://github.com/wailsapp/wails/pull/4693)
|
||||
|
||||
### Fixed
|
||||
- Added url validation for BrowserOpenURL by @APshenkin in [PR](https://github.com/wailsapp/wails/pull/4484)
|
||||
- Fixed C compilation error in onWayland on Linux due to declaration after label [#4446](https://github.com/wailsapp/wails/pull/4446) by [@jaesung9507](https://github.com/jaesung9507)
|
||||
- Use computed style when adding 'wails-drop-target-active' [PR](https://github.com/wailsapp/wails/pull/4420) by [@riannucci](https://github.com/riannucci)
|
||||
- Fixed panic when adding menuroles on Linux [#4558](https://github.com/wailsapp/wails/pull/4558) by [@jaesung9507](https://github.com/jaesung9507)
|
||||
- Fixed generated enums ordering [#4664](https://github.com/wailsapp/wails/pull/4664) by [@rprtr258](https://github.com/rprtr258).
|
||||
- Fixed Discord badge in README by @sharkmu in [PR](https://github.com/wailsapp/wails/pull/4626)
|
||||
- Fixed HTML DnD by @leaanthony
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ The number of Wails users is growing at an incredible rate, and if you're readin
|
|||
|
||||
### Code of Conduct
|
||||
|
||||
This [Code of Conduct](./coc) is an easy guide to develop the technical communities in which we participate.
|
||||
This [Code of Conduct](/coc) is an easy guide to develop the technical communities in which we participate.
|
||||
|
||||
### Stay in the Know
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
|
|
@ -73,6 +73,10 @@ import TabItem from "@theme/TabItem";
|
|||
|
||||
Run `go install github.com/wailsapp/wails/v2/cmd/wails@latest` to install the Wails CLI.
|
||||
|
||||
```shell
|
||||
go install github.com/wailsapp/wails/v2/cmd/wails@latest
|
||||
```
|
||||
|
||||
Note: If you get an error similar to this:
|
||||
|
||||
```shell
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for
|
|||
- [wails-template-naive](https://github.com/tk103331/wails-template-naive) - Wails template based on Naive UI (A Vue 3 Component Library)
|
||||
- [wails-template-primevue-sakai](https://github.com/TekWizely/wails-template-primevue-sakai) - Wails starter using [PrimeVue's Sakai Application Template](https://sakai.primevue.org) (Vite, Vue, PrimeVue, TailwindCSS, Vue Router, Themes, Dark Mode, UI Components, and more)
|
||||
- [wails-template-tdesign-js](https://github.com/tongque0/wails-template-tdesign-js) - Wails template based on TDesign UI (a Vue 3 UI library by Tencent), using Vite, Pinia, Vue Router, ESLint, and Prettier.
|
||||
- [wails-nuxt-template](https://github.com/paulbrickwell/wails-nuxt-template) - Wails template using Nuxt 4
|
||||
- [wails-nuxt-tailwind-template](https://github.com/paulbrickwell/wails-nuxt-tailwind-template) - Wails template using Nuxt 4 and the official Nuxt Tailwind and Nuxt Color Mode modules
|
||||
|
||||
## Angular
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue