Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement file association Open a file from Finder/Explorer #2918

Merged
merged 39 commits into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c40aee3
implement MacOS openFile/openFiles events
APshenkin Sep 13, 2023
7342a45
Merge branch 'master' into feature/mac-open-files
APshenkin Sep 13, 2023
4d461c1
wip: windows file association
APshenkin Sep 16, 2023
006c383
fix macro import
APshenkin Sep 16, 2023
77f7a67
add file icon copy
APshenkin Sep 16, 2023
54ea8bb
try copy icon
APshenkin Sep 16, 2023
8e0543a
keep only required part of scripts
APshenkin Sep 16, 2023
48d02bf
update config schema
APshenkin Sep 16, 2023
51d391f
fix json
APshenkin Sep 16, 2023
d5ccecc
Merge branch 'feature/mac-open-files' into feature/open-files
APshenkin Sep 16, 2023
329b9a5
set fileAssociation for mac via config
APshenkin Sep 16, 2023
2670bde
proper iconName handling
APshenkin Sep 16, 2023
82bc909
add fileAssociation icon generator
APshenkin Sep 16, 2023
2a836c8
fix file association icons bundle
APshenkin Sep 16, 2023
3b1c600
don't break compatibility
APshenkin Sep 16, 2023
410746c
Merge branch 'master' into feature/open-files
APshenkin Sep 17, 2023
c56c628
Merge branch 'master' into feature/open-files
APshenkin Sep 21, 2023
8f242ec
remove mimeType as not supported linux for now
APshenkin Sep 21, 2023
a1cba0d
Merge branch 'feature/open-files' of github.com:APshenkin/wails into …
APshenkin Sep 21, 2023
97553de
Merge branch 'master' into feature/open-files
APshenkin Sep 22, 2023
9326562
add documentation
APshenkin Sep 22, 2023
b6c0e94
Merge branch 'feature/open-files' of github.com:APshenkin/wails into …
APshenkin Sep 22, 2023
e695663
adjust config schema
APshenkin Sep 22, 2023
045aa05
restore formatting
APshenkin Sep 22, 2023
a9b12a5
Merge branch 'master' into feature/open-files
leaanthony Sep 25, 2023
ab1e0a9
remove unused option in file association
APshenkin Sep 25, 2023
e1f8be2
Merge branch 'feature/open-files' of github.com:APshenkin/wails into …
APshenkin Sep 25, 2023
d2abb67
Merge branch 'master' into feature/open-files
APshenkin Sep 26, 2023
4e3861a
Merge branch 'master' into feature/open-files
leaanthony Sep 26, 2023
faed07a
get rid of openFiles mac os. change configuration structure
APshenkin Sep 28, 2023
424484e
Merge branch 'feature/open-files' of github.com:APshenkin/wails into …
APshenkin Sep 28, 2023
d7077b9
remove unused channel
APshenkin Sep 28, 2023
cbb5a24
fix documentation
APshenkin Sep 29, 2023
e82ed4e
fix typo
APshenkin Sep 29, 2023
dc248b7
Merge branch 'master' into feature/open-files
APshenkin Sep 30, 2023
a6d9b3e
Merge branch 'master' into feature/open-files
APshenkin Oct 2, 2023
df709e5
Merge branch 'master' into feature/open-files
APshenkin Oct 5, 2023
430b12b
Merge branch 'master' into feature/open-files
APshenkin Oct 7, 2023
7c1a362
Merge branch 'master' into feature/open-files
leaanthony Oct 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion v2/internal/frontend/desktop/darwin/AppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#import <Cocoa/Cocoa.h>
#import "WailsContext.h"

@interface AppDelegate : NSResponder <NSTouchBarProvider>
@interface AppDelegate : NSResponder <NSApplicationDelegate, NSTouchBarProvider>

@property bool alwaysOnTop;
@property bool startHidden;
Expand All @@ -20,4 +20,6 @@

@end

extern void HandleOpenFile(char *);

#endif /* AppDelegate_h */
10 changes: 9 additions & 1 deletion v2/internal/frontend/desktop/darwin/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,17 @@
#import "AppDelegate.h"

@implementation AppDelegate
-(BOOL)application:(NSApplication *)sender openFile:(NSString *)filename
{
const char* utf8FileName = filename.UTF8String;
HandleOpenFile((char*)utf8FileName);
return YES;
}

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
return NO;
return NO;
}

- (void)applicationWillFinishLaunching:(NSNotification *)aNotification {
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
if (self.alwaysOnTop) {
Expand Down
21 changes: 21 additions & 0 deletions v2/internal/frontend/desktop/darwin/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const startURL = "wails://wails/"
var messageBuffer = make(chan string, 100)
var requestBuffer = make(chan webview.Request, 100)
var callbackBuffer = make(chan uint, 10)
var openFilepathBuffer = make(chan string, 100)

type Frontend struct {

Expand Down Expand Up @@ -107,15 +108,23 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.

go result.startMessageProcessor()
go result.startCallbackProcessor()
go result.startFileOpenProcessor()

return result
}

func (f *Frontend) startFileOpenProcessor() {
for filePath := range openFilepathBuffer {
f.ProcessOpenFileEvent(filePath)
}
}

func (f *Frontend) startMessageProcessor() {
for message := range messageBuffer {
f.processMessage(message)
}
}

func (f *Frontend) startRequestProcessor() {
for request := range requestBuffer {
f.assets.ServeWebViewRequest(request)
Expand Down Expand Up @@ -355,6 +364,12 @@ func (f *Frontend) processMessage(message string) {

}

func (f *Frontend) ProcessOpenFileEvent(filePath string) {
if f.frontendOptions.Mac != nil && f.frontendOptions.Mac.OnFileOpen != nil {
f.frontendOptions.Mac.OnFileOpen(filePath)
}
}

func (f *Frontend) Callback(message string) {
escaped, err := json.Marshal(message)
if err != nil {
Expand Down Expand Up @@ -398,3 +413,9 @@ func processCallback(callbackID uint) {
func processURLRequest(_ unsafe.Pointer, wkURLSchemeTask unsafe.Pointer) {
requestBuffer <- webview.NewRequest(wkURLSchemeTask)
}

//export HandleOpenFile
func HandleOpenFile(filePath *C.char) {
goFilepath := C.GoString(filePath)
openFilepathBuffer <- goFilepath
}
19 changes: 14 additions & 5 deletions v2/internal/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,20 @@ type Author struct {
}

type Info struct {
CompanyName string `json:"companyName"`
ProductName string `json:"productName"`
ProductVersion string `json:"productVersion"`
Copyright *string `json:"copyright"`
Comments *string `json:"comments"`
CompanyName string `json:"companyName"`
ProductName string `json:"productName"`
ProductVersion string `json:"productVersion"`
Copyright *string `json:"copyright"`
Comments *string `json:"comments"`
FileAssociations []FileAssociation `json:"fileAssociations"`
}

type FileAssociation struct {
Ext string `json:"ext"`
Name string `json:"name"`
Description string `json:"description"`
IconName string `json:"iconName"`
Role string `json:"role"`
}

type Bindings struct {
Expand Down
21 changes: 20 additions & 1 deletion v2/pkg/buildassets/build/darwin/Info.dev.plist
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,29 @@
<string>true</string>
<key>NSHumanReadableCopyright</key>
<string>{{.Info.Copyright}}</string>
{{if .Info.FileAssociations}}
<key>CFBundleDocumentTypes</key>
<array>
{{range .Info.FileAssociations}}
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>{{.Ext}}</string>
</array>
<key>CFBundleTypeName</key>
<string>{{.Name}}</string>
<key>CFBundleTypeRole</key>
<string>{{.Role}}</string>
<key>CFBundleTypeIconFile</key>
<string>{{.IconName}}</string>
</dict>
{{end}}
</array>
{{end}}
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
</dict>
</plist>
</plist>
21 changes: 20 additions & 1 deletion v2/pkg/buildassets/build/darwin/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,24 @@
<string>true</string>
<key>NSHumanReadableCopyright</key>
<string>{{.Info.Copyright}}</string>
{{if .Info.FileAssociations}}
<key>CFBundleDocumentTypes</key>
<array>
{{range .Info.FileAssociations}}
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>{{.Ext}}</string>
</array>
<key>CFBundleTypeName</key>
<string>{{.Name}}</string>
<key>CFBundleTypeRole</key>
<string>{{.Role}}</string>
<key>CFBundleTypeIconFile</key>
<string>{{.IconName}}</string>
</dict>
{{end}}
</array>
{{end}}
</dict>
</plist>
</plist>
16 changes: 10 additions & 6 deletions v2/pkg/buildassets/build/windows/installer/project.nsi
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ Unicode true
####
## Please note: Template replacements don't work in this file. They are provided with default defines like
## mentioned underneath.
## If the keyword is not defined, "wails_tools.nsh" will populate them with the values from ProjectInfo.
## If they are defined here, "wails_tools.nsh" will not touch them. This allows to use this project.nsi manually
## If the keyword is not defined, "wails_tools.nsh" will populate them with the values from ProjectInfo.
## If they are defined here, "wails_tools.nsh" will not touch them. This allows to use this project.nsi manually
## from outside of Wails for debugging and development of the installer.
##
##
## For development first make a wails nsis build to populate the "wails_tools.nsh":
## > wails build --target windows/amd64 --nsis
## Then you can call makensis on this file with specifying the path to your binary:
Expand All @@ -17,7 +17,7 @@ Unicode true
## For a installer with both architectures:
## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app-amd64.exe -DARG_WAILS_ARM64_BINARY=..\..\bin\app-arm64.exe
####
## The following information is taken from the ProjectInfo file, but they can be overwritten here.
## The following information is taken from the ProjectInfo file, but they can be overwritten here.
####
## !define INFO_PROJECTNAME "MyProject" # Default "{{.Name}}"
## !define INFO_COMPANYNAME "MyCompany" # Default "{{.Info.CompanyName}}"
Expand Down Expand Up @@ -85,16 +85,18 @@ Section
!insertmacro wails.webview2runtime

SetOutPath $INSTDIR

!insertmacro wails.files

CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"

!insertmacro wails.associateFiles

!insertmacro wails.writeUninstaller
SectionEnd

Section "uninstall"
Section "uninstall"
!insertmacro wails.setShellContext

RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath
Expand All @@ -104,5 +106,7 @@ Section "uninstall"
Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk"
Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk"

!insertmacro wails.unassociateFiles

!insertmacro wails.deleteUninstaller
SectionEnd
49 changes: 45 additions & 4 deletions v2/pkg/buildassets/build/windows/installer/wails_tools.nsh
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,58 @@ RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
Goto ok
${EndIf}
${EndIf}

SetDetailsPrint both
DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}"
SetDetailsPrint listonly

InitPluginsDir
CreateDirectory "$pluginsdir\webview2bootstrapper"
SetOutPath "$pluginsdir\webview2bootstrapper"
File "tmp\MicrosoftEdgeWebview2Setup.exe"
ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install'

SetDetailsPrint both
ok:
!macroend
!macroend

# Copy of APP_ASSOCIATE and APP_UNASSOCIATE macros from here https://gist.github.com/nikku/281d0ef126dbc215dd58bfd5b3a5cd5b
!macro APP_ASSOCIATE EXT FILECLASS DESCRIPTION ICON COMMANDTEXT COMMAND
; Backup the previously associated file class
ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" ""
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "${FILECLASS}_backup" "$R0"

WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "${FILECLASS}"

WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}" "" `${DESCRIPTION}`
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\DefaultIcon" "" `${ICON}`
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell" "" "open"
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open" "" `${COMMANDTEXT}`
WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open\command" "" `${COMMAND}`
!macroend

!macro APP_UNASSOCIATE EXT FILECLASS
; Backup the previously associated file class
ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" `${FILECLASS}_backup`
WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "$R0"

DeleteRegKey SHELL_CONTEXT `Software\Classes\${FILECLASS}`
!macroend

!macro wails.associateFiles
; Create file associations
{{range .Info.FileAssociations}}
!insertmacro APP_ASSOCIATE "{{.Ext}}" "{{.Name}}" "{{.Description}}" "$INSTDIR\{{.IconName}}.ico" "Open with ${INFO_PRODUCTNAME}" "$INSTDIR\${PRODUCT_EXECUTABLE} $\"%1$\""

File "..\{{.IconName}}.ico"
{{end}}
!macroend

!macro wails.unassociateFiles
; Delete app associations
{{range .Info.FileAssociations}}
!insertmacro APP_UNASSOCIATE "{{.Ext}}" "{{.Name}}"

Delete "$INSTDIR\{{.IconName}}.ico"
{{end}}
!macroend
46 changes: 36 additions & 10 deletions v2/pkg/commands/build/packager.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/leaanthony/winicon"
"github.com/tc-hib/winres"
"github.com/tc-hib/winres/version"
"github.com/wailsapp/wails/v2/internal/project"
"image"
"os"
"path/filepath"
Expand Down Expand Up @@ -95,12 +96,20 @@ func packageApplicationForDarwin(options *Options) error {
return err
}

// Generate Icons
err = processApplicationIcon(options, resourceDir)
// Generate App Icon
err = processDarwinIcon(options.ProjectData, "appicon", resourceDir, "iconfile")
if err != nil {
return err
}

// Generate FileAssociation Icons
for _, fileAssociation := range options.ProjectData.Info.FileAssociations {
err = processDarwinIcon(options.ProjectData, fileAssociation.IconName, resourceDir, "")
if err != nil {
return err
}
}

options.CompiledBinary = packedBinaryPath

return nil
Expand All @@ -124,8 +133,8 @@ func processPList(options *Options, contentsDirectory string) error {
return os.WriteFile(targetFile, content, 0644)
}

func processApplicationIcon(options *Options, resourceDir string) (err error) {
appIcon, err := buildassets.ReadFile(options.ProjectData, "appicon.png")
func processDarwinIcon(projectData *project.Project, iconName string, resourceDir string, destIconName string) (err error) {
appIcon, err := buildassets.ReadFile(projectData, iconName+".png")
if err != nil {
return err
}
Expand All @@ -135,7 +144,11 @@ func processApplicationIcon(options *Options, resourceDir string) (err error) {
return err
}

tgtBundle := filepath.Join(resourceDir, "iconfile.icns")
if destIconName == "" {
destIconName = iconName
}

tgtBundle := filepath.Join(resourceDir, destIconName+".icns")
dest, err := os.Create(tgtBundle)
if err != nil {
return err
Expand All @@ -151,13 +164,21 @@ func processApplicationIcon(options *Options, resourceDir string) (err error) {
}

func packageApplicationForWindows(options *Options) error {
// Generate icon
// Generate app icon
var err error
err = generateIcoFile(options)
err = generateIcoFile(options, "appicon", "icon")
if err != nil {
return err
}

// Generate FileAssociation Icons
for _, fileAssociation := range options.ProjectData.Info.FileAssociations {
err = generateIcoFile(options, fileAssociation.IconName, "")
if err != nil {
return err
}
}

// Create syso file
err = compileResources(options)
if err != nil {
Expand All @@ -171,13 +192,18 @@ func packageApplicationForLinux(_ *Options) error {
return nil
}

func generateIcoFile(options *Options) error {
content, err := buildassets.ReadFile(options.ProjectData, "appicon.png")
func generateIcoFile(options *Options, iconName string, destIconName string) error {
content, err := buildassets.ReadFile(options.ProjectData, iconName+".png")
if err != nil {
return err
}

if destIconName == "" {
destIconName = iconName
}

// Check ico file exists already
icoFile := buildassets.GetLocalPath(options.ProjectData, "windows/icon.ico")
icoFile := buildassets.GetLocalPath(options.ProjectData, "windows/"+destIconName+".ico")
if !fs.FileExists(icoFile) {
if dir := filepath.Dir(icoFile); !fs.DirExists(dir) {
if err := fs.MkDirs(dir, 0755); err != nil {
Expand Down
Loading