diff --git a/v2/cmd/wails/internal/dev/watcher.go b/v2/cmd/wails/internal/dev/watcher.go index 1406f0c1..499b76df 100644 --- a/v2/cmd/wails/internal/dev/watcher.go +++ b/v2/cmd/wails/internal/dev/watcher.go @@ -44,14 +44,16 @@ func initialiseWatcher(cwd string) (*fsnotify.Watcher, error) { func getIgnoreDirs(cwd string) []string { ignoreDirs := []string{filepath.Join(cwd, "build/*"), ".*", "node_modules"} - + baseDir := filepath.Base(cwd) // Read .gitignore into ignoreDirs f, err := os.Open(filepath.Join(cwd, ".gitignore")) if err == nil { scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() - ignoreDirs = append(ignoreDirs, line) + if line != baseDir { + ignoreDirs = append(ignoreDirs, line) + } } } diff --git a/v2/go.mod b/v2/go.mod index 837f86b1..60bf75ab 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -22,6 +22,7 @@ require ( github.com/leaanthony/go-ansi-parser v1.6.0 github.com/leaanthony/gosod v1.0.3 github.com/leaanthony/slicer v1.6.0 + github.com/leaanthony/u v1.1.0 github.com/leaanthony/winicon v1.0.0 github.com/matryer/is v1.4.0 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 @@ -33,7 +34,7 @@ require ( github.com/tc-hib/winres v0.1.5 github.com/tidwall/sjson v1.1.7 github.com/tkrajina/go-reflector v0.5.6 - github.com/wailsapp/go-webview2 v1.0.1 + github.com/wailsapp/go-webview2 v1.0.7 github.com/wailsapp/mimetype v1.4.1 github.com/wzshiming/ctc v1.2.3 golang.org/x/mod v0.12.0 diff --git a/v2/go.sum b/v2/go.sum index d9180212..7769626f 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -119,6 +119,10 @@ github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRC github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js= github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8= +github.com/leaanthony/u v1.0.1 h1:tJLpf9EsuCgSB02ojRxg8KRqMgRN6mCTvFwI55kxRFE= +github.com/leaanthony/u v1.0.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI= +github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= github.com/leaanthony/winicon v1.0.0 h1:ZNt5U5dY71oEoKZ97UVwJRT4e+5xo5o/ieKuHuk8NqQ= github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c= @@ -214,8 +218,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/wailsapp/go-webview2 v1.0.1 h1:dEJIeEApW/MhO2tTMISZBFZPuW7kwrFA1NtgFB1z1II= -github.com/wailsapp/go-webview2 v1.0.1/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= +github.com/wailsapp/go-webview2 v1.0.7 h1:s95+7irJsAsTy1RsjJ6N0cYX7tZ4gP7Uzawds0L2urs= +github.com/wailsapp/go-webview2 v1.0.7/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= github.com/wzshiming/ctc v1.2.3 h1:q+hW3IQNsjIlOFBTGZZZeIXTElFM4grF4spW/errh/c= diff --git a/v2/internal/app/app.go b/v2/internal/app/app.go index 226b5a0b..0cd6bf61 100644 --- a/v2/internal/app/app.go +++ b/v2/internal/app/app.go @@ -22,7 +22,7 @@ type App struct { debug bool // Indicates if the devtools is enabled - devtools bool + devtoolsEnabled bool // OnStartup/OnShutdown startupCallback func(ctx context.Context) diff --git a/v2/internal/app/app_dev.go b/v2/internal/app/app_dev.go index 2e04f448..51e3e63b 100644 --- a/v2/internal/app/app_dev.go +++ b/v2/internal/app/app_dev.go @@ -44,7 +44,7 @@ func CreateApp(appoptions *options.App) (*App, error) { ctx := context.Background() ctx = context.WithValue(ctx, "debug", true) - ctx = context.WithValue(ctx, "devtools", true) + ctx = context.WithValue(ctx, "devtoolsEnabled", true) // Set up logger myLogger := logger.New(appoptions.Logger) @@ -230,7 +230,7 @@ func CreateApp(appoptions *options.App) (*App, error) { startupCallback: appoptions.OnStartup, shutdownCallback: appoptions.OnShutdown, debug: true, - devtools: true, + devtoolsEnabled: true, } result.options = appoptions diff --git a/v2/internal/app/app_devtools.go b/v2/internal/app/app_devtools.go index fc3b7090..a84e8c28 100644 --- a/v2/internal/app/app_devtools.go +++ b/v2/internal/app/app_devtools.go @@ -2,6 +2,7 @@ package app +// Note: devtools flag is also added in debug builds func IsDevtoolsEnabled() bool { return true } diff --git a/v2/internal/app/app_devtools_not.go b/v2/internal/app/app_devtools_not.go index 2855d800..4e7274ad 100644 --- a/v2/internal/app/app_devtools_not.go +++ b/v2/internal/app/app_devtools_not.go @@ -2,6 +2,7 @@ package app +// Note: devtools flag is also added in debug builds func IsDevtoolsEnabled() bool { return false } diff --git a/v2/internal/app/app_production.go b/v2/internal/app/app_production.go index 5b847260..96ed84e3 100644 --- a/v2/internal/app/app_production.go +++ b/v2/internal/app/app_production.go @@ -34,9 +34,9 @@ func CreateApp(appoptions *options.App) (*App, error) { options.MergeDefaults(appoptions) debug := IsDebug() - devtools := IsDevtoolsEnabled() + devtoolsEnabled := IsDevtoolsEnabled() ctx = context.WithValue(ctx, "debug", debug) - ctx = context.WithValue(ctx, "devtools", devtools) + ctx = context.WithValue(ctx, "devtoolsEnabled", devtoolsEnabled) // Set up logger myLogger := logger.New(appoptions.Logger) @@ -95,7 +95,7 @@ func CreateApp(appoptions *options.App) (*App, error) { startupCallback: appoptions.OnStartup, shutdownCallback: appoptions.OnShutdown, debug: debug, - devtools: devtools, + devtoolsEnabled: devtoolsEnabled, options: appoptions, } diff --git a/v2/internal/frontend/desktop/darwin/AppDelegate.m b/v2/internal/frontend/desktop/darwin/AppDelegate.m index 6d46deae..b66979e7 100644 --- a/v2/internal/frontend/desktop/darwin/AppDelegate.m +++ b/v2/internal/frontend/desktop/darwin/AppDelegate.m @@ -17,7 +17,7 @@ - (void)applicationWillFinishLaunching:(NSNotification *)aNotification { [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; if (self.alwaysOnTop) { - [self.mainWindow setLevel:NSStatusWindowLevel]; + [self.mainWindow setLevel:NSFloatingWindowLevel]; } if ( !self.startHidden ) { [self.mainWindow makeKeyAndOrderFront:self]; diff --git a/v2/internal/frontend/desktop/darwin/Application.h b/v2/internal/frontend/desktop/darwin/Application.h index 8be3826d..458016cc 100644 --- a/v2/internal/frontend/desktop/darwin/Application.h +++ b/v2/internal/frontend/desktop/darwin/Application.h @@ -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 devtools, int defaultContextMenu, int windowStartState, int startsHidden, int minWidth, int minHeight, int maxWidth, int maxHeight, bool fraudulentWebsiteWarningEnabled); +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 devtoolsEnabled, int defaultContextMenuEnabled, int windowStartState, int startsHidden, int minWidth, int minHeight, int maxWidth, int maxHeight, bool fraudulentWebsiteWarningEnabled, struct Preferences preferences); void Run(void*, const char* url); void SetTitle(void* ctx, const char *title); diff --git a/v2/internal/frontend/desktop/darwin/Application.m b/v2/internal/frontend/desktop/darwin/Application.m index 082018f6..e0c05212 100644 --- a/v2/internal/frontend/desktop/darwin/Application.m +++ b/v2/internal/frontend/desktop/darwin/Application.m @@ -14,20 +14,20 @@ #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 devtools, int defaultContextMenu, int windowStartState, int startsHidden, int minWidth, int minHeight, int maxWidth, int maxHeight, bool fraudulentWebsiteWarningEnabled) { +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 devtoolsEnabled, int defaultContextMenuEnabled, int windowStartState, int startsHidden, int minWidth, int minHeight, int maxWidth, int maxHeight, bool fraudulentWebsiteWarningEnabled, struct Preferences preferences) { [NSApplication sharedApplication]; WailsContext *result = [WailsContext new]; - result.devtools = devtools; - result.defaultContextMenu = defaultContextMenu; + result.devtoolsEnabled = devtoolsEnabled; + result.defaultContextMenuEnabled = defaultContextMenuEnabled; if ( windowStartState == WindowStartsFullscreen ) { fullscreen = 1; } - [result CreateWindow:width :height :frameless :resizable :fullscreen :fullSizeContent :hideTitleBar :titlebarAppearsTransparent :hideTitle :useToolbar :hideToolbarSeparator :webviewIsTransparent :hideWindowOnClose :safeInit(appearance) :windowIsTranslucent :minWidth :minHeight :maxWidth :maxHeight :fraudulentWebsiteWarningEnabled]; + [result CreateWindow:width :height :frameless :resizable :fullscreen :fullSizeContent :hideTitleBar :titlebarAppearsTransparent :hideTitle :useToolbar :hideToolbarSeparator :webviewIsTransparent :hideWindowOnClose :safeInit(appearance) :windowIsTranslucent :minWidth :minHeight :maxWidth :maxHeight :fraudulentWebsiteWarningEnabled :preferences]; [result SetTitle:safeInit(title)]; [result Center]; diff --git a/v2/internal/frontend/desktop/darwin/WailsContext.h b/v2/internal/frontend/desktop/darwin/WailsContext.h index 7cdf034b..fe66f354 100644 --- a/v2/internal/frontend/desktop/darwin/WailsContext.h +++ b/v2/internal/frontend/desktop/darwin/WailsContext.h @@ -44,8 +44,8 @@ @property bool alwaysOnTop; -@property bool devtools; -@property bool defaultContextMenu; +@property bool devtoolsEnabled; +@property bool defaultContextMenuEnabled; @property (retain) WKUserContentController* userContentController; @@ -55,7 +55,12 @@ @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 :(int)minWidth :(int)minHeight :(int)maxWidth :(int)maxHeight :(bool)fraudulentWebsiteWarningEnabled; +struct Preferences { + bool *tabFocusesLinks; + bool *textInteractionEnabled; +}; + +- (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 :(bool)fraudulentWebsiteWarningEnabled :(struct Preferences)preferences; - (void) SetSize:(int)width :(int)height; - (void) SetPosition:(int)x :(int) y; - (void) SetMinSize:(int)minWidth :(int)minHeight; diff --git a/v2/internal/frontend/desktop/darwin/WailsContext.m b/v2/internal/frontend/desktop/darwin/WailsContext.m index cc5aa234..1c84a576 100644 --- a/v2/internal/frontend/desktop/darwin/WailsContext.m +++ b/v2/internal/frontend/desktop/darwin/WailsContext.m @@ -136,7 +136,7 @@ typedef void (^schemeTaskCaller)(id); 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 :(int)minWidth :(int)minHeight :(int)maxWidth :(int)maxHeight :(bool)fraudulentWebsiteWarningEnabled { +- (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 :(bool)fraudulentWebsiteWarningEnabled :(struct Preferences)preferences { NSWindowStyleMask styleMask = 0; if( !frameless ) { @@ -214,6 +214,16 @@ typedef void (^schemeTaskCaller)(id); config.suppressesIncrementalRendering = true; config.applicationNameForUserAgent = @"wails.io"; [config setURLSchemeHandler:self forURLScheme:@"wails"]; + + if (preferences.tabFocusesLinks != NULL) { + config.preferences.tabFocusesLinks = *preferences.tabFocusesLinks; + } + + if (@available(macOS 11.3, *)) { + if (preferences.textInteractionEnabled != NULL) { + config.preferences.textInteractionEnabled = *preferences.textInteractionEnabled; + } + } // [config.preferences setValue:[NSNumber numberWithBool:true] forKey:@"developerExtrasEnabled"]; @@ -225,16 +235,18 @@ typedef void (^schemeTaskCaller)(id); [userContentController addScriptMessageHandler:self name:@"external"]; config.userContentController = userContentController; self.userContentController = userContentController; - if (self.devtools) { + + if (self.devtoolsEnabled) { [config.preferences setValue:@YES forKey:@"developerExtrasEnabled"]; - } else if (!self.defaultContextMenu) { + } + + if (!self.defaultContextMenuEnabled) { // Disable default context menus WKUserScript *initScript = [WKUserScript new]; [initScript initWithSource:@"window.wails.flags.disableDefaultContextMenu = true;" injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:false]; [userContentController addUserScript:initScript]; - } self.webview = [WKWebView alloc]; @@ -394,7 +406,7 @@ typedef void (^schemeTaskCaller)(id); - (void) SetAlwaysOnTop:(int)onTop { if (onTop) { - [self.mainWindow setLevel:NSStatusWindowLevel]; + [self.mainWindow setLevel:NSFloatingWindowLevel]; } else { [self.mainWindow setLevel:NSNormalWindowLevel]; } diff --git a/v2/internal/frontend/desktop/darwin/frontend.go b/v2/internal/frontend/desktop/darwin/frontend.go index bfe470b4..a6a0808f 100644 --- a/v2/internal/frontend/desktop/darwin/frontend.go +++ b/v2/internal/frontend/desktop/darwin/frontend.go @@ -47,7 +47,7 @@ type Frontend struct { frontendOptions *options.App logger *logger.Logger debug bool - devtools bool + devtoolsEnabled bool // Assets assets *assetserver.AssetServer @@ -154,16 +154,16 @@ func (f *Frontend) Run(ctx context.Context) error { f.ctx = ctx var _debug = ctx.Value("debug") - var _devtools = ctx.Value("devtools") + var _devtoolsEnabled = ctx.Value("devtoolsEnabled") if _debug != nil { f.debug = _debug.(bool) } - if _devtools != nil { - f.devtools = _devtools.(bool) + if _devtoolsEnabled != nil { + f.devtoolsEnabled = _devtoolsEnabled.(bool) } - mainWindow := NewWindow(f.frontendOptions, f.debug, f.devtools) + mainWindow := NewWindow(f.frontendOptions, f.debug, f.devtoolsEnabled) f.mainWindow = mainWindow f.mainWindow.Center() @@ -323,6 +323,11 @@ func (f *Frontend) processMessage(message string) { return } + if message == "wails:openInspector" { + showInspector(f.mainWindow.context) + return + } + //if strings.HasPrefix(message, "systemevent:") { // f.processSystemEvent(message) // return @@ -390,6 +395,6 @@ func processCallback(callbackID uint) { } //export processURLRequest -func processURLRequest(ctx unsafe.Pointer, wkURLSchemeTask unsafe.Pointer) { +func processURLRequest(_ unsafe.Pointer, wkURLSchemeTask unsafe.Pointer) { requestBuffer <- webview.NewRequest(wkURLSchemeTask) } diff --git a/v2/internal/frontend/desktop/darwin/inspector.go b/v2/internal/frontend/desktop/darwin/inspector.go index 3021c5c1..8499745d 100644 --- a/v2/internal/frontend/desktop/darwin/inspector.go +++ b/v2/internal/frontend/desktop/darwin/inspector.go @@ -1,4 +1,4 @@ -//go:build darwin && !(dev || debug) +//go:build darwin && !(dev || debug || devtools) package darwin @@ -6,6 +6,6 @@ import ( "unsafe" ) -func showInspector(context unsafe.Pointer) { +func showInspector(_ unsafe.Pointer) { } diff --git a/v2/internal/frontend/desktop/darwin/inspector_dev.go b/v2/internal/frontend/desktop/darwin/inspector_dev.go index e948435c..f3520cb3 100644 --- a/v2/internal/frontend/desktop/darwin/inspector_dev.go +++ b/v2/internal/frontend/desktop/darwin/inspector_dev.go @@ -1,4 +1,4 @@ -//go:build darwin && (dev || debug) +//go:build darwin && (dev || debug || devtools) package darwin @@ -11,6 +11,8 @@ package darwin #import #import "WailsContext.h" +extern void processMessage(const char *message); + @interface _WKInspector : NSObject - (void)show; - (void)detach; @@ -21,28 +23,44 @@ package darwin @end void showInspector(void *inctx) { - if (@available(macOS 12.0, *)) { - WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + if (@available(macOS 12.0, *)) { + WailsContext *ctx = (__bridge WailsContext*) inctx; - @try { - [ctx.webview._inspector show]; - } @catch (NSException *exception) { - NSLog(@"Opening the inspector failed: %@", exception.reason); - return; - } - - dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC); - dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ - // Detach must be deferred a little bit and is ignored directly after a show. @try { - [ctx.webview._inspector detach]; + [ctx.webview._inspector show]; } @catch (NSException *exception) { - NSLog(@"Detaching the inspector failed: %@", exception.reason); + NSLog(@"Opening the inspector failed: %@", exception.reason); + return; } - }); - } else { - NSLog(@"Opening the inspector needs at least MacOS 12"); - } + + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC); + dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ + // Detach must be deferred a little bit and is ignored directly after a show. + @try { + [ctx.webview._inspector detach]; + } @catch (NSException *exception) { + NSLog(@"Detaching the inspector failed: %@", exception.reason); + } + }); + } else { + NSLog(@"Opening the inspector needs at least MacOS 12"); + } + ); + +} + +void setupF12hotkey() { + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { + if (event.keyCode == 111 && + event.modifierFlags & NSEventModifierFlagFunction && + event.modifierFlags & NSEventModifierFlagCommand && + event.modifierFlags & NSEventModifierFlagShift) { + processMessage("wails:openInspector"); + return nil; + } + return event; + }]; } */ import "C" @@ -50,6 +68,10 @@ import ( "unsafe" ) +func init() { + C.setupF12hotkey() +} + func showInspector(context unsafe.Pointer) { C.showInspector(context) } diff --git a/v2/internal/frontend/desktop/darwin/main.m b/v2/internal/frontend/desktop/darwin/main.m index 45a7d268..1dd8bb1f 100644 --- a/v2/internal/frontend/desktop/darwin/main.m +++ b/v2/internal/frontend/desktop/darwin/main.m @@ -215,11 +215,11 @@ int main(int argc, const char * argv[]) { int hideWindowOnClose = 0; const char* appearance = "NSAppearanceNameDarkAqua"; int windowIsTranslucent = 1; - int devtools = 1; - int defaultContextMenu = 1; + int devtoolsEnabled = 1; + int defaultContextMenuEnabled = 1; 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, devtools, defaultContextMenu, windowStartState, + WailsContext *result = Create("OI OI!",400,400, frameless, resizable, fullscreen, fullSizeContent, hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent, alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, devtoolsEnabled, defaultContextMenuEnabled, windowStartState, startsHidden, 400, 400, 600, 600, false); SetBackgroundColour(result, 255, 0, 0, 255); void *m = NewMenu(""); diff --git a/v2/internal/frontend/desktop/darwin/window.go b/v2/internal/frontend/desktop/darwin/window.go index 2586b1b4..803d04c1 100644 --- a/v2/internal/frontend/desktop/darwin/window.go +++ b/v2/internal/frontend/desktop/darwin/window.go @@ -40,6 +40,11 @@ func bool2Cint(value bool) C.int { return C.int(0) } +func bool2CboolPtr(value bool) *C.bool { + v := C.bool(value) + return &v +} + func NewWindow(frontendOptions *options.App, debug bool, devtools bool) *Window { c := NewCalloc() @@ -52,11 +57,12 @@ func NewWindow(frontendOptions *options.App, debug bool, devtools bool) *Window hideWindowOnClose := bool2Cint(frontendOptions.HideWindowOnClose) startsHidden := bool2Cint(frontendOptions.StartHidden) devtoolsEnabled := bool2Cint(devtools) - defaultContextMenu := bool2Cint(frontendOptions.EnableDefaultContextMenu) + defaultContextMenuEnabled := bool2Cint(debug || frontendOptions.EnableDefaultContextMenu) var fullSizeContent, hideTitleBar, hideTitle, useToolbar, webviewIsTransparent C.int var titlebarAppearsTransparent, hideToolbarSeparator, windowIsTranslucent C.int var appearance, title *C.char + var preferences C.struct_Preferences width := C.int(frontendOptions.Width) height := C.int(frontendOptions.Height) @@ -80,6 +86,17 @@ func NewWindow(frontendOptions *options.App, debug bool, devtools bool) *Window titlebarAppearsTransparent = bool2Cint(mac.TitleBar.TitlebarAppearsTransparent) hideToolbarSeparator = bool2Cint(mac.TitleBar.HideToolbarSeparator) } + + if mac.Preferences != nil { + if mac.Preferences.TabFocusesLinks.IsSet() { + preferences.tabFocusesLinks = bool2CboolPtr(mac.Preferences.TabFocusesLinks.Get()) + } + + if mac.Preferences.TextInteractionEnabled.IsSet() { + preferences.textInteractionEnabled = bool2CboolPtr(mac.Preferences.TextInteractionEnabled.Get()) + } + } + windowIsTranslucent = bool2Cint(mac.WindowIsTranslucent) webviewIsTransparent = bool2Cint(mac.WebviewIsTransparent) @@ -87,8 +104,8 @@ func NewWindow(frontendOptions *options.App, debug bool, devtools 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, devtoolsEnabled, defaultContextMenu, - windowStartState, startsHidden, minWidth, minHeight, maxWidth, maxHeight, enableFraudulentWebsiteWarnings) + alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, devtoolsEnabled, defaultContextMenuEnabled, + windowStartState, startsHidden, minWidth, minHeight, maxWidth, maxHeight, enableFraudulentWebsiteWarnings, preferences) // Create menu result := &Window{ diff --git a/v2/internal/frontend/desktop/linux/frontend.go b/v2/internal/frontend/desktop/linux/frontend.go index 7a25df4a..70f8468b 100644 --- a/v2/internal/frontend/desktop/linux/frontend.go +++ b/v2/internal/frontend/desktop/linux/frontend.go @@ -110,7 +110,7 @@ type Frontend struct { frontendOptions *options.App logger *logger.Logger debug bool - devtools bool + devtoolsEnabled bool // Assets assets *assetserver.AssetServer @@ -182,16 +182,16 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger. go result.startMessageProcessor() var _debug = ctx.Value("debug") - var _devtools = ctx.Value("devtools") + var _devtoolsEnabled = ctx.Value("devtoolsEnabled") if _debug != nil { result.debug = _debug.(bool) } - if _devtools != nil { - result.devtools = _devtools.(bool) + if _devtoolsEnabled != nil { + result.devtoolsEnabled = _devtoolsEnabled.(bool) } - result.mainWindow = NewWindow(appoptions, result.debug, result.devtools) + result.mainWindow = NewWindow(appoptions, result.debug, result.devtoolsEnabled) C.install_signal_handlers() @@ -410,6 +410,11 @@ func (f *Frontend) processMessage(message string) { return } + if message == "wails:showInspector" { + f.mainWindow.ShowInspector() + return + } + if strings.HasPrefix(message, "resize:") { if !f.mainWindow.IsFullScreen() { sl := strings.Split(message, ":") diff --git a/v2/internal/frontend/desktop/linux/window.c b/v2/internal/frontend/desktop/linux/window.c index a70a3be8..7cd1c249 100644 --- a/v2/internal/frontend/desktop/linux/window.c +++ b/v2/internal/frontend/desktop/linux/window.c @@ -474,8 +474,7 @@ void DevtoolsEnabled(void *webview, int enabled, bool showInspector) if (genabled && showInspector) { - WebKitWebInspector *inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview)); - webkit_web_inspector_show(WEBKIT_WEB_INSPECTOR(inspector)); + ShowInspector(webview); } } @@ -732,3 +731,21 @@ GtkFileFilter *newFileFilter() g_object_ref(result); return result; } + +void ShowInspector(void *webview) { + WebKitWebInspector *inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview)); + webkit_web_inspector_show(WEBKIT_WEB_INSPECTOR(inspector)); +} + +void sendShowInspectorMessage() { + processMessage("wails:showInspector"); +} + +void InstallF12Hotkey(void *window) +{ + // When the user presses Ctrl+Shift+F12, call ShowInspector + GtkAccelGroup *accel_group = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); + GClosure *closure = g_cclosure_new(G_CALLBACK(sendShowInspectorMessage), window, NULL); + gtk_accel_group_connect(accel_group, GDK_KEY_F12, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE, closure); +} \ No newline at end of file diff --git a/v2/internal/frontend/desktop/linux/window.go b/v2/internal/frontend/desktop/linux/window.go index 6d9b256f..82030f43 100644 --- a/v2/internal/frontend/desktop/linux/window.go +++ b/v2/internal/frontend/desktop/linux/window.go @@ -37,7 +37,7 @@ func gtkBool(input bool) C.gboolean { type Window struct { appoptions *options.App debug bool - devtools bool + devtoolsEnabled bool gtkWindow unsafe.Pointer contentManager unsafe.Pointer webview unsafe.Pointer @@ -56,17 +56,17 @@ func bool2Cint(value bool) C.int { return C.int(0) } -func NewWindow(appoptions *options.App, debug bool, devtools bool) *Window { +func NewWindow(appoptions *options.App, debug bool, devtoolsEnabled bool) *Window { validateWebKit2Version(appoptions) result := &Window{ - appoptions: appoptions, - debug: debug, - devtools: devtools, - minHeight: appoptions.MinHeight, - minWidth: appoptions.MinWidth, - maxHeight: appoptions.MaxHeight, - maxWidth: appoptions.MaxWidth, + appoptions: appoptions, + debug: debug, + devtoolsEnabled: devtoolsEnabled, + minHeight: appoptions.MinHeight, + minWidth: appoptions.MinWidth, + maxHeight: appoptions.MaxHeight, + maxWidth: appoptions.MaxWidth, } gtkWindow := C.gtk_window_new(C.GTK_WINDOW_TOPLEVEL) @@ -103,9 +103,13 @@ func NewWindow(appoptions *options.App, debug bool, devtools bool) *Window { defer C.free(unsafe.Pointer(buttonPressedName)) C.ConnectButtons(unsafe.Pointer(webview)) - if devtools { + if devtoolsEnabled { C.DevtoolsEnabled(unsafe.Pointer(webview), C.int(1), C.bool(debug && appoptions.Debug.OpenInspectorOnStartup)) - } else if !appoptions.EnableDefaultContextMenu { + // Install Ctrl-Shift-F12 hotkey to call ShowInspector + C.InstallF12Hotkey(unsafe.Pointer(gtkWindow)) + } + + if !(debug || appoptions.EnableDefaultContextMenu) { C.DisableContextMenu(unsafe.Pointer(webview)) } @@ -446,6 +450,10 @@ func (w *Window) ToggleMaximise() { } } +func (w *Window) ShowInspector() { + invokeOnMainThread(func() { C.ShowInspector(w.webview) }) +} + // showModalDialogAndExit shows a modal dialog and exits the app. func showModalDialogAndExit(title, message string) { go func() { diff --git a/v2/internal/frontend/desktop/linux/window.h b/v2/internal/frontend/desktop/linux/window.h index eabcdd1e..aa9499d7 100644 --- a/v2/internal/frontend/desktop/linux/window.h +++ b/v2/internal/frontend/desktop/linux/window.h @@ -120,4 +120,9 @@ void MessageDialog(void *data); GtkFileFilter **AllocFileFilterArray(size_t ln); void Opendialog(void *data); +// Inspector +void sendShowInspectorMessage(); +void ShowInspector(void *webview); +void InstallF12Hotkey(void *window); + #endif /* window_h */ diff --git a/v2/internal/frontend/desktop/windows/frontend.go b/v2/internal/frontend/desktop/windows/frontend.go index dabd1e2c..a753bbd3 100644 --- a/v2/internal/frontend/desktop/windows/frontend.go +++ b/v2/internal/frontend/desktop/windows/frontend.go @@ -7,11 +7,8 @@ import ( "context" "encoding/json" "fmt" - "io" "log" "net" - "net/http" - "net/http/httptest" "net/url" "os" "runtime" @@ -31,6 +28,7 @@ import ( "github.com/wailsapp/wails/v2/internal/logger" "github.com/wailsapp/wails/v2/internal/system/operatingsystem" "github.com/wailsapp/wails/v2/pkg/assetserver" + "github.com/wailsapp/wails/v2/pkg/assetserver/webview" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/options/windows" ) @@ -48,7 +46,7 @@ type Frontend struct { logger *logger.Logger chromium *edge.Chromium debug bool - devtools bool + devtoolsEnabled bool // Assets assets *assetserver.AssetServer @@ -143,13 +141,13 @@ func (f *Frontend) Run(ctx context.Context) error { f.mainWindow = mainWindow var _debug = ctx.Value("debug") - var _devtools = ctx.Value("devtools") + var _devtoolsEnabled = ctx.Value("devtoolsEnabled") if _debug != nil { f.debug = _debug.(bool) } - if _devtools != nil { - f.devtools = _devtools.(bool) + if _devtoolsEnabled != nil { + f.devtoolsEnabled = _devtoolsEnabled.(bool) } f.WindowCenter() @@ -458,6 +456,18 @@ func (f *Frontend) setupChromium() { chromium.WebResourceRequestedCallback = f.processRequest chromium.NavigationCompletedCallback = f.navigationCompleted chromium.AcceleratorKeyCallback = func(vkey uint) bool { + if vkey == w32.VK_F12 && f.devtoolsEnabled { + var keyState [256]byte + if w32.GetKeyboardState(keyState[:]) { + // Check if CTRL is pressed + if keyState[w32.VK_CONTROL]&0x80 != 0 && keyState[w32.VK_SHIFT]&0x80 != 0 { + chromium.OpenDevToolsWindow() + return true + } + } else { + f.logger.Error("Call to GetKeyboardState failed") + } + } w32.PostMessage(f.mainWindow.Handle(), w32.WM_KEYDOWN, uintptr(vkey), 0) return false } @@ -494,16 +504,24 @@ func (f *Frontend) setupChromium() { } chromium.Embed(f.mainWindow.Handle()) + + if chromium.HasCapability(edge.SwipeNavigation) { + swipeGesturesEnabled := f.frontendOptions.Windows != nil && f.frontendOptions.Windows.EnableSwipeGestures + err := chromium.PutIsSwipeNavigationEnabled(swipeGesturesEnabled) + if err != nil { + log.Fatal(err) + } + } chromium.Resize() settings, err := chromium.GetSettings() if err != nil { log.Fatal(err) } - err = settings.PutAreDefaultContextMenusEnabled(f.devtools || f.frontendOptions.EnableDefaultContextMenu) + err = settings.PutAreDefaultContextMenusEnabled(f.debug || f.frontendOptions.EnableDefaultContextMenu) if err != nil { log.Fatal(err) } - err = settings.PutAreDevToolsEnabled(f.devtools) + err = settings.PutAreDevToolsEnabled(f.devtoolsEnabled) if err != nil { log.Fatal(err) } @@ -526,10 +544,6 @@ func (f *Frontend) setupChromium() { if err != nil { log.Fatal(err) } - err = settings.PutIsSwipeNavigationEnabled(false) - if err != nil { - log.Fatal(err) - } if f.debug && f.frontendOptions.Debug.OpenInspectorOnStartup { chromium.OpenDevToolsWindow() @@ -596,36 +610,31 @@ func (f *Frontend) processRequest(req *edge.ICoreWebView2WebResourceRequest, arg return } - rw := httptest.NewRecorder() - f.assets.ProcessHTTPRequestLegacy(rw, coreWebview2RequestToHttpRequest(req)) + webviewRequest, err := webview.NewRequest( + f.chromium.Environment(), + args, + func(fn func()) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if f.mainWindow.InvokeRequired() { + var wg sync.WaitGroup + wg.Add(1) + f.mainWindow.Invoke(func() { + fn() + wg.Done() + }) + wg.Wait() + } else { + fn() + } + }) - headers := []string{} - for k, v := range rw.Header() { - headers = append(headers, fmt.Sprintf("%s: %s", k, strings.Join(v, ","))) - } - - code := rw.Code - if code == http.StatusNotModified { - // WebView2 has problems when a request returns a 304 status code and the WebView2 is going to hang for other - // requests including IPC calls. - f.logger.Error("%s: AssetServer returned 304 - StatusNotModified which are going to hang WebView2, changed code to 505 - StatusInternalServerError", uri) - code = http.StatusInternalServerError - } - - env := f.chromium.Environment() - response, err := env.CreateWebResourceResponse(rw.Body.Bytes(), code, http.StatusText(code), strings.Join(headers, "\n")) if err != nil { - f.logger.Error("CreateWebResourceResponse Error: %s", err) + f.logger.Error("%s: NewRequest failed: %s", uri, err) return } - defer response.Release() - // Send response back - err = args.PutResponse(response) - if err != nil { - f.logger.Error("PutResponse Error: %s", err) - return - } + f.assets.ServeWebViewRequest(webviewRequest) } var edgeMap = map[string]uintptr{ @@ -816,93 +825,3 @@ func (f *Frontend) ShowWindow() { func (f *Frontend) onFocus(arg *winc.Event) { f.chromium.Focus() } - -func coreWebview2RequestToHttpRequest(coreReq *edge.ICoreWebView2WebResourceRequest) func() (*http.Request, error) { - return func() (r *http.Request, err error) { - header := http.Header{} - headers, err := coreReq.GetHeaders() - if err != nil { - return nil, fmt.Errorf("GetHeaders Error: %s", err) - } - defer headers.Release() - - headersIt, err := headers.GetIterator() - if err != nil { - return nil, fmt.Errorf("GetIterator Error: %s", err) - } - defer headersIt.Release() - - for { - has, err := headersIt.HasCurrentHeader() - if err != nil { - return nil, fmt.Errorf("HasCurrentHeader Error: %s", err) - } - if !has { - break - } - - name, value, err := headersIt.GetCurrentHeader() - if err != nil { - return nil, fmt.Errorf("GetCurrentHeader Error: %s", err) - } - - header.Set(name, value) - if _, err := headersIt.MoveNext(); err != nil { - return nil, fmt.Errorf("MoveNext Error: %s", err) - } - } - - // WebView2 has problems when a request returns a 304 status code and the WebView2 is going to hang for other - // requests including IPC calls. - // So prevent 304 status codes by removing the headers that are used in combinationwith caching. - header.Del("If-Modified-Since") - header.Del("If-None-Match") - - method, err := coreReq.GetMethod() - if err != nil { - return nil, fmt.Errorf("GetMethod Error: %s", err) - } - - uri, err := coreReq.GetUri() - if err != nil { - return nil, fmt.Errorf("GetUri Error: %s", err) - } - - var body io.ReadCloser - if content, err := coreReq.GetContent(); err != nil { - return nil, fmt.Errorf("GetContent Error: %s", err) - } else if content != nil { - body = &iStreamReleaseCloser{stream: content} - } - - req, err := http.NewRequest(method, uri, body) - if err != nil { - if body != nil { - body.Close() - } - return nil, err - } - req.Header = header - return req, nil - } -} - -type iStreamReleaseCloser struct { - stream *edge.IStream - closed bool -} - -func (i *iStreamReleaseCloser) Read(p []byte) (int, error) { - if i.closed { - return 0, io.ErrClosedPipe - } - return i.stream.Read(p) -} - -func (i *iStreamReleaseCloser) Close() error { - if i.closed { - return nil - } - i.closed = true - return i.stream.Release() -} diff --git a/v2/internal/frontend/desktop/windows/winc/w32/user32.go b/v2/internal/frontend/desktop/windows/winc/w32/user32.go index 17e0d999..ec5e4a59 100644 --- a/v2/internal/frontend/desktop/windows/winc/w32/user32.go +++ b/v2/internal/frontend/desktop/windows/winc/w32/user32.go @@ -1023,9 +1023,11 @@ func EndPaint(hwnd HWND, paint *PAINTSTRUCT) { uintptr(unsafe.Pointer(paint))) } -func GetKeyboardState(lpKeyState *[]byte) bool { - ret, _, _ := procGetKeyboardState.Call( - uintptr(unsafe.Pointer(&(*lpKeyState)[0]))) +func GetKeyboardState(keyState []byte) bool { + if len(keyState) != 256 { + panic("keyState slice must have a size of 256 bytes") + } + ret, _, _ := procGetKeyboardState.Call(uintptr(unsafe.Pointer(&keyState[0]))) return ret != 0 } diff --git a/v2/pkg/assetserver/assetserver_legacy.go b/v2/pkg/assetserver/assetserver_legacy.go deleted file mode 100644 index 4df671bc..00000000 --- a/v2/pkg/assetserver/assetserver_legacy.go +++ /dev/null @@ -1,78 +0,0 @@ -package assetserver - -import ( - "io" - "net/http" - - "github.com/wailsapp/wails/v2/pkg/assetserver/webview" -) - -// ProcessHTTPRequest processes the HTTP Request by faking a golang HTTP Server. -// The request will be finished with a StatusNotImplemented code if no handler has written to the response. -func (d *AssetServer) ProcessHTTPRequestLegacy(rw http.ResponseWriter, reqGetter func() (*http.Request, error)) { - d.processWebViewRequest(&legacyRequest{reqGetter: reqGetter, rw: rw}) -} - -type legacyRequest struct { - req *http.Request - rw http.ResponseWriter - - reqGetter func() (*http.Request, error) -} - -func (r *legacyRequest) URL() (string, error) { - req, err := r.request() - if err != nil { - return "", err - } - return req.URL.String(), nil -} - -func (r *legacyRequest) Method() (string, error) { - req, err := r.request() - if err != nil { - return "", err - } - return req.Method, nil -} - -func (r *legacyRequest) Header() (http.Header, error) { - req, err := r.request() - if err != nil { - return nil, err - } - return req.Header, nil -} - -func (r *legacyRequest) Body() (io.ReadCloser, error) { - req, err := r.request() - if err != nil { - return nil, err - } - return req.Body, nil -} - -func (r legacyRequest) Response() webview.ResponseWriter { - return &legacyRequestNoOpCloserResponseWriter{r.rw} -} - -func (r legacyRequest) Close() error { return nil } - -func (r *legacyRequest) request() (*http.Request, error) { - if r.req != nil { - return r.req, nil - } - - req, err := r.reqGetter() - if err != nil { - return nil, err - } - r.req = req - return req, nil -} - -type legacyRequestNoOpCloserResponseWriter struct { - http.ResponseWriter -} - -func (*legacyRequestNoOpCloserResponseWriter) Finish() {} diff --git a/v2/pkg/assetserver/assetserver_webview.go b/v2/pkg/assetserver/assetserver_webview.go index 7fad1b01..575c81bb 100644 --- a/v2/pkg/assetserver/assetserver_webview.go +++ b/v2/pkg/assetserver/assetserver_webview.go @@ -26,19 +26,15 @@ type assetServerWebView struct { func (d *AssetServer) ServeWebViewRequest(req webview.Request) { d.dispatchInit.Do(func() { workers := d.dispatchWorkers - if workers == 0 { - workers = 10 + if workers <= 0 { + return } workerC := make(chan webview.Request, workers*2) for i := 0; i < workers; i++ { go func() { for req := range workerC { - uri, _ := req.URL() d.processWebViewRequest(req) - if err := req.Close(); err != nil { - d.logError("Unable to call close for request for uri '%s'", uri) - } } }() } @@ -49,19 +45,38 @@ func (d *AssetServer) ServeWebViewRequest(req webview.Request) { d.dispatchReqC = dispatchC }) - d.dispatchReqC <- req + if d.dispatchReqC == nil { + go d.processWebViewRequest(req) + } else { + d.dispatchReqC <- req + } +} + +func (d *AssetServer) processWebViewRequest(r webview.Request) { + uri, _ := r.URL() + d.processWebViewRequestInternal(r) + if err := r.Close(); err != nil { + d.logError("Unable to call close for request for uri '%s'", uri) + } } // processHTTPRequest processes the HTTP Request by faking a golang HTTP Server. // The request will be finished with a StatusNotImplemented code if no handler has written to the response. -func (d *AssetServer) processWebViewRequest(r webview.Request) { +func (d *AssetServer) processWebViewRequestInternal(r webview.Request) { + uri := "unknown" + var err error + wrw := r.Response() - defer wrw.Finish() + defer func() { + if err := wrw.Finish(); err != nil { + d.logError("Error finishing request '%s': %s", uri, err) + } + }() var rw http.ResponseWriter = &contentTypeSniffer{rw: wrw} // Make sure we have a Content-Type sniffer defer rw.WriteHeader(http.StatusNotImplemented) // This is a NOP when a handler has already written and set the status - uri, err := r.URL() + uri, err = r.URL() if err != nil { d.logError("Error processing request, unable to get URL: %s (HttpResponse=500)", err) http.Error(rw, err.Error(), http.StatusInternalServerError) diff --git a/v2/pkg/assetserver/webview/request_windows.go b/v2/pkg/assetserver/webview/request_windows.go new file mode 100644 index 00000000..fa83cd8d --- /dev/null +++ b/v2/pkg/assetserver/webview/request_windows.go @@ -0,0 +1,217 @@ +//go:build windows +// +build windows + +package webview + +import ( + "fmt" + "io" + "net/http" + "strings" + + "github.com/wailsapp/go-webview2/pkg/edge" +) + +// NewRequest creates as new WebViewRequest for chromium. This Method must be called from the Main-Thread! +func NewRequest(env *edge.ICoreWebView2Environment, args *edge.ICoreWebView2WebResourceRequestedEventArgs, invokeSync func(fn func())) (Request, error) { + req, err := args.GetRequest() + if err != nil { + return nil, fmt.Errorf("GetRequest failed: %s", err) + } + defer req.Release() + + r := &request{ + invokeSync: invokeSync, + } + + code := http.StatusInternalServerError + r.response, err = env.CreateWebResourceResponse(nil, code, http.StatusText(code), "") + if err != nil { + return nil, fmt.Errorf("CreateWebResourceResponse failed: %s", err) + } + + if err := args.PutResponse(r.response); err != nil { + r.finishResponse() + return nil, fmt.Errorf("PutResponse failed: %s", err) + } + + r.deferral, err = args.GetDeferral() + if err != nil { + r.finishResponse() + return nil, fmt.Errorf("GetDeferral failed: %s", err) + } + + r.url, r.urlErr = req.GetUri() + r.method, r.methodErr = req.GetMethod() + r.header, r.headerErr = getHeaders(req) + + if content, err := req.GetContent(); err != nil { + r.bodyErr = err + } else if content != nil { + // It is safe to access Content from another Thread: https://learn.microsoft.com/en-us/microsoft-edge/webview2/concepts/threading-model#thread-safety + r.body = &iStreamReleaseCloser{stream: content} + } + + return r, nil +} + +var _ Request = &request{} + +type request struct { + response *edge.ICoreWebView2WebResourceResponse + deferral *edge.ICoreWebView2Deferral + + url string + urlErr error + + method string + methodErr error + + header http.Header + headerErr error + + body io.ReadCloser + bodyErr error + rw *responseWriter + + invokeSync func(fn func()) +} + +func (r *request) URL() (string, error) { + return r.url, r.urlErr +} + +func (r *request) Method() (string, error) { + return r.method, r.methodErr +} + +func (r *request) Header() (http.Header, error) { + return r.header, r.headerErr +} + +func (r *request) Body() (io.ReadCloser, error) { + return r.body, r.bodyErr +} + +func (r *request) Response() ResponseWriter { + if r.rw != nil { + return r.rw + } + + r.rw = &responseWriter{req: r} + return r.rw +} + +func (r *request) Close() error { + var errs []error + if r.body != nil { + if err := r.body.Close(); err != nil { + errs = append(errs, err) + } + r.body = nil + } + + if err := r.Response().Finish(); err != nil { + errs = append(errs, err) + } + + return combineErrs(errs) +} + +// finishResponse must be called on the main-thread +func (r *request) finishResponse() error { + var errs []error + if r.response != nil { + if err := r.response.Release(); err != nil { + errs = append(errs, err) + } + r.response = nil + } + if r.deferral != nil { + if err := r.deferral.Complete(); err != nil { + errs = append(errs, err) + } + + if err := r.deferral.Release(); err != nil { + errs = append(errs, err) + } + r.deferral = nil + } + return combineErrs(errs) +} + +type iStreamReleaseCloser struct { + stream *edge.IStream + closed bool +} + +func (i *iStreamReleaseCloser) Read(p []byte) (int, error) { + if i.closed { + return 0, io.ErrClosedPipe + } + return i.stream.Read(p) +} + +func (i *iStreamReleaseCloser) Close() error { + if i.closed { + return nil + } + i.closed = true + return i.stream.Release() +} + +func getHeaders(req *edge.ICoreWebView2WebResourceRequest) (http.Header, error) { + header := http.Header{} + headers, err := req.GetHeaders() + if err != nil { + return nil, fmt.Errorf("GetHeaders Error: %s", err) + } + defer headers.Release() + + headersIt, err := headers.GetIterator() + if err != nil { + return nil, fmt.Errorf("GetIterator Error: %s", err) + } + defer headersIt.Release() + + for { + has, err := headersIt.HasCurrentHeader() + if err != nil { + return nil, fmt.Errorf("HasCurrentHeader Error: %s", err) + } + if !has { + break + } + + name, value, err := headersIt.GetCurrentHeader() + if err != nil { + return nil, fmt.Errorf("GetCurrentHeader Error: %s", err) + } + + header.Set(name, value) + if _, err := headersIt.MoveNext(); err != nil { + return nil, fmt.Errorf("MoveNext Error: %s", err) + } + } + + // WebView2 has problems when a request returns a 304 status code and the WebView2 is going to hang for other + // requests including IPC calls. + // So prevent 304 status codes by removing the headers that are used in combinationwith caching. + header.Del("If-Modified-Since") + header.Del("If-None-Match") + return header, nil +} + +func combineErrs(errs []error) error { + // TODO use Go1.20 errors.Join + if len(errs) == 0 { + return nil + } + + errStrings := make([]string, len(errs)) + for i, err := range errs { + errStrings[i] = err.Error() + } + + return fmt.Errorf(strings.Join(errStrings, "\n")) +} diff --git a/v2/pkg/assetserver/webview/responsewriter.go b/v2/pkg/assetserver/webview/responsewriter.go index d67802a0..dacbb567 100644 --- a/v2/pkg/assetserver/webview/responsewriter.go +++ b/v2/pkg/assetserver/webview/responsewriter.go @@ -21,5 +21,5 @@ type ResponseWriter interface { http.ResponseWriter // Finish the response and flush all data. A Finish after the request has already been finished has no effect. - Finish() + Finish() error } diff --git a/v2/pkg/assetserver/webview/responsewriter_darwin.go b/v2/pkg/assetserver/webview/responsewriter_darwin.go index 1c0cbee7..77de3c45 100644 --- a/v2/pkg/assetserver/webview/responsewriter_darwin.go +++ b/v2/pkg/assetserver/webview/responsewriter_darwin.go @@ -133,15 +133,16 @@ func (rw *responseWriter) WriteHeader(code int) { C.URLSchemeTaskDidReceiveResponse(rw.r.task, C.int(code), headers, C.int(headersLen)) } -func (rw *responseWriter) Finish() { +func (rw *responseWriter) Finish() error { if !rw.wroteHeader { rw.WriteHeader(http.StatusNotImplemented) } if rw.finished { - return + return nil } rw.finished = true C.URLSchemeTaskDidFinish(rw.r.task) + return nil } diff --git a/v2/pkg/assetserver/webview/responsewriter_linux.go b/v2/pkg/assetserver/webview/responsewriter_linux.go index 9b3f53a7..52e28aa5 100644 --- a/v2/pkg/assetserver/webview/responsewriter_linux.go +++ b/v2/pkg/assetserver/webview/responsewriter_linux.go @@ -84,18 +84,19 @@ func (rw *responseWriter) WriteHeader(code int) { } } -func (rw *responseWriter) Finish() { +func (rw *responseWriter) Finish() error { if !rw.wroteHeader { rw.WriteHeader(http.StatusNotImplemented) } if rw.finished { - return + return nil } rw.finished = true if rw.w != nil { rw.w.Close() } + return nil } func (rw *responseWriter) finishWithError(code int, err error) { diff --git a/v2/pkg/assetserver/webview/responsewriter_windows.go b/v2/pkg/assetserver/webview/responsewriter_windows.go new file mode 100644 index 00000000..748d9511 --- /dev/null +++ b/v2/pkg/assetserver/webview/responsewriter_windows.go @@ -0,0 +1,105 @@ +//go:build windows +// +build windows + +package webview + +import ( + "bytes" + "fmt" + "net/http" + "strings" +) + +var _ http.ResponseWriter = &responseWriter{} + +type responseWriter struct { + req *request + + header http.Header + wroteHeader bool + code int + body *bytes.Buffer + + finished bool +} + +func (rw *responseWriter) Header() http.Header { + if rw.header == nil { + rw.header = http.Header{} + } + return rw.header +} + +func (rw *responseWriter) Write(buf []byte) (int, error) { + if rw.finished { + return 0, errResponseFinished + } + + rw.WriteHeader(http.StatusOK) + + return rw.body.Write(buf) +} + +func (rw *responseWriter) WriteHeader(code int) { + if rw.wroteHeader || rw.finished { + return + } + rw.wroteHeader = true + + if rw.body == nil { + rw.body = &bytes.Buffer{} + } + + rw.code = code +} + +func (rw *responseWriter) Finish() error { + if !rw.wroteHeader { + rw.WriteHeader(http.StatusNotImplemented) + } + + if rw.finished { + return nil + } + rw.finished = true + + var errs []error + + code := rw.code + if code == http.StatusNotModified { + // WebView2 has problems when a request returns a 304 status code and the WebView2 is going to hang for other + // requests including IPC calls. + errs = append(errs, fmt.Errorf("AssetServer returned 304 - StatusNotModified which are going to hang WebView2, changed code to 505 - StatusInternalServerError")) + code = http.StatusInternalServerError + } + + rw.req.invokeSync(func() { + resp := rw.req.response + + hdrs, err := resp.GetHeaders() + if err != nil { + errs = append(errs, fmt.Errorf("Resp.GetHeaders failed: %s", err)) + } else { + for k, v := range rw.header { + if err := hdrs.AppendHeader(k, strings.Join(v, ",")); err != nil { + errs = append(errs, fmt.Errorf("Resp.AppendHeader failed: %s", err)) + } + } + hdrs.Release() + } + + if err := resp.PutStatusCode(code); err != nil { + errs = append(errs, fmt.Errorf("Resp.PutStatusCode failed: %s", err)) + } + + if err := resp.PutByteContent(rw.body.Bytes()); err != nil { + errs = append(errs, fmt.Errorf("Resp.PutByteContent failed: %s", err)) + } + + if err := rw.req.finishResponse(); err != nil { + errs = append(errs, fmt.Errorf("Resp.finishResponse failed: %s", err)) + } + }) + + return combineErrs(errs) +} diff --git a/v2/pkg/commands/build/build.go b/v2/pkg/commands/build/build.go index 65d55aa9..a29840b1 100644 --- a/v2/pkg/commands/build/build.go +++ b/v2/pkg/commands/build/build.go @@ -348,8 +348,8 @@ func execBuildApplication(builder Builder, options *Options) (string, error) { } } - if options.Platform == "darwin" && options.Mode == Debug { - pterm.Warning.Println("A darwin debug build contains private APIs, please don't distribute this build. Please use it only as a test build for testing and debug purposes.") + if options.Platform == "darwin" && (options.Mode == Debug || options.Devtools) { + pterm.Warning.Println("This darwin build contains the use of private APIs. This will not pass Apple's AppStore approval process. Please use it only as a test build for testing and debug purposes.") } return options.CompiledBinary, nil diff --git a/v2/pkg/options/mac/mac.go b/v2/pkg/options/mac/mac.go index 033cfd1a..9838b145 100644 --- a/v2/pkg/options/mac/mac.go +++ b/v2/pkg/options/mac/mac.go @@ -20,6 +20,7 @@ type Options struct { Appearance AppearanceType WebviewIsTransparent bool WindowIsTranslucent bool + Preferences *Preferences //ActivationPolicy ActivationPolicy About *AboutInfo //URLHandlers map[string]func(string) diff --git a/v2/pkg/options/mac/preferences.go b/v2/pkg/options/mac/preferences.go new file mode 100644 index 00000000..2c690e7f --- /dev/null +++ b/v2/pkg/options/mac/preferences.go @@ -0,0 +1,16 @@ +package mac + +import "github.com/leaanthony/u" + +var Enabled = u.True +var Disabled = u.False + +// Preferences allows to set webkit preferences +type Preferences struct { + // A Boolean value that indicates whether pressing the tab key changes the focus to links and form controls. + // Set to false by default. + TabFocusesLinks u.Bool + // A Boolean value that indicates whether to allow people to select or otherwise interact with text. + // Set to true by default. + TextInteractionEnabled u.Bool +} diff --git a/v2/pkg/options/options.go b/v2/pkg/options/options.go index 2fbcbc8b..b64befe1 100644 --- a/v2/pkg/options/options.go +++ b/v2/pkg/options/options.go @@ -74,7 +74,7 @@ type App struct { CSSDragValue string // EnableDefaultContextMenu enables the browser's default context-menu in production - // This menu is already enabled in development, as well as in debug builds and production builds with the `-devtools` flag + // This menu is already enabled in development and debug builds EnableDefaultContextMenu bool // EnableFraudulentWebsiteDetection enables scan services for fraudulent content, such as malware or phishing attempts. diff --git a/v2/pkg/options/windows/windows.go b/v2/pkg/options/windows/windows.go index ea6fdc35..7adf0bf7 100644 --- a/v2/pkg/options/windows/windows.go +++ b/v2/pkg/options/windows/windows.go @@ -113,6 +113,9 @@ type Options struct { // // !! Please keep in mind when disabling this feature, this also allows malicious software to inject into the WebView2 !! WebviewDisableRendererCodeIntegrity bool + + // Configure whether swipe gestures should be enabled + EnableSwipeGestures bool } func DefaultMessages() *Messages { diff --git a/website/.gitattributes b/website/.gitattributes new file mode 100644 index 00000000..65ac44a9 --- /dev/null +++ b/website/.gitattributes @@ -0,0 +1,3 @@ +# Mark all files as documentation so it gets excluded from github language statistics +# https://github.com/github-linguist/linguist/blob/master/docs/overrides.md#documentation +** linguist-documentation diff --git a/website/docs/guides/crossplatform-build.mdx b/website/docs/guides/crossplatform-build.mdx new file mode 100644 index 00000000..a9afc616 --- /dev/null +++ b/website/docs/guides/crossplatform-build.mdx @@ -0,0 +1,65 @@ +# Crossplatform build with Github Actions + +To build a Wails project for all the available platforms, you need to create an application build for each operating system. One effective method to achieve this is by utilizing GitHub Actions. + +An action that facilitates building a Wails app is available at: +https://github.com/dAppServer/wails-build-action + +In case the existing action doesn't fulfill your requirements, you can select only the necessary steps from the source: +https://github.com/dAppServer/wails-build-action/blob/main/action.yml + +Below is a comprehensive example that demonstrates building an app upon the creation of a new Git tag and subsequently uploading it to the Actions artifacts: + +```yaml +name: Wails build + +on: + push: + tags: + # Match any new tag + - '*' + +env: + # Necessary for most environments as build failure can occur due to OOM issues + NODE_OPTIONS: "--max-old-space-size=4096" + +jobs: + build: + strategy: + # Failure in one platform build won't impact the others + fail-fast: false + matrix: + build: + - name: 'App' + platform: 'linux/amd64' + os: 'ubuntu-latest' + - name: 'App' + platform: 'windows/amd64' + os: 'windows-latest' + - name: 'App' + platform: 'darwin/universal' + os: 'macos-latest' + + runs-on: ${{ matrix.build.os }} + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Build wails + uses: dAppServer/wails-build-action@v2.2 + id: build + with: + build-name: ${{ matrix.build.name }} + build-platform: ${{ matrix.build.platform }} + package: false + go-version: '1.20' +``` + +This example offers opportunities for various enhancements, including: +- Caching dependencies +- Code signing +- Uploading to platforms like S3, Supbase, etc. +- Injecting secrets as environment variables +- Utilizing environment variables as build variables (such as version variable extracted from the current Git tag) diff --git a/website/docs/guides/troubleshooting.mdx b/website/docs/guides/troubleshooting.mdx index bd965a79..1ce18f2d 100644 --- a/website/docs/guides/troubleshooting.mdx +++ b/website/docs/guides/troubleshooting.mdx @@ -201,4 +201,15 @@ you can make the webview background transparent using the following config: WebviewIsTransparent: true, }, }) -``` \ No newline at end of file +``` + +## WebView2 installation succeeded, but the wails doctor command shows that it is not installed + +If you have installed WebView2, but the `wails doctor` command shows that it is not installed, it is likely that the +WebView2 runtime installed was for a different architecture. You can download the correct runtime from [here](https://developer.microsoft.com/en-us/microsoft-edge/webview2/). + +Source: https://github.com/wailsapp/wails/issues/2917 + +## WebVie2wProcess failed with kind + +If your Windows app generates this kind of error, you can check out what the error means [here](https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2processfailedkind?view=webview2-winrt-1.0.2045.28). diff --git a/website/docs/reference/cli.mdx b/website/docs/reference/cli.mdx index 9dcc25bc..bbfe5bea 100644 --- a/website/docs/reference/cli.mdx +++ b/website/docs/reference/cli.mdx @@ -52,35 +52,35 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for `wails build` is used for compiling your project to a production-ready binary. -| Flag | Description | Default | -|:---------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------| -| -clean | Cleans the `build/bin` directory | | -| -compiler "compiler" | Use a different go compiler to build, eg go1.15beta1 | go | -| -debug | Retains debug information in the application and shows the debug console. Allows the use of the devtools in the application window | | -| -devtools | Allows the use of the devtools in the application window in production (when -debug is not used) | | -| -dryrun | Prints the build command without executing it | | -| -f | Force build application | | -| -garbleargs | Arguments to pass to garble | `-literals -tiny -seed=random` | -| -ldflags "flags" | Additional ldflags to pass to the compiler | | -| -m | Skip mod tidy before compile | | -| -nopackage | Do not package application | | -| -nocolour | Disable colour in output | | -| -nosyncgomod | Do not sync go.mod with the Wails version | | -| -nsis | Generate NSIS installer for Windows | | -| -o filename | Output filename | | -| -obfuscated | Obfuscate the application using [garble](https://github.com/burrowers/garble) | | -| -platform | Build for the given (comma delimited) [platforms](../reference/cli.mdx#platforms) eg. `windows/arm64`. Note, if you do not give the architecture, `runtime.GOARCH` is used. | platform = `GOOS` environment variable if given else `runtime.GOOS`.
arch = `GOARCH` envrionment variable if given else `runtime.GOARCH`. | -| -race | Build with Go's race detector | | -| -s | Skip building the frontend | | -| -skipbindings | Skip bindings generation | | -| -tags "extra tags" | Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated | | -| -trimpath | Remove all file system paths from the resulting executable. | | -| -u | Updates your project's `go.mod` to use the same version of Wails as the CLI | | -| -upx | Compress final binary using "upx" | | -| -upxflags | Flags to pass to upx | | -| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 | -| -webview2 | WebView2 installer strategy: download,embed,browser,error | download | -| -windowsconsole | Keep the console window for Windows builds | | +| Flag | Description | Default | +|:---------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------| +| -clean | Cleans the `build/bin` directory | | +| -compiler "compiler" | Use a different go compiler to build, eg go1.15beta1 | go | +| -debug | Retains debug information in the application and shows the debug console. Allows the use of the devtools in the application window | | +| -devtools | Allows the use of the devtools in the application window in production (when -debug is not used). Ctrl/Cmd+Shift+F12 may be used to open the devtools window. *NOTE*: This option will make your application FAIL Mac appstore guidelines. Use for debugging only. | | +| -dryrun | Prints the build command without executing it | | +| -f | Force build application | | +| -garbleargs | Arguments to pass to garble | `-literals -tiny -seed=random` | +| -ldflags "flags" | Additional ldflags to pass to the compiler | | +| -m | Skip mod tidy before compile | | +| -nopackage | Do not package application | | +| -nocolour | Disable colour in output | | +| -nosyncgomod | Do not sync go.mod with the Wails version | | +| -nsis | Generate NSIS installer for Windows | | +| -o filename | Output filename | | +| -obfuscated | Obfuscate the application using [garble](https://github.com/burrowers/garble) | | +| -platform | Build for the given (comma delimited) [platforms](../reference/cli.mdx#platforms) eg. `windows/arm64`. Note, if you do not give the architecture, `runtime.GOARCH` is used. | platform = `GOOS` environment variable if given else `runtime.GOOS`.
arch = `GOARCH` envrionment variable if given else `runtime.GOARCH`. | +| -race | Build with Go's race detector | | +| -s | Skip building the frontend | | +| -skipbindings | Skip bindings generation | | +| -tags "extra tags" | Build tags to pass to Go compiler. Must be quoted. Space or comma (but not both) separated | | +| -trimpath | Remove all file system paths from the resulting executable. | | +| -u | Updates your project's `go.mod` to use the same version of Wails as the CLI | | +| -upx | Compress final binary using "upx" | | +| -upxflags | Flags to pass to upx | | +| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 | +| -webview2 | WebView2 installer strategy: download,embed,browser,error | download | +| -windowsconsole | Keep the console window for Windows builds | | For a detailed description of the `webview2` flag, please refer to the [Windows](../guides/windows.mdx) Guide. @@ -170,8 +170,8 @@ Your system is ready for Wails development! - A webserver is started on `http://localhost:34115` which serves your application (not just frontend) over http. This allows you to use your favourite browser development extensions - All application assets are loaded from disk. If they are changed, the application will automatically reload (not rebuild). All connected browsers will also reload - A JS module is generated that provides the following: - - JavaScript wrappers of your Go methods with autogenerated JSDoc, providing code hinting - - TypeScript versions of your Go structs, that can be constructed and passed to your go methods +- JavaScript wrappers of your Go methods with autogenerated JSDoc, providing code hinting +- TypeScript versions of your Go structs, that can be constructed and passed to your go methods - A second JS module is generated that provides a wrapper + TS declaration for the runtime - On macOS, it will bundle the application into a `.app` file and run it. It will use a `build/darwin/Info.dev.plist` for development. diff --git a/website/docs/reference/options.mdx b/website/docs/reference/options.mdx index af772c8b..67e89707 100644 --- a/website/docs/reference/options.mdx +++ b/website/docs/reference/options.mdx @@ -424,7 +424,7 @@ Type: `string` EnableDefaultContextMenu enables the browser's default context-menu in production. -By default, the browser's default context-menu is only available in development and in a `-debug` or `-devtools` [build](../reference/cli.mdx#build) along with the devtools inspector, Using this option you can enable the default context-menu in `production` while the devtools inspector won't be available unless the `-devtools` build flag is used. +By default, the browser's default context-menu is only available in development and in a `-debug` [build](../reference/cli.mdx#build) along with the devtools inspector, Using this option you can enable the default context-menu in `production` while the devtools inspector won't be available unless the `-devtools` build flag is used. When this option is enabled, by default the context-menu will only be shown for text contexts (where Cut/Copy/Paste is needed), to override this behavior, you can use the CSS property `--default-contextmenu` on any HTML element (including the `body`) with the following values : @@ -687,6 +687,13 @@ Setting this to `true` will disable GPU hardware acceleration for the webview. Name: WebviewGpuIsDisabled
Type: `bool` +#### EnableSwipeGestures + +Setting this to `true` will enable swipe gestures for the webview. + +Name: EnableSwipeGestures
+Type: `bool` + ### Mac This defines [Mac specific options](#mac). @@ -790,6 +797,40 @@ with [WebviewIsTransparent](#WebviewIsTransparent) to make frosty-looking applic Name: WindowIsTranslucent
Type: `bool` +#### Preferences + +The Preferences struct provides the ability to configure the Webview preferences. + +Name: Preferences
+Type: [`*mac.Preferences`](#preferences-struct) + +##### Preferences struct + +You can specify the webview preferences. + +```go +type Preferences struct { + TabFocusesLinks u.Bool + TextInteractionEnabled u.Bool +} +``` + +| Name | Description | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| TabFocusesLinks | A Boolean value that indicates whether pressing the tab key changes the focus to links and form controls. [Apple Docs](https://developer.apple.com/documentation/webkit/wkpreferences/2818595-tabfocuseslinks?language=objc) | +| TextInteractionEnabled | A Boolean value that indicates whether to allow people to select or otherwise interact with text. [Apple Docs](https://developer.apple.com/documentation/webkit/wkpreferences/3727362-textinteractionenabled?language=objc) | + +Example: + +```go +Mac: &mac.Options{ + Preferences: &mac.Preferences{ + TabFocusesLinks: mac.Enabled, + TextInteractionEnabled: mac.Disabled, + } +} +``` + #### About This configuration lets you set the title, message and icon for the "About" menu item in the app menu created by the "AppMenu" role. diff --git a/website/src/pages/changelog.mdx b/website/src/pages/changelog.mdx index d1ed1b51..d49cf6d3 100644 --- a/website/src/pages/changelog.mdx +++ b/website/src/pages/changelog.mdx @@ -14,6 +14,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Added support for enabling/disabling swipe gestures for Windows WebView2. Added by @leaanthony in [PR](https://github.com/wailsapp/wails/pull/2878) +- When building with `-devtools` flag, CMD/CTRL+SHIFT+F12 can be used to open the devtools. Added by @leaanthony in [PR](https://github.com/wailsapp/wails/pull/2915) +- Added support for setting some of the Webview preferences, `textInteractionEnabled` and `tabFocusesLinks` on Mac. Added by @fkhadra in [PR](https://github.com/wailsapp/wails/pull/2937) + +### Changed + +- AssetServer requests are now processed asynchronously without blocking the main thread on Windows. Changed by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2926) +- AssetServer requests are now processed concurrently by spawning a goroutine per request. Changed by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2926) +- Now building with `-devtools` flag doesn't enable the default context-menu. Changed by @mmghv in [PR](https://github.com/wailsapp/wails/pull/2923) +- Change Window Level. Changed by @almas1992 in [PR](https://github.com/wailsapp/wails/pull/2944) #### Fixed - Fixed typo on docs/reference/options page. Added by [@pylotlight](https://github.com/pylotlight) in [PR](https://github.com/wailsapp/wails/pull/2887) @@ -32,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed `SetBackgroundColour` so it sets the window's background color to reduce resize flickering on Linux. Added by @lyimmi in [PR](https://github.com/wailsapp/wails/pull/2853) - Fixed disable window resize option and wrong initial window size when its enabled. Added by @lyimmi in [PR](https://github.com/wailsapp/wails/pull/2863) - Fixed build hook command parsing. Added by @smac89 in [PR](https://github.com/wailsapp/wails/pull/2836) +- Fixed filesystem watcher from filtering top level project directory if binary name is included in .gitignore. Added by [@haukened](https://github.com/haukened) in [PR #2869](https://github.com/wailsapp/wails/pull/2869) - Fixed `-reloaddir` flag to watch additional directories (non-recursively). [@haukened](https://github.com/haukened) in [PR #2871](https://github.com/wailsapp/wails/pull/2871) - Fixed support for Go 1.21 `go.mod` files. Fixed by @stffabi in [PR](https://github.com/wailsapp/wails/pull/2876) @@ -49,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added new community template wails-sveltekit-tailwind. Added by [@pylotlight](https://github.com/pylotlight) in [PR](https://github.com/wailsapp/wails/pull/2851) - Added support for print dialogs. Added by [@aangelisc](https://github.com/aangelisc) in [PR](https://github.com/wailsapp/wails/pull/2822) - Added new `wails dev -nogorebuild` flag to prevent restarts on back end file changes. [@haukened](https://github.com/haukened) in [PR #2870](https://github.com/wailsapp/wails/pull/2870) +- Added a guide to describe a cross-platform build using GitHub Actions. Added by [@dennypenta](https://github.com/dennypenta) in [PR](https://github.com/wailsapp/wails/pull/2879) ### Changed @@ -716,9 +730,6 @@ Experimental: &options.Experimental{ - [v2, nsis] Seems like / as path separator works only for some directives in a cross platform way by [@stffabi](https://github.com/stffabi) in #1227 - import models on binding definition by [@adalessa](https://github.com/adalessa) in #123 - - 1 - - Use local search on website by [@leaanthony](https://github.com/leaanthony) in #1234 - Ensure binary resources can be served by [@napalu](https://github.com/napalu) in #1240 - Only retry loading assets when loading from disk by [@leaanthony](https://github.com/leaanthony) in #1241 diff --git a/website/static/img/sponsors.svg b/website/static/img/sponsors.svg index 0eee13aa..bee039e5 100644 --- a/website/static/img/sponsors.svg +++ b/website/static/img/sponsors.svg @@ -1,5 +1,5 @@ - + diff --git a/website/versioned_docs/version-v2.6.0/community/templates.mdx b/website/versioned_docs/version-v2.6.0/community/templates.mdx index 6ff8344d..e1afd456 100644 --- a/website/versioned_docs/version-v2.6.0/community/templates.mdx +++ b/website/versioned_docs/version-v2.6.0/community/templates.mdx @@ -42,6 +42,7 @@ If you are unsure about a template, inspect `package.json` and `wails.json` for - [wails-template-nextjs](https://github.com/LGiki/wails-template-nextjs) - A template using Next.js and TypeScript - [wails-vite-react-ts-tailwind-template](https://github.com/hotafrika/wails-vite-react-ts-tailwind-template) - A template for React + TypeScript + Vite + TailwindCSS - [wails-vite-react-ts-tailwind-shadcnui-template](https://github.com/Mahcks/wails-vite-react-tailwind-shadcnui-ts) - A template with Vite, React, TypeScript, TailwindCSS, and shadcn/ui +- [wails-vite-react](https://github.com/vixalie/wails-vite-react) - A minimal template for React, using Vite for assets building ## Svelte