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

V3 alpha feature/file association port #3873

Merged
merged 10 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 29 additions & 0 deletions mkdocs-website/docs/en/API/application_dialogs.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,33 @@

!!! warning "MacOS Dialogs and Application Lifecycle"

If you show dialogs during application startup or file open events, you should set `ApplicationShouldTerminateAfterLastWindowClosed` to `false` to prevent the application from terminating when those dialogs close. Otherwise, the application may quit before your main window appears.

```go
app := application.New(application.Options{
Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: false,
},
// ... rest of options
})
```

Alternatively, you can show startup dialogs after the main window has been displayed:

```go
var filename string
app.OnApplicationEvent(events.Common.ApplicationOpenedWithFile, func(event *application.ApplicationEvent) {
filename = event.Context().Filename()
})

window.OnWindowEvent(events.Common.WindowShow, func(event *application.WindowEvent) {
application.InfoDialog().
SetTitle("File Opened").
SetMessage("Application opened with file: " + filename).
Show()
})
```

### ShowAboutDialog

API: `ShowAboutDialog()`
Expand Down
1 change: 1 addition & 0 deletions mkdocs-website/docs/en/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Templates for sveltekit and sveltekit-ts that are set for non-SSR development by [atterpac](https://github.com/atterpac) in [#3829](https://github.com/wailsapp/wails/pull/3829)
- Update build assets using new `wails3 update build-assets` command by [leaanthony](https://github.com/leaanthony).
- Example to test the HTML Drag and Drop API by [FerroO2000](https://github.com/FerroO2000) in [#3856](https://github.com/wailsapp/wails/pull/3856)
- File Association support by [leaanthony](https://github.com/leaanthony) in [#3873](https://github.com/wailsapp/wails/pull/3873)
- New `wails3 generate runtime` command by [leaanthony](https://github.com/leaanthony)

### Changed
Expand Down
139 changes: 139 additions & 0 deletions mkdocs-website/docs/en/learn/guides/file-associations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# File Associations

File associations allow your application to handle specific file types when users open them. This is particularly useful for text editors, image viewers, or any application that works with specific file formats. This guide explains how to implement file associations in your Wails v3 application.

## Overview

File association support in Wails v3 is currently available for:

- Windows (NSIS installer packages)
- macOS (application bundles)

## Configuration

File associations are configured in the `config.yml` file located in your project's `build` directory.

### Basic Configuration

To set up file associations:

1. Open `build/config.yml`
2. Add your file associations under the `fileAssociations` section
3. Run `wails3 update build-assets` to update the build assets
4. Set the `FileAssociations` field in the application options
5. Package your application using `wails3 package`

Here's an example configuration:

```yaml
fileAssociations:
- ext: myapp
name: MyApp Document
description: MyApp Document File
iconName: myappFileIcon
role: Editor
- ext: custom
name: Custom Format
description: Custom File Format
iconName: customFileIcon
role: Editor
```
### Configuration Properties
| Property | Description | Platform |
|-------------|------------------------------------------------------------------|----------|
| ext | File extension without the leading period (e.g., `txt`) | All |
| name | Display name for the file type | All |
| description | Description shown in file properties | Windows |
| iconName | Name of the icon file (without extension) in the build folder | All |
| role | Application's role for this file type (e.g., `Editor`, `Viewer`) | macOS |

## Listening for File Open Events

To handle file open events in your application, you can listen for the `events.Common.ApplicationOpenedWithFile` event:

```go
func main() {
app := application.New(application.Options{
Name: "MyApp",
FileAssociations: []string{".txt", ".md"}, // Specify supported extensions
})
// Listen for files being used to open the application
app.OnApplicationEvent(events.Common.ApplicationOpenedWithFile, func(event *application.ApplicationEvent) {
associatedFile := event.Context().Filename()
application.InfoDialog().SetMessage("Application opened with file: " + associatedFile).Show()
})
// Create your window and run the app...
}
```
Comment on lines +56 to +72
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix code formatting and enhance the example.

  1. Replace hard tabs with spaces for consistent formatting.
  2. Consider these improvements to the code example:
 func main() {
     app := application.New(application.Options{
-        Name: "MyApp", 
-        FileAssociations: []string{".txt", ".md"}, // Specify supported extensions
+        Name: "MyApp",
+        // Define supported extensions as constants for better maintainability
+        FileAssociations: []string{
+            ".txt",
+            ".md",
+        },
     }) 
 
-    // Listen for files being used to open the application
+    // Handle files used to open the application
     app.OnApplicationEvent(events.Common.ApplicationOpenedWithFile, func(event *application.ApplicationEvent) {
         associatedFile := event.Context().Filename()
-        application.InfoDialog().SetMessage("Application opened with file: " + associatedFile).Show()
+        // Add error handling for file operations
+        if err := handleFile(associatedFile); err != nil {
+            application.ErrorDialog().
+                SetTitle("Error").
+                SetMessage("Failed to open file: " + err.Error()).
+                Show()
+        }
     }) 
 
     // Create your window and run the app...
 }

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Markdownlint

59-59: Column: 1
Hard tabs

(MD010, no-hard-tabs)


60-60: Column: 1
Hard tabs

(MD010, no-hard-tabs)


62-62: Column: 1
Hard tabs

(MD010, no-hard-tabs)


63-63: Column: 1
Hard tabs

(MD010, no-hard-tabs)


64-64: Column: 1
Hard tabs

(MD010, no-hard-tabs)


66-66: Column: 1
Hard tabs

(MD010, no-hard-tabs)


67-67: Column: 1
Hard tabs

(MD010, no-hard-tabs)


68-68: Column: 1
Hard tabs

(MD010, no-hard-tabs)


69-69: Column: 1
Hard tabs

(MD010, no-hard-tabs)

## Step-by-Step Tutorial

Let's walk through setting up file associations for a simple text editor:

### 1. Create Icons

- Create icons for your file type (recommended sizes: 16x16, 32x32, 48x48, 256x256)
- Save the icons in your project's `build` folder
- Name them according to your `iconName` configuration (e.g., `textFileIcon.png`)

!!! tip
You can use `wails3 generate icons` to generate the required icons for you. Run `wails3 generate icons --help` for more information.

### 2. Configure File Associations

Edit the `build/config.yml` file to add your file associations:

```yaml
# build/config.yml
fileAssociations:
- ext: txt
name: Text Document
description: Plain Text Document
iconName: textFileIcon
role: Editor
```

### 3. Update Build Assets

Run the following command to update the build assets:

```bash
wails3 update build-assets
```

### 4. Set File Associations in the Application Options

In your `main.go` file, set the `FileAssociations` field in the application options:

```go
app := application.New(application.Options{
Name: "MyApp",
FileAssociations: []string{".txt", ".md"}, // Specify supported extensions
})
```

??? question "Why do the file extensions need to be set in the application config when it's set in `config.yml`?"

On Windows, when a file is opened with a file association, the application is launched with the filename as the first argument to the application.
The application has no way of knowing if the first argument is a file or a command line argument, so it uses the `FileAssociations` field in the application options to determine if the first argument is an associated file or not.

### 5. Package Your Application

Package your application using the following command:

```bash
wails3 package
```

The packaged application will be created in the `bin` directory. You can then install and test the application.

## Additional Notes

- Icons should be provided in PNG format in the build folder
- Testing file associations requires installing the packaged application


Comment on lines +1 to +139
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Based on the verification results, I can now provide a final response:

Documentation accurately reflects implementation with minor clarifications needed

The documentation is mostly accurate, but a few clarifications would improve it:

  • The FileAssociation struct in the codebase matches the documented configuration properties exactly, including ext, name, description, iconName, and role fields.
  • The event handling for ApplicationOpenedWithFile is correctly implemented across platforms (verified in application_{darwin,windows}.go).
  • The example in the documentation matches the actual implementation pattern seen in the examples/file-association/main.go.

However, two minor points should be clarified:

  • The documentation should mention that the fileAssociations configuration is supported in both v2 and v3 (found in both version paths)
  • The role field values (Editor, Viewer) should be documented with their exact string values since they appear to be free-form strings rather than enforced constants
🔗 Analysis chain

Verify documentation against implementation.

Let's verify that the documented features match the implementation:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the documented features match the implementation

# Test 1: Verify the ApplicationOpenedWithFile event exists
echo "Verifying ApplicationOpenedWithFile event..."
rg -l "ApplicationOpenedWithFile" --type go

# Test 2: Verify FileAssociations field in application options
echo "Verifying FileAssociations option..."
rg -l "FileAssociations.*\[\]string" --type go

# Test 3: Check for platform-specific file association code
echo "Checking platform-specific implementations..."
rg -l "fileAssociations" --glob "**/*_{darwin,windows}.go"

Length of output: 657


Script:

#!/bin/bash
# Let's verify the implementation details more thoroughly

# Check the actual event definition
echo "=== Checking event definition ==="
rg -A 2 "ApplicationOpenedWithFile.*=" v3/pkg/events/events.go

# Check the FileAssociations option structure
echo "=== Checking FileAssociations option structure ==="
rg -A 5 "type.*Options.*struct" v3/pkg/application/application_options.go

# Check the example implementation
echo "=== Checking example implementation ==="
cat v3/examples/file-association/main.go

# Check platform-specific handlers
echo "=== Checking Darwin implementation ==="
rg -A 5 "ApplicationOpenedWithFile" v3/pkg/application/application_darwin.go

echo "=== Checking Windows implementation ==="
rg -A 5 "ApplicationOpenedWithFile" v3/pkg/application/application_windows.go

# Check if there's any config.yml parsing logic
echo "=== Checking config.yml parsing ==="
rg -l "fileAssociations" --type go

Length of output: 5722


Script:

#!/bin/bash
# Let's check the build-assets implementation to verify config.yml parsing

# Check the build-assets implementation for file associations
echo "=== Checking build-assets implementation ==="
rg -B 2 -A 10 "fileAssociations" v3/internal/commands/build-assets.go

# Check the config structure
echo "=== Checking config structure ==="
ast-grep --pattern 'type $_ struct {
  $$$
  FileAssociations $_ `$_`
  $$$
}'

# Check for any yaml tag definitions related to file associations
echo "=== Checking YAML definitions ==="
rg -B 2 -A 2 '`yaml:"fileAssociations"`' --type go

# Check for any additional file association related code
echo "=== Checking file association related code ==="
rg -l "role.*Editor|role.*Viewer" --type go

Length of output: 2266


Script:

#!/bin/bash
# Let's check the FileAssociation type definition and implementation

# Check the FileAssociation struct definition
echo "=== Checking FileAssociation struct ==="
rg -B 2 -A 6 "type FileAssociation struct" --type go

# Check for role constants or type definitions
echo "=== Checking role definitions ==="
rg -B 2 -A 2 "type.*Role.*string" --type go

# Check the actual implementation of file association handling
echo "=== Checking file association handling in build process ==="
rg -B 2 -A 10 "func.*BuildAssets" v3/internal/commands/build-assets.go

# Check for any platform-specific file association implementation
echo "=== Checking platform implementations ==="
fd -e go -x rg -l "FileAssociation" {}

Length of output: 2380

🧰 Tools
🪛 Markdownlint

79-79: Expected: 0; Actual: 3
Unordered list indentation

(MD007, ul-indent)


80-80: Expected: 0; Actual: 3
Unordered list indentation

(MD007, ul-indent)


81-81: Expected: 0; Actual: 3
Unordered list indentation

(MD007, ul-indent)


59-59: Column: 1
Hard tabs

(MD010, no-hard-tabs)


60-60: Column: 1
Hard tabs

(MD010, no-hard-tabs)


62-62: Column: 1
Hard tabs

(MD010, no-hard-tabs)


63-63: Column: 1
Hard tabs

(MD010, no-hard-tabs)


64-64: Column: 1
Hard tabs

(MD010, no-hard-tabs)


66-66: Column: 1
Hard tabs

(MD010, no-hard-tabs)


67-67: Column: 1
Hard tabs

(MD010, no-hard-tabs)


68-68: Column: 1
Hard tabs

(MD010, no-hard-tabs)


69-69: Column: 1
Hard tabs

(MD010, no-hard-tabs)


114-114: Column: 1
Hard tabs

(MD010, no-hard-tabs)


115-115: Column: 1
Hard tabs

(MD010, no-hard-tabs)


116-116: Column: 1
Hard tabs

(MD010, no-hard-tabs)


121-121: Expected: fenced; Actual: indented
Code block style

(MD046, code-block-style)

7 changes: 5 additions & 2 deletions mkdocs-website/docs/en/status.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Roadmap

## Current Status: Alpha 6
## Current Status: Alpha 7

Our goal is to reach Beta status. This roadmap outlines the key features and improvements we need to implement before transitioning to Beta. Please note that this is a living document and may be updated as priorities shift or new insights emerge.

Expand All @@ -20,5 +20,8 @@ The roadmap is a living document and is subject to change. If you have any
suggestions, please open an issue. Each milestone will have a set of goals that
we are aiming to achieve. These are subject to change.

## Known Issues
## Alpha 8 Status

- In Progress: Add support for File Associations
- In Progress: Drag and Drop support for Linux

1 change: 1 addition & 0 deletions mkdocs-website/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ nav:
- Build System: learn/build.md
- Guides:
- Customising Windows: learn/guides/customising-windows.md
- File Associations: learn/guides/file-associations.md
- Feedback: getting-started/feedback.md
- Feedback: getting-started/feedback.md
- What's New in v3?: whats-new.md
Expand Down
3 changes: 3 additions & 0 deletions v3/examples/file-association/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.task
bin
wails.syso
93 changes: 93 additions & 0 deletions v3/examples/file-association/Inter Font License.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
11 changes: 11 additions & 0 deletions v3/examples/file-association/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# File Association Sample Project

This sample project demonstrates how to associate a file type with an application.
More info at: https://v3alpha.wails.io/learn/guides/file-associations/

To run the sample, follow these steps:

1. Run `wails3 package` to generate the package.
2. On Windows, run the installer that was built in the `bin` directory.
3. Double-click on the `test.wails` file to open it with the application.
4. On macOS, double-click on the `test.wails` file and select the built application.
33 changes: 33 additions & 0 deletions v3/examples/file-association/Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
version: '3'

includes:
common: ./build/Taskfile.common.yml
windows: ./build/Taskfile.windows.yml
darwin: ./build/Taskfile.darwin.yml
linux: ./build/Taskfile.linux.yml

vars:
APP_NAME: "fileassoc"
BIN_DIR: "bin"
VITE_PORT: '{{.WAILS_VITE_PORT | default 9245}}'

tasks:
build:
summary: Builds the application
cmds:
- task: "{{OS}}:build"

package:
summary: Packages a production build of the application
cmds:
- task: "{{OS}}:package"

run:
summary: Runs the application
cmds:
- task: "{{OS}}:run"

dev:
summary: Runs the application in development mode
cmds:
- wails3 dev -config ./build/devmode.config.yaml -port {{.VITE_PORT}}
32 changes: 32 additions & 0 deletions v3/examples/file-association/build/Info.dev.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>My Product</string>
<key>CFBundleExecutable</key>
<string>fileassoc</string>
<key>CFBundleIdentifier</key>
<string></string>
<key>CFBundleVersion</key>
<string>0.1.0</string>
<key>CFBundleGetInfoString</key>
<string>This is a comment</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<key>CFBundleIconFile</key>
<string>icons</string>
<key>LSMinimumSystemVersion</key>
<string>10.13.0</string>
<key>NSHighResolutionCapable</key>
<string>true</string>
<key>NSHumanReadableCopyright</key>
<string>© now, My Company</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
</dict>
</plist>
Loading
Loading